Сохранение и восстановление объектов
Текстовые форматы, например, CSV и JSON могут сериализовать (преобразовать в текст) только базовые типы: строки, числа, списки и словари, однако они не позволят сохранить данные более сложных типов, например, экземпляры классов.
Модуль pickle был создан специально для решения этой проблемы. С английского слово «pickle» переводится как «соленья», что является метафорой для «консервирования» данных.
Ключевой особенностью модуля pickle является использование двоичного формата хранения данных. Он не пытается быть читаемым человеком, а сосредоточен на сохранении внутренней структуры объекта. По этой причине файлы, созданные с помощью pickle, всегда открываются в двоичном режиме (с буквой «b» в конце, например, "rb" или "wb").
Перед началом работы модуль pickle следует импортировать:
import pickle
Сохранение и восстановление объектов в файлах
Модуль pickle работает с файлами с расширением .pkl, то есть он может как записать в такой файл состояние объекта c помощью функции pickle.dump(), так и прочитать его, то есть восстановить записанный объект с помощью функции pickle.load().
Сохранение состояния объекта в файл
Использование функции pickle.dump() является самым простым способом для сохранения сложной структуры данных Python.
|
Функция |
|
|
Описание |
Преобразует объект |
|
Параметры |
|
|
Возвращаемое значение |
None |
Одним из главных преимуществ модуля pickle является возможность сохранять экземпляры классов. Допустим, у нас есть класс Artist для представления информации о художнике:
class Artist:
def __init__(self, name: str, style: str):
self.name = name
self.style = style
def get_info(self) -> str:
return f"{self.name}, стиль: {self.style}"
Мы можем создать экземпляр этого класса и сохранить его в файл с расширением .pkl:
shishkin_obj = Artist("Шишкин В.В.", "Реализм")
with open("shishkin.pkl", "wb") as shishkin_file:
# Сохраняем не только данные, но и принадлежность к классу Artist
pickle.dump(shishkin_obj, shishkin_file)
После выполнения этого кода в папке с программой появится файл shishkin.pkl с сохранённым экземпляром класса Artist.
Важно понимать, что модуль pickle сохраняет только сами данные и информацию о типе, но не код класса. Другими словами, он сохраняет состояние экземпляра класса, то есть значения всех его атрибутов и включает в себя все данные, которые были присвоены переменным внутри объекта, например, self.name или self.style. Однако методы являются частью определения класса, а не конкретного экземпляра, поэтому они не сохраняются.
В дополнение к данным, модуль pickle записывает в двоичный поток информацию о том, к какому классу принадлежит этот объект, например, <class '__main__.Artist'>. Эта информация является ссылкой на определение класса в коде Python.
Восстановление сохранённого объекта из файла
Для восстановления сохраненного объекта необходимо использовать функцию pickle.load().
|
Функция |
|
|
Описание |
Восстанавливает объект Python из потока байтов в файле |
|
Параметры |
|
|
Возвращаемое значение |
Восстановленный объект |
Давайте восстановим объект из файла shishkin.pkl, который мы сохранили ранее:
with open("shishkin.pkl", "rb") as shishkin_file:
shishkin_restored_obj = pickle.load(shishkin_file)
print(shishkin_restored_obj.name)
# Вывод: Шишкин В.В.
Как видно, объект восстановлен успешно, так как мы можем обратиться к его атрибутам.
При восстановлении объекта Python считывает из файла информацию о типе и пытается найти определение этого класса в текущем пространстве имён или среди импортированных модулей. После этого он создаёт новый, пустой экземпляр найденного класса и заполняет его атрибуты сохраненными значениями, извлеченными из файла.
Работа со строками
Модуль pickle позволяет работать с объектами без непосредственного сохранения их в файл на диске, сохраняя и восстанавливая их через байтовую строку в памяти с помощью функций pickle.dumps() и pickle.loads() соответственно.
Эти функции выполняют ту же самую работу, что и функции для работы с файлами pickle.dump() и pickle.load(), но используют в качестве носителя данных не файл, а объект типа bytes в оперативной памяти.
Сохранение состояния объекта в байтовую строку
Функция pickle.dumps() принимает объект Python и преобразует его в последовательность байтов bytes, возвращая её как результат.
|
Функция |
|
|
Описание |
Преобразует объект |
|
Параметры |
|
|
Возвращаемое значение |
Байтовая строка |
Чаще всего эта функция применяется для подготовки данных для передачи по сети, встраивания в базу данных или передачи данных между процессами внутри одной программы.
Давайте создадим ещё один экземпляр класса Artist и преобразуем его в последовательность байтов:
aivazovsky_obj = Artist("Айвазовский И.К.", "Романтизм")
binary_payload = pickle.dumps(aivazovsky_obj)
print(binary_payload)
# Вывод: b'\x80\x04\x95c\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x06Artist\x94\x93\x94)\x81\x94}\x94(\x8c\x04name\x94\x8c\x1d\xd0\x90\xd0\xb9\xd0\xb2\xd0\xb0\xd0\xb7\xd0\xbe\xd0\xb2\xd1\x81\xd0\xba\xd0\xb8\xd0\xb9 \xd0\x98.\xd0\x9a.\x94\x8c\x05style\x94\x8c\x12\xd0\xa0\xd0\xbe\xd0\xbc\xd0\xb0\xd0\xbd\xd1\x82\xd0\xb8\xd0\xb7\xd0\xbc\x94ub.'
print(type(binary_payload))
# Вывод: <class 'bytes'>
Результат всегда имеет тип bytes (байтовая строка), а не str (текстовая строка), поскольку модуль pickle работает только с двоичными файлами.
Восстановление объекта из байтовой строки
Функция pickle.loads() принимает байтовую строку, содержащую данные pickle, и восстанавливает исходный объект Python в памяти.
|
Функция |
|
|
Описание |
Восстанавливает объект из байтовой строки |
|
Параметры |
|
|
Возвращаемое значение |
Восстановленный объект |
Эта функция применяется для восстановления объекта, который был сериализован с помощью функции pickle.dumps() или получен из внешнего источника, например, как ответ сервера.
Давайте восстановим байтовую строку binary_payload, в которую мы ранее сохранили экземпляр класса Artist:
aivazovsky_restored_obj = pickle.loads(binary_payload)
print(aivazovsky_restored_obj.name)
# Вывод: Айвазовский И.К.
print(type(aivazovsky_restored_obj))
# Вывод: <class '__main__.Artist'>
Как видно, объект полностью восстановлен, так как мы можем обращаться к его атрибутам, а его тип определяется как экземпляр класса Artist.
Безопасность при восстановлении данных
Никогда не используйте функцию pickle.load() для восстановления данных, полученных из недостоверных источников, так как они могут содержать вредоносные инструкции.
Модуль pickle следует использовать только для обмена данными между процессами, которые вы полностью контролируете, например, для сохранения состояния вашей собственной программы.
Примеры
Пример 1. Восстановление множества тегов
Класс Article описывает статью, у которой есть название title и множество уникальных тегов tags. JSON не поддерживает напрямую тип множества set и при сериализации преобразует его в список, теряя семантику уникальности элементов. Модуль pickle сохраняет тип объекта, гарантируя, что при восстановлении атрибут tag_set останется множеством:
import pickle
class Article:
"""Описывает статью с уникальными тегами."""
def __init__(self, title: str, tags: set):
"""Конструктор класса Article.
Параметры:
title: Заголовок статьи.
tags: Множество уникальных тегов.
"""
self.title = title
self.tag_set = {tag.lower() for tag in tags}
def add_tag(self, tag: str) -> None:
"""Добавляет новый тег в множество."""
self.tag_set.add(tag.lower())
article_obj = Article("Основы Python", {"Программирование", "Учеба"})
article_obj.add_tag("Python") # Добавляем тег
article_obj.add_tag("Учеба") # Тег "учеба" уже есть, дубликат не добавится
# Сохраняем экземпляр класса
with open("article_object.pkl", "wb") as article_object:
pickle.dump(article_obj, article_object)
# Восстанавливаем экземпляр класса
with open("article_object.pkl", "rb") as article_object:
restored_article = pickle.load(article_object)
# Проверяем, что множество сохранилось
print(f"Теги после восстановления: {restored_article.tag_set}")
Вывод:
Теги после восстановления: {'программирование', 'учеба', 'python'}
Пример 2. Сохранение нескольких объектов настроек в одном файле
Класс Config упрощённо описывает настройки в системе, а его экземпляр config_obj представляет настройки логирования. Кроме этого, в программе определён список user_list с именами пользователей и словарь metadata с метаданными. Для удобства архивирования состояния программы все объекты сохраняются в одном файле multi_archive.pkl. Однако восстановление должно происходить строго в том же порядке, в котором они были записаны, для того чтобы функция pickle.load() могла правильно восстановить каждый объект:
import pickle
class Config:
"""Описывает настройки системы"""
def __init__(self, key: str, value: str):
"""Конструктор класса Config.
Параметры:
key: Название настройки.
value: Значение настройки.
"""
self.key = key
self.value = value
config_obj = Config("LOG_LEVEL", "DEBUG")
user_list = ["alastor77", "big_vox", "adam999"]
metadata = {"Количество записей": 3, "Количество комментариев": 2}
# Последовательно сохраняем
with open("multi_archive.pkl", "wb") as multi_archive:
pickle.dump(config_obj, multi_archive)
pickle.dump(user_list, multi_archive)
pickle.dump(metadata, multi_archive)
# Последовательно восстанавливаем
with open("multi_archive.pkl", "rb") as multi_archive:
restored_config = pickle.load(multi_archive)
restored_user_list = pickle.load(multi_archive)
restored_metadata = pickle.load(multi_archive)
print(f"Восстановленный объект 1: {restored_config.key}: {re-stored_config.value}")
print(f"Восстановленный объект 2: Последний пользователь: {restored_user_list[-1]}")
print(f"Восстановленный объект 3: Всего записей: {restored_metadata['Количество записей']}")
Вывод:
Восстановленный объект 1: LOG_LEVEL: DEBUG
Восстановленный объект 2: Последний пользователь: adam999
Восстановленный объект 3: Всего записей: 3
Пример 3. Сохранение состояния программы
В задачах анализа данных или машинного обучения промежуточные результаты, такие как обученные модели или предварительно обработанные большие наборы данных, могут быть получены после многих часов работы. Модуль pickle позволяет сохранить весь контекст как есть (включая сложные объекты Python), а затем мгновенно его восстановить.
Вместо того, чтобы каждый раз открывать файл и записывать в него данные, в программе определена функция save_context(context, filename), которая сохраняет словарь context в файл filename, и функция load_context(filename), которая восстанавливает объекты из этого файла:
import pickle
from datetime import datetime
def save_context(context: dict, filename: str) -> None:
"""Сохраняет словарь с рабочим контекстом в файл.
Параметры:
context: Словарь, с состоянием программы (переменные, данные).
filename: Имя файла, в который сохраняется контекст.
"""
with open(filename, "wb") as file:
pickle.dump(context, file)
print(f"Данные сохранены в файл {filename}")
def load_context(filename: str) -> dict:
"""Восстанавливает словарь с рабочим контекстом из файла.
Аргументы:
filename: Имя файла, из которого восстанавливается контекст.
Возвращает:
Восстановленный словарь с состоянием программы.
"""
with open(filename, "rb") as file:
context = pickle.load(file)
print(f"Данные восстановлены из файла {filename}")
return context
# Имитация сложного рабочего процесса
project_context = {
"step_name": "Feature Engineering",
"dataset_size": 15_000,
"processed_cols": ["age", "income", "city_id"],
"is_model_trained": False,
"last_run": datetime.now()
}
# Сохраняем данные
save_context(project_context, "data_pipeline_context.pkl")
# Восстанавливаем данные
restored_context = load_context("data_pipeline_context.pkl")
print(f"Восстановленная дата запуска: {restored_context['last_run']}")
print(f"Восстановленные имена столбцов: {re-stored_context['processed_cols']}")
Вывод:
Данные сохранены в файл data_pipeline_context.pkl
Данные восстановлены из файла data_pipeline_context.pkl
Восстановленная дата запуска: 2025-12-01 00:06:49.297822
Восстановленные имена столбцов: ['age', 'income', 'city_id']
Итоги
- Модуль
pickleпредоставляет функции для сохранения и восстановления объектов Python. - Функция
pickle.dump()преобразует объект в байтовую строку и записывает её в файл с расширением.pkl, а функцияpickle.load()восстанавливает объект из этого файла. - Функции
pickle.dumps()иpickle.loads()выполняют ту же самую работу, но используют в качестве носителя данных не файловый объект, а объект типаbytesв оперативной памяти. - Не рекомендуется использовать функцию
pickle.load()для восстановления данных, полученных из недостоверных источников, так как они могут содержать вредоносные инструкции.
Задания для самопроверки
1. В каком формате модуль pickle сохраняет данные? Какие есть особенности открытия этого файла с помощью функции open()?
Данные сохраняются в двоичном формате в файлах с расширением .pkl. Они должны открываться в бинарном режиме (с буквой «b» в конце режима, например, "rb")
2. Можно ли восстановить экземпляр класса из файла, если код этого класса не определён в текущем пространстве имён?
Нет, так как модуль picke сохраняет данные, а не код класса.
3. Дан следующий класс, описывающий задачу в системе:
class Task:
"""Описывает задачу в системе"""
def __init__(self, title: str, due_date: str, status: str="В процессе"):
"""Конструктор класса Task.
Параметры:
title: Название задачи.
due_date: Крайний срок.
status: Статус. По умолчанию 'В процессе'
"""
self.title = title
self.due_date = due_date
self.status = status
def complete(self) -> None:
"""Отмечает задачу завершённой.
"""
self.status = "Завершена"
Создайте экземпляр этого класса task1 = Task("Написать отчёт", "2025-12-05") и сохраните его в файле task1.pkl.
task1 = Task("Написать отчёт", "2025-12-05")
with open("task1.pkl", "wb") as task_backup:
pickle.dump(task1, task_backup)
4. Используйте файл task1.pkl из задания 3 и восстановите объект из него. Выведите на экран атрибут title этого объекта.
with open("task1.pkl", "rb") as task_backup:
restored_task = pickle.load(task_backup)
print(restored_task.title)
# Вывод: Написать отчёт
5. Преобразуйте восстановленный объект из задания 4 в байтовую строку и выведите на экран её тип.
with open("task1.pkl", "rb") as task_backup:
restored_task = pickle.load(task_backup)
byte_string = pickle.dumps(restored_task)
print(type(byte_string))
# Вывод: <class 'bytes'>
0 комментариев