【问题标题】:Why isn't Pickle calling __new__ like the documentation says?为什么 Pickle 不像文档中所说的那样调用 __new__ ?
【发布时间】:2016-04-17 02:31:28
【问题描述】:

documentation for Pickle 明确表示:

新样式类 C 的实例是使用以下方法创建的:

obj = C.__new__(C, *args)

试图利用这一点,我创建了一个没有实例属性或方法的单例:

class ZeroResultSentinel(object):
    instance = None
    def __new__(cls, *args):
        if not cls.instance:
            cls.instance = super(ZeroResultSentinel, cls).__new__(cls, *args)
        return cls.instance

(该类在缓存层中用于区分无结果的结果和缓存中的任何内容。)

单例效果很好(每次调用ZeroResultSentinel() 都会在内存中产生相同的实例,ZeroResultSentinel() == ZeroResultSentinel() 的计算结果为True)。而且我可以腌制和取消腌制实例而不会出错。但是,当我解开它时,我得到了一个不同的实例。所以我在__new__ 中放置了一个断点。每次调用ZeroResultSentinel() 时我都会遇到断点,但是当我解开腌制的ZeroResultSentinel 时我不会遇到断点。这与文档直接矛盾。那么是我做错了什么,还是文档不正确?

【问题讨论】:

  • 你指定了什么pickle协议?

标签: python deserialization instance pickle


【解决方案1】:

文档并没有真正说清楚,但您的 __new__ 方法将仅用于 pickle 协议 2 及更高版本:

>>> class Foo(object):
...     def __new__(cls):
...         print "New"
...         return object.__new__(cls)
...
>>> foo = Foo()
New
>>> pickle.loads(pickle.dumps(foo, protocol=0))
<__main__.Foo object at 0x00000000025E9A20>
>>> pickle.loads(pickle.dumps(foo, protocol=2))
New
<__main__.Foo object at 0x00000000022A3F60>

在 Python 2 上,默认协议为 0,因此如果您使用默认协议,则必须更改它。

【讨论】:

  • 好吧,糟了。我已经确认可以修复它。但是我们使用的是一个非常旧的 Django 版本,它没有为缓存后端指定协议,所以在我们升级之前我无能为力。如果不使用__new__,Pickle 如何在早期协议中创建实例?
  • 更正:这与 Django 无关。我们只是在没有指定协议的情况下进行酸洗的缓存库的摆布
  • 我能够将'pickleProtocol': pickle.HIGHEST_PROTOCOL, 添加到我们的缓存后端的配置设置中以使其正常工作。非常感谢你的帮忙! (我仍然很好奇,如果您有时间,Pickle 可以在不调用 __new__ 的情况下创建对象的实例。)
  • @NickWilliams:它使用 a __new__ 方法,只是不是你的。具体来说,就是用C写的MRO中第一个类的__new__方法。通常是object.__new__copy_reg._reduce_ex 为协议 0 和 1 中的新型类实例酸洗完成了大部分工作;你可以阅读它的代码了解详情。
  • copy_reg._reduce_ex 返回的_reconstructorargs 用于初始创建未腌制对象为_reconstructor(*args)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-18
  • 1970-01-01
  • 2015-03-28
  • 1970-01-01
相关资源
最近更新 更多