Haladó Python Fejlesztői Tanfolyam
Fejezet
>
Szint

Szerializációs modulok
Struct modul

Célkitűzés

Állítsd be az új szántóföld végső adatelosztását a struct modul használatával.

Az út végén van egy szolgáltatóállomás, amely az újonnan kiépített szántóföldet és már ültetett terményeket kezeli. Itt megvizsgáljuk és feldolgozzuk a már ültetett termények adatait, valamint a szántóföld várható hozamát. A fejezet eddigi szintjeihez hasonlóan most is adatokat szerializálunk és deszerializálunk, ezúttal a struct modul segítségével.

A struct modul számos sorosítási függvényt kínál, amelyek az adatokat bináris formátumban csomagolják. Az eddig ismert modulokkal ellentétben itt azonban nagyobb kontrollunk van az adatok szerializálásakor és későbbi deszerializálásakor is, így rugalmasabban alakíthatjuk az adatstruktúrát. Használd az import struct utasítást a következő, az adatok feldolgozásához szükséges függvények eléréséhez:

  • struct.calcsize(): Megállapítja, hogy a megadott formátumú karakterlánc hány bájtot foglal; egy (1) argumentumot vár, amely a formátum. A következő formátumokat fogjuk használni:
    • integer: 'i' formátum egész számok ábrázolására
    • float: 'f' formátum tizedes törtek ábrázolására
    • double: 'd' formátum bonyolultabb tizedes törtek ábrázolására, ahol a float nem elegendő
  • struct.pack(): Bináris formátumban szerializál (csomagol) adatokat a választott formátum szerint. Két (2) vagy több argumentumot vesz fel: az első a használni kívánt formátum, a továbbiak a szerializálandó értékek. A formátumok azok, amelyeket a fentiekben részleteztünk, és annyiszor kell őket megadni, ahány értéket csomagolunk.
  • struct.unpack(): Bináris formátumban csomagolt adatokat deszerializál (kinyit), két (2) argumentumot vár: az első a formátum, amelyhez a csomagoláskor használt formátumnak egyeznie kell, a második maga a szerializált adat.
  • struct.iter_unpack(): Szintén bináris formátumban csomagolt adatok deszerializálására szolgál, ugyanúgy működik, mint a struct.unpack(), de egy ciklussal egyesével bejárja az adategységeket.
  • struct.pack_into(): A struct.pack() egy fejlettebb változata, négy (4) argumentumot vár: a használni kívánt formátumot, a szóban forgó adatbuffert, amelybe az adatokat helyezzük, a buffer azon pozícióját, ahol az adatok legyenek elhelyezve, és végül a csomagolandó adatok magukat.
  • struct.unpack_from(): A struct.unpack() fejlettebb változata, három (3) argumentumot vár: a használni kívánt formátumot, a deszerializálandó adatbuffert, és végül a buffer azon helyét, ahonnan az adatokat ki szeretnénk csomagolni. Ez lehetővé teszi, hogy az adatok csak egy részét olvassuk ki.

Sétálj el a szolgáltatóállomáson található világító X-jelig, és fordulj az íróasztal felé! Hozz létre három (3) változót integer, float és double névvel. Ezek segítségével ellenőrizzük majd az egyes formátumok által foglalt bájtméretet a struct.calcsize() függvény használatával. Az integer változóhoz hívd meg a függvényt 'i' argumentummal, a float változóhoz 'f'-fel, végül a double változóhoz 'd'-vel. Például: integer = struct.calcsize('i'). Add hozzá mindhárom változót az előre megírt write() függvényhíváshoz.

Sétálj az arany X-jelig, és használd a read() függvényt, hogy begyűjtsd az új szántófölddel kapcsolatos adatokat; jegyezd fel az adatpontokat és azok formátumát a későbbi felhasználás érdekében, nevezetesen: Resources, Size és Estimate. Miután feljegyezted ezeket, lépj a kék szőnyegen lévő világító X-jelig, és hozz létre egy blue_data nevű változót.

A blue_data változóban tárold a struct.pack() függvény visszatérési értékét, az előzőleg feljegyzett formátum és értékek megadásával. A formátum megadásakor a formátumtípusokat egyetlen karakterláncba kell „összecsomagolni”. Például az egész számot jelölő formátum 'i'; ha három egész számot szeretnél csomagolni, használd a 'iii' formátumot. Hasonlóan, ha egy egész számot, egy floatot és egy double-t csomagolsz, a formátum 'ifd' lesz. Az értékeket egyenként add át argumentumként. Íme egy példa:

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)

