[英]How to get unpickling to work with iPython?
我正在嘗試在iPython中加載pickle對象。
我得到的錯誤是:
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"))
它適用於vanilla python REPL但不適用於iPython。
我正在使用來自Enthought Python Distribution的Python 2.6.5和iPython 0.10,但我也遇到了以前版本的問題。
看起來你在腌制數據的時間和你試圖取消它的時間之間修改了FakeModule
:具體來說,你已經從那個模塊中刪除了一些名為World
頂級對象(也許是一個類,也許是一個函數) 。
Pickling將類和函數“按名稱”序列化,因此它們需要在其模塊的頂層是名稱, 並且不得修改該模塊(至少不會以這種方式嚴重影響這些名稱 - 絕對不能通過 從名稱中刪除這些名稱模塊!)在酸洗時間和開槽時間之間。
一旦你確定你做了什么改變阻礙了破壞,它通常會被黑客攻擊,如果由於其他原因你不能只是恢復變化。 例如,如果您剛剛將World
從FakeModule
移動到CoolModule
,請執行以下操作:
import FakeModule
import CoolModule
FakeModule.World = CoolModule.World
在取消開始之前(並且記得再次使用新結構進行腌制,這樣你就不必在每次開發時都不斷重復這些黑客攻擊;-)。
編輯 :OP對Q的編輯使他的錯誤更容易理解。 由於他現在正在測試__name__
等於'__main__'
,這很明顯,當寫入時,pickle將保存類__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
確實有一個,但是OP沒有“連接點”,正如我在答案的前一部分中所解釋的那樣,在正確的名稱中使用正確名稱的引用,其中pickle文件需要它。 那是:
>>> World = world.World
>>> pickle.load(open("file", "rb"))
<world.World instance at 0xf5300>
>>>
現在,這當然完美無缺(正如我之前所說)。 也許OP沒有看到這個問題,因為他正在使用導入的形式我討厭, from world import World
(直接從模塊中導入函數或類,而不是模塊本身)。
解決在IPython中的問題的黑客正是在底層的Python架構方面是相同的-只是需要的代碼,因為IPython中,以提供其所有的額外服務一對夫婦更行, 不會使模塊__main__
直接提供給直接錄制在交互式命令行中會發生什么,而是設置一個(稱為FakeModule,作為從錯誤消息中發現的OP ;-)並用它做黑魔法以便“酷”&c。 但是,無論何時你想直接使用具有給定名稱的模塊,它在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)。
第二課:由於你編寫它的方式,pickle文件基本上都被破壞了,因為它只能在模塊__main__
有一個名為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最佳實踐,在模塊頂層沒有任何實質性代碼 - 只有導入,類,def和瑣碎的任務 - 其他一切都屬於函數;如果你沒有'遵循這個最佳實踐,然后編輯您的代碼,這將使您在靈活性和性能方面更加快樂)。
當你腌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嘗試unpickle 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'>
這是IPython在AttributeError中提到的FakeModule
。
如果你看看fake.__dict__
你會看到它不包括World
即使你在__import__
之前或之后from test import World
說。
如果你跑
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__
命名空間內進行pickle。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.