为什么我无法对这个对象进行pickle处理?

16

我有一个类(如下):

class InstrumentChange(object):
    '''This class acts as the DTO object to send instrument change information from the
       client to the server. See InstrumentChangeTransport below
    '''
    def __init__(self, **kwargs):
        self.kwargs = kwargs
        self._changed = None

    def _method_name(self, text):
        return text.replace(' ','_').lower()

    def _what_changed(self):
        ''' Denotes the column that changed on the instrument returning the column_name of what changed.'''
        if not self._changed:
            self._changed = self._method_name(self.kwargs.pop('What Changed'))

        return self._changed

    def __getattr__(self, attr):
        for key in self.kwargs.iterkeys():
            if self._method_name(key) == attr:
                return self.kwargs[key]

    def __str__(self):
        return "Instrument:%s" % self.kwargs

    __repr__ = __str__

    what_changed = property(_what_changed)

当我运行以下的测试:

def test_that_instrumentchangetransport_is_picklable(self):
        test_dict = {'Updated': 'PAllum', 'Description': 'BR/EUR/BRAZIL/11%/26/06/2017/BD',
        'Ask Q': 500, 'Bbg': 'On', 'C Bid': 72.0, 'Benchmark': 'NL/USD/KKB/7.000%/03/11/2009/BD',
        'ISIN': 'XS0077157575', 'Bid YTM': 0.0, 'Bid Q': 100, 'C Ask': 72.25, 'Ask YTM': 0.0, 'Bid ASW': 0.0,
        'Position': 1280000, 'What Changed': 'C Bid', 'Ask ASW': 0.0}
        ins_change = InstrumentChangeTransport(**test_dict)
        assert isinstance(ins_change, InstrumentChangeTransport)

        # Create a mock filesystem object
        file = open('testpickle.dat', 'w')
        file = Mock()
        pickle.dump(ins_change, file)

我得到:

Traceback (most recent call last):
  File "c:\python23\lib\site-packages\nose-0.11.0-py2.3.egg\nose\case.py", line 183, in runTest
    self.test(*self.arg)
  File "C:\Code\branches\demo\tests\test_framework.py", line 142, in test_that_instrumentchangetransport_is_picklable
    pickle.dump(ins_change, file)
  File "C:\Python23\Lib\copy_reg.py", line 83, in _reduce_ex
    dict = getstate()
TypeError: 'NoneType' object is not callable

我已经查看了pickle文档,但我并不完全明白。

有什么想法吗?

Ben


1
顺便提一下:'key in self.kwargs.iterkeys()' 会生成一个键列表并搜索它们。最好使用 'key in self.kwargs',它尝试进行哈希表查找(更快)。 - ebo
1
当请求调试帮助时,提供足够的代码以重现问题是有帮助的。更好的做法是从您发布的代码中删除所有不相关的内容。 - Jason Orendorff
3个回答

39

你的代码存在一些小的“附带”问题:测试中类名突然出现了“Transport”(不是你正在定义的类名),将内置标识符 file 作为本地变量使用是可疑的(不要这样做——这里不会有影响,但是践踏内置标识符的习惯总有一天会导致神秘的错误),已经被指出的Mock 的误用,以及默认使用最慢、最肮脏的 pickle 协议和文本而不是二进制的 pickle 文件。

然而,正如 @coonj 所说,核心问题在于状态控制的缺失。一个“普通”的类不需要它(因为在缺少状态控制且没有其他特殊情况的类中,默认情况下会对 self.__dict__ 进行序列化和反序列化)——但由于你覆盖了 __getattr__,所以这不适用于你的类。只需要两个非常简单的方法:

def __getstate__(self): return self.__dict__
def __setstate__(self, d): self.__dict__.update(d)

这基本上告诉pickle将你的类视为普通类,使用self.__dict__代表整个实例状态,而不考虑__getattr__的存在。


5
@coonj,我猜大家很难理解为什么需要定义状态处理方法,因为通常情况下它们是不需要的(而且没有明确记录 __getattr__ 使它们必需的)。 - Alex Martelli

8
它失败了,因为它无法找到您的对象的__getstate __()。Pickle需要这些来确定如何对对象进行pickling / unpickling。您只需要__getstate __()__setstate __()方法。
请参见文档中的TextReader示例:http://docs.python.org/library/pickle.html 更新:我刚刚查看了Mock模块的sourceforge页面,我认为您也在错误地使用它。您正在模拟一个文件对象,但当pickle尝试从中读取时,它将不会得到任何返回值,这就是为什么getattr()返回none的原因。

2
    file = open('testpickle.dat', 'w')
    file = Mock()

您正在失去对已打开文件的引用。这可能会成为一个问题吗?

这似乎是问题的可能原因。 - jathanism

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