Продвинутый курс разработки на Python
Глава
>
Уровень
Модули сериализации
Модуль структуры
Цель
Настройте окончательный разбор данных для новой сельскохозяйственной земли с использованием модуля struct.
В конце дороги находится сервисная станция, которая управляет новой сельскохозяйственной землей, уже построенной, а также посевами, уже посаженными. Здесь мы проведем проверку и обработку данных о посевах и прогнозируемой урожайности сельхозземли. Как и в других уровнях этой главы, мы будем работать с сериализацией и десериализацией данных, вводя последний модуль с именем struct.
Модуль struct предоставляет ряд функций сериализации, которые упаковывают данные в бинарном формате. В отличие от других модулей, с которыми мы работали, вы получаете больше контроля над тем, как структурировать данные как во время сериализации, так и при последующей десериализации, что делает его более универсальным. Используйте import struct, чтобы получить доступ к следующим функциям, которые мы будем использовать для обработки данных:
struct.calcsize(): Определяет, сколько байт занимает упакованная строка формата; принимает один (1) аргумент — формат, размер которого вы хотите проверить. Форматы, которые мы будем использовать:- integer: обозначается как
'i', представляет формат для целочисленных данных - float: обозначается как
'f', представляет формат для данных с десятичными числами - double: обозначается как
'd', представляет формат для более сложных десятичных чисел, когда формат float недостаточен.
- integer: обозначается как
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'. Аналогично, если вы добавляете целое число, число с плавающей запятой и число 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 байт незанятых, при этом позиции 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(), чтобы завершить уровень.