有没有办法让python的pickle忽略“这不是同一个对象”的错误?

10 投票
2 回答
8855 浏览
提问于 2025-04-17 07:42

有没有办法让 Python 的 pickle 忽略 "它不是同一个对象" 的错误呢?

我正在写一个测试,使用 Mock 来精确控制 datetime.utcnow() 产生的结果。因为我的代码对时间很敏感,所以使用 Mock 的 patch 功能可以让我更方便地进行测试。

这些测试需要把对象进行序列化(也就是 pickle),然后把结果发送到远程服务器。为了测试的目的,如果一个标准的 datetime 被序列化并且被远程服务器接收到,那就没问题了。

可惜的是,pickle 模块报了以下错误:

无法序列化 <type 'datetime.datetime'>: 它不是同一个对象,和 datetime.datetime 不一样

下面是一个简单的例子,可以复现这个错误。

from mock import patch
from datetime import datetime
import pickle

class MockDatetime(datetime):
  frozendt = datetime(2011,05,31)

  @classmethod
  def advance(cls, **kw):
    cls.frozendt = cls.frozendt + timedelta(**kw)

  @classmethod
  def utcnow(cls):
    return cls.frozendt

@patch('datetime.datetime', MockDatetime)
def test():
  pickle.dumps(datetime.utcnow())

if __name__ == '__main__':
  test()

有没有什么组合,比如 __reduce____getstate__ 方法,可以让 pickle 误以为 MockDatetime 是一个 datetime,当我进行序列化的时候?

2 个回答

5

如果有人想要一个通用的方法来保存模拟对象(mock)的状态:

m = mock.MagicMock()
m.__reduce__ = lambda self: (mock.MagicMock, ())

请注意,这个方法似乎并不能保存模拟对象内部的内容,比如调用记录。

6

在文档的“在哪里打补丁”部分,我看到有这样的建议:

基本原则是,你应该在对象被使用的地方打补丁,这个地方不一定和对象被定义的地方是一样的。

根据这个建议,我尝试把:

@patch('datetime.datetime', MockDatetime)

替换成:

@patch('__main__.datetime', MockDatetime)

而且,我没有从pickle那里收到任何错误。此外,我还加了一个print语句,以确保datetime确实被打了补丁,结果得到了预期的值。

撰写回答