为什么我不能序列化这个对象?

16 投票
3 回答
26465 浏览
提问于 2025-04-15 17:55

我有一个类(如下所示):

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的文档,但我还是不太明白。

有什么想法吗?

3 个回答

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

你在这里失去了对打开文件的引用。这可能会是个问题吗?

8

这个问题出现是因为你的对象找不到 __getstate__() 这个方法。Pickle(一个用于序列化和反序列化的工具)需要这个方法来知道如何保存和读取你的对象。你只需要实现 __getstate__()__setstate__() 这两个方法。

你可以查看文档中的 TextReader 示例: http://docs.python.org/library/pickle.html

更新:我刚刚看了 Mock 模块的 SourceForge 页面, 我觉得你可能也在错误地使用它。你在模拟一个文件对象,但当 pickle 尝试从中读取时,它什么也读不到,这就是为什么 getattr() 返回 None 的原因。

39

你的代码有几个小问题:首先,在测试中出现了一个叫'Transport'的类名,但这并不是你定义的类名;其次,你把内置的标识符file当作局部变量使用,这样做不好——虽然现在没什么影响,但养成覆盖内置标识符的习惯将来可能会导致一些神秘的错误;还有就是之前提到的Mock的错误用法,以及使用了最慢、最复杂的序列化协议和文本格式,而不是二进制格式来处理pickle文件。

不过,正如@coonj所说,问题的核心在于缺乏状态控制。一个“正常”的类不需要状态控制(因为在没有状态控制和其他特殊情况的类中,self.__dict__会默认被序列化和反序列化)——但由于你重写了__getattr__,这就不适用于你的类。你只需要再添加两个非常简单的方法:

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

这些方法基本上是告诉pickle把你的类当作一个正常的类来处理,认为self.__dict__代表了整个实例的状态,尽管存在__getattr__

撰写回答