Пусть объект сериализует сам себя

Вопрос или проблема

С помощью JSON или Pickle я могу создать объект и сохранить его следующим образом:

import pickle 
thing = Thingy(some_data) # занимает некоторое время...
# ... делаем с thing что-то, а затем сохраняем, так как он в основном одинаков
with open(filename, 'wb') as f: 
    pickle.dump(thing, f)

И считать его обратно, если файл сериализации существует:

if thingy_file.exists():
    with open(filename, 'rb') as f:
        thing=pickle.load(f)  # Thingy(some_data) восстановлен
    thing.updateyourself()  # довольно быстро
else: 
    thing=Thingy(some_data) # занимает некоторое время...

Тем не менее, эта сериализация выполняется внешне к созданию объекта Thingy…

Теперь предположим, что у меня есть какой-то пользовательский объект с внутренними данными, создание которого занимает долгое время:

class Thingy:
   def __init__(self, stuff...)
      # это занимает некоторое время в первый раз, но может быть мгновенным, если считать обратно
      # псевдо'шное:
        if self.file.exists():
            print(f'Существующий Thingy {self.file} найден...')
            # То, что я пытаюсь сделать здесь, это считать из файла сериализации
            # данные, составляющие в последний раз, когда ЭТА ИНСТАНСИЯ Thingy существовала
             with open(self.file, 'rb') as f:
                self=pickle.load(f)
        else:
           # печально, это займет некоторое время...
        
        # остальная часть кода объекта...
        # ЗДЕСЬ мы либо вычислили Thingy, либо считали его обратно в себя
        # Экземпляр может быть изменен -- теперь сохраните себя, чтобы быть прочитанным в будущем:
     
    def save_my_own_instance(self):
       # теперь self - это этот экземпляр Thingy, который будет сериализован на диск
       # магия, о которой я не знаю...
       with open(self.file, 'wb') as f:
            pickle.dump(my_own_instance_data, f, pickle.HIGHEST_PROTOCOL)

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


Некоторые конкретные сведения о том, что такое Thingy:

from pathlib import Path

p=Path(a_HUGE_tree_of_data_files) # 750k плюс большие изображения

tree_data=Thingy(p)
# первоначальный запуск на p занимает более часа
# дерево меняется очень мало, поэтому обновление Thingy(p) 
# занимает всего несколько секунд

# Я не хочу учить пользователя проверять предыдущие запуски
# Это должно происходить автоматически

Одно из решений этого могло бы быть созданием статического метода в Thingy.

class Thingy:
    @staticmethod
    def load_instance_from_file_or_create_new(filename, stuff):
        if filename.exists():# проверяем, существует ли файл
            with open(filename, 'rb') as f:
                instance=pickle.load(f)
            return instance
        return Thingy(stuff)

    def __init__(self, stuff):
        # создаем экземпляр обычным образом

    def __del__(self):
        # экземпляр удаляется
        with open(self.file, 'wb') as f:
            pickle.dump(my_own_instance_data, f, pickle.HIGHEST_PROTOCOL)

который можно использовать как

thing = Thingy.load_instance_from_file_or_create_new("xyz",stuff)

Статический метод загружает экземпляр Thingy из файла, если файл существует, иначе создает новый экземпляр с предоставленным stuff.
Вам нужно будет хранить filename вне Thingy, если у вас нет значения по умолчанию для filename.
А метод __del__ автоматически вызывается всякий раз, когда экземпляр уничтожается, поэтому вы можете использовать его для сохранения экземпляра в файл.

Ответ или решение

Как обеспечить само сериализацию объекта в Python

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

1. Автоматическая загрузка состояния объекта

Можно использовать статический метод для проверки существования сериализованного файла и загрузки из него объекта, если файл доступен. Это позволяет избавиться от необходимости вручную проверять существование файла перед созданием нового объекта.

import pickle
from pathlib import Path

class Thingy:
    def __init__(self, stuff, file_name):
        self.file = Path(file_name)

        if self.file.exists():
            print(f'Existing Thingy {self.file} found...')
            # Дезериализация
            with open(self.file, 'rb') as f:
                loaded_instance = pickle.load(f)
                self.__dict__.update(loaded_instance.__dict__)
        else:
            print('Creating a new Thingy instance...')
            self.stuff = stuff
            # Остальные операции по инициализации

    def save_my_own_instance(self):
        # Сериализация
        with open(self.file, 'wb') as f:
            pickle.dump(self, f, pickle.HIGHEST_PROTOCOL)

# Использование
thing = Thingy('some_data', 'thingy_data.pkl')
# Производим операции с объектом
thing.save_my_own_instance()  # Сохраняем текущее состояние

2. Метод для сохранения состояния

В методе save_my_own_instance объект использует его текущее состояние и сохраняет его в файл. Этот метод позволяет пользователю легко сохранять изменения, которые были внесены в объект, чтобы их можно было восстанавливать позже.

3. Использование специального метода и делегирования

Кроме статического метода, можно использовать метод экземпляра для загрузки состояния. Например, можно включить вызов save_my_own_instance в метод __del__, который вызовется при уничтожении объекта.

    def __del__(self):
        self.save_my_own_instance()

Однако следует быть осторожным при использовании этого метода, так как механизмы сборки мусора Python могут не всегда вызывать __del__ как ожидается. Предпочтительнее явно вызывать метод сохранения состояния, когда это необходимо.

4. Обработка состояния

При загрузке состояния с помощью уменьшения self.__dict__, мы передаем данные из загруженного экземпляра копируя атрибуты, что гарантирует, что все необходимые данные будут доступны в экземпляре.

Заключение

Реализация само-сериализации в классе в Python позволяет сократить время на создание объектов, избегая ненужных вычислений при повторной инициализации. Подход с использованием метода для загрузки и создания новых экземпляров, а также возможности сериализовать себя, делает работу с объектами более оптимизированной и интуитивной. Это особенно полезно в сценариях, когда данные объекта требуют значительных вычислительных ресурсов и время создания объекта должно быть минимизировано.

Оцените материал
Добавить комментарий

Капча загружается...