Python:无法pickle类型X,属性查找失败

问题描述 投票:0回答:6

我正在尝试腌制一个

namedtuple

from collections import namedtuple
import cPickle

class Foo:

    Bar = namedtuple('Bar', ['x', 'y'])

    def baz(self):
        s = set()
        s.add(Foo.Bar(x=2, y=3))
        print cPickle.dumps(s)

if __name__ == '__main__':
    f = Foo()
    f.baz()

这会产生以下输出:

Traceback (most recent call last):
  File "scratch.py", line 15, in <module>
    f.baz()
  File "scratch.py", line 11, in baz
    print cPickle.dumps(s)
cPickle.PicklingError: Can't pickle <class '__main__.Bar'>: attribute lookup __main__.Bar failed

我做错了什么?问题是

Bar
Foo
的成员吗? (将
Bar
的定义移至顶层可以解决问题,尽管我仍然很好奇为什么会发生这种情况。)

python pickle
6个回答
41
投票

是的,作为班级成员这一事实是一个问题:

>>> class Foo():
...     Bar = namedtuple('Bar', ['x','y'])
...     def baz(self):
...         b = Foo.Bar(x=2, y=3)
...         print(type(b))
...
>>> a = Foo()
>>> a.baz()
<class '__main__.Bar'>

问题是,当

namedtuple()
返回类型对象时,它不知道它被分配给类成员这一事实 - 因此,它告诉类型对象它的类型名称应该是
__main__.Bar
,甚至虽然它确实应该是
__main__.Foo.Bar


16
投票

嵌套类会使 pickle 失败,因为它依赖于应用程序内对象的路径来稍后重建它。

直接的解决方案是不要嵌套类,即将

Bar
定义移到
Foo
之外。代码仍然可以工作。

但更好的做法是根本不使用

pickle
来存储数据。使用其他序列化格式,例如
json
,或数据库,例如
sqlite3

您刚刚遇到了pickle的众多不便之处之一,如果您更改代码,移动内容,或者有时进行小的结构更改,您的数据将变得无法加载。

除此之外,pickle 还有其他缺点:速度慢、不安全、仅限 python...


10
投票

用莳萝代替泡菜可以让这个起作用


2
投票

这里的解决方案是将命名元组定义移动到模块级别,然后 pickle 就可以工作了。这里有详细的解答:

如何正确pickle一个namedtuple实例


1
投票

如果您通过搜索引擎来到这里,问题也可能是类型名称和第一个参数不相同。例如:

FooBar = namedtuple('foo_bar', ['x', 'y'])

应该是

FooBar = namedtuple('FooBar', ['x', 'y'])

0
投票

在 Python 3.5+ 中,您可以使用

typing.NamedTuple
而不是
collections.namedtuple
来修复此问题:

from typing import NamedTuple

class Foo:
    class Bar(NamedTuple):
        x: int
        y: int

if __name__ == '__main__':
    import pickle
    s = {Foo.Bar(x=2, y=3)}
    p = pickle.dumps(s, 4)
    assert pickle.loads(p) == s
© www.soinside.com 2019 - 2024. All rights reserved.