web-dev-qa-db-ja.com

Python:タイプXをピクルできません。属性の検索に失敗しました

私は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 __== '__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

私は何を間違えていますか? BarFooのメンバーですか? (Barの定義をトップレベルに移動すると問題が解決しますが、なぜこれが起こるのか私はまだ知りません。)

40
Nick Heiner

はい、クラスメンバーであるという事実は問題です。

>>> 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である必要があります。

29
Amber

クラスをネストすると、アプリケーション内のオブジェクトのパスに依存して後で再構築するため、pickleは失敗します。

直接の解決策は、クラスをネストしないことです。つまり、Bar定義をFooの外部に移動します。コードはすべて同じように機能します。

ただし、データを保存するために使用しないpickleを使用することをお勧めします。 jsonなどの他のシリアル化形式、またはsqlite3などのデータベースを使用します。

あなたはコードを変更したり、物事を動かしたり、時には小さな構造的な変更を加えたりすると、データがロードできなくなります。

それに加えて、pickleには他の欠点があります:遅い、安全でない、Pythonのみです...

13
nosklo

ここで漬物の代わりにディルを使用すると、これが機能します

7
Maximilian