看起来您在FakeModule
腌制数据和尝试取消腌制数据之间进行了修改:具体来说,您已经从该模块中删除了一些名为的顶级对象World
(可能是一个类,也可能是一个函数)。
Pickling “按名称”序列化类和函数,因此它们必须是模块顶层的名称,并且不得修改该模块(至少不能以这种方式严重影响这些名称 -绝对不能通过从模块!)在酸洗时间和解酸时间之间。
一旦您确切地确定了您所做的哪些更改阻碍了解封,如果由于其他原因您不能只恢复更改,它通常会被黑客攻击。例如,如果您刚刚World
从移动FakeModule
到CoolModule
,请执行以下操作:
import FakeModule
import CoolModule
FakeModule.World = CoolModule.World
就在 unpickling 之前(记得用新的结构再次 pickle,这样你就不必每次 unpickle 时都重复这些 hack;-)。
编辑:OP 对 Q 的编辑使他的错误更容易理解。由于他现在正在测试 if __name__
equals '__main__'
,因此很明显 pickle 在编写时将保存 class 的对象__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 没有“连接点”,在腌制文件需要它的模块中放置一个具有正确名称的引用。那是:
>>> 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 本身及其核心机制,例如 pickling 和 sys .modules)。
第二课:由于你编写它的方式,pickle 文件本质上是损坏的,因为它只能在模块__main__
有一个按 name 的类时加载Word
,当然如果没有上面的一些技巧,它通常不会有。pickle 文件应该将类记录为存在于 module 中world
。如果您绝对认为必须if __name__ == '__main__':
在 中的子句上生成文件world.py
,则为此目的使用一些冗余:
import pickle
class World:
""
if __name__ == '__main__':
import world
w = world.World()
pickle.dump(w, open("file", "wb"))
这可以正常工作并且没有 hack(至少如果您遵循 Python 最佳实践,即在模块顶层永远不会有任何实质性代码——只有导入、类、def 和琐碎的赋值——其他一切都属于函数;如果你没有t 遵循这个最佳实践,然后编辑你的代码来这样做,它会让你在灵活性和性能方面更快乐)。