Corso avanzato di sviluppo Python
Capitolo
>
Livello

Moduli di Serializzazione
Modulo Struct

Obiettivo

Configura la ripartizione finale dei dati per la nuova terra coltivabile usando il modulo struct.

Alla fine della strada c’è una stazione di servizio che amministra la nuova terra coltivabile già costruita e i raccolti già piantati. Qui esamineremo e processeremo i dati dei raccolti già piantati e la produzione prevista della terra coltivabile. Come in altri livelli di questo capitolo lavoreremo con la serializzazione e la deserializzazione dei dati, introducendo un ultimo modulo chiamato struct.

Il modulo struct introduce una serie di funzioni di serializzazione che impacchettano i dati in formato binario; a differenza degli altri moduli con cui abbiamo lavorato, però, offre maggiore controllo su come possiamo strutturare i dati sia durante la serializzazione sia in fase di deserializzazione, rendendolo più versatile. Usa import struct per accedere alle seguenti funzioni che utilizzeremo per processare i dati:

  • struct.calcsize(): Determina quanti byte occupa una data stringa di formato; accetta un argomento (1) che è il formato di cui vuoi verificare la dimensione in byte. I formati che useremo sono:
    • integer: rappresentato da 'i', è il formato per dati rappresentati in numeri interi
    • float: rappresentato da 'f', è il formato per dati rappresentati in numeri decimali
    • double: rappresentato da 'd', è il formato per dati rappresentati in numeri decimali più complessi, dove il formato float non è sufficiente.
  • struct.pack(): Serializza dati in binario, impacchettandoli in un formato a tua scelta. Può accettare due (2) o più argomenti: il formato da usare e i valori da serializzare. I formati sono quelli illustrati in precedenza e devi aggiungerli in corrispondenza del numero di argomenti.
  • struct.unpack(): Deserializza dati binari impacchettati; accetta due (2) argomenti: il formato in cui i dati devono essere interpretati (deve corrispondere a quello usato in pack) e i dati serializzati.
  • struct.iter_unpack(): Deserializza dati binari impacchettati; funziona come struct.unpack() ma itera su ogni blocco di dati singolarmente mediante un ciclo.
  • struct.pack_into(): Una versione avanzata di struct.pack(); prende quattro (4) argomenti: il formato da usare, il buffer di dati in cui inserire i dati, la posizione nel buffer in cui posizionarli e infine i dati da impacchettare.
  • struct.unpack_from(): Una versione avanzata di struct.unpack(); prende tre (3) argomenti: il formato da usare, il buffer di dati da cui estrarre i dati e infine la posizione nel buffer da cui iniziare l’estrazione. Ciò permette di deserializzare porzioni specifiche dei dati.

Vai al segno X luminoso nella stazione di servizio e di fronte alla scrivania, crea tre (3) variabili di nome: integer, float e double. Le useremo per verificare la dimensione in byte di ciascun formato usando la funzione struct.calcsize(). Per la variabile integer usa la funzione con 'i' come argomento, per la variabile float usa la funzione con 'f' come argomento e infine per la variabile double usa la funzione con 'd' come argomento. Per esempio: integer = struct.calcsize('i'). Aggiungi le tre (3) variabili alla funzione pre-scritta write().

Vai al segno X dorato e usa la funzione read() per raccogliere i dati sulla nuova terra coltivabile, annota i punti dati e il formato da usare in futuro, cioè: Resources, Size e Estimate. Una volta annotati, vai al segno X luminoso sopra il tappeto blu e crea una variabile chiamata blue_data.

Con la variabile blue_data memorizza il valore restituito da struct.pack(), impostando come argomenti il formato e i valori che hai annotato in precedenza. Quando scrivi il formato, devi “impacchettare” i tipi di formato in un’unica stringa. Per esempio, il formato integer è etichettato come 'i' come dettagliato in precedenza; se aggiungi tre valori interi, li specifichi come 'iii'. Allo stesso modo, se stai aggiungendo un intero, un float e un double, si scrive 'ifd'. Quando inserisci i dati, li elenchi singolarmente come argomenti. Ecco un esempio:

data_1 = 8 # è un intero data_2 = 2.25 # è un float data_3 = 900.702938103 # è un double blue_data = struct.pack('ifd', data_1, data_2, data_3)

