【问题标题】:Why can't Ellipsis and NotImplemented be pickled?为什么 Ellipsis 和 NotImplemented 不能腌制?
【发布时间】:2012-01-23 14:34:31
【问题描述】:

我惊讶地发现 python(3.2.2 版)拒绝腌制一个对象,因为它的 dict 包含对 Ellipsis 的引用。在其他built-in constants 中,pickle 很高兴与False、True 和None 合作,正如pickle documentation 中明确指出的那样,但也对NotImplemented 感到窒息。

Python 3.2.2 (default, Sep  5 2011, 21:17:14) 
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pickle
>>> pickle.dumps(True)
b'\x80\x03\x88.'
>>> pickle.dumps(False)
b'\x80\x03\x89.'
>>> pickle.dumps(None)
b'\x80\x03N.'
>>> pickle.dumps(Ellipsis)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <class 'ellipsis'>: attribute lookup builtins.ellipsis failed
>>> pickle.dumps(NotImplemented)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <class 'NotImplementedType'>: attribute lookup builtins.NotImplementedType failed

为了完整性,在不太有用的内置常量中,__debug__ 只是一个布尔值,所以不会引起任何问题; copyright、license 和 credits 工作(它们的类型是 site._Printer); quit 和 exit 没有(它们的类型是 site.Quitter,无法找到,因为它是在函数内部定义的)。

谁能解释为什么会这样?Ellipsis 和 NotImplemented 肯定没有被忽视?我能找到的唯一相关信息是this bug,它抱怨NoneType(即type(None))不可腌制。其中一位评论者提到 type(Ellipsis) 和 type(NotImplemented) 不能被腌制,显然没有注意到它们的实例也不能被腌制。

【问题讨论】:

  • 奇怪的遗漏,尽管有人可能会争辩说你不应该从一开始就保留对这些值的引用。
  • @Inderdial:我的特定用例是在一个容器中,该容器跟踪已访问的切片。由于切片可以腌制,Ellipsis(实际上只是一种特殊的切片)不能腌制似乎很奇怪。存储对 NotImplemented 的引用可能不太有用,尽管我可以想象有人可能会想要它用于某种复杂的比较方案。
  • @James - 严格来说,Ellipsis 不是一种特殊的切片,但它的解释绝对是用户定义的。在 Numpy 中,它更像是一个由零个或多个切片组成的序列,自动确定。

标签: python pickle


【解决方案1】:

python 绝对没有理由不腌制 Ellipsis 和 NotImplemented 之类的东西,坦率地说,不让它们可腌制会导致 python 作为并行/异步语言的脆弱性。您可以使用dill 腌制这些类型的对象,替代pickle。是的,我知道这是一个温和的咆哮,但我认为你的目标代码中的 NotImplemented 不应该阻止你使用 multiprocessing 或其他并行 python ......或者保存你的 python 会话状态以供使用以后……或其他什么。

Python 3.2.5 (default, May 19 2013, 14:25:55) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> dill.dumps(True)
b'\x80\x03\x88.'
>>> dill.dumps(False)
b'\x80\x03\x89.'
>>> dill.dumps(None)
b'\x80\x03N.'
>>> dill.dumps(Ellipsis)
b'\x80\x03cdill.dill\n_eval_repr\nq\x00X\x08\x00\x00\x00Ellipsisq\x01\x85q\x02Rq\x03.'
>>> dill.dumps(NotImplemented)
b'\x80\x03cdill.dill\n_eval_repr\nq\x00X\x0e\x00\x00\x00NotImplementedq\x01\x85q\x02Rq\x03.'

在此处获取dill:https://github.com/uqfoundation/dill

【讨论】:

  • 谢谢你——它让我寻找并找到了这个链接,它帮助我解决了我在 IPython 中并行化的问题:nbviewer.ipython.org/gist/anonymous/5241793
  • @GB:太棒了。这是你发布的一个很好的链接。我还知道dill 在multiprocessing、mpi4py 和其他一些用于并行计算的python 包中被用作pickle 替代品。
【解决方案2】:

引用documentation:

可以腌制以下几种:

  • 无、真和假
  • 整数、浮点数、复数
  • 字符串、字节、字节数组
  • 仅包含可提取对象的元组、列表、集合和字典
  • 在模块顶层定义的函数
  • 在模块顶层定义的内置函数
  • 在模块顶层定义的类
  • 此类类的实例,其 __dict__ 或 __setstate__() 是可腌制的(有关详细信息,请参阅腌制类实例部分)

有问题的两个对象Ellipsis 和NotImplemented 不符合任何这些规则,因此不能被腌制。

我怀疑除了没有人看到需要它之外,没有更好的理由不在第一条规则中包含所有内置常量。如果您真的认为 pickle 应该支持这一点,请考虑发布功能请求(最好提供令人信服的用例!)。

【讨论】:

  • 我看到了那个列表,但不确定它是否是独家的。特别是,像 map 和 open 这样的内置函数可以被腌制,但似乎不符合任何这些标准(除非您认为它们是“在模块的顶层定义的”,在这种情况下它NoneType 之类的东西不能腌制似乎很奇怪)。
  • map 和open 定义在__builtin__ 模块的顶层(试试import __builtin__ as B; B.map),NoneType 是内置类,而不是函数。文档对我来说似乎是一致的!
  • 嗯,这是有道理的 - 感谢您的解释(尽管现在我想知道 NoneType 是在哪里定义的,如果有的话!)。虽然我真的很想知道这种行为背后的原因是什么——也许我应该提交一份错误报告。
猜你喜欢
  • 2011-11-03
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
  • 2014-12-04
  • 2016-02-21
  • 1970-01-01
  • 2014-06-17
  • 1970-01-01
相关资源
最近更新 更多