7

iPythonでピクルスオブジェクトを読み込もうとしています。

私が得ているエラーは次のとおりです。

AttributeError:'FakeModule'オブジェクトに属性'World'がありません

誰かがそれを機能させる方法、または少なくともオブジェクトをインタラクティブに参照するためにiPythonでオブジェクトをロードするための回避策を知っていますか?

ありがとう

追加するために編集:

基本的に次のようなworld.pyというスクリプトがあります。

import pickle
class World:
    ""
if __name__ == '__main__':
    w = World()
    pickle.dump(w, open("file", "wb"))

REPLよりも:

import pickle  
from world import World  
w = pickle.load(open("file", "rb"))

これはバニラPythonREPLで機能しますが、iPythonでは機能しません。

Enthought PythonDistributionのPython2.6.5とiPython0.10を使用していますが、以前のバージョンでも問題が発生していました。

4

2 に答える 2

12

FakeModuleデータをピクルスにしたときと、データをアンピックしようとしたときの間に変更したようです。具体的には、そのモジュールから、名前の付いたトップレベルオブジェクトWorld(おそらくクラス、おそらく関数)を削除しました。

Picklingはクラスと関数を「名前で」シリアル化するため、モジュールのトップレベルの名前である必要があり、そのモジュール変更しないでください(少なくとも、これらの名前に悪影響を与えるような方法ではなく、これらの名前をから削除することできません)。モジュール!)ピクルス時間とアンピクルス時間の間。

選択解除を妨げる変更を正確に特定したら、他の理由で変更を元に戻すことができない場合、ハッキングされることがよくあります。たとえば、WorldからFakeModuleに移動したばかりの場合は、次のようCoolModuleにします。

import FakeModule
import CoolModule
FakeModule.World = CoolModule.World

ピクルスを外す直前(そして、ピクルスを外すたびにこれらのハックを繰り返し続ける必要がないように、新しい構造でもう一度ピクルスにすることを忘れないでください;-)。

編集:OPによるQの編集により、彼のエラーがはるかに理解しやすくなります。__name__彼は現在、等しいかどうかをテストしているので'__main__'、これにより、ピクルスが書き込まれると、クラスのオブジェクトが保存されることが明らかになります__main__.World。彼はASCIIピクルスを使用しているので(ちなみに、パフォーマンスとディスク容量の点で非常に悪い選択です)、以下を確認するのは簡単です。

