← Назад к вопросам

Сериализация и десериализация (pickle)

1.3 Junior🔥 181 комментариев
#Python Core

Условие

Объясните, что такое pickling и unpickling в Python.

Задача

  1. Сериализуйте Python-объект в файл
  2. Десериализуйте его обратно
  3. Объясните риски безопасности при использовании pickle
  4. Какие альтернативы существуют (json, msgpack)?

Пример

import pickle

data = {"name": "Alice", "age": 30}
# Сохраните в файл и загрузите обратно

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Сериализация и десериализация (pickle)

Что такое Pickling?

Pickling — это процесс преобразования Python объекта в байтовый поток (serialization). Unpickling — обратный процесс восстановления объекта из байтов (deserialization).

Essentially, pickle позволяет сохранить состояние Python объекта и восстановить его позже, даже если программа закончилась.

1. Базовое использование pickle

import pickle
from typing import Any

# СЕРИАЛИЗАЦИЯ (pickle)
data = {"name": "Alice", "age": 30, "tags": ["python", "django"]}

# Способ 1: Сохранить в файл
with open("data.pkl", "wb") as f:  # 'wb' = write binary
    pickle.dump(data, f)

# Способ 2: Получить байты напрямую
bytes_data = pickle.dumps(data)  # 's' = returns bytes
print(type(bytes_data))  # <class 'bytes'>
print(bytes_data[:50])   # b'\x80\x04}\x94(X\x04\x00\x00\x00name...'

# ДЕСЕРИАЛИЗАЦИЯ (unpickle)
# Способ 1: Из файла
with open("data.pkl", "rb") as f:  # 'rb' = read binary
    restored_data = pickle.load(f)

print(restored_data)  # {'name': 'Alice', 'age': 30, 'tags': ['python', 'django']}
print(restored_data == data)  # True

# Способ 2: Из байтов
restored = pickle.loads(bytes_data)  # 's' = takes bytes
print(restored)  # {'name': 'Alice', 'age': 30, 'tags': ['python', 'django']}

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

import pickle
from datetime import datetime
from dataclasses import dataclass

@dataclass
class User:
    """Пользовательский класс"""
    id: int
    name: str
    email: str
    created_at: datetime
    
    def greet(self):
        return f"Hello, {self.name}!"

# Создаем объект
user = User(
    id=1,
    name="Alice",
    email="alice@example.com",
    created_at=datetime.now()
)

# Сохраняем
with open("user.pkl", "wb") as f:
    pickle.dump(user, f)

# Загружаем
with open("user.pkl", "rb") as f:
    loaded_user = pickle.load(f)

print(loaded_user.name)    # Alice
print(loaded_user.greet()) # Hello, Alice!
print(loaded_user == user) # True
print(type(loaded_user))   # <class '__main__.User'>

Важно: Для unpickle класс должен быть доступен (импортирован).

3. Протоколы Pickle (версии)

import pickle

data = {"name": "Bob", "age": 25}

# Разные протоколы pickle
for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
    serialized = pickle.dumps(data, protocol=protocol)
    print(f"Protocol {protocol}: {len(serialized)} bytes")

# Protocol 0: 54 bytes (ASCII, читаемо)
# Protocol 1: 47 bytes (старше, совместимость)
# Protocol 2: 43 bytes
# Protocol 3: 43 bytes
# Protocol 4: 43 bytes
# Protocol 5: 42 bytes (новый, Python 3.8+, быстрый)

Рекомендация: Используй protocol=pickle.HIGHEST_PROTOCOL для лучшей производительности.

4. РИСКИ БЕЗОПАСНОСТИ ⚠️

КРИТИЧЕСКИ ВАЖНО: Никогда не используй pickle для untrusted данных!

import pickle
import os

# ОПАСНОСТЬ: Malicious pickle может выполнить произвольный код
malicious_data = b'\x80\x03cbuiltins\neval\nq\x00X\x0b\x00\x00\x00os.system("rm -rf /")q\x01\x85q\x02Rq\x03.'

# ❌ НИКОГДА НЕ ДЕЛАЙ ЭТОГО с untrusted данными!
# pickle.loads(malicious_data)  # Выполнит os.system()!

# Правильный способ: используй json или другие безопасные форматы

