Сериализация функционального объекта с атрибутами, один атрибут отсутствует при загрузке - PullRequest
1 голос
/ 11 июля 2019

Я использую укроп в Python 3.7, но один из атрибутов функции теряется, когда я перезагружаю его последним.

У меня есть класс с именем session, который я сохраняю при выходе из программы, загружаю при запуске. Этот объект косвенно содержит Tranform экземпляров, которые имеют атрибут function, который ссылается на конкретную функцию. Эта функция имеет несколько атрибутов.

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

Вот код сохранения:

def save(self):
    print ('\n SAVING SESSION STATE, DO NOT EXIT')
    breakpoint()

    sessionDirectory='__PETL__'
    if not os.path.exists(sessionDirectory):
        os.makedirs(sessionDirectory)
    with open(sessionDirectory+'/'+self.name, 'wb') as f: 
        dill.dump(self,f)
    print ('\nSession Saved, exiting')

Вот код загрузки:

def loadSession(self, sessionName):
    if (Session.dontLoad):
        print ('Creating New Session')
        return None
    try:
        with open('__PETL__/'+ sessionName, 'rb') as f:
            session=dill.load(f)
    except FileNotFoundError:
        print ('No session found, creating new one')
        return None

    return session

А вот выходные данные отладчика:

Сохранение:

> /home/osboxes/stage/inspireDataBase2/migrations/src/session/session.py(160)save()
-> sessionDirectory='__PETL__'
(Pdb) print( self.transforms[0].transform.function.queryRes)
None
(Pdb) print (dir(self.transforms[0].transform.function)[-9:])
['after', 'args', 'columns', 'fetch', 'indexs', 'query', 'queryRes', 'sameorderasafter', 'transformvar']
(Pdb) dill.dumps(self.transforms[0].transform.function)
b'\x80\x03cuserTransformModulePreparsed\ntransform__constru__buildinggeometry2d\nq\x00.'
(Pdb) c
Session Saved, exiting

Загрузка:

> /home/osboxes/stage/inspireDataBase2/migrations/src/session/session.py(39)__init__()
-> session.printJobDone()
(Pdb) print( self.transforms[0].transform.function.queryRes)
*** AttributeError: 'function' object has no attribute 'queryRes'
(Pdb) print( session.transforms[0].transform.function.queryRes)
*** AttributeError: 'function' object has no attribute 'queryRes'
(Pdb) print (dir(session.transforms[0].transform.function)[-9:])
['__subclasshook__', 'after', 'args', 'columns', 'fetch', 'indexs', 'query', 'sameorderasafter', 'transformvar']

Как видите, остальные атрибуты работают как положено.

Поскольку часть сохранения - это последнее, что я делаю в своем проекте, я просто не понимаю, как работает укроп. Атрибут отличается от другого, потому что он установлен в другом классе (не в том же модуле, что и функция). Другие атрибуты устанавливаются непосредственно в модуле функции. Тем не менее, модуль получается путем компиляции AST Tree, но я не понимаю, почему это будет проблемой.

И я вижу, что действительно в первом выводе есть только ссылка на модуль функции в выводе укропа (но я не знаю, как работает укроп, возможно, это нормально).

1 Ответ

1 голос
/ 11 июля 2019

dill не захватывает атрибуты функций, не для функций, которые можно импортировать напрямую.Любые атрибуты, которые вы видите при загрузке, были добавлены в этот функциональный объект с помощью другого кода , возможно, во время импорта.функциональный объект;в вашей сессии отладки это userTransformModulePreparsed.transform__constru__buildinggeometry2d.При загрузке этой сериализации все, что нужно сделать, это import userTransformModulePreparsed, а затем использовать атрибут transform__constru__buildinggeometry2d этого модуля.Функции считаются синглетонами , в таких случаях для каждого процесса Python должна существовать только одна копия.Предполагается, что вся загрузка этого объекта обрабатывается обычным import процессом.Это включает атрибуты, добавленные к объекту функции!

dill может обрабатывать сгенерированные объекты функций, то есть любой объект функции, который не может быть импортирован напрямую,в этот момент он будет охватывать все аспекты функции, включая атрибуты.Например, использование def внутри функции (вложенные функции) всегда будет создавать новые отдельные объекты-функции каждый раз, когда вы вызываете родительскую функцию.Сериализация таких объектов обрабатывается по-разному:

>>> import dill
>>> def foo():
...     def bar(): pass  # nested function
...     bar.spam = 'ham'
...     return bar
...
>>> foo()
<function foo.<locals>.bar at 0x110621e50>
>>> foo() is not foo()  # new calls produce new function objects
True
>>> bar = foo()
>>> vars(bar)   # the resulting function object has attributes
{'spam': 'ham'}
>>> bar_restored = dill.loads(dill.dumps(bar))
>>> vars(bar_restored)  # the attributes are preserved by dill
{'spam': 'ham'}
>>> bar.extra = 'additional'
>>> vars(dill.loads(dill.dumps(bar)))  # this extends to new attributes added later.
{'spam': 'ham', 'extra': 'additional'}

Таким образом, у вас есть два варианта;либо установите атрибут функции во время импорта, или сгенерируйте функцию во вложенной функции.

...