Иногда нашим программам нужно сохранить "всё как есть": объекты, данные, состояние — в файл или переслать по сети. Для этого Python предлагает удобные инструменты: сериализация (преобразование объекта в байты) и десериализация (обратное превращение в объект).
Самые популярные модули:
- pickle — сериализует почти всё (но не безопасен для данных из ненадёжных источников).
- json — сериализует данные в человекочитаемом виде (строго типизирован).
- marshal — используется интерпретатором Python (но не рекомендуется для обычного кода).
- dill — расширение pickle, умеет больше.
🔰 Для новичков: сериализуем объект с помощью pickle
import pickle
# Класс, объект которого мы хотим сериализовать
class User:
def __init__(self, name, age):
self.name = name # Имя пользователя
self.age = age # Возраст пользователя
# Создаём экземпляр класса
user = User("Alice", 30)
# Сохраняем объект в файл
with open("user.pkl", "wb") as f: # 'wb' означает запись в бинарном режиме
pickle.dump(user, f) # Сериализация объекта user
# Загружаем объект обратно из файла
with open("user.pkl", "rb") as f: # 'rb' — чтение в бинарном режиме
loaded_user = pickle.load(f) # Десериализация объекта
print(loaded_user.name) # Вывод: Alice
💡 Важно: pickle не безопасен. Никогда не загружай файлы от незнакомцев — это как кушать суши с асфальта.
🔁 Задача с собеседования: сериализуем словарь с кастомным классом
import pickle
class Product:
def __init__(self, id, title, price):
self.id = id
self.title = title
self.price = price
def __repr__(self):
return f"{self.title} (${self.price})"
# Словарь, где ключи — строки, значения — объекты Product
products = {
"item1": Product(1, "Book", 12.99),
"item2": Product(2, "Pen", 1.50)
}
# Сохраняем словарь
with open("products.pkl", "wb") as f:
pickle.dump(products, f)
# Восстанавливаем
with open("products.pkl", "rb") as f:
loaded_products = pickle.load(f)
print(loaded_products["item1"]) # Вывод: Book ($12.99)
🔎 Комментарий: pickle справляется даже со сложными структурами данных. Главное — все объекты внутри должны быть сериализуемыми.
🔥 Продвинутый уровень: сериализуем с __getstate__ и __setstate__
Иногда мы хотим сериализовать не все поля объекта, например, пропустить кэш или временные переменные. Используем __getstate__ и __setstate__:
class Session:
def __init__(self, user, token):
self.user = user
self.token = token
self.cache = {"last_action": "login"} # временное поле
def __getstate__(self):
state = self.__dict__.copy()
del state["cache"] # не сериализуем кэш
return state
def __setstate__(self, state):
self.__dict__.update(state)
self.cache = {} # создаём пустой кэш при десериализации
# Проверим сериализацию
session = Session("admin", "XYZ123")
with open("session.pkl", "wb") as f:
pickle.dump(session, f)
with open("session.pkl", "rb") as f:
restored = pickle.load(f)
print(restored.cache) # Вывод: {}
🤓 Уровень "гуру": сериализация через JSON с __dict__ и кастомным классом
Если вы хотите сериализовать объекты в формат JSON (например, для REST API), нужно немного вручную.
import json
class Note:
def __init__(self, title, tags):
self.title = title
self.tags = tags
note = Note("Learn Python", ["coding", "fun"])
# Преобразуем объект в словарь
note_dict = note.__dict__
# Сериализуем в JSON
json_string = json.dumps(note_dict)
print(json_string) # {"title": "Learn Python", "tags": ["coding", "fun"]}
# Десериализуем обратно
note_data = json.loads(json_string)
restored_note = Note(**note_data)
print(restored_note.title) # Learn Python
💼 Частый вопрос на собеседовании:
❓ Как сериализовать объект, в котором есть несериализуемое поле (например, threading.Lock)?
Ответ:
Исключить это поле из сериализации через __getstate__, а потом создать заново в __setstate__.
🧩 Ещё одна задача: сериализуем иерархию объектов
Класс Person, поле address, объект класса Address.
class Address:
def __init__(self, city):
self.city = city
class Person:
def __init__(self, name, address):
self.name = name
self.address = address
person = Person("Bob", Address("London"))
# Сохраняем
with open("person.pkl", "wb") as f:
pickle.dump(person, f)
# Загружаем
with open("person.pkl", "rb") as f:
loaded = pickle.load(f)
print(loaded.address.city) # London
🐍 Юмор в тему
Сериализация — это когда твой объект, как Феникс, умирает в файл, чтобы потом возродиться обратно в память… только без лишнего веса вроде кэша.
🧊 Десериализация в Python — достаём объекты из файла (или сети!)
Десериализация — это процесс восстановления объекта из сериализованной формы (файла, строки, байтов и т.п.). В Python это особенно просто, но важно понимать, что, как и откуда ты десериализуешь. Разберём всё от простого к сложному.
🎯 1. Простейший пример с pickle
import pickle
# Допустим, у нас уже есть файл с сериализованным объектом (например, User)
class User:
def __init__(self, name, age):
self.name = name
self.age = age
# Десериализация из файла
with open("user.pkl", "rb") as f:
user = pickle.load(f) # загружаем объект
print(user.name) # Alice
print(user.age) # 30
🔍 Что здесь происходит:
- Открываем файл в бинарном режиме на чтение ('rb').
- pickle.load() сам "распознаёт", как воссоздать объект.
- Объект возвращается полностью: методы, поля, даже вложенные объекты.
🧩 2. Десериализация сложных структур (словарь с объектами)
# Допустим, в файле сохранён словарь с объектами Product
class Product:
def __init__(self, id, title, price):
self.id = id
self.title = title
self.price = price
def __repr__(self):
return f"{self.title} (${self.price})"
# Десериализуем словарь
with open("products.pkl", "rb") as f:
products = pickle.load(f)
print(products["item1"]) # Book ($12.99)
✅ Важно: Все классы должны быть определены ДО загрузки, иначе pickle не сможет воссоздать объект.
🦾 3. Десериализация с кастомной логикой (__setstate__)
class Session:
def __init__(self, user, token):
self.user = user
self.token = token
self.cache = {"last_action": "login"}
def __getstate__(self):
state = self.__dict__.copy()
del state["cache"] # не сериализуем кэш
return state
def __setstate__(self, state):
self.__dict__.update(state)
self.cache = {} # после загрузки создаём пустой кэш
# Восстановление объекта из файла
with open("session.pkl", "rb") as f:
session = pickle.load(f)
print(session.user) # admin
print(session.cache) # {}
🔧 Преимущество:
Мы контролируем, что именно сохраняется и что происходит при загрузке. Особенно полезно для временных или невалидируемых полей.
🧙♂️ 4. JSON-десериализация
Когда работаешь с REST API, ты получаешь JSON. А это не pickle, так что нужно собирать объекты вручную.
import json
class Note:
def __init__(self, title, tags):
self.title = title
self.tags = tags
# JSON-строка, полученная из API или файла
json_string = '{"title": "Learn Python", "tags": ["fun", "code"]}'
# Преобразуем JSON в словарь
data = json.loads(json_string)
# Превращаем в объект Note
note = Note(**data)
print(note.title) # Learn Python
🧪 5. Задача с собеседования: безопасная десериализация
❓ Как безопасно десериализовать JSON, в котором может быть неизвестная структура?
import json
def safe_deserialize(json_string):
try:
return json.loads(json_string)
except json.JSONDecodeError:
return None # или логировать ошибку
data = safe_deserialize('{"key": "value"}')
print(data["key"]) # value
🤔 Совет:
- JSON — безопаснее pickle, но требует ручного создания объектов.
- pickle — удобнее, но только для "своих" данных.
💼 Интервью-вопрос: Десериализация с восстановлением связи объектов
class Author:
def __init__(self, name):
self.name = name
class Book:
def __init__(self, title, author: Author):
self.title = title
self.author = author
# Загрузка из файла
with open("book.pkl", "rb") as f:
book = pickle.load(f)
print(book.title) # Clean Code
print(book.author.name) # Robert C. Martin
🔍 Важно:
pickle сам восстанавливает вложенные объекты — и это его суперсила 💪
🤡 Немного юмора:
Десериализация — это как магия: ты шепчешь "load", и объект возвращается из цифрового небытия. Главное — чтобы его не звали Voldemort.