A struct.pack() nagy rugalmasságot kínál: eldöntheted, hogyan formázod az adatokat, és egyszerre több adatpontot is szerializálhatsz. A korábban beolvasott adatokat használd fel a fenti példa alapján. A csomagolt adatok megjelenítéséhez hívd meg a display() függvényt blue_data-val.

Sétálj be a kék szőnyeg melletti sötét X jelzéshez, és fordulj a terminál felé. Hozz létre egy blue_unpack nevű változót, és tárold benne a struct.unpack() függvény visszatérési értékét, megadva argumentumként ugyanazt a formátumot, amivel csomagoltad az adatokat, valamint a blue_data változót. A formátum megadása megegyezik a struct.pack() használatakorival; így a deszerializálás ugyanúgy történik, ahogy a szerializálás. A korábban csomagolt adatok ellenőrzéséhez hívd meg a write() függvényt blue_unpack-kal.

Ugyanezen a helyen használjuk a struct.iter_unpack() függvényt is: ennek ugyanazok az argumentumai, mint a struct.unpack()-nak, de itt egy for ciklus segítségével járhatjuk be az adatokat, nemcsak egyszerre írjuk ki az egészet. A függvény használata a következő:

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

A speak() függvénnyel fogjuk kiírni az értékeket; a struct.iter_unpack() függvényhívás hiányzó argumentumaihoz add meg ugyanazokat, amelyeket korábban a struct.unpack()-nál használtál, hiszen használat szempontjából ugyanarról a függvénycsaládról van szó.

Sétálj az arany X jelzéshez a piros szőnyegen, és fordulj az asztal felé; használd a read() függvényt a terménymennyiségek áttekintéséhez. Jegyezd fel minden termény esetén a mennyiségeket és a formátumukat. Ezután lépj a piros szőnyegen lévő világító X jelzéshez, és hozz létre egy buffer nevű objektumot, amelynek értékéül bytearray(16)-ot adjuk meg. Ez egy bájtokból álló gyűjtemény, amelyet feltöltés előtt tetszőlegesen címezhetünk; számunkra egyfajta adatbankként funkcionál. A 16 argumentum azt jelzi, hogy a buffer objektum legfeljebb 16 bájtot tárolhat.

Használd a struct.pack_into() függvényt az előbb létrehozott buffer objektum feltöltéséhez. Ebben az esetben nem kell változót létrehozni az eredmény tárolására, mivel a függvény közvetlenül a buffer-be írja az adatokat. A korábban feljegyzett mindhárom érték esetén hívd meg a függvényt, és add át neki az adatokat a megfelelő argumentumokkal.

Például az első termény esetén megkapod a formátumát, a bájtpozíciót és a mennyiséget. Első argumentumként add meg a formátumot, másodiknak a célbuffer objektumot (buffer), harmadiknak pedig a buffer-en belüli pozíciót, ahová az adatot írni szeretnéd (a formátum határozza meg, hány bájtot foglal el, ezért ügyelj arra, hogy ne írj már foglalt mezőkre). Végül add meg a csomagolandó mennyiséget az argumentumok között:

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

A buffer objektum korábban említett 16 bájtos hosszúságú. A fenti példában az integer formátum 4 bájtot foglal, és a buffer elején (pozíció 0-tól 3-ig) kerül tárolásra. Ez azt jelenti, hogy a fennmaradó 12 bájt szabadon használható. Ismételd ezt meg a három (3) termény adatával, amelyeket korábban feljegyeztél. A szerializált adatok megtekintéséhez hívd meg a display() függvényt buffer-rel.

Sétálj a piros szőnyegen lévő sötét X jelhez, és fordulj a terminál felé. Itt deszerializáljuk az adatokat, hogy ellenőrizzük a tartalmat és biztosítsuk a helyes tárolást. Hozz létre három változót: lettuce, carrots és melons, és mindegyikhez rendeld hozzá a struct.unpack_from() eredményét, megadva első argumentumnak a formátumot, másodiknak a buffer objektumot, harmadiknak annak a pozíciónak a számát, ahonnan olvasni szeretnél. Példa:

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

Ez a csomagolási példa deszerializálásának felel meg. Ugyanezt végezd el a másik két változóval is, majd add át a lettuce, carrots és melons változókat az előre megírt write() függvénynek a szint befejezéséhez.

Kódkönyv