$ cat file
(i__main__
World
p0
(dp1

検索されているモジュールは(明らかにそして明らかに)__main__です。これで、ipythonを気にすることなく、単純なPythonインタラクティブインタープリターを使用できます。

$ py26
Python 2.6.5 (r265:79359, Mar 24 2010, 01:32:55) 
[GCC 4.0.1 (Apple Inc. build 5493)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import world
>>> import pickle
>>> pickle.load(open("file", "rb"))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py", line 1370, in load
    return Unpickler(file).load()
  File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py", line 858, in load
    dispatch[key](self)
  File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py", line 1069, in load_inst
    klass = self.find_class(module, name)
  File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/pickle.py", line 1126, in find_class
    klass = getattr(mod, name)
AttributeError: 'module' object has no attribute 'World'
>>> 

エラーは簡単に再現でき、その理由も同様に明白です。クラス名のルックアップが実行されるモジュール(つまり、__main__)には、実際には「World」という名前の属性がありません。モジュールworldには1つありますが、回答の前の部分で説明したように、OPは「ドットを接続」しておらず、ピクルスファイルが必要とするモジュールに正しい名前の参照を配置しています。あれは:

>>> World = world.World
>>> pickle.load(open("file", "rb"))
<world.World instance at 0xf5300>
>>> 

もちろん、これは完全に機能します(そして前に言ったように)。おそらく、OPは、私が嫌うインポートの形式を使用しているため、この問題を認識していませんfrom world import World(モジュール自体ではなく、モジュール内から関数またはクラスを直接インポートします)。

ipythonの問題を回避するためのハックは、基盤となるPythonアーキテクチャに関してはまったく同じです。つまり、ipythonは、すべての追加サービスを提供するために、モジュールを直接利用可能にして、何を直接記録することができない__main__ため、さらに数行のコードが必要です。インタラクティブなコマンドラインで発生しますが、1つを挿入し(OPがエラーメッセージから見つけたのでFakeModuleと呼ばれます;-)、「クール」になるためにそれを使って黒魔術を行います。それでも、特定の名前のモジュールに直接アクセスしたい場合は、もちろん、Pythonでは非常に簡単です。

In [1]: import world

In [2]: import pickle

In [3]: import sys

In [4]: sys.modules['__main__'].World = world.World

In [5]: pickle.load(open("file", "rb"))
Out[5]: <world.World instance at 0x118fc10>

In [6]: 

保持する教訓、ナンバーワン:少なくとも、あなたが魔術師の見習いとして、時折の暴走状況を見つけて修正できるようになるまでは、黒魔術を避けてください(そうしないと、バケツを運ぶほうきが世界に殺到する可能性がありますあなたが昼寝している間;-)。

または、別の読み方:抽象化の特定のレイヤー(ipythonがPythonの上に置く「クールな」レイヤーなど)を適切に使用するには、基盤となるレイヤー(ここでは、Python自体とピクルスやsysなどのコアメカニズム)を十分に理解する必要があります.modules)。

__main__レッスン2:そのpickleファイルは、モジュールに名前のクラスがある場合にのみロードできるため、作成方法が原因で本質的に壊れています。Wordもちろん、上記のようなハックがなければ通常はロードできません。代わりに、pickleファイルはクラスをモジュールに存在するものとして記録する必要がありますworld。の句でファイルを作成する必要があると絶対に感じる場合は、次の目的で冗長性を使用してください。if __name__ == '__main__':world.py

import pickle
class World:
    ""
if __name__ == '__main__':
    import world
    w = world.World()
    pickle.dump(w, open("file", "wb"))

これは正常に機能し、ハッキングはありません(少なくとも、モジュールのトップレベルに実質的なコードがないというPythonのベストプラクティスに従っている場合は、インポート、クラス、定義、および些細な割り当てのみです。他のすべては関数に属します。このベストプラクティスに従ってから、コードを編集してください。柔軟性とパフォーマンスの両方の点で、はるかに幸せになります)。

于 2010-08-07T18:03:26.257 に答える
2

でモジュールをピクルスwにすると、モジュールからの事実が次の最初の行に記録されます。__main__pickle.dump(w, open("file", "wb"))w__main__file

% xxd file
0000000: 2869 5f5f 6d61 696e 5f5f 0a57 6f72 6c64  (i__main__.World
0000010: 0a70 300a 2864 7031 0a62 2e              .p0.(dp1.b.

IPythonがピックルを解除しようとするとfile、次の行が実行されます。

/usr/lib/python2.6/pickle.pyc in find_class(self, module, name)
   1124         __import__(module)
   1125         mod = sys.modules[module]
-> 1126         klass = getattr(mod, name)
   1127         return klass
   1128 

特に、を実行しようとし__import__('__main__')ます。REPLでそれを試してみると、

In [29]: fake=__import__('__main__')

In [32]: fake
Out[32]: <module '__main__' from '/usr/lib/pymodules/python2.6/IPython/FakeModule.pyc'>

これは、FakeModuleIPythonがAttributeErrorで言及していることです。

中を見ると、前後に言ってもfake.__dict__含まれていないことがわかります。Worldfrom test import World__import__

実行した場合

In [35]: fake.__dict__['World']=World

その後、pickle.load動作します:

In [37]: w = pickle.load(open("file", "rb"))

よりクリーンな方法があるかもしれません。知らない。名前空間に配置することを考えることができる任意の方法が機能するはずWorldです。fake

PS。2008年、IPythonの作成者であるFernando Perezは、この問題について少し書いています。彼は私の汚いハックを避ける何らかの方法でこれを修正したかもしれません。IPythonユーザーのメーリングリストで質問するか、おそらくもっと簡単に、__main__名前空間内でピクルスにしないでください。

于 2010-08-08T15:30:06.990 に答える