Курс розробки на Python для просунутих
Розділ
>
Рівень
Модулі серіалізації
Модуль Struct
Мета
Налаштуйте остаточну розбивку даних для нового сільськогосподарського угіддя з використанням модуля struct.
На кінці дороги розташована сервісна станція, яка адмініструє вже побудоване нове сільськогосподарське угіддя та посаджені культури. Тут ми перевірятимемо та оброблятимемо дані щодо вже посаджених культур і прогнозованого врожаю угіддя. Як і в інших рівнях цього розділу, ми працюватимемо з серіалізацією та десеріалізацією даних, використовуючи останній модуль — модуль struct.
Модуль struct пропонує низку функцій для серіалізації, які упаковують дані в двійковому форматі. На відміну від інших модулів, які ми використовували, тут ви маєте більший контроль над тим, як структурувати дані під час серіалізації та пізнішої десеріалізації, що робить його більш універсальним. Використовуйте import struct, щоб отримати доступ до таких функцій:
struct.calcsize(): визначає, скільки байтів займає заданий рядок формату. Приймає один аргумент — формат, розмір байта якого ви хочете перевірити. Формати, які ми використовуватимемо, такі:- integer: представлений як
'i'— формат для даних, що зберігаються цілими числами - float: представлений як
'f'— формат для даних у десятковому вигляді - double: представлений як
'd'— формат для складніших десяткових чисел, колиfloatнедостатньо
- integer: представлений як
struct.pack(): серіалізує дані в двійковий формат згідно з обраним форматом. Приймає два або більше аргументів: формат і значення, які потрібно серіалізувати. Формати — ті, що описані вище, їх додають відповідно до кількості аргументів.struct.unpack(): десеріалізує упаковані двійкові дані. Приймає два аргументи: формат, сумісний із тим, у якому дані були серіалізовані, та самі серіалізовані дані.struct.iter_unpack(): десеріалізує упаковані двійкові дані так само, якstruct.unpack(), але працює в циклі, ітеруючи через кожен блок даних окремо.struct.pack_into(): розширена версіяstruct.pack(). Приймає чотири аргументи: формат, буфер даних, у який вставити дані, позицію в буфері та самі дані для упаковки.struct.unpack_from(): розширена версіяstruct.unpack(). Приймає три аргументи: формат, буфер даних, з якого потрібно розпаковувати, та позицію в буфері. Це дозволяє розпаковувати конкретні частини даних.
Перейдіть до світлої X позначки на сервісній станції й зверніться до столу. Створіть три змінні: integer, float та double. Ми використаємо їх, щоб перевірити розмір у байтах кожного формату за допомогою функції struct.calcsize(). Для змінної integer застосуйте struct.calcsize('i'), для float — struct.calcsize('f'), а для double — struct.calcsize('d'). Наприклад:
integer = struct.calcsize('i')
Додайте ці три змінні до заготовленої функції write().
Перейдіть до золотої X позначки та скористайтеся функцією read(), щоб зібрати дані про нове сільськогосподарське угіддя. Зафіксуйте точки даних та їх формати для подальшого використання: Resources, Size та Estimate. Після цього перейдіть до світлої X позначки на блакитному килимі та створіть змінну blue_data.
У змінній blue_data збережіть результат виклику struct.pack(), передавши формат і значення, які ви зазначили раніше. При записі формату необхідно згрупувати типи форматів в один рядок. Наприклад, цілий формат позначається як 'i', тож якщо додаєте три цілі числа, запишіть 'iii'. Аналогічно, якщо додаєте ціле число, дробове та подвійне, рядок формату буде '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 байтів. У наведеному коді формат integer займає 4 байти та вставляється на позиції 0. Це означає, що після вставлення даних у buffer залишається 12 байтів незайнятими, а позиції 0–3 зайняті значенням 82. Зробіть те саме для всіх трьох раніше зафіксованих даних про врожай. Використайте функцію display() з аргументом buffer, щоб відобразити серіалізовані дані.
Перейдіть до темної X позначки на червоному килимі й зверніться до терміналу. Тут ми будемо десеріалізувати дані, щоб перевірити їх зміст і забезпечити правильне зберігання. Створіть три змінні: lettuce, carrots та melons; ми десеріалізуємо дані кожної точки окремо. Для кожної змінної збережіть результат виклику struct.unpack_from(), передавши ті ж дані, які ви зазначали під час упаковки. Першим аргументом задайте формат, другим — об’єкт buffer, з якого потрібно розпаковувати, а третім — позицію в buffer, з якої слід почати. Наприклад:
lettuce = struct.unpack_from('i', buffer, 0)
Ці дані відповідають наведеному раніше прикладу упаковки. Виконайте те саме для двох інших змінних і передайте lettuce, carrots та melons у заготовлену функцію write(), щоб завершити рівень.