Курс за напреднала разработка на Python
Глава
>
Ниво

Модули за сериализация
Модул Struct

Цел

Настройте окончателното разделяне на данните за новата ферма, използвайки модула struct.

В края на пътя има сервизна станция, която администрира новата ферма, която вече е изградена и посевите вече са засадени. Тук ще преглеждаме и обработваме данните за вече засадените култури и прогнозирания добив от фермата. Както при другите нива в тази глава, ще работим с сериализация и десериализация на данни, като въвеждаме последен модул, наречен struct.

Модулът struct въвежда набор от функции за сериализация, които обвиват данните в двоичен формат. За разлика от другите модули, с които сме работили, при него имате по-голям контрол върху това как се структурира информацията както при сериализация, така и при десериализация, което го прави по-гъвкав от другите модули. Използвайте import struct, за да получите достъп до следните функции, които ще използваме за обработка на данните:

  • struct.calcsize(): Определя колко байта заема даден низ от формати. Тази функция приема един (1) аргумент – формата, за който искате да проверите размера в байтове. Форматите, които ще използваме, са:
    • integer: представя се като 'i' и се използва за данни, представени с цели числа
    • float: представя се като 'f' и се използва за данни, представени с десетични числа
    • double: представя се като 'd' и се използва за по-сложни десетични числа, когато форматът float не е достатъчен.
  • struct.pack(): Сериализира данни в двоичен вид, обвити според избрания от вас формат. Функцията приема два (2) или повече аргумента: първият е форматът, който искате да използвате, а останалите са стойностите, които искате да сериализирате. Форматите са описаните по-горе и трябва да ги добавите съответно на броя на предадените стойности.
  • struct.unpack(): Десериализира обвити двоични данни. Приема два (2) аргумента: форматът, в който трябва да бъдат сравними с начина, по който са били сериализирани, и данните, които са били сериализирани.
  • struct.iter_unpack(): Десериализира обвити двоични данни, работи по същия начин като struct.unpack(), но итерира през всеки блок от данни поотделно чрез използване на цикъл.
  • struct.pack_into(): Разширена версия на struct.pack(). Приема четири (4) аргумента: формата, който искате да използвате; буферът, в който искате да поставите данните; позицията в буфера, където искате да ги поставите; и самите данни, които обвивате.
  • struct.unpack_from(): Разширена версия на struct.unpack(). Приема три (3) аргумента: формата, който искате да използвате; буферът, от който искате да десериализирате; и позицията в буфера, от която да десериализирате. Това ви позволява да извлечете конкретни части от данните.

Отидете до светлата X отметка в сервизната станция и се изправете пред бюрото. Създайте три (3) променливи с имена: integer, float и double. Ще ги използваме, за да проверим размера в байтове на всеки формат чрез функцията struct.calcsize(). За променливата integer използвайте функцията с аргумент 'i', за float – с аргумент 'f', а за double – с аргумент 'd'. Например: integer = struct.calcsize('i'). Добавете трите (3) променливи в предварително написаната функция write().

Отидете до златната X отметка и използвайте функцията read(), за да съберете данни за новата ферма. Запишете важните данни и формата им за бъдеща употреба, а именно: Resources, Size и Estimate. След като ги запишете, отидете до светлата X отметка над синьото килимче и създайте променлива с име blue_data.

В променливата blue_data запишете стойността, върната от struct.pack(), като подадете като аргументи формата и стойностите, които записахте по-рано. Когато пишете формата, трябва да обедините типовете формати в един низ. Например, форматът за цяло число е 'i'; ако добавяте три цели числа, го записвате като 'iii'. По същия начин, ако добавяте цяло число, дробено число (float) и двойна прецизност (double), ще бъде 'ifd'. Стойностите се предават отделно като аргументи. Ето пример:

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)

Функцията struct.pack() е много гъвкава и ви позволява да зададете как да форматирате данните и да сериализирате няколко точки данни едновременно по ваше усмотрение. Добавете данните, които прочетохте по-рано, като използвате примера по-горе като шаблон. Използвайте функцията display() с аргумент blue_data, за да видите обвитите данни.

Отидете до тъмната X отметка над синьото килимче и се обърнете към терминала. Създайте променлива blue_unpack и запишете стойността, върната от struct.unpack(), като подадете същия формат, използван при опаковането, и променливата blue_data като аргументи. Форматът се записва по същия начин, както при struct.pack(), за да може да десериализирате данните по същия начин, по който сте ги сериализирали. Използвайте функцията write() с аргумент blue_unpack, за да потвърдите данните, които опаковахте.

На същото място ще използваме и функцията struct.iter_unpack(), която приема точно същите аргументи като struct.unpack(), но се използва в цикъл for, за да можем да итерираме през данните, вместо да ги извеждаме наведнъж. Функцията се кодира по следния начин:

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

Ще използваме функцията speak(), за да покажем стойностите. За липсващите аргументи във struct.iter_unpack(), добавете същите, които използвахте със struct.unpack(), тъй като това са варианти на една и съща функция по отношение на употребата.

Отидете до златната X отметка над червеното килимче и използвайте функцията read(), за да прегледате количествата на културите. Запишете всички количества и формата за всяка култура. После отидете до светлата X отметка над червеното килимче и създайте обект с име buffer, като му присвоите bytearray(16). Това е колекция от байтове, която можем да адресираме преди да я напълним – тук служи като банката с данни, където можете да съхранявате ръчно данните. Числото 16 като аргумент е броят байтове, които можете да съхранявате в обекта buffer.

Използвайте функцията struct.pack_into(), за да напълните създадения обект buffer. Няма нужда да създавате променлива за стойността на функцията, тъй като тя директно ще запише данните в обекта buffer. Ще ползваме функцията за всяка от стойностите, които записахте по-рано, и ще попълним аргументите ѝ съответно.

Например, за първата стойност на културата ще имате нейн формат, позицията в байтовете и количеството. Добавете формата като първи аргумент, след това посочете буфера (buffer), в който искате да сериализирате байтовете. Като трети аргумент задайте позицията в buffer, където да добавите стойността – форматът определя колко байта се заемат, затова внимавайте да не презапишете вече заети байтове. Накрая добавете сами стойността, която искате да сериализирате и запишете в buffer.

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

Обектът buffer има 16 байта, както беше посочено. В кода по-горе форматът за цяло число заема 4 байта и се поставя в позиция 0. Това означава, че след записването в buffer остават само 12 свободни байта, като позиции 03 са заети от стойността (в случая 82). Направете това за всички три (3) стойности на културите, които записахте по-рано. Използвайте функцията display() с аргумент buffer, за да видите сериализираните данни.

Отидете до тъмната X отметка над червеното килимче и се обърнете към терминала. Тук ще десериализираме данните, за да проверим съдържанието и да осигурим правилното им съхранение. Създайте три променливи с имена: lettuce, carrots и melons. Ще извлечем данните за всяка стойност поотделно. За всяка променлива запишете стойността, върната от struct.unpack_from(), като подадете същите аргументи, които използвахте при опаковането. Първият аргумент е форматът, вторият – обектът buffer, от който да извлечете данните, а третият – позицията в buffer, от която да започнете извличането. Например:

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

Тези данни съответстват на примера за опаковане по-горе, който сега се разопакова. Направете същото и за останалите две променливи и подайте lettuce, carrots и melons във вече написаната функция write(), за да завършите нивото.

Книга с Код