2013-11-22 55 views
6

今天早些时候,我尝试腌制一个namedtuple实例时遇到了问题。作为一个完整的检查,我试着运行一些代码in another answer。这是,简化多了几分:了解Python中namedtuple typename和pickle的问题

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

在Python文档的标题为What can be pickled and unpickled?的部分,它表示只有“在模块的顶层定义的类”才能被酸洗。但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