JavaRush /Курсы /Модуль 1: Python Core /Управление сериализацией

Управление сериализацией

Модуль 1: Python Core
12 уровень , 9 лекция
Открыта

10.1 Все будет по-моему!

Иногда в ваших объектах хранится много ссылок на различные служебные объекты, которые вы не хотите передавать по сети, или которые невозможно передать по сети: ссылки на файлы, базы данных и т.п.

Чтобы сериализация работала и в таких случаях, придумали дать классу возможность самому управлять своей сериализацией. Для этого используются специальные методы: __reduce__(), __getstate__(), __setstate__(). Эти методы позволяют указать, как объекты должны быть сериализованы и восстановлены.

Основные методы управляемой сериализации:

  • __reduce__(): Указывает, как объект должен быть сериализован.
  • __getstate__(): Возвращает состояние объекта для сериализации.
  • __setstate__(self, state): Восстанавливает объект из состояния.

Ниже я расскажу о них подробнее, и как их вместе использовать.

10.2 Метод __reduce__()

Метод __reduce__() возвращает кортеж, который указывает, как объект должен быть сериализован и десериализован. Кортеж обычно содержит:

  • Ссылку на функцию или класс, который будет использоваться для восстановления объекта.
  • Кортеж аргументов для этой функции или класса.
  • Дополнительное состояние объекта (если необходимо).

Пример:


import pickle

class CustomClass:
    def __init__(self, value):
        self.value = value
            
    def __reduce__(self):
        return (self.__class__, (self.value,))
            
    def __repr__(self):
        return f"CustomClass(value={self.value})"
            
# Создание объекта
obj = CustomClass(42)
            
# Сериализация объекта
serialized_obj = pickle.dumps(obj)
print("Сериализованный объект:", serialized_obj)
            
# Десериализация объекта
deserialized_obj = pickle.loads(serialized_obj)
print("Десериализованный объект:", deserialized_obj)

По умолчанию функция __reduce__() имеет такое поведение:


class CustomClass:
    def __init__(self, value):
        self.value = value
        
    def __reduce__(self):
        # Определение класса
        cls = self.__class__
        # Аргументы конструктора
        args = (self.value,)
        # Состояние объекта
        state = self.__dict__
        return (cls, args, state)

Она возвращает кортеж, состоящий из трёх объектов:

  • Ссылка на текущий класс
  • Аргументы конструктора (кортеж)
  • Ссылка на текущее состояние объекта

Если вас такое поведение устраивает — __reduce__() можете не переопределять.

10.3 Чтение и запись состояния

Методы __getstate__() и __setstate__()

Эти методы используются для управления состоянием объекта во время сериализации и десериализации.

  • __getstate__(): Возвращает состояние объекта, которое должно быть сериализовано.
  • __setstate__(self, state): Восстанавливает объект из состояния.

Пример:

Допустим, мы хотим сохранить не все поля объекта, а исключить некоторые из них. Для этого в методе __getstate__() нужно:

  1. Скопировать текущее состояние объекта (заданное служебным полем __dict__) в отдельную переменную — словарь state.
  2. Удалить из неё все поля, которые не нужно сериализовать.
  3. Вернуть полученный объект как результат функции __getstate__().

import pickle

class CustomClass:
    def __init__(self, value):
        self.value = value
        self.internal_state = "internal"
            
    def __getstate__(self):
        state = self.__dict__.copy()
        del state['internal_state']  # Исключаем внутреннее состояние
        return state
            
    def __setstate__(self, state):
        self.__dict__.update(state)
        self.internal_state = "restored internal"  # Восстанавливаем внутреннее состояние
            
    def __repr__(self):
        return f"CustomClass(value={self.value}, internal_state={self.internal_state})"
            
# Создание объекта
obj = CustomClass(42)
print("Оригинальный объект:", obj)
            
# Сериализация объекта
serialized_obj = pickle.dumps(obj)
print("Сериализованный объект:", serialized_obj)
            
