Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Python
Code

Сериализация и десериализация объектов Python: часть 1

by
Difficulty:IntermediateLength:MediumLanguages:

Russian (Pусский) translation by Ilya Nikov (you can also view the original English article)

Сериализация и десериализация объектов Python является важным аспектом любой нетривиальной программы. Если в Python вы сохраняете что-то в файле, если вы читаете файл конфигурации или отвечаете на HTTP-запрос, вы выполняете сериализацию и десериализацию объектов.

В некотором смысле, сериализация и десериализация - самые скучные вещи в мире. Кто заботится обо всех форматах и протоколах? Вы просто хотите сохранить или стримить некоторые объекты Python и вернуть их позже.

Это очень здоровый способ взглянуть на мир на концептуальном уровне. Но на прагматическом уровне, какая схема сериализации, формат или протокол, которые вы выбираете, могут определять, насколько быстро работает ваша программа, насколько она безопасна, насколько свободно вы должны поддерживать свое состояние и насколько хорошо вы собираетесь взаимодействовать с другими системы.

Причина в том, что существует так много вариантов, что разные обстоятельства требуют разных решений. Нет серебренной поли. В этом учебнике из двух частей я расскажу о преимуществах и недостатках самых успешных схем сериализации и десериализации, покажу, как их использовать, и дам рекомендации по выбору между ними, когда вы сталкиваетесь с конкретным вариантом использования.

Пример выполнения

В следующих разделах я буду сериализовать и десериализовать одни и те же графы объектов Python, используя различные сериализаторы. Чтобы избежать повторения, я определяю эти графы объектов здесь.

Простой Graph объект

Простой graph объект - это словарь, который содержит список целых чисел, строку, float, логическое и None.

Сложный Graph объект

Граф сложного объекта также является словарем, но он содержит объект datetime и пользовательский экземпляр класса с атрибутом self.simple, который устанавливается на простой граф объектов.

Pickle

Pickle является основным продуктом. Это собственный формат сериализации объекта Python. Интерфейс pickle обеспечивает четыре метода: dump, dumps, load, и loads. Метод dump() сериализует в открытый файл (файл-подобный объект). Метод dumps() сериализует в строку. Метод load() десериализует из открытого файлового объекта. Метод loads() десериализует из строки.

Pickle поддерживает по умолчанию текстовый протокол, но имеет также двоичный протокол, который более эффективен, но не читается человеком (полезно при отладке).

Вот как вы распиливаете граф объекта Python на строку и в файл, используя оба протокола.

Бинарное представление может показаться большим, но это иллюзия из-за его представления. При сбрасывании в файл текстовый протокол составляет 130 байт, а бинарный протокол - всего 85 байт.

Выделение из строки так же просто, как:

Обратите внимание, что pickle может автоматически определять протокол. Нет необходимости указывать протокол даже для двоичного.

Получить из файла так же просто. Вам просто нужно предоставить открытый файл.

Согласно документации, вы должны открыть бинарные pickles, используя режим «rb», но, как видите, это работает в любом случае.

Посмотрим, как pickle справляется со сложным графом объектов.

Эффективность двоичного протокола еще больше при использовании сложных объектов.

JSON

JSON (JavaScript Object Notation) является частью стандартной библиотеки Python с Python 2.5. На данный момент я буду считать его родным. Это текстовый формат и является неофициальным королем сети, поскольку идет сериализация объектов. Его система типов, естественно, моделирует JavaScript, поэтому она довольно ограничена.

Давайте сериализуем и десериализируем простые и сложные графы объектов и выясним, что происходит. Интерфейс почти идентичен интерфейсу pickle. У вас есть функции dump(), dumps(), load(), и loads(). Но нет выбора протоколов, и есть много дополнительных аргументов для управления процессом. Давайте начнем просто, сбросив простой граф объектов без каких-либо специальных аргументов:

Результат выглядит довольно читаемым, но нет отступов. Для более крупного графа объектов это может быть проблемой. Давайте отпечатаем вывод:

Это выглядит намного лучше. Перейдем к графу сложных объектов.

Вау! Это не выглядит хорошо. Что случилось? Сообщение об ошибке состоит в том, что объект A не является сериализуемым JSON. Помните, что JSON имеет очень ограниченную систему типов и не может автоматически сериализовать пользовательские классы. Способ обращения к ней заключается в подклассе класса JSONEncoder, используемого модулем json, и реализации default(), которая вызывается всякий раз, когда кодер JSON запускается в объект, который он не может сериализовать.

Задача пользовательского кодировщика состоит в том, чтобы преобразовать его в граф объектов Python, который кодировщик JSON способен кодировать. В этом случае у нас есть два объекта, для которых требуется специальная кодировка: объект datetime и класс A. Следующий кодер выполняет задание. Каждый специальный объект преобразуется в dict, где ключ - это имя типа, окруженного dunders (двойные подчеркивания). Это будет важно для декодирования.

Давайте попробуем еще раз с нашим пользовательским кодировщиком:

Это прекрасно. Граф комплексного объекта был сериализован правильно, а исходная информация о типе компонентов была сохранена с помощью ключей: «__A__» и «__datetime__». Если вы используете dunders для ваших имен, вам нужно придумать другое соглашение для обозначения специальных типов.

Давайте расшифруем граф сложных объектов.

Хм, десериализация работала (без ошибок), но она отличается от первоначального графа комплексного объекта, который мы сериализуем. Что-то не так. Давайте рассмотрим граф десериализованного объекта. Я использую функцию pprint модуля pprint для печати.

Хорошо. Проблема в том, что модуль json ничего не знает о классе A или даже стандартном объекте datetime. Он просто десериализует все по умолчанию объекту Python, который соответствует его системе типов. Чтобы вернуться к богатому графику объектов Python, вам потребуется собственное декодирование.

Нет необходимости в подклассе пользовательского декодера. Функции load() и loads() предоставляют параметр "object_hook", который позволяет вам предоставить настраиваемую функцию, которая преобразует dicts в объекты.

Давайте расшифруем с помощью функции decode_object() в качестве параметра параметра loads() object_hook.

Заключение

В первой части этого учебника вы узнали об общей концепции сериализации и десериализации объектов Python и изучили входы и выходы сериализации объектов Python с использованием Pickle и JSON.

Во второй части вы узнаете о YAML, проблемах производительности и безопасности и получите быстрый обзор дополнительных схем сериализации.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.