当变量名称与类型名称不匹配时,namedtuple 酸洗失败

namedtuple pickling fails when variable name doesn't match typename

下面的 python 代码失败并出现错误 pickle.PicklingError: Can't pickle <class '__main__.SpecialName'>: it's not found as __main__.SpecialName

import pickle
from collections import namedtuple

different_SpecialName = namedtuple('SpecialName', 'foo bar')
def save():
    foo = different_SpecialName(1, 2)
    with open('foosave.pkl', 'w') as f:
        pickle.dump(foo, f)

if __name__ == '__main__':
    save()

这似乎是 pickle 模块的不良行为,因为它取决于 变量名称 的正确性。将 different_SpecialName 更改为 SpecialName 并重新 运行 代码使其成功完成。将代码更改为以下代码,其中 SpecialName 的变量被实例化为与 different_SpecialName 相同的值,也让代码 运行 成功

import pickle
from collections import namedtuple

different_SpecialName = namedtuple('SpecialName', 'foo bar')

## create new variable with 'correct' name
SpecialName = different_SpecialName

def save():
    # foo = different_SpecialName(1, 2)
    foo = SpecialName(1, 2)
    with open('foosave.pkl', 'w') as f:
        pickle.dump(foo, f)

if __name__ == '__main__':
    save()

我的问题:这基本上是 pickle(和 cPickle)错误吗?似乎 pickle 不应该使用变量名称查找 class 定义(尽管我不确定它还能做什么)。或者,这是 namedtuple API 的问题吗?我浏览了 namedtuple documentation 并找不到任何明确告诉我将我的 namedtuple 变量命名为与我的 typename 参数相同的东西(namedtuple() 函数的第一个参数)

这不是错误。 pickle requires那个

the class definition must be importable and live in the same module as when the object was stored.

namedtuple__reduce__方法来看,类型名是SpecialName(毕竟你传的就是这个)。所以当 unpickling 时,它会尝试导入它在其中声明的模块并查找 SpecialName。但是因为你没有保存为SpecialName,所以找不到它。

如果不诉诸 namedtuples,您可以使用以下方法产生完全相同的问题:

class Foo:
    pass
Bar = Foo
del Foo

并尝试腌制和解腌 Bar();在幕后,您实际上已经用 namedtuple.

不匹配的名称做了同样的事情