Curso Avanzado de Desarrollo en Python
Capítulo
>
Nivel
Módulos de serialización
Módulo Struct
Objetivo
Configurar el desglose final de datos para la nueva tierra agrícola utilizando el módulo struct.
Al final del camino hay una estación de servicio que administra la nueva tierra agrícola que ya está construida y los cultivos ya plantados. Aquí inspeccionaremos y procesaremos los datos de los cultivos ya plantados y la producción proyectada de la tierra agrícola. Al igual que en otros niveles de este capítulo, trabajaremos con la serialización y deserialización de datos, introduciendo un último módulo llamado struct.
El módulo struct introduce una serie de funciones de serialización que empaquetan datos en formato binario, a diferencia de los otros módulos con los que hemos trabajado; sin embargo, tienes mayor control sobre cómo podemos estructurar los datos tanto al serializarlos como al deserializarlos posteriormente, lo que lo hace más versátil que otros módulos. Usa import struct para acceder a las siguientes funciones que utilizaremos para procesar los datos:
struct.calcsize(): Determina cuántos bytes ocupa un formato de cadena dado, toma un (1) argumento que es el formato cuyo tamaño en bytes deseas verificar. Los formatos que usaremos son:- entero: representado como
'i'es el formato para datos representados en números enteros - flotante: representado como
'f'es el formato para datos representados en números decimales - doble: representado como
'd'es el formato para datos representados en números decimales más complejos cuando el formato flotante es insuficiente.
- entero: representado como
struct.pack(): Serializa datos en formato binario, empaquetándolos en un formato de tu elección. Puedes tomar dos (2) o más argumentos, que son el formato que deseas usar y los valores que te gustaría serializar. Los formatos son los mencionados anteriormente y debes agregarlos correspondiendo a la cantidad de argumentos que añadas.struct.unpack(): Deserializa datos binarios empaquetados, toma dos (2) argumentos, el formato con el que debe compararse al formato en el que se serializó, y el segundo es el dato que se serializó.struct.iter_unpack(): Deserializa datos binarios empaquetados, funciona de la misma manera questruct.unpack()pero itera a través de cada bloque de datos individualmente mediante el uso de un bucle.struct.pack_into(): Una versión avanzada destruct.pack(), toma cuatro (4) argumentos que serían el formato que te gustaría usar, el búfer de datos en el que deseas colocar los datos, la posición en el búfer que deseas que ocupe el dato, y finalmente el valor que estás empaquetando.struct.unpack_from(): Una versión avanzada destruct.unpack(), toma tres (3) argumentos: el formato que deseas usar, el búfer de datos desde el que deseas desempacar y, finalmente, la ubicación en el búfer desde la cual deseas desempacar. Esto te permite desempacar porciones específicas de los datos.
Camina hacia la marca X de luz en la estación de servicio y enfréntate al mostrador, crea tres (3) variables llamadas: integer, float y double. Usaremos estas para verificar el tamaño en bytes de cada formato utilizando la función struct.calcsize(). Para la variable integer utiliza la función con 'i' como argumento, para la variable float utiliza la función con 'f' como argumento y, por último, para la variable double utiliza la función con 'd' como argumento. Por ejemplo: integer = struct.calcsize('i'). Agrega las tres (3) variables a la función write() ya preescrita.
Camina hacia la marca X dorada y utiliza la función read() para recopilar datos sobre la nueva tierra agrícola, toma nota de los puntos de datos y el formato para su uso futuro, a saber: Resources, Size y Estimate. Una vez que hayas tomado nota de esto, camina hacia la marca X de luz sobre la alfombra azul y crea una variable llamada blue_data.
Con la variable blue_data almacena el valor de la función struct.pack(), configurando como argumentos el formato y los valores que anotaste anteriormente. Al escribir el formato, debes “empaquetar” los tipos de formato en una sola unidad. Por ejemplo, el formato de entero se etiqueta como 'i' como se detalló previamente; si vas a agregar tres tipos de datos enteros, los añades como 'iii'. De la misma manera, si estás añadiendo un entero, un flotante y un doble, se escribiría como 'ifd'. Al agregar los datos, los colocas individualmente como argumentos. Aquí tienes un ejemplo general:
data_1 = 8 # es un entero data_2 = 2.25 # es un flotante data_3 = 900.702938103 # es un doble blue_data = struct.pack('ifd', data_1, data_2, data_3)
La función struct.pack() permite mucha flexibilidad, permitiéndote establecer cómo te gustaría formatear los datos y permitiéndote serializar múltiples puntos de datos a la vez, a tu discreción. Agrega los datos leídos previamente usando el ejemplo anterior como base. Usa la función display() con blue_data como argumento para ver los datos empaquetados.
Camina hacia la marca X oscura sobre la alfombra azul y enfréntate al terminal, crea una variable denominada blue_unpack y almacena el valor de la función struct.unpack(), añadiendo el mismo formato utilizado para empaquetar los datos y la variable blue_data como argumentos. El formato se escribe de la misma manera que la función struct.pack(), de esta forma puedes deserializar los datos de la misma manera en que se serializaron inicialmente. Usa la función write() con blue_unpack como argumento para verificar los datos que empaquetaste previamente.
En la misma ubicación también utilizaremos la función struct.iter_unpack(), la cual toma los mismos argumentos exactos que struct.unpack(), pero ahora está formateada como un bucle for, lo que nos permite iterar sobre los datos en lugar de simplemente escribirlos todos de una vez. La función se codifica de la siguiente manera:
for values in struct.iter_unpack(-insert value-, -insert value-): player.speak(values)
Utilizaremos la función speak() para mostrar los valores, para los argumentos faltantes en la función struct.iter_unpack(), añade los mismos que se usaron previamente con struct.unpack() ya que son variaciones de la misma función, en cuanto a uso.
Camina hacia la marca X dorada sobre la alfombra roja y enfréntate al mostrador, utiliza la función read() para revisar las cantidades de cultivo. Toma nota de todas las cantidades y el formato para cada cultivo. Camina hacia la marca X de luz sobre la alfombra roja y crea un objeto llamado buffer, y asigna el valor de bytearray(16). Esto es una colección de bytes a la que podemos acceder antes de poblarla; para nuestros propósitos funciona como un banco de datos donde puedes almacenar la información manualmente. El número 16 como argumento es la longitud de cuántos bytes puedes almacenar en el objeto buffer.
Utiliza la función struct.pack_into() para poblar el objeto buffer que creaste. No es necesario crear una variable para almacenar el valor de la función ya que la función insertará los valores directamente en el objeto buffer. Vamos a utilizar la función para cada uno de los valores que anotaste previamente y poblar sus argumentos con ellos.
Por ejemplo, para el primer valor de cultivo proporcionado, se te dará su formato, su posición en bytes y su cantidad. Agrega el formato para el primer argumento, la ubicación a la que deseas serializar los bytes, en este caso es buffer. Para el tercer argumento, establece la posición en la que te gustaría agregar el valor dentro del buffer; el formato determina los bytes que se utilizan, así que asegúrate de empaquetar datos que no ocupen espacio previamente.
struct.pack_into('i', buffer, 0, 82)
El objeto buffer tiene 16 bytes como se indicó anteriormente. En el código proporcionado anteriormente, el formato entero tiene 4 bytes y se inserta en la posición 0. Esto significa que, una vez que se inserta el dato en el buffer, solo quedan 12 bytes libres en el buffer, con la posición 0-3 ocupadas por el valor proporcionado, en este caso, 82. Haz esto para todos los puntos de datos del cultivo que se leyeron y anotaron previamente; hay un total de tres (3). Usa la función display() y añade buffer para mostrar los datos serializados.
Camina hacia la marca X oscura sobre la alfombra roja y enfréntate al terminal, aquí deserializaremos los datos para verificar el contenido y asegurar un almacenamiento correcto. Crea tres variables llamadas: lettuce, carrots y melons; desempacaremos los datos de cada uno de los puntos de datos individualmente. Para cada variable, almacena el valor de struct.unpack_from() y establece como argumentos utilizando los mismos datos que anotaste para empaquetar los datos. Para el primer argumento establece el formato, el segundo añade el objeto buffer que es la ubicación desde la cual desempacar, y por último añade la posición en el buffer desde donde deseas desempacar. Por ejemplo:
lettuce = struct.unpack_from('i', buffer, 0)
Estos datos corresponden al ejemplo de empaquetado previamente desempacado; haz lo mismo para las otras dos variables e inserta lettuce, carrots y melons en la función write() ya preescrita para completar el nivel.