Мой модуль содержит класс, который должен быть разборчивым, как экземпляр, так и определение. Я имею следующую структуру:
MyModule
|-Submodule
|-MyClass
В других вопросах о SO я уже нашел, что укроп способен рассортировать определения классов, и, безусловно, он работает, копируя определение MyClass
в отдельный скрипт и травля его там, например:
import dill as pickle
class MyClass(object):
...
instance = MyClass(...)
with open(..., 'wb') as file:
pickle.dump(instance, file)
Однако при импорте этого класса он не работает:
Травление:
from MyModule.Submodule import MyClass
import dill as pickle
instance = MyClass(...)
with open(.., 'wb') as file:
pickle.dump(instance, file)
Загрузка:
import dill as pickle
with open(..., 'rb') as file:
instance = pickle.load(file)
>>> ModuleNotFoundError: No module named 'MyModule'
Я думаю, что определение класса сохраняется по ссылке, хотя оно не должно иметь значения по умолчанию в укропе. Это сделано правильно, когда MyClass
известен как __main__.MyClass
, который происходит, когда класс определен в основном скрипте.
Мне интересно, есть ли способ отделить MyClass
от MyModule
? Любой способ заставить его действовать как импорт верхнего уровня (__main__.MyClass
), поэтому укроп знает, как загрузить его на другой машине?
Соответствующий вопрос: почему укроп сбрасывает внешние классы по ссылке независимо от того, что
Мне удалось сохранить экземпляр и определение моего класса, используя следующий грязный хак:
class MyClass(object):
def save(path):
import __main__
with open(__file__) as f:
code = compile(f.read(), "somefile.py", 'exec')
globals = __main__.__dict__
locals = {'instance': self, 'savepath': path}
exec(code, globals, locals)
if __name__ == '__main__':
# Script is loaded in top level, MyClass is now available under the qualname '__main__.MyClass'
import dill as pickle
# copy the attributes of the 'MyModule.Submodule.MyClass' instance to a bew 'MyClass' instance.
new_instance = MyClass.__new__(MyClass)
new_instance.__dict__ = locals()['instance'].__dict__
with open(locals()['savepath'], 'wb') as f:
pickle.dump(new_instance, f)
Используя оператор exec
файл может быть выполнен из __main__
, поэтому определение класса также будет сохранено. Этот сценарий не должен выполняться как основной скрипт без использования функции сохранения.
Я автор dill
. Это дубликат вопроса, на который вы ссылаетесь выше. Соответствующий запрос функции GitHub: https://github.com/uqfoundation/dill/issues/128.
Я думаю, что большая проблема заключается в том, что вы хотите рассортировать объект, определенный в другом файле, который не установлен. По-моему, в настоящее время это невозможно.
В качестве обходного пути я полагаю, что вы сможете dill.source
с dill.source
, извлекая исходный код класса (или модуля) и трассируя его динамически или извлекая исходный код и компилируя новый объект в __main__
.