- Сериализация и десериализация объектов Python: часть 1
- Пример выполнения
- Простой Graph объект
- Как работать с модулем JSON в Python
- JSON в Python
- Сериализация и десериализация
- Функции
- Как работать с пользовательскими объектами
- 🚀 Написать функцию
- 🚀 Создать расширение классов
- 🚀 Применить паттерн «Адаптер»
- Главное о работе с JSON в Python
Сериализация и десериализация объектов Python: часть 1
Gigi Sayfan Last updated May 26, 2022
Сериализация и десериализация объектов Python является важным аспектом любой нетривиальной программы. Если в Python вы сохраняете что-то в файле, если вы читаете файл конфигурации или отвечаете на HTTP-запрос, вы выполняете сериализацию и десериализацию объектов.
В некотором смысле, сериализация и десериализация — самые скучные вещи в мире. Кто заботится обо всех форматах и протоколах? Вы просто хотите сохранить или стримить некоторые объекты Python и вернуть их позже.
Это очень здоровый способ взглянуть на мир на концептуальном уровне. Но на прагматическом уровне, какая схема сериализации, формат или протокол, которые вы выбираете, могут определять, насколько быстро работает ваша программа, насколько она безопасна, насколько свободно вы должны поддерживать свое состояние и насколько хорошо вы собираетесь взаимодействовать с другими системы.
Причина в том, что существует так много вариантов, что разные обстоятельства требуют разных решений. Нет серебренной поли. В этом учебнике из двух частей я расскажу о преимуществах и недостатках самых успешных схем сериализации и десериализации, покажу, как их использовать, и дам рекомендации по выбору между ними, когда вы сталкиваетесь с конкретным вариантом использования.
Пример выполнения
В следующих разделах я буду сериализовать и десериализовать одни и те же графы объектов Python, используя различные сериализаторы. Чтобы избежать повторения, я определяю эти графы объектов здесь.
Простой Graph объект
Простой graph объект — это словарь, который содержит список целых чисел, строку, float, логическое и None.
simple = dict(int_list=[1, 2, 3],
Как работать с модулем JSON в Python
JSON часто применяют, когда разрабатывают API и веб-приложения.
Основной принцип работы интернета — обмен данными. Они бывают разных видов, например файл, строка или число. Есть структура данных, которая позволяет быстро и просто воссоздавать объекты и обмениваться этими данными по сети, — JSON.
JSON в Python
JSON — это строка со словарем. Она представлена в виде байтовой последовательности. Вы можете отправить ее по сети приложению, а в нём воссоздать полученную структуру в объекты языка.
💡 Пример JSON:
Сериализация и десериализация
В Python есть множество библиотек, чтобы работать с JSON, но мы рассмотрим встроенную библиотеку JSON Python. Она позволяет приводить любые структуры данных к JSON-объекту — вплоть до пользовательских классов. А из него получать совместимую для работы в Python сущность — объект языка.
Упаковка объектов в байтовую последовательность называется сериализацией. А распаковка байтов в объекты языка программирования, приведение последовательности назад к типам и структурам, — десериализацией.
В байты данные необходимо переводить, чтобы отправлять их по сети или локально другому приложению, так как иной формат передать невозможно. Вот так преобразовывают данные из объектов Python в JSON и обратно:
>>> # импортируем библиотеку >>> import json >>> >>> # объявляем переменные >>> string = "Some test string" >>> integer = 211 >>> array = [1, 2, 3, 4, 5] >>> >>> # создаем словарь >>> mydict = >>> >>> # сериализуем его в JSON-структуру, как строку >>> x = json.dumps(mydict) >>> x '' >>> >>> # проводим десериализацию JSON-объекта >>> y = json.loads(x) >>> y >>> >>> y["title"] 'Some test string' >>>
Функции
Dumps позволяет создать JSON-строку из переданного в нее объекта. Loads — преобразовать строку назад в объекты языка.
Dump и load используют, чтобы сохранить результат в файл или воссоздать объект. Работают они схожим образом, но требуют передачи специального объекта для работы с файлом — filehandler.
>>> import json # импортируем библиотеку >>> >>> # создаем filehandler с помощью контекстного менеджера >>> with open("data.json", "w") as fh: . json.dump([1, 2, 3, 4, 5], fh) # записываем структуру в файл . >>> >>> # открываем тот же файл, но уже на чтение >>> with open("data.json", "r") as fh: . json.load(fh) # загружаем структуру из файла . [1, 2, 3, 4, 5] >>>
Как работать с пользовательскими объектами
Пользовательские классы не относятся к JSON-сериализуемым. Это значит, что просто применить к ним функции dumps, loads или dump и load не получится:
>>> # создаем пользовательский класс >>> class Test: . def __init__(self, title, body): . self.title = title . self.body = body . >>> # создаем экземпляр класса >>> t = Test("Some string", "Here is a bit more text, but still isn't enough") >>> >>> # пытаемся сериализовать его в JSON, но. >>> json.dumps(t) >>> # получаем ошибку TypeError, что класс несериализуем >>>
Решить эту проблему можно тремя способами.
🚀 Написать функцию
Чтобы сериализовать пользовательский объект в JSON-структуру данных, нужен аргумент default. Указывайте вызываемый объект, то есть функцию или статический метод.
Чтобы получить аргументы класса с их значениями, нужна встроенная функция __dict__, потому что любой класс — это словарь со ссылками на значения по ключу.
Чтобы сериализовать аргументы класса и их значения в JSON, напишите функцию:
>>> # используем анонимную функцию (лямбду), которая >>> # в качестве сериализуемых данных указывает полученный __dict__ объекта >>> json.dumps(t, default=lambda x: x.__dict__) '' >>>
Но можно создать отдельную функцию и указать ее в качестве аргумента:
>>> def to_json(obj): . if isinstance(obj, Test): . result = obj.__dict__ . result["className"] = obj.__class__.__name__ . return result . >>> json.dumps(t, default=to_json) '' >>>
❗ Мы добавили название класса в получаемую структуру. Такой подход позволяет безошибочно понять: сущность какого класса нужно десериализовать в объект.
🚀 Создать расширение классов
Такого же результата добьетесь, если примените расширения специальных классов библиотеки:
>>> class TestEncoder(json.JSONEncoder): . def default(self, o): . return . >>> x = json.dumps(t, cls=TestEncoder) >>> x '' >>> >>> y = json.loads(x) >>> y >>> >>> y["TITLE"] 'Some string' >>>
Такой подход можно использовать и в том случае, если класс состоит из нескольких других.
🚀 Применить паттерн «Адаптер»
Идея в том, чтобы написать класс, который приводит к JSON пользовательские объекты и восстанавливает их. Определите класс фигуры, формы и цвета:
class Figure: def __init__(self, title, form, color): self.title = title self.form = form self.color = color def __str__(self): return f"Figure: , , " class Form: def __init__(self, name): self.name = name def __repr__(self): return f">" class Color: def __init__(self, name): self.name = name def __repr__(self): return f">"
Напишите класс, который будет приводить объекты фигуры к JSON, а из JSON воссоздавать полученный объект:
class JSONDataAdapter: @staticmethod def to_json(o): if isinstance(o, Figure): return json.dumps(< "title": o.title, "form": o.form.name, "color": o.color.name, >) @staticmethod def from_json(o): o = json.loads(o) try: form = Form(o["form"]) color = Color(o["color"]) figure = Figure(o["title"], form, color) return figure except AttributeError: print("Неверная структура")
if __name__ == '__main__': # создадим несколько цветов black = Color("Black") yellow = Color("Yellow") green = Color("Green") # несколько форм rountt = Form("Rounded") square = Form("Squared") # объекты фигур figure_one = Figure("Black Square", form=square, color=black) figure_two = Figure("Yellow Circle", form=rountt, color=yellow) print(“Отображение объектов”) print(figure_one) print(figure_two) print() # преобразуем данные в JSON jone = JSONDataAdapter.to_json(figure_one) jtwo = JSONDataAdapter.to_json(figure_two) print(“Отображение JSON”) print(jone) print(jtwo) print() # восстановим объекты restored_one = JSONDataAdapter.from_json(jone) restored_two = JSONDataAdapter.from_json(jtwo) print(“Отображение восстановленных объектов”) print(restored_one) print(restored_two)
Вывод терминала:
Figure: Black Square, , Figure: Yellow Circle, ,
Отображение восстановленных объектов
Главное о работе с JSON в Python
- JSON — это стандарт обмена данными. Он позволяет легко сериализовать и десериализовать объекты.
- Стандарт часто применяют, когда разрабатывают API и веб-приложения.
- Для сериализации и десериализации объектов в строку или из строки используйте функции json.dumps/json.loads. Из файлов — json.dump/json.load.
- Сериализовать можно любую пользовательскую структуру. Для этого создайте функцию, напишите расширение классов JSONEncoder/JSONDecoder или свою реализацию «Адаптера».
Изучайте Python на онлайн-курсе от Skypro «Python-разработчик». Программа рассчитана на новичков без опыта программирования и технического образования. Курс проходит в формате записанных коротких видеолекций. Будет много проверочных заданий и мастер-классов. В конце каждой недели — живая встреча с экспертами разработки для ответов на вопросы и разбора домашек.