如何让iPython与unpickling一起工作?

2024-10-03 02:35:48 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图在iPython中加载pickled对象。在

我得到的错误是:

AttributeError: 'FakeModule' object has no attribute 'World'

有没有人知道如何让它工作,或者至少有一个变通方法来加载iPython中的对象,以便以交互方式浏览它们?在

谢谢

编辑后添加:

我有一个剧本叫世界.py基本上是这样的:

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

而不是在回复中:

^{pr2}$

它在vanilla python REPL中工作,但在iPython中不起作用。在

我使用的python2.6.5和ipython0.10都来自于热情的Python发行版,但是我在以前的版本中也遇到了问题。在


Tags: 对象no编辑worldobject错误ipythonattribute
2条回答

看起来您已经修改了FakeModule在您对数据进行pickle的这段时间内:具体地说,您已经从该模块中删除了一些名为World(可能是一个类,也许是一个函数)的顶级对象。在

Pickling将类和函数“按名称”序列化,因此它们必须是模块顶层的名称,并且不能修改该模块(至少不能通过将这些名称从模块中删除而严重影响这些名称)在酸洗时间和不酸洗时间之间。在

一旦你确定了你做了什么样的改变阻碍了不停的工作,如果因为其他原因你不能恢复这些改变,它通常会被黑客攻击。例如,如果您刚刚将WorldFakeModule移动到CoolModule,请执行以下操作:

import FakeModule
import CoolModule
FakeModule.World = CoolModule.World

在取消拾取之前(记住再次使用新结构进行pickle,这样您就不必每次取消拾取时都重复这些技巧;-)。在

编辑:操作员对Q的编辑使他的错误更容易理解。因为他现在正在测试__name__是否等于'__main__',这使得pickle在编写时将保存一个__main__.World类的对象。因为他使用的是ASCII pickle(顺便说一下,对于性能和磁盘空间来说,这是一个非常不好的选择),所以检查一下:

^{pr2}$

正在查找的模块是__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没有看到这个问题,因为他使用的是import I detest,from world import World(直接从模块中导入函数或类,而不是从模块本身导入)。在

ipython中解决这个问题的方法在底层Python架构方面是完全相同的,只需要多出几行代码,因为ipython要提供所有额外的服务,没有使模块__main__直接用于记录交互命令行上发生的事情,而是插入一个(称为FakeModule,正如OP从错误msg;-)中发现的那样,并对其执行魔术,以使其“酷”&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本身及其核心机制,如pickling和系统模块). 在

第二课:pickle文件本质上是损坏的,这是由于您编写它的方式造成的,因为它只能在模块__main__有一个名为Word的类时加载,当然,没有上面的一些技巧,它通常不会有。pickle文件应该将类记录为活在模块world中。如果您绝对认为必须world.py中的if __name__ == '__main__':子句上生成文件,那么请使用一些冗余来达到以下目的:

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

这个很好用出窍门(至少,如果您遵循Python的最佳实践,即在模块顶层没有任何实质性的代码,只有导入、类、def和琐碎的赋值;如果您没有遵循这个最佳实践,那么就编辑您的代码,这样做,它将使您在灵活性和性能方面更加快乐。在

当用pickle.dump(w, open("file", "wb"))__main__模块中pickle w时,w来自{}模块的事实记录在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时,它执行以下行:

^{pr2}$

特别是,它尝试执行__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,即使你在^{之前或之后说from test import World。在

如果你跑了

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

那么pickle.load将起作用:

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

可能有更干净的方法,我不知道。任何你能想到的将World放在fake命名空间中的方法都应该有效。在

2008年,IPython的创造者费尔南多·佩雷斯(fernandoperez)就这个问题发表了看法。他可能已经用某种方法解决了这个问题,以避免我的下流。您可能想询问IPython用户邮件列表,或者,也许更简单一些,只是不要在__main__名称空间内进行pickle。在

相关问题 更多 >