Почему pickle опасен:

  • Может выполнять произвольный код при deserialization
  • Позволяет вызывать любые Python функции
  • Может удалять файлы, кражу данные и т.д.

5. Альтернативы (безопасные)

JSON (самый безопасный)

import json
from datetime import datetime
from typing import Any

# JSON поддерживает: dict, list, str, int, float, bool, null

data = {
    "name": "Alice",
    "age": 30,
    "tags": ["python", "django"],
    "is_active": True,
    "salary": None
}

# СЕРИАЛИЗАЦИЯ
with open("data.json", "w") as f:
    json.dump(data, f, indent=2)

# JSON файл (читаемо):
# {
#   "name": "Alice",
#   "age": 30,
#   ...
# }

# ДЕСЕРИАЛИЗАЦИЯ
with open("data.json", "r") as f:
    loaded_data = json.load(f)

print(loaded_data)

# Для datetime нужен custom encoder
class DateTimeEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        return super().default(obj)

data_with_date = {"created_at": datetime.now()}
json_str = json.dumps(data_with_date, cls=DateTimeEncoder)
print(json_str)  # {"created_at": "2024-01-15T10:30:45.123456"}

Преимущества:

  • Безопасен для untrusted данных
  • Читаемо
  • Поддерживается везде (JavaScript, Java, и т.д.)
  • Меньше размер файла

Недостатки:

  • Медленнее pickle
  • Не поддерживает все типы Python (datetime, custom классы)

MessagePack (бинарный, быстрый)

import msgpack

data = {"name": "Alice", "age": 30}

# СЕРИАЛИЗАЦИЯ
serialized = msgpack.packb(data)
print(type(serialized))  # <class 'bytes'>
print(len(serialized))   # Меньше чем JSON

# Сохраняем
with open("data.msgpack", "wb") as f:
    f.write(serialized)

# ДЕСЕРИАЛИЗАЦИЯ
with open("data.msgpack", "rb") as f:
    restored = msgpack.unpackb(f.read())

print(restored)  # {b'name': b'Alice', b'age': 30}

Преимущества:

  • Быстро как pickle
  • Безопасно
  • Компактно
  • Поддерживается многими языками

YAML (читаемо, но медленно)

import yaml

data = {"name": "Alice", "age": 30, "tags": ["python", "django"]}

# СЕРИАЛИЗАЦИЯ
with open("data.yaml", "w") as f:
    yaml.dump(data, f)

# YAML файл (очень читаемо):
# name: Alice
# age: 30
# tags:
# - python
# - django

# ДЕСЕРИАЛИЗАЦИЯ
with open("data.yaml", "r") as f:
    restored = yaml.safe_load(f)  # safe_load! не load!

Важно: Используй yaml.safe_load(), а не yaml.load() (как pickle, может выполнить код).

Сравнение форматов

ФорматСкоростьРазмерБезопасностьЧитаемоТипы
PickleОчень быстроСредне❌ ОпасноНетВсе Python
JSONМедленноСредне✅ Безопасно✅ ДаБазовые
MessagePackБыстроМалко✅ БезопасноНетБазовые
YAMLМедленноСредне✅ safe_load✅ ДаБазовые
ProtobufОчень быстроОчень мало✅ БезопасноНетSchema

Лучшие практики

# ❌ ПЛОХО
import pickle
data = pickle.loads(untrusted_bytes)  # Опасно!

# ✅ ХОРОШО
import json
data = json.loads(untrusted_str)  # Безопасно

# ✅ ХОРОШО: Pickle только для trusted данных
with open("my_data.pkl", "wb") as f:
    pickle.dump(my_object, f)

# ✅ ХОРОШО: Использовать protocol=pickle.HIGHEST_PROTOCOL
pickle.dumps(data, protocol=pickle.HIGHEST_PROTOCOL)

# ✅ ХОРОШО: Используй JSON для API и межсервисного взаимодействия
import json
response = json.dumps({"status": "ok", "data": data})

Правило выбора

  1. Untrusted данные → JSON (безопасно)
  2. Собственные объекты, нужна скорость → Pickle (but trusted source only)
  3. Межсервисное взаимодействие → JSON или Protobuf
  4. Локальный cache, производительность критична → MessagePack
  5. Конфигурационные файлы → YAML или JSON
Сериализация и десериализация (pickle) | PrepBro