Python Pickle中的命名空间
我在使用pickle和unittest的时候遇到了一个错误。
我写了三个程序文件:
- 一个用于定义要被pickle的类,
- 一个使用第一个类的类,
- 一个用于测试第二个类的unittest。
这三个程序的代码分别如下。
#1. ClassToPickle.py
import pickle
class ClassToPickle(object):
def __init__(self, x):
self.x = x
if __name__=="__main__":
p = ClassToPickle(10)
pickle.dump(p, open('10.pickle', 'w'))
#2. SomeClass.py
from ClassToPickle import ClassToPickle
import pickle
class SomeClass(object):
def __init__(self):
self.pickle = pickle.load(open("10.pickle", 'r'))
self.x = self.pickle.x
print self.x
if __name__ == "__main__":
SomeClass()
#3. SomeClassTest.py
import unittest
from SomeClass import SomeClass
from ClassToPickle import ClassToPickle # REQUIRED_LINE
class SomeClassTest(unittest.TestCase):
def testA(self):
sc = SomeClass()
self.assertEqual(sc.x, 10)
def main():
unittest.main()
if __name__ == "__main__":
main()
我先运行了第一个程序来生成pickle文件。
然后,当我单独运行第二个程序(也就是输入“python SomeClass.py”)时,它能正常工作。
而且,当我单独运行第三个程序(也就是输入“python SomeClassTest.py”)时,它也能正常工作。
但是,当我在eclipse+pydev中以“单元测试”的方式运行第三个程序时,出现了下面的错误信息。
======================================================================
ERROR: testA (SomeClassTest.SomeClassTest)
----------------------------------------------------------------------
Traceback (most recent call last):
$ File "/home/tmp/pickle_problem/SomeClassTest.py", line 9, in testA
sc = SomeClass()
$ File "/home/tmp/pickle_problem/SomeClass.py", line 8, in init
self.pickle = pickle.load(open("10.pickle", 'r'))
$ File "/usr/lib/python2.7/pickle.py", line 1378, in load
return Unpickler(file).load()
$ File "/usr/lib/python2.7/pickle.py", line 858, in load
dispatchkey
File "/usr/lib/python2.7/pickle.py", line 1090, in load_global
klass = self.find_class(module, name)
$ File "/usr/lib/python2.7/pickle.py", line 1126, in find_class
klass = getattr(mod, name)
$ AttributeError: 'module' object has no attribute 'ClassToPickle'
----------------------------------------------------------------------
Ran 1 test in 0.002s
FAILED (errors=1)
而且,当我注释掉导入ClassToPickle类的那一行(在第三个程序的第3行,注释为“REQUIRED_LINE”)时,它也不工作,并且返回了下面的错误信息。
E
======================================================================
ERROR: testA (main.SomeClassTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "SomeClassTest.py", line 9, in testA
sc = SomeClass()
File "/home/tmp/pickle_problem/SomeClass.py", line 8, in init
self.pickle = pickle.load(open("10.pickle", 'r'))
File "/usr/lib/python2.7/pickle.py", line 1378, in load
return Unpickler(file).load()
File "/usr/lib/python2.7/pickle.py", line 858, in load
dispatchkey
File "/usr/lib/python2.7/pickle.py", line 1090, in load_global
klass = self.find_class(module, name)
File "/usr/lib/python2.7/pickle.py", line 1126, in find_class
klass = getattr(mod, name)
AttributeError: 'module' object has no attribute 'ClassToPickle'
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (errors=1)
我猜这个问题和Python中的命名空间有关,但我不知道具体发生了什么,也不知道该怎么解决。
我该如何正确地在eclipse+pydev中以“单元测试”的方式运行第三个程序,
以及如何在命令行中运行第三个程序而不包含导入ClassToPickle的那一行?
请帮帮我。
1 个回答
这是因为 __main__.ClassToPickle != ClassToPickle.ClassToPickle
,可以这样理解:
当你在 ClassToPickle.py
脚本中对 ClassToPickle 的实例进行了“腌制”(也就是序列化),pickle 模块会把这个类的所有引用都腌制起来。这意味着它会把定义这个类的模块名称也腌制进去。由于你执行了 ClassToPickle.py
这个脚本,所以这个模块会被标记为 __main__
,这就导致 pickle 模块会腌制 __main__.ClassToPickle
。
当你尝试加载这个腌制的实例时,会失败,因为它找不到实例的类是 __main__.ClassToPickle
,而不是你通过 from ClassToPickle import ClassToPickle
导入的那个类,因为后者是 ClassToPickle.ClassToPickle
。
解决这个问题的方法是创建另一个脚本来处理腌制,而不是在 ClassToPickle.py
中进行,例如:
import pickle
from ClassToPickle import ClassToPickle
if __name__=="__main__":
p = ClassToPickle(10)
pickle.dump(p, open('10.pickle', 'w'))