【问题标题】:How to pickle NotImplementedType如何腌制 NotImplementedType
【发布时间】:2017-08-22 19:59:57
【问题描述】:

如何在 Python 中添加对酸洗传统上不可选择的类型的支持?

我有一个需要腌制的复杂对象,它包括对 NotImplementedType 类的引用。该类是第三方的,所以我不能覆盖它的 __copy__()__deepcopy__()__getstate__() 方法。

我不完全确定为什么 Pickle 不能序列化 NotImplementedType。我敢肯定有一些愚蠢的教条理由,我不在乎。事实是这个类永远不会改变,也不包含任何状态,所以它应该是可序列化的。果然dill package可以序列化这个类就好了。

所以我尝试为NotImplementedType 实现一个自定义的copy_reg 处理程序,该处理程序使用莳萝,例如:

copy_reg.pickle(NotImplementedType,
   lambda code: (dill.loads, (dill.dumps(code),)),
   dill.loads)

但是,尝试深度复制我的对象会引发异常:

'NoneType' object has no attribute 'update'

来自/usr/lib/python2.7/copy.py 中的第 347 行。深入研究这段代码表明,copy_reg/deepcopy/pickle 模块期望序列化和反序列化实例,而不是对类的引用,并且它抛出这个异常是因为它试图实例化 NotImplementedType 而不是仅仅查找类引用。有什么解决办法吗?

【问题讨论】:

  • 我刚刚尝试在 Python 3.6 中对 NotImplementedType 进行酸洗,它就这样发生了,没有任何错误。 (NotImplementedType 获取为ref = type(NotImplemented)
  • 另一方面,我可以确认您在 Python 2.7 中的错误
  • 您是否尝试过使用协议 2 显式酸洗? 2.7 上的默认 pickle 协议是 0,并且它缺乏对新式类的许多特性的支持(即使它工作,它在时间和空间上的效率都非常低下)。协议 2 原生支持新型类,它更快、更小,并且可以处理协议 0/1 根本无法处理的许多事情。
  • 啊,没关系。我以为NotImplementedType 是一些特殊的第三方东西,看起来它只是type(NotImplemented)。它不适用于 Py2 上的任何协议,它适用于 Py3 上的所有协议,在那里它生成了一个专门的__reduce__,基本上是pickles,以便在反序列化时调用__builtin__.type(__builtin__.NotImplemented)

标签: python python-2.7 pickle deep-copy


【解决方案1】:

所讨论的类实际上包含NotImplementedType 的可能性很小;相反,它可能正在腌制NotImplemented,而NotImplemented 的归约函数试图根据其类型腌制它,但NotImplementedType 并未直接暴露(您只能通过type(NotImplemented) 获得),因此尝试创建可以找到NotImplementedType 的限定名称失败。这在 Python 3 中已修复(移动的另一个参数),但在 Python 2 中修复看起来也相当简单,因为为 NotImplementedType 定义一个普通的 copy_reg 处理程序将确保 NotImplemented 在不引用其自身类型的情况下被腌制.

执行此操作的简单方法是让您的 reducer 函数返回命名为 NotImplemented 的字符串,然后在 unpickled 时将其作为全局查找。方便的是,这意味着您不需要定义任何辅助方法(在 unpickling 端可能不存在):

copy_reg.pickle(type(NotImplemented), lambda obj: 'NotImplemented')

您根本没有定义构造函数(第三个copy_reg.pickle 参数); pickle 将包含重新创建 NotImplemented 所需的所有信息,而不依赖于 unpickling 方面,在不尝试序列化 NotImplementedType 的情况下返回 NotImplemented

【讨论】:

  • 是的,我刚刚检查了 CPython 源代码。在 Py2 中,没有为 NotImplemented 定义特殊的 __reduce__(它是 has a custom repr, nothing else),但在 Py3 中,它是 explicitly defines __reduce__ to return the string "NotImplemented",与我的 copy_reg 解决方案完全相同。所以copy_reg 方法完全等同于 Py3 所做的。
  • 有了这个解决方案我仍然得到AttributeError: 'NoneType' object has no attribute 'update'
  • @IvanCastellanos:你得到了什么错误? OP 甚至没有提供代码示例,所以你可以运行任何东西。我可以告诉你,在我的答案中运行确切的代码(在导入copy_reg 之后)使NotImplemented 在 Python 2.7 上的往返可picklable(虽然不执行注册使它与TypeError: can't pickle NotImplementedType objects 一起死),但我无法开始猜测您的代码可能有什么问题。测试非常简单(pickle.loads(pickle.dumps(NotImplemented)),换成cPickle 进行加速测试)。
【解决方案2】:

copy_reg.pickle 函数明确允许返回字符串的函数。如果它是一个字符串,它将使用该字符串作为名称来“查找”(另请参阅__reduce__ documentation):

import copy_reg
copy_reg.pickle(type(NotImplemented), lambda x: 'NotImplemented')

这将允许复制(和腌制)NotImplemented

import copy

>>> type(copy.deepcopy(NotImplemented))              # still a NotImplementedType object
NotImplementedType

>>> copy.deepcopy(NotImplemented) is NotImplemented  # and still the same object!
True

【讨论】:

  • 我在你发帖的时候正在编辑这个,叹息。在重读时捕捉到字符串行为,以确保我没有错过更好的解决方案。忽略这一点,误读:您的演示代码是错误的;它应该产生NotImplemented,而不是NotImplementedType
  • @ShadowRanger 我在deepcopy 的返回值上使用了type,所以它应该是NotImplementedType,但我还添加了另一个示例,即使在深度复制之后它仍然是相同的“全局”。跨度>
  • 啊,哎呀。这就是我通过略读得到的。是的,没错。这确实假设第三方类实际上并没有在实例中存储NotImplementedType 本身,但如果这样做会非常奇怪。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多