Fortgeschrittener Python-Entwicklungskurs
Kapitel
>
Stufe

Serialisierungs-Module
Strukturmodul

Ziel

Richte die endgültige Datenaufbereitung für das neue Ackerland mithilfe des Struct-Moduls ein.

Am Ende des Weges befindet sich eine Servicestation, die das bereits angelegte Ackerland und die schon gepflanzten Kulturen verwaltet. Hier werden wir die Daten der bereits gepflanzten Kulturen und die prognostizierten Erträge des Ackerlandes überprüfen und verarbeiten. Wie in anderen Levels dieses Kapitels werden wir mit der Serialisierung und Deserialisierung von Daten arbeiten und führen ein letztes Modul namens Struct-Modul ein.

Verwende import struct, um auf die folgenden Funktionen zuzugreifen, die wir zur Datenverarbeitung nutzen werden:

  • struct.calcsize(): Bestimmt, wie viele Bytes eine gegebene Formatzeichenfolge belegt. Sie benötigt ein (1) Argument, nämlich das Format, dessen Bytegröße du überprüfen möchtest. Die Formate, die wir verwenden werden, sind:
    • integer: dargestellt durch 'i', ist das Format für Daten in ganzen Zahlen
    • float: dargestellt durch 'f', ist das Format für Daten in Dezimalzahlen
    • double: dargestellt durch 'd', ist das Format für komplexere Dezimalzahlen, für die das Float-Format nicht ausreicht.
  • struct.pack(): Serialisiert Daten im Binärformat in einem von dir gewählten Format. Es nimmt zwei (2) oder mehr Argumente entgegen: das gewünschte Format und die Werte, die du serialisieren möchtest. Die Formate sind die zuvor aufgeführten, und du musst sie entsprechend der Anzahl der übergebenen Werte hinzufügen.
  • struct.unpack(): Deserialisiert gepackte Binärdaten. Es benötigt zwei (2) Argumente: das Format, in dem die Daten serialisiert wurden, und die zu deserialisierenden Daten.
  • struct.iter_unpack(): Deserialisiert gepackte Binärdaten. Funktioniert wie struct.unpack(), iteriert jedoch mithilfe einer Schleife über jeden Datenblock einzeln.
  • struct.pack_into(): Eine erweiterte Variante von struct.pack(). Sie benötigt vier (4) Argumente: das gewünschte Format, den Datenpuffer, in den die Daten geschrieben werden sollen, die Position im Puffer, an der die Daten eingefügt werden sollen, und schließlich die zu packenden Daten.
  • struct.unpack_from(): Eine erweiterte Variante von struct.unpack(). Sie benötigt drei (3) Argumente: das gewünschte Format, den Datenpuffer, aus dem entpackt werden soll, und die Position im Puffer, ab der entpackt wird. Dadurch kannst du gezielt bestimmte Datenabschnitte auslesen.

Geh zur hellen X-Markierung in der Servicestation und stelle dich zum Schreibtisch. Erstelle drei (3) Variablen mit den Namen: integer, float und double. Wir verwenden diese, um die Größe in Bytes jedes Formats mit der Funktion struct.calcsize() zu überprüfen. Für die Variable integer rufst du die Funktion mit 'i' als Argument auf, für float mit 'f' und schließlich für double mit 'd'. Zum Beispiel: integer = struct.calcsize('i'). Füge die drei (3) Variablen der vorgefertigten Funktion write() hinzu.

Geh zur goldenen X-Markierung und verwende die Funktion read(), um Daten über das neue Ackerland zu sammeln. Notiere die Datenpunkte und Formate für die spätere Verwendung, nämlich: Resources, Size und Estimate. Nachdem du diese notiert hast, geh zur hellen X-Markierung über dem blauen Teppich und erstelle eine Variable namens blue_data.

Speichere in der Variable blue_data den Rückgabewert der Funktion struct.pack(), wobei du als Argumente das zuvor notierte Format und die Datenwerte übergibst. Beim Schreiben der Format-Zeichenfolge musst du die einzelnen Formattypen zu einer Einheit „packen“. Zum Beispiel hat das Integer-Format wie zuvor erwähnt das Label 'i'. Wenn du drei Integer-Datentypen hinzufügst, schreibst du 'iii'. Ebenso würde ein Integer, gefolgt von einem Float und einem Double als 'ifd' geschrieben. Die Daten werden als einzelne Argumente übergeben. Hier ein Gesamtbeispiel:

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)

