Сериализация и десериализация данных

Сериализация и десериализация данных REST API

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

В этой статье мы разберём:

  • Что такое сериализация и десериализация
  • Форматы: JSON, pickle
  • Сериализация кастомных объектов
  • Сохранение и загрузка данных
  • Практические примеры

Что нужно знать перед началом

Для этого урока тебе понадобится:

  • Установленный Python
  • Базовое понимание объектов и файлов

Совет: Сериализация — это процесс превращения объекта в байтовый поток или строку для хранения или передачи.

Основная часть

Что такое сериализация и десериализация

Сериализация — процесс преобразования объекта в формат, который можно сохранить или передать.

Десериализация — обратный процесс: восстановление объекта из сериализованного формата.

# Сериализация: объект → JSON-строка
data = {"name": "Анна", "age": 25}
json_string = json.dumps(data)

# Десериализация: JSON-строка → объект
data = json.loads(json_string)

Совет: Сериализация нужна для хранения данных (файлы, БД) и передачи по сети (API, микросервисы).

Формат JSON

JSON — самый популярный формат для обмена данными. Он поддерживается почти всеми языками программирования.

import json

data = {
    "name": "Анна",
    "age": 25,
    "is_active": True,
    "scores": [95, 87, 92],
    "address": None
}

# Сериализация в JSON-строку
json_string = json.dumps(data, ensure_ascii=False, indent=4)
print(json_string)

# Десериализация из JSON-строки
restored_data = json.loads(json_string)
print(restored_data)

Результат:

{
    "name": "Анна",
    "age": 25,
    "is_active": true,
    "scores": [95, 87, 92],
    "address": null
}

Совет: json.dumps() и json.loads() — для строк. json.dump() и json.load() — для файлов.

Формат pickle

Pickle — встроенный формат Python для сериализации объектов. Он поддерживает все типы Python, включая кастомные классы.

import pickle

data = {
    "name": "Анна",
    "age": 25,
    "scores": [95, 87, 92]
}

# Сериализация в байты
pickled_data = pickle.dumps(data)
print(pickled_data)   # b'\x80\x04\x95...'

# Десериализация из байтов
restored_data = pickle.loads(pickled_data)
print(restored_data)

Работа с файлами:

# Сохранение в файл
with open("data.pkl", "wb") as file:
    pickle.dump(data, file)

# Загрузка из файла
with open("data.pkl", "rb") as file:
    loaded_data = pickle.load(file)

print(loaded_data)

Важно: Pickle опасен при загрузке данных из ненадёжных источников — может выполнить вредоносный код.

JSON vs pickle

ХарактеристикаJSONpickle
ЧеловекочитаемыйДаНет
БезопасностьБезопасенОпасен
Поддержка других языковДаНет (только Python)
СкоростьМедленнееБыстрее
Поддерживает кастомные классыНетДа
Размер данныхБольшеМеньше

Совет: Для API используй JSON. Для внутреннего хранения в Python — можно использовать pickle.

Сериализация кастомных объектов

По умолчанию JSON не умеет сериализовать кастомные классы.

class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age

user = User("Анна", 25)

# Ошибка! TypeError: Object of type User is not JSON serializable
# json.dumps(user)

Решение 1: Функция default

def user_to_dict(obj):
    if isinstance(obj, User):
        return {"name": obj.name, "age": obj.age}
    raise TypeError(f"Объект {obj} не сериализуем")

json_string = json.dumps(user, default=user_to_dict)
print(json_string)   # {"name": "Анна", "age": 25}

Решение 2: Метод to_dict

class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def to_dict(self):
        return {"name": self.name, "age": self.age}

user = User("Анна", 25)
json_string = json.dumps(user.to_dict())
print(json_string)   # {"name": "Анна", "age": 25}

Решение 3: Pickle (для Python-объектов)

user = User("Анна", 25)
pickled_user = pickle.dumps(user)
restored_user = pickle.loads(pickled_user)
print(restored_user.name, restored_user.age)   # Анна 25

Сериализация в другие форматы

XML

import xml.etree.ElementTree as ET

