Python中pickle的命名空间

7

使用 pickle 时,我遇到了一个错误。

我编写了三个程序文件:

  1. 用于 pickling 的类,
  2. 使用 #1 中的类的类,
  3. 用于测试 #2 中的类的 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()

我先运行了程序#1来生成pickle文件。
然后,当我单独运行程序文件#2(即输入“python SomeClass.py”)时,它能正常工作。
并且,当我单独运行程序#3(即输入“python SomeClassTest.py”)时,它也能正常工作。
但是,当我在eclipse + pydev中将程序#3作为“单元测试”运行时,它会返回下面的错误信息。

================================================== ======================
错误:testA(SomeClassTest.SomeClassTest)
----------------------------------------------------------------------
Traceback(most recent call last):
$文件 "/home/tmp/pickle_problem/SomeClassTest.py",行9,在 testA
sc = SomeClass()
$文件"/home/tmp/pickle_problem/SomeClass.py",第8行,在 init
self.pickle = pickle.load(open("10.pickle", 'r'))
$文件"/usr/lib/python2.7/pickle.py",第1378行,在load上
return Unpickler(file).load()
$文件"/usr/lib/python2.7/pickle.py",第858行,在load上
dispatchkey
文件“/usr/lib/python2.7/pickle.py”,第1090行,在load_global上
klass = self.find_class(module, name)
$文件"/usr/lib/python2.7/pickle.py",第1126行,在find_class上
klass = getattr(mod, name)
$ AttributeError:'module'对象没有属性'ClassToPickle'

----------------------------------------------------------------------
在0.002秒内运行了1个测试

FAILED(errors = 1)

此外,当我注释掉导入ClassToPickle类的代码行(程序#3中的第3行,并注释为“REQUIRED_LINE”)时,它也无法工作并返回下面描述的错误消息。

E
======================================================================
错误: testA (main.SomeClassTest)
----------------------------------------------------------------------
回溯信息如下:
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)
属性错误: 'module'对象没有名为'ClassToPickle'的属性

----------------------------------------------------------------------
共运行了1个测试用例,用时0.001s

失败了1个测试用例(有1个错误)

我猜这个问题与Python中的命名空间有关,但我不知道具体发生了什么以及我该如何解决它。

如何正确地“作为单元测试在eclipse+pydev中运行”#3程序,
并在不导入ClassToPickle的情况下在命令行中运行#3程序?
请帮助我。


有没有其他的ClassToPickle.py文件,它可能会找到而不是你在这里展示的文件? - Thomas K
@ThomasK 感谢您的评论,但答案是否定的。这个目录中只有三个 .py 文件,并且我从未创建过名为“ClassToPickle.py”的文件。 - fbessho
1个回答

9
因为__main__.ClassToPickle != ClassToPickle.ClassToPickle,可以这样理解:
当你在ClassToPickle.py脚本中pickle了ClassToPickle类的实例时,pickle模块将pickle所有对该类的引用,这意味着它将pickle定义该类的模块名称。由于你执行了ClassToPickle.py脚本,这意味着该模块将被设置为__main__,因此模块将pickle __main__.ClassToPickle
当你尝试加载pickled实例时失败,是因为它找不到实例的类,即__main__.ClassToPickle,而不是使用from ClassToPickle import ClassToPickle导入的那个类,即ClassToPickle.ClassToPickle
修复方法是创建另一个脚本来处理dumping,而不是在ClassToPickle.py中进行。
import pickle

from ClassToPickle import ClassToPickle

if __name__=="__main__":
    p = ClassToPickle(10)
    pickle.dump(p, open('10.pickle', 'w'))

感谢你的解释!我都明白了,而且你的脚本在控制台和Eclipse中都可行 :) - fbessho

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接