43

Python 的泡菜(我在这里说的是标准的 Python 2.5/2.6/2.7)不能泡菜锁、文件对象等。

它也不能腌制生成器和 lambda 表达式(或任何其他匿名代码),因为腌制实际上只存储名称引用。

对于锁和依赖于操作系统的功能,您不能腌制它们的原因显而易见的并且是有道理的。

但是为什么你不能腌制发电机呢?


注意:为了清楚起见——我对根本原因(或进入该设计决策的假设和选择)感兴趣,而不是“因为它给你一个 Pickle 错误”。

我意识到这个问题的目标有点广泛,所以这里有一个经验法则来判断你是否回答了这个问题:“如果提出这些假设,或者允许的发电机类型以某种方式受到更多限制,酸洗发电机会再次工作吗?”

4

2 回答 2

54

有很多关于这方面的信息。有关该问题的“官方说法”,请阅读(已关闭)Python bugtracker issue

做出决定的人之一的核心推理在此博客上有详细说明:

由于生成器本质上是一个增强的函数,我们需要保存它的字节码,它不能保证在 Python 版本之间向后兼容,以及它的框架,它保存了生成器的状态,例如局部变量、闭包和指令指针。而后者完成起来相当麻烦,因为它基本上需要使整个解释器变得可挑选。因此,任何对酸洗生成器的支持都需要对 CPython 的核心进行大量更改。

现在,如果在生成器的局部变量中出现了 pickle 不支持的对象(例如,文件句柄、套接字、数据库连接等),那么无论我们可能对生成器的任何 pickle 支持如何,该生成器都无法自动腌制实施。所以在这种情况下,您仍然需要提供自定义__getstate____setstate__方法。这个问题使得对生成器的任何酸洗支持都相当有限。

并提到了两个建议的解决方法:

无论如何,如果您需要这样的功能,那么请查看可以完成上述所有操作的 Stackless Python。而且由于 Stackless 的解释器是可挑选的,您还可以免费获得进程迁移。这意味着您可以中断一个小任务(Stackless 的绿色线程的名称),对其进行腌制,将腌制发送到另一台机器,取消腌制,恢复小任务,然后瞧,您刚刚迁移了一个进程。这是一个非常酷的功能!

但在我看来,这个问题的最佳解决方案是将生成器重写为简单的迭代器(即,一个带有__next__方法的迭代器)。迭代器在空间方面很容易和有效地腌制,因为它们的状态是明确的。但是,您仍然需要明确处理表示某些外部状态的对象;你无法解决这个问题。

于 2011-08-24T18:29:08.457 回答
26

您实际上可以,具体取决于实现。PyPyStackless Python都允许这样做(无论如何在某种程度上):

Python 2.7.1 (dcae7aed462b, Aug 17 2011, 09:46:15)
[PyPy 1.6.0 with GCC 4.0.1] on darwin
Type "help", "copyright", "credits" or "license" for more information.
And now for something completely different: ``Not your usual analyses.''
>>>> import pickle
>>>> gen = (x for x in range(100))
>>>> next(gen)
0
>>>> pickled = pickle.dumps(gen)
>>>> next(pickle.loads(pickled))
1

在 CPython 中,还可以创建一个迭代器对象来模拟一个可选择的生成器。

于 2011-08-24T18:31:31.830 回答