[英]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.