Текстовые форматы, например, CSV и JSON могут сериализовать (преобразовать в текст) только базовые типы: строки, числа, списки и словари, однако они не позволят сохранить данные более сложных типов, например, экземпляры классов.

Модуль pickle был создан специально для решения этой проблемы. С английского слово «pickle» переводится как «соленья», что является метафорой для «консервирования» данных.

Ключевой особенностью модуля pickle является использование двоичного формата хранения данных. Он не пытается быть читаемым человеком, а сосредоточен на сохранении внутренней структуры объекта. По этой причине файлы, созданные с помощью pickle, всегда открываются в двоичном режиме (с буквой «b» в конце, например, "rb" или "wb").

Перед началом работы модуль pickle следует импортировать:

import pickle

Сохранение и восстановление объектов в файлах

Модуль pickle работает с файлами с расширением .pkl, то есть он может как записать в такой файл состояние объекта c помощью функции pickle.dump(), так и прочитать его, то есть восстановить записанный объект с помощью функции pickle.load().

Сохранение состояния объекта в файл

Использование функции pickle.dump() является самым простым способом для сохранения сложной структуры данных Python.

Функция

pickle.dump(obj, file)

Описание

Преобразует объект obj в последовательность байтов и записывает её в файл file

Параметры

  • obj – объект Python, который требуется сохранить
  • file – файловый объект

Возвращаемое значение

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().

Функция

pickle.load(file)

Описание

Восстанавливает объект Python из потока байтов в файле file

Параметры

  • file – файловый объект

Возвращаемое значение

Восстановленный объект

Давайте восстановим объект из файла 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, возвращая её как результат.

Функция

pickle.dumps(obj)

Описание

Преобразует объект obj в последовательность байтов

Параметры

  • obj – объект Python, который требуется сохранить

Возвращаемое значение

Байтовая строка

Чаще всего эта функция применяется для подготовки данных для передачи по сети, встраивания в базу данных или передачи данных между процессами внутри одной программы.

Давайте создадим ещё один экземпляр класса 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.loads(data)

Описание

Восстанавливает объект из байтовой строки data

Параметры

  • data – байтовая строка

Возвращаемое значение

Восстановленный объект

Эта функция применяется для восстановления объекта, который был сериализован с помощью функции 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'>