6

今天早些时候,我在尝试腌制一个命名元组实例时遇到了麻烦。作为健全性检查,我尝试运行一些发布在另一个答案中的代码。在这里,简化了一点:

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参数需要与工厂名称匹配才能工作?

4

1 回答 1

9

在标题为什么可以腌制和解封的部分中?在 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定义来进行 unpickling 并且人们通常将结果分配给与传递给工厂函数的名称相同的变量(但您在第二个示例中没有)。

于 2013-11-22T12:36:51.460 回答