Работа с файлами и сериализацией: pickle, shelve, marshal
Зачем нужна сериализация? 🤔
Когда мы работаем с данными в Python, часто возникает необходимость сохранить их между запусками программы или передать другому приложению. Вот тут на сцену выходит сериализация — процесс преобразования объектов Python в поток байтов (и обратно).
Представьте, что ваш объект — это сложный LEGO-домик. Сериализация аккуратно разбирает его, упаковывает в коробку (файл), а десериализация — снова собирает домик из деталей!
📦 Модуль pickle — ваш универсальный упаковщик
Стандартный модуль pickle — самый популярный инструмент для сериализации в Python. Он умеет работать практически с любыми объектами!
Основные методы:
pickle.dump(obj, file)— сохраняет объект в файлpickle.load(file)— загружает объект из файлаpickle.dumps(obj)— возвращает сериализованный объект как bytespickle.loads(bytes)— восстанавливает объект из bytes
import pickle
# Сохраняем список в файл
data = {'name': 'Alice', 'age': 25, 'pets': ['cat', 'dog']}
with open('data.pickle', 'wb') as f:
pickle.dump(data, f)
# Загружаем обратно
with open('data.pickle', 'rb') as f:
loaded_data = pickle.load(f)
print(loaded_data) # {'name': 'Alice', 'age': 25, 'pets': ['cat', 'dog']}
🔐 Важно: pickle небезопасен! Загружайте данные только из доверенных источников.
🗄️ Модуль shelve — как словарь, но в файле
Если вам нужно хранить много данных с быстрым доступом по ключу, shelve — отличное решение. По сути, это персистентный словарь!
import shelve
# Записываем данные
with shelve.open('mydata') as db:
db['user1'] = {'name': 'Bob', 'score': 100}
db['user2'] = {'name': 'Alice', 'score': 200}
# Читаем позже
with shelve.open('mydata') as db:
print(db['user1']) # {'name': 'Bob', 'score': 100}
✨ Особенности:
- Ключи должны быть строками
- Значения — любые объекты, поддерживаемые
pickle - Автоматически создает несколько файлов для хранения данных
🚀 Модуль marshal — для внутренних нужд Python
Этот модуль используется в основном для сериализации байт-кода Python. Он быстрее pickle, но имеет ограничения:
import marshal
data = [1, 2, 3, 'hello']
serialized = marshal.dumps(data)
restored = marshal.loads(serialized)
print(restored) # [1, 2, 3, 'hello']
⚠️ Осторожно:
- Не гарантирует совместимость между версиями Python
- Не подходит для сложных объектов
- Документация прямо говорит: "Не используйте для сериализации произвольных данных"
🏆 Какой модуль выбрать?
Вот простая шпаргалка:
| Критерий | pickle | shelve | marshal |
|---|---|---|---|
| Универсальность | ★★★★★ | ★★★★☆ | ★★☆☆☆ |
| Скорость | ★★★☆☆ | ★★★☆☆ | ★★★★★ |
| Безопасность | ★☆☆☆☆ | ★☆☆☆☆ | ★★★☆☆ |
| Простота | ★★★★☆ | ★★★★★ | ★★★☆☆ |
Для большинства задач подходит pickle. Нужно хранить много данных с доступом по ключу — выбирайте shelve. А marshal лучше оставить для специальных случаев.
💡 Продвинутые техники
Сериализация собственных классов
pickle умеет работать с пользовательскими классами, если они определены в модуле:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
person = Person('Alice', 30)
with open('person.pickle', 'wb') as f:
pickle.dump(person, f)
Сжатие данных
Комбинируйте pickle с модулем gzip для экономии места:
import gzip
data = [i**2 for i in range(1000)]
with gzip.open('data.pgz', 'wb') as f:
pickle.dump(data, f)
🛠️ Реальный пример: кэширование функций
Давайте применим знания на практике — реализуем простой кэш:
import pickle
from functools import wraps
from pathlib import Path
def cache_to_file(filename):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
cache_file = Path(filename)
if cache_file.exists():
with open(cache_file, 'rb') as f:
return pickle.load(f)
result = func(*args, **kwargs)
with open(cache_file, 'wb') as f:
pickle.dump(result, f)
return result
return wrapper
return decorator
@cache_to_file('factorial_cache.pickle')
def factorial(n):
return 1 if n < 2 else n * factorial(n-1)
Теперь результаты вычислений будут сохраняться между запусками программы!
🚨 Ошибки и как их избежать
-
Смешивание режимов файлов
Всегда открывайте файлы в правильном режиме:
✅'wb'для записи,'rb'для чтения вpickle
❌ Использование текстового режима ('w','r') вызовет ошибку -
Бесконечная рекурсия
При сериализации объектов с циклическими ссылками используйте:
python pickle.dump(obj, file, protocol=pickle.HIGHEST_PROTOCOL) -
Совместимость версий
Для кросс-версионной совместимости используйте протокол 4:
python pickle.dumps(obj, protocol=4)
🔍 Дополнительные ресурсы
Для тех, кто хочет копнуть глубже:
- Официальная документация Python по pickle и shelve
- Видео Данилы Бежина о сериализации и JSON
- Модуль json для работы с текстовыми форматами (альтернатива pickle)