def dict_to_xml(data, root_name="root"):
    root = ET.Element(root_name)
    for key, value in data.items():
        child = ET.SubElement(root, key)
        child.text = str(value)
    return ET.tostring(root, encoding="unicode")

data = {"name": "Анна", "age": "25"}
xml_string = dict_to_xml(data)
print(xml_string)   # <root><name>Анна</name><age>25</age></root>

YAML

# pip install pyyaml
import yaml

data = {"name": "Анна", "age": 25}
yaml_string = yaml.dump(data)
print(yaml_string)

restored = yaml.load(yaml_string, Loader=yaml.FullLoader)
print(restored)

Пример из реальной жизни

Сохранение настроек приложения:

import json
from datetime import datetime

class AppConfig:
    def __init__(self, theme="dark", language="ru"):
        self.theme = theme
        self.language = language
        self.updated_at = datetime.now()

    def to_dict(self):
        return {
            "theme": self.theme,
            "language": self.language,
            "updated_at": self.updated_at.isoformat()
        }

    @classmethod
    def from_dict(cls, data):
        config = cls(data["theme"], data["language"])
        config.updated_at = datetime.fromisoformat(data["updated_at"])
        return config

# Сохранение
config = AppConfig()
with open("config.json", "w", encoding="utf-8") as file:
    json.dump(config.to_dict(), file, ensure_ascii=False, indent=4)

# Загрузка
with open("config.json", "r", encoding="utf-8") as file:
    data = json.load(file)
    loaded_config = AppConfig.from_dict(data)

print(loaded_config.theme, loaded_config.language)

Задачи для закрепления

Задача 1. Сериализуй словарь {"name": "Иван", "age": 30} в JSON-строку.

Задача 2. Сериализуй объект пользователя в JSON с помощью метода to_dict().

Задача 3. Сохрани данные в файл с помощью pickle.

Задача 4. Загрузи данные из файла с помощью pickle.

Задача 5. В чём разница между JSON и pickle?

Ответы:

Задача 1.

import json
data = {"name": "Иван", "age": 30}
json_str = json.dumps(data, ensure_ascii=False)
print(json_str)   # {"name": "Иван", "age": 30}

Задача 2.

class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def to_dict(self):
        return {"name": self.name, "age": self.age}

user = User("Иван", 30)
json_str = json.dumps(user.to_dict())
print(json_str)   # {"name": "Иван", "age": 30}

Задача 3.

import pickle
data = {"name": "Иван", "age": 30}
with open("data.pkl", "wb") as file:
    pickle.dump(data, file)

Задача 4.

with open("data.pkl", "rb") as file:
    loaded = pickle.load(file)
print(loaded)   # {'name': 'Иван', 'age': 30}

Задача 5.

# JSON — безопасен, человекочитаемый, поддерживается другими языками.
# pickle — быстрее, поддерживает кастомные классы, но опасен и только для Python.

Нюансы и подводные камни

Pickle опасен

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

JSON не поддерживает все типы

datetimeDecimalset, кастомные классы — не сериализуются по умолчанию.

Кодировка

Для русского текста используй ensure_ascii=False в JSON.

Циклические ссылки

Pickle поддерживает циклические ссылки, JSON — нет.

Частые ошибки и как их избежать

Ошибка 1: Попытка сериализовать datetime в JSON

Преобразуй в строку через isoformat() или используй кастомный encoder.

Ошибка 2: Путаница между json.dumps() и json.dump()

dumps() — в строку. dump() — в файл.

Ошибка 3: Загрузка pickle из ненадёжного источника

Используй JSON для данных из внешних источников.

Шпаргалка

ФорматДля строкДля файлов
JSONjson.dumps()json.loads()json.dump()json.load()
picklepickle.dumps()pickle.loads()pickle.dump()pickle.load()

Заключение

Сегодня мы:

  • Узнали, что такое сериализация и десериализация
  • Изучили форматы JSON и pickle
  • Научились сериализовать кастомные объекты
  • Практические примеры

КВИЗ

Что дальше?

Оцените статью
IMI-DS - PYTHON LERNEN
Содержание
Оглавление ×