Avansert Pythonutviklingskurs
Kapittel
>
Nivå
Serialiseringsmoduler
Struct-modul
Målsetning
Sett opp den endelige datafordelingen for den nye gården ved hjelp av struct-modulen.
Ved slutten av veien ligger en servicestasjon som administrerer den nye gården som allerede er bygget og avlingene som allerede er plantet. Her skal vi inspisere og behandle data for de allerede plantede avlingene samt den forventede utbyttet fra gården. Som med andre nivåer i dette kapitlet skal vi arbeide med serialisering og deserialisering av data, og introdusere en siste modul, nemlig 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, gir den deg større kontroll over hvordan vi kan strukturere data både når de er serialisert og senere deserialisert, noe som gjør den mer allsidig enn andre moduler. Bruk import struct for å få tilgang til følgende funksjoner som vi skal bruke for å behandle data:
struct.calcsize(): Bestemmer hvor mange bytes som kreves for en gitt formatstreng. Den tar ett (1) argument, nemlig formatet du ønsker å verifisere byte-størrelsen til. Formatene vi skal bruke er:- heltall: representert som
'i'er formatet for data gitt som hele tall - flyttall: representert som
'f'er formatet for data gitt som desimaltall - dobbelt: representert som
'd'er formatet for data gitt som mer komplekse desimaltall der flytformatet ikke er tilstrekkelig.
- heltall: representert som
struct.pack(): Serialiserer data til binært format, pakket i et format du selv velger. Du kan oppgi to (2) eller flere argumenter, der det første er formatet du ønsker å bruke og de påfølgende verdiene du vil serialisere. Formatene er de som er beskrevet tidligere, og du må legge til et format for hvert argument du legger til.struct.unpack(): Deserialiserer pakket binærdata. Den tar to (2) argumenter: formatet som dataene skal samsvare med (dvs. det formatet de ble serialisert med), og dataene som skal deserialiseres.struct.iter_unpack(): Deserialiserer pakket binærdata, og fungerer på samme måte somstruct.unpack(), men itererer gjennom hver datablokk individuelt ved hjelp av en løkke.struct.pack_into(): En avansert versjon avstruct.pack(), tar fire (4) argumenter: formatet du ønsker å bruke, databufferen du vil legge dataene i, posisjonen i bufferen der du ønsker å plassere dataene, og til slutt dataene du pakker.struct.unpack_from(): En avansert versjon avstruct.unpack(), tar tre (3) argumenter: formatet du ønsker å bruke, databufferen du vil pakke ut fra, og til slutt posisjonen i bufferen du ønsker å pakke ut fra. Dette lar deg hente ut bestemte deler av dataene.
Gå til det lyse X-merket ved servicestasjonen og vend deg mot skranken. Opprett tre (3) variabler med navnene: integer, float og double. Vi skal bruke disse for å verifisere størrelsen i bytes for hvert format ved hjelp av funksjonen struct.calcsize(). For variabelen integer skal du bruke funksjonen med 'i' som argument, for variabelen float bruker du funksjonen med 'f' som argument, og til slutt for variabelen double bruker du funksjonen med 'd' som argument. For eksempel: integer = struct.calcsize('i'). Legg til de tre (3) variablene i den forhåndsskrevne write()-funksjonen.
Gå til det gullfargede X-merket og bruk read()-funksjonen for å samle data om den nye gården; noter deg datapunktene og formatet for fremtidig bruk, nemlig: Resources, Size og Estimate. Når du har notert dette, gå til det lyse X-merket over det blå teppet og opprett en variabel med navn blue_data.
Med variabelen blue_data lagrer du verdien fra funksjonen struct.pack(), der du setter inn formatet og verdiene du tidligere har notert som argumenter. Når du skriver formatet, må du "pakke" formattypene sammen til en enkelt enhet. For eksempel, for heltallsformatet som er angitt med 'i' slik det ble beskrevet tidligere, hvis du skal legge til tre heltallsdatatyper, skriver du det som 'iii'. På samme måte, hvis du skal legge til et heltall, et flyttall og et dobbelt, skrives det som 'ifd'. Når du legger til dataene, oppgir du dem individuelt som separate argumenter. Her er et samlet 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, og lar deg bestemme hvordan du vil formatere dataene samtidig som du serialiserer flere datapunkter om gangen, etter eget skjønn. Legg til de tidligere innhentede dataene ved å bruke eksemplet over som utgangspunkt. Bruk funksjonen display() med blue_data som argument for å vise de pakkede dataene.
Gå til det mørke X-merket over det blå teppet og vend deg mot terminalen. Opprett en variabel med navnet blue_unpack og lagre verdien fra funksjonen struct.unpack(), der du oppgir det samme formatet som ble brukt til å pakke dataene og variabelen blue_data som argumenter. Formatet skrives på samme måte som i struct.pack()-funksjonen, slik at du kan deserialisere dataene på samme måte som de først ble serialisert. Bruk funksjonen write() med blue_unpack som argument for å verifisere dataene du tidligere pakket.
På samme sted skal vi også bruke struct.iter_unpack()-funksjonen. Den tar nøyaktig de samme argumentene som struct.unpack(), men nå er den formatert som en for-løkke, noe som lar oss iterere gjennom dataene i stedet for å skrive dem alle ut. Funksjonen kodes som følger:
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 tidligere brukte med struct.unpack(), ettersom disse er varianter av den samme funksjonen med hensyn til bruken.
Gå til det gullfargede X-merket over det røde teppet og vend deg mot skranken. Bruk read()-funksjonen for å gjennomgå avlingsmengdene. Noter alle mengdene og formatet for hver avling. Gå deretter til det lyse X-merket over det røde teppet og opprett et objekt med navnet buffer, og tilordne det verdien bytearray(16). Dette er en samling bytes som vi kan adressere før vi fyller den, og for vårt formål fungerer den som en databank der du kan lagre dataene manuelt. Tallet 16 som argument angir hvor mange bytes som kan lagres i buffer-objektet.
Bruk funksjonen struct.pack_into() for å fylle buffer-objektet du opprettet. Det er ikke nødvendig å opprette en variabel for å lagre verdien fra funksjonen, ettersom funksjonen selv setter inn verdiene direkte i buffer-objektet. Vi skal bruke funksjonen for hver av de verdiene vi tidligere noterte, og fylle ut de respektive argumentene med disse.
Eksempelvis, for den første avlingsverdien vil du få oppgitt formatet, byteposisjonen og mengden. Legg til formatet som det første argumentet, plasseringen du ønsker å serialisere bytes til (i dette tilfellet buffer). For det tredje argumentet angir du posisjonen der du vil legge inn verdien i buffer; formatet bestemmer antall bytes som benyttes, så vær forsiktig med å pakke data i områder som ikke allerede er opptatt. Til slutt legger du til verdien du ønsker å serialisere og pakke inn i buffer-objektet.
struct.pack_into('i', buffer, 0, 82)
buffer-objektet har 16 bytes som tidligere angitt. I koden ovenfor har heltallsformatet 4 bytes og settes inn ved posisjon 0. Dette betyr at når dataene er satt inn i buffer, er det kun 12 bytes ledig, der posisjon 0–3 er opptatt av den oppgitte verdien, i dette tilfellet 82. Gjør dette for alle de avlingsdatapunktene du tidligere har lest og notert, totalt tre (3). Bruk funksjonen display() og legg til buffer for å vise de serialiserte dataene.
Gå til det mørke X-merket over det røde teppet og vend deg mot terminalen. Her skal vi deserialisere dataene for å verifisere innholdet og sikre riktig lagring. Opprett tre variabler med navnene: lettuce, carrots og melons. Vi skal pakke ut dataene for hvert enkelt datapunkt individuelt. For hver variabel lagrer du verdien fra struct.unpack_from() og oppgir de samme dataene du noterte da du pakket dataene. For det første argumentet angir du formatet, for det andre legger du til buffer-objektet du vil pakke ut fra, og til slutt angir du posisjonen i buffer du ønsker å starte utpakking fra. For eksempel:
lettuce = struct.unpack_from('i', buffer, 0)
Disse dataene tilsvarer det forrige pakkeeksempelet som nå pakkes ut. Gjør det samme for de to andre variablene, og sett lettuce, carrots og melons inn i den forhåndsskrevne write()-funksjonen for å fullføre nivået.