Основной принцип работы интернета — обмен данными. Они бывают разных видов, например файл, строка или число. Есть структура данных, которая позволяет быстро и просто воссоздавать объекты и обмениваться этими данными по сети, — JSON.
JSON в Python
JSON — это строка со словарем. Она представлена в виде байтовой последовательности. Вы можете отправить ее по сети приложению, а в нём воссоздать полученную структуру в объекты языка.
💡 Пример JSON:
{ "kwarg1": "value_1", "kwarg2": "value_2", "kwarg3": "value_3", "additional": ["value_4", "value_5", "value_6", ] }
Сериализация и десериализация
В Python есть множество библиотек, чтобы работать с JSON, но мы рассмотрим встроенную библиотеку JSON Python. Она позволяет приводить любые структуры данных к JSON-объекту — вплоть до пользовательских классов. А из него получать совместимую для работы в Python сущность — объект языка.
Полностью разобраться в Python вы сможете на курсе «Python-разработчик». Преподаватели объяснят все нюансы доступным языком, а вы выполните практические задания. Например, разработаете виджет банковских операций. После обучения у вас будет готовое портфолио и диплом о профессиональной переподготовке.
Упаковка объектов в байтовую последовательность называется сериализацией. А распаковка байтов в объекты языка программирования, приведение последовательности назад к типам и структурам, — десериализацией.
В байты данные необходимо переводить, чтобы отправлять их по сети или локально другому приложению, так как иной формат передать невозможно. Вот так преобразовывают данные из объектов Python в JSON и обратно:
>>> # импортируем библиотеку >>> import json >>> >>> # объявляем переменные >>> string = "Some test string" >>> integer = 211 >>> array = [1, 2, 3, 4, 5] >>> >>> # создаем словарь >>> mydict = {"title": string, "code": integer, "data": array} >>> >>> # сериализуем его в JSON-структуру, как строку >>> x = json.dumps(mydict) >>> x '{"title": "Some test string", "code": 211, "data": [1, 2, 3, 4, 5]}' >>> >>> # проводим десериализацию JSON-объекта >>> y = json.loads(x) >>> y {'title': 'Some test string', 'code': 211, 'data': [1, 2, 3, 4, 5]} >>> >>> 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__) '{"title": "Some string", "body": "Here is a bit more text, but still isn\'t enough"}' >>>
Но можно создать отдельную функцию и указать ее в качестве аргумента:
>>> def to_json(obj): ... if isinstance(obj, Test): ... result = obj.__dict__ ... result["className"] = obj.__class__.__name__ ... return result ... >>> json.dumps(t, default=to_json) '{"title": "Some string", "body": "Here is a bit more text, but still isn\'t enough", "className": "Test"}' >>>
❗ Мы добавили название класса в получаемую структуру. Такой подход позволяет безошибочно понять: сущность какого класса нужно десериализовать в объект.
Более подробно писать функции вы научитесь на курсе «Python-разработчик». А еще сами сделаете сервис проверки файлов с возможностью регистрации, отправки уведомлений и формирования отчета о задачах.
🚀 Создать расширение классов
Такого же результата добьетесь, если примените расширения специальных классов библиотеки:
>>> class TestEncoder(json.JSONEncoder): ... def default(self, o): ... return {"TITLE": o.title, "BODY": o.body, "CLASSNAME": o.__class__.__name__} ... >>> x = json.dumps(t, cls=TestEncoder) >>> x '{"TITLE": "Some string", "BODY": "Here is a bit more text, but still isn\'t enough"}' >>> >>> y = json.loads(x) >>> y {'TITLE': 'Some string', 'BODY': "Here is a bit more text, but still isn't enough", 'CLASSNAME': 'Test'} >>> >>> 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: {self.title}, {repr(self.form)}, {repr(self.color)}" class Form: def __init__(self, name): self.name = name def __repr__(self): return f"<Form: {self.name}>" class Color: def __init__(self, name): self.name = name def __repr__(self): return f"<Color: {self.name}>"
Напишите класс, который будет приводить объекты фигуры к 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, <Form: Squared>, <Color: Black> Figure: Yellow Circle, <Form: Rounded>, <Color: Yellow>
Отображение JSON
{"title": "Black Square", "form": "Squared", "color": "Black"} {"title": "Yellow Circle", "form": "Rounded", "color": "Yellow"}
Отображение восстановленных объектов
{Figure: Black Square, <Form: Squared>, <Color: Black> Figure: Yellow Circle, <Form: Rounded>, <Color: Yellow>
Главное о работе с JSON в Python
- JSON — это стандарт обмена данными. Он позволяет легко сериализовать и десериализовать объекты.
- Стандарт часто применяют, когда разрабатывают API и веб-приложения.
- Для сериализации и десериализации объектов в строку или из строки используйте функции json.dumps/json.loads. Из файлов — json.dump/json.load.
- Сериализовать можно любую пользовательскую структуру. Для этого создайте функцию, напишите расширение классов JSONEncoder/JSONDecoder или свою реализацию «Адаптера».
Изучайте Python на онлайн-курсе от Skypro «Python-разработчик». Программа рассчитана на новичков без опыта программирования и технического образования. Курс проходит в формате записанных коротких видеолекций. Будет много проверочных заданий и мастер-классов. В конце каждой недели — живая встреча с экспертами разработки для ответов на вопросы и разбора домашек.
Добавить комментарий