2013-11-22 7 views
6

Раньше сегодня у меня возникли проблемы с попыткой рассортировать экземпляр namedtuple. Как проверка работоспособности, я попробовал запустить код, который был отправлен in another answer. Вот она, упрощена немного больше:Понимание проблемы с именем именованным именем и рассолом в Python

from collections import namedtuple 
import pickle 

P = namedtuple("P", "one two three four") 

def pickle_test(): 
    abe = P("abraham", "lincoln", "vampire", "hunter") 
    f = open('abe.pickle', 'w') 
    pickle.dump(abe, f) 
    f.close() 

pickle_test() 

Затем я изменил две строки этого использовать мой названный кортеж:

from collections import namedtuple 
import pickle 

P = namedtuple("my_typename", "A B C") 

def pickle_test(): 
    abe = P("ONE", "TWO", "THREE") 
    f = open('abe.pickle', 'w') 
    pickle.dump(abe, f) 
    f.close() 

pickle_test() 

Однако это дало мне ошибку

File "/path/to/anaconda/lib/python2.7/pickle.py", line 748, in save_global 
    (obj, module, name)) 
pickle.PicklingError: Can't pickle <class '__main__.my_typename'>: it's not found as __main__.my_typename 

т.е. модуль Pickle ищет my_typename. Я изменил линию P = namedtuple("my_typename", "A B C") на P = namedtuple("P", "A B C"), и это сработало.

Я смотрел на источник namedtuple.py и в итоге мы имеем то, что выглядит уместно, но я не совсем понимаю, что происходит:

# For pickling to work, the __module__ variable needs to be set to the frame 
# where the named tuple is created. Bypass this step in enviroments where 
# sys._getframe is not defined (Jython for example) or sys._getframe is not 
# defined for arguments greater than 0 (IronPython). 
try: 
    result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') 
except (AttributeError, ValueError): 
    pass 

return result 

Так что мой вопрос, что именно происходит? Почему аргумент typename должен соответствовать названию фабрики, чтобы это работало?

ответ

8

В разделе под названием What can be pickled and unpickled? документации Python указывается, что только «классы, которые определены на верхнем уровне модуля» могут быть протравлены. Однако namedtuple() является фабрикой функции, которая эффективно определяет класс (my_typename(tuple) в вашем втором примере), однако он не присваивает изготовленному типу переменной с именем my_typename на верхнем уровне модуля.

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

Это можно проиллюстрировать, рассматривая один обходной путь для задачи-которое было бы изменить одну строку кода так, чтобы тип имени my_typenameявляется определен на верхнем уровне:

P = my_typename = namedtuple("my_typename", "A B C") 

В качестве альтернативы, вы могли бы просто дать namedtuple имя "P" вместо "my_typename":

P = namedtuple("P", "A B C") 

Что касается того, что namedtuple.py исходный код вы искали: делает попытку определить имя модуля, вызывающего (создателя namedtuple), потому что автор знает, что pickle может попытаться использовать его для import определение для разблокировки и что люди обычно назначают результат к переменной с тем же именем, которое они передали фабричной функции (но вы не во втором примере).

+0

Благодарим вас за «альтернативный» способ. Работает как шарм. – Vor

 Смежные вопросы

  • Нет связанных вопросов^_^