Avansert Python-utviklingskurs
Kapittel
>
Nivå
Serialiseringsmoduler
Struct-modul
Mål
Sett opp den endelige datainndelingen for det nye jordbruksområdet ved hjelp av struct-modulen.
I enden av veien ligger en servicestasjon som administrerer det nye jordbruksområdet som allerede er bygd og der avlingene allerede er plantet. Her skal vi inspisere og behandle data for avlingene som allerede er plantet og det forventede utbyttet av jordbruksområdet. Slik som i de andre nivåene i dette kapitlet skal vi jobbe med å serialisere og deserialisere data, og introdusere en siste modul kalt struct-modulen.
Struct-modulen introduserer en rekke serialiseringsfunksjoner som pakker data i binært format. I motsetning til de andre modulene vi har jobbet med, får du imidlertid større kontroll over hvordan vi kan strukturere dataene både når de er serialiserte og senere deserialiserte, noe som gjør modulen mer allsidig enn de andre modulene. Bruk import struct for å få tilgang til følgende funksjoner som vi skal bruke for å behandle dataene:
struct.calcsize(): Bestemmer hvor mange bytes som pakkes for en gitt formatstreng. Den tar ett (1) argument, som er formatet du vil kontrollere størrelsen på. Formatene vi skal bruke er:- integer: representert som
'i'er formatet for data som er heltall - float: representert som
'f'er formatet for data med desimaltall - double: representert som
'd'er formatet for data med mer komplekse desimaltall, der float-formatet er utilstrekkelig
- integer: representert som
struct.pack(): Serialiserer data i binærform, pakket i et format du velger. Den kan ta to (2) eller flere argumenter, der det første er formatet du vil bruke, og de neste er verdiene du vil serialisere. Formatene er de som er beskrevet tidligere, og du må legge dem til i samsvar med antall argumenter du gir.struct.unpack(): Deserialiserer pakket binærdata. Tar to (2) argumenter: formatet som dataene ble pakket med, og dataene som skal deserialiseres.struct.iter_unpack(): Deserialiserer pakket binærdata. Fungerer på samme måte somstruct.unpack(), men går gjennom hver datablokk individuelt ved hjelp av en løkke.struct.pack_into(): En avansert versjon avstruct.pack(). Tar fire (4) argumenter: formatet du vil bruke, databufferet du vil plassere dataene i, posisjonen i bufferet der dataene skal plasseres, og endelig selve dataene du pakker.struct.unpack_from(): En avansert versjon avstruct.unpack(). Tar tre (3) argumenter: formatet du vil bruke, databufferet du vil pakke ut data fra, og posisjonen i bufferet der du vil pakke ut. Dette lar deg pakke ut spesifikke deler av dataene.
Gå til lysmerket (X) i servicestasjonen og vend deg mot pulten. Opprett tre (3) variabler kalt integer, float og double. Vi skal bruke disse for å verifisere størrelsen i bytes på hvert format ved å bruke funksjonen struct.calcsize(). For variabelen integer bruker du funksjonen med 'i' som argument, for float bruker du 'f', og til slutt for double bruker du 'd' som argument. For eksempel: integer = struct.calcsize('i'). Legg deretter de tre (3) variablene til i den forhåndsskrevne write()-funksjonen.
Gå til gullmerket (X) og bruk read()-funksjonen for å hente data om det nye jordbruksområdet. Noter deg datapunktene og formatet for fremtidig bruk, nemlig: Resources, Size og Estimate. Når du har notert dette, gå til lysmerket (X) over det blå teppet og opprett en variabel kalt blue_data.
For variabelen blue_data lagrer du verdien fra struct.pack()-funksjonen, med formatet og verdiene du noterte som argumenter. Når du skriver formatet, må du “pakke” formattypene inn i én enkelt enhet. For eksempel er heltallsformatet merket 'i' som forklart tidligere; om du legger til tre heltallstyper, skriver du 'iii'. På samme måte, hvis du legger til et heltall, en float og en double, skrives det 'ifd'. Når du legger til dataene, plasserer du dem enkeltvis som argumenter. Her er et eksempel:
data_1 = 8 # is an integer data_2 = 2.25 # is a float data_3 = 900.702938103 # is a double blue_data = struct.pack('ifd', data_1, data_2, data_3)
Funksjonen struct.pack() gir stor fleksibilitet, slik at du selv kan bestemme hvordan du vil formatere dataene, og du kan serialisere flere datapunkter samtidig etter eget ønske. Legg til de tidligere leste dataene ved å bruke eksemplet ovenfor som mal. Bruk display()-funksjonen med blue_data som argument for å vise de pakkede dataene.
Gå til det mørke merket (X) over det blå teppet og vend deg mot terminalen. Opprett en variabel kalt blue_unpack og lagre verdien fra struct.unpack()-funksjonen, der du bruker det samme formatet som du brukte for å pakke dataene, og variabelen blue_data som argumenter. Formatet skrives på samme måte som i struct.pack(), slik at du kan deserialisere dataene på samme måte som du først serialiserte dem. Bruk write()-funksjonen med blue_unpack som argument for å verifisere dataene du pakket tidligere.
På samme sted vil vi også bruke funksjonen struct.iter_unpack(). Den tar de nøyaktig samme argumentene som struct.unpack(), men er nå formatert som en for-løkke, noe som gjør at vi kan iterere gjennom dataene i stedet for bare å skrive dem ut. Funksjonen kodes slik:
for values in struct.iter_unpack(-insert value-, -insert value-): player.speak(values)
Vi vil bruke funksjonen speak() for å vise verdiene. For de manglende argumentene i struct.iter_unpack()-funksjonen, legg til de samme som du brukte i struct.unpack(), ettersom dette er variasjoner av samme funksjon med hensyn til bruk.
Gå til gullmerket (X) på det røde teppet og vend deg mot pulten. Bruk read()-funksjonen for å gjennomgå mengdene avlinger. Noter alle mengdene og formatet for hver avling. Gå deretter til lysmerket (X) på det røde teppet og opprett et objekt kalt buffer, og gi det verdien bytearray(16). Dette er en samling av bytes som vi kan adressere før vi fyller det, og for våre formål fungerer det som en datalagringsbank der du kan lagre dataene manuelt. Tallet 16 som argument er lengden på hvor mange bytes du kan lagre i objektet buffer.
Bruk funksjonen struct.pack_into() for å fylle objektet buffer du opprettet. Du trenger ikke å lage en variabel for å lagre funksjonsverdien, da funksjonen selv setter inn verdiene direkte i objektet buffer. Vi skal bruke funksjonen for hver av verdiene vi noterte tidligere, og fylle inn argumentene med dem.
For eksempel, for den første avlingsverdien som oppgis, får du oppgitt formatet, byte-posisjonen og mengden. Legg til formatet som det første argumentet, databufferet som skal fylles – i dette tilfellet buffer. For det tredje argumentet angir du posisjonen i buffer der du vil legge inn verdien. Formatet bestemmer hvor mange bytes som brukes, så pass på å pakke data som ikke allerede er opptatt. Til slutt legger du til verdien du vil serialisere og pakke inn i buffer-objektet.
struct.pack_into('i', buffer, 0, 82)
Objektet buffer har 16 bytes som nevnt tidligere. I koden som er gitt ovenfor, bruker heltallsformatet 4 bytes og settes inn ved posisjon 0. Dette betyr at når dataene er lagt inn i buffer, er det kun 12 bytes igjen som ikke er opptatt, med posisjonene 0–3 okkupert av den angitte verdien på 82. Gjør dette for alle de tre (3) avlingsdatapunktene du leste og noterte. Bruk display()-funksjonen med buffer som argument for å vise de serialiserte dataene.
Gå til det mørke merket (X) på det røde teppet og vend deg mot terminalen. Her skal vi deserialisere dataene for å verifisere innholdet og sikre riktig lagring. Opprett tre variabler kalt lettuce, carrots og melons; vi skal pakke ut hvert datapunkt individuelt. For hver variabel lagrer du verdien fra struct.unpack_from() og bruker de samme dataene du noterte da du pakket dataene, som argumenter. For det første argumentet setter du formatet, for det andre legger du til buffer-objektet som angir hvor dataene skal pakkes ut fra, og til slutt angir du posisjonen i buffer hvor du vil pakke ut dataene. For eksempel:
lettuce = struct.unpack_from('i', buffer, 0)
Disse dataene tilsvarer eksemplet på pakking som nå blir pakket ut. Gjør det samme for de to andre variablene, og sett inn lettuce, carrots og melons i den forhåndsskrevne write()-funksjonen for å fullføre nivået.