Die Funktion struct.pack() bietet große Flexibilität: Du kannst festlegen, wie die Daten formatiert werden sollen, und mehrere Datenpunkte gleichzeitig serialisieren. Füge die zuvor gelesenen Daten basierend auf obigem Beispiel ein. Verwende die Funktion display() mit blue_data als Argument, um die gepackten Daten anzuzeigen.

Geh zur dunklen X-Markierung auf dem blauen Teppich und stelle dich an das Terminal. Erstelle eine Variable namens blue_unpack und speichere den Rückgabewert der Funktion struct.unpack(), bei der du dasselbe Format wie beim Packen und die Variable blue_data als Argumente übergibst. Das Format wird genauso geschrieben wie bei struct.pack(), damit du die Daten in der gleichen Reihenfolge deserialisieren kannst, in der du sie serialisiert hast. Verwende die Funktion write() mit blue_unpack als Argument, um die zuvor gepackten Daten zu überprüfen.

An derselben Stelle verwenden wir außerdem die Funktion struct.iter_unpack(), die dieselben Argumente wie struct.unpack() benötigt, aber nun in Form einer for-Schleife arbeitet. Dadurch können wir die Daten nacheinander iterieren, anstatt sie alle auf einmal auszugeben. Die Funktion sieht folgendermaßen aus:

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

Wir verwenden die Funktion speak(), um die Werte anzuzeigen. Für die fehlenden Argumente in der Funktion struct.iter_unpack() fügst du dieselben hinzu, die du zuvor bei struct.unpack() verwendet hast, da es sich um Variationen derselben Funktion handelt.

Geh zur goldenen X-Markierung auf dem roten Teppich und wende dich zum Schreibtisch. Verwende die Funktion read(), um die Mengen der Kulturen zu überprüfen. Notiere alle Mengen und das Format jeder Kultur. Geh dann zur hellen X-Markierung auf dem roten Teppich und erstelle ein Objekt namens buffer mit dem Wert bytearray(16). Das ist eine Bytesammlung, die wir vor dem Befüllen adressieren können – in unserem Fall funktioniert sie wie eine Datenspeicherbank, in der man die Daten manuell ablegen kann. Die Zahl 16 als Argument bestimmt die Anzahl der Bytes, die im Objekt buffer gespeichert werden können.

Verwende die Funktion struct.pack_into(), um das zuvor erstellte Objekt buffer zu befüllen. Du musst keinen neuen Variablenname für den Rückgabewert anlegen, da die Funktion die Werte direkt in das Objekt buffer schreibt. Wir verwenden die Funktion für jeden der zuvor notierten Werte und übergeben sie als Argumente.

Zum Beispiel erhältst du für den ersten Wert einer Kultur das Format, die Byte-Position und die Menge. Gib als erstes Argument das Format an, als zweites den Zielpuffer, in diesem Fall buffer. Für das dritten Argument legst du die Position im Puffer fest, an der der Wert gespeichert werden soll – das Format bestimmt dabei die belegten Bytes, daher solltest du darauf achten, Bytes zu wählen, die noch frei sind. Schließlich übergibst du die zu serialisierende Menge, die in das Objekt buffer gepackt wird.

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

Das Objekt buffer hat, wie bereits erwähnt, 16 Bytes. Im obigen Codebeispiel belegt das Integer-Format 4 Bytes und wird bei Position 0 eingefügt. Das bedeutet, dass im Puffer anschließend nur noch 12 Bytes frei sind, da Position 03 durch den angegebenen Wert (hier 82) belegt sind. Führe dies für alle zuvor notierten Kulturdaten durch; insgesamt sind es drei (3). Verwende die Funktion display() mit buffer, um die serialisierten Daten anzuzeigen.

Geh zur dunklen X-Markierung auf dem roten Teppich und stelle dich an das Terminal. Hier werden wir die Daten deserialisieren, um den Inhalt zu überprüfen und eine korrekte Speicherung sicherzustellen. Erstelle drei Variablen mit den Namen: lettuce, carrots und melons. Wir werden die Datenpunkte jeweils einzeln entpacken. Speichere in jede Variable den Rückgabewert von struct.unpack_from(), wobei du als Argumente das zuvor verwendete Format, das Objekt buffer (als Quelle) und zuletzt die Position im Puffer angibst, ab der entpackt werden soll. Zum Beispiel:

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

Diese Daten entsprechen dem zuvor gepackten Beispiel, das hier entpackt wird. Gehe genauso für die beiden anderen Variablen vor und übergib lettuce, carrots und melons an die vorgefertigte Funktion write(), um das Level abzuschließen.

Codebuch