# Десериализация объекта
deserialized_obj = pickle.loads(serialized_obj)
print("Десериализованный объект:", deserialized_obj)

При десериализации, в функции __setstate__(), мы делаем две вещи:

  1. Обновляем текущее состояние объекта с помощью метода update().
  2. Поле internal_state (и другие несериализуемые поля) получают новые значения.
2
Задача
Модуль 1: Python Core, 12 уровень, 9 лекция
Недоступна
Использованиее метода reduce()
Использованиее метода reduce()
2
Задача
Модуль 1: Python Core, 12 уровень, 9 лекция
Недоступна
Исключение несериализуемых полей
Исключение несериализуемых полей
1
Опрос
Работа с директориями, 12 уровень, 9 лекция
Недоступен
Работа с директориями
Работа с директориями
Комментарии (6)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Slevin Уровень 7
10 июля 2025
Первая задача интересная, пришлось повозиться. Вторая задача могла бы быть тоже интересной и с ней пришлось повозиться, но затем валидатор в ответ на ПРАВИЛЬНОЕ решение поел говна и начал требовать вообще непонятно чего. По сути просто копируя размытое условие задачи. Принимать РАБОТАЮЩЕЕ решение никак не хотел, потому пришлось просто скопировать код из "правильного" ответа. Это полная хрень и тотальный дизлайк. Пример непринятого решения:

import pickle
from typing import Any, Union

class Student:

    def __init__(self, name: str, age: int, **internal_attr: Any) -> None:
        self.name = name
        self.age = age
        self.internal_attr: dict[str, Union[int|float|str]] = internal_attr

    def __getstate__(self):
        state = self.__dict__.copy()
        del state['internal_attr']
        return state

    def __setstate__(self, state):
        self.__dict__.update(state)
        self.internal_attr = {'internal_attr': 'restored'}

    def __repr__(self):
        extras = ', '.join(f'{key}: {value!r}' for key, value in self.internal_attr.items())
        return f'Student: {self.name!r}, {self.age!r}, {extras}'


student_1 = Student("John", 22, sex = 'male', grade = 3)
print(student_1)

student_ser = pickle.dumps(student_1)
print(student_ser)

student_deser = pickle.loads(student_ser)
print(student_deser)

Mike Starman Уровень 23
23 марта 2025
Чому у вас є вимоги до задач, а в мене не може бути. Отже вимоги: 1. Обов'язкові поля класу мають бути чітко описані в умові задачі! Не у вимогах після перевірки, а у тексті завдання!!! 🤬 2. Давати більш чітко і зрозуміло теоретичний матеріал до задач. Не в наступній лекції, не "гугліть собі щоб зрозуміти", не "ведіть розумні діалоги з ЧатЖПТ" чи ще щось.
Семён Уровень 34
10 сентября 2024
В очередной раз в правильном решении, то чего нет в лекции, просто класс
Денис Уровень 33
5 января 2025
Если ты про

    @staticmethod
    def _serialize(name, age):
То он для решения задачи не нужен. Она прекрасно решается средствами из лекции. Для починки конструктора достаточно вспомнить тему про значения по умолчанию. Например

class MyClass:
    
    def __init__(self, first=None, second=None):
        self.first = first
        self.second = second
Дмитрий Уровень 27
13 мая 2025
Собственно, статические методы и их применение были в предыдущих лекциях. Скорее сбивает с толку магический префикс, хотя в Python 3 нет магического метода _serialize (но он есть в PHP, например). Так что вполне можно было написать так:

@staticmethod
def pickling(name, age):
    return my_class(name = name, age = age)
и было бы менее непонятно.
Dmitry Ryabov Уровень 23
16 июля 2025
Задача действительно прекрасно решается теми методами, которые описаны в лекции. На самом деле можно обойтись и без дефолтных значений у конструктора. По сути это недостающее значение можно прописать в __reduce()__