La funzione struct.pack() offre molta flessibilità, permettendoti di scegliere come formattare i dati e di serializzare più punti dati contemporaneamente, a tua discrezione. Aggiungi i dati letti in precedenza usando l’esempio sopra come base. Usa la funzione display() con blue_data come argomento per visualizzare i dati impacchettati.

Vai al segno X scuro sopra il tappeto blu e di fronte al terminale, crea una variabile chiamata blue_unpack e memorizza il valore restituito da struct.unpack(), utilizzando lo stesso formato usato per pack e la variabile blue_data come argomenti. Il formato si scrive nello stesso modo di struct.pack() in modo da deserializzare i dati nello stesso formato in cui sono stati serializzati. Usa la funzione write() con blue_unpack come argomento per verificare i dati che hai impacchettato in precedenza.

Nello stesso punto useremo anche la funzione struct.iter_unpack(), che prende gli stessi argomenti di struct.unpack() ma è formattata come un ciclo for, permettendoci di iterare sui dati anziché scriverli tutti in una volta. La funzione è definita come segue:

for values in struct.iter_unpack(-insert value-, -insert value-): player.speak(values)

Useremo la funzione speak() per visualizzare i valori; per gli argomenti mancanti in struct.iter_unpack() inserisci gli stessi usati in precedenza con struct.unpack(), poiché sono varianti della stessa funzione in termini di utilizzo.

Vai al segno X dorato sopra il tappeto rosso e di fronte alla scrivania usa la funzione read() per esaminare le quantità dei raccolti. Annota tutte le quantità e il formato di ciascun raccolto. Vai al segno X luminoso sopra il tappeto rosso e crea un oggetto chiamato buffer, assegnandogli il valore bytearray(16). Si tratta di una raccolta di byte a cui possiamo accedere prima di popolarla; ai nostri fini funziona come una banca dati in cui memorizzare manualmente i dati. Il numero 16 come argomento indica quanti byte può contenere l’oggetto buffer.

Usa la funzione struct.pack_into() per popolare l’oggetto buffer che hai creato. Non è necessario creare una variabile per memorizzare il valore della funzione, poiché essa inserirà direttamente i valori nell’oggetto buffer. Useremo la funzione per ciascuno dei valori annotati in precedenza, impostando opportunamente gli argomenti.

Per esempio, per il primo valore del raccolto fornito, ti sarà indicato il formato, la posizione di byte e la quantità. Aggiungi il formato come primo argomento, il buffer in cui serializzare i dati come secondo argomento (buffer in questo caso), la posizione in cui aggiungere il valore all’interno del buffer come terzo argomento (il formato determina quanti byte vengono occupati, quindi fai attenzione a non sovrascrivere dati già presenti) e infine il valore da serializzare e impacchettare nell’oggetto buffer.

struct.pack_into('i', buffer, 0, 82)

L’oggetto buffer ha 16 byte come detto in precedenza; nel codice sopra il formato integer occupa 4 byte ed è inserito nella posizione 0. Ciò significa che, una volta inserito il dato nel buffer, rimangono solo 12 byte liberi, con le posizioni 0-3 occupate dal valore fornito, in questo caso 82. Procedi allo stesso modo per tutti i punti dati dei raccolti annotati in precedenza, sono in totale tre (3). Usa la funzione display() con buffer come argomento per visualizzare i dati serializzati.

Vai al segno X scuro sopra il tappeto rosso e di fronte al terminale, qui deserializzeremo i dati per verificarne il contenuto e assicurarci che siano stati memorizzati correttamente. Crea tre variabili di nome: lettuce, carrots e melons; estrarremo i dati di ciascun punto uno per uno. Per ogni variabile memorizza il valore restituito da struct.unpack_from() impostando come argomenti gli stessi dati usati per il packing. Per il primo argomento usa il formato, per il secondo il buffer da cui estrarre i dati (buffer) e infine la posizione nel buffer da cui iniziare l’estrazione. Per esempio:

lettuce = struct.unpack_from('i', buffer, 0)

Questi dati corrispondono all’esempio di packing precedente che stiamo deserializzando; ripeti lo stesso procedimento per le altre due variabili e inserisci lettuce, carrots e melons nella funzione pre-scritta write() per completare il livello.

Libro del Codice