【问题标题】:How to pickle a callback chain如何腌制回调链
【发布时间】:2011-01-28 17:25:10
【问题描述】:

我在 Twisted 中有一个用户定义的状态机。用户可以为不同的状态更改定义处理程序,我通过使用 Twisted deferred 来实现,我让他们向其中添加回调。每当我从一种状态转移到另一种状态时,我只需触发适当的延迟。

项目要求之一是能够将此状态机及其所有回调保存到磁盘。我以为我可以简单地腌制状态机,然后就完成了,但是当我尝试序列化用户定义的函数时,我得到了 PickleError。

有人知道序列化函数的方法吗?该错误在以下代码示例中重现:

import pickle
from twisted.internet.utils import defer

def foo(*args):
  def bar():
    print args
  return bar

d = defer.Deferred()
d.addCallback(foo("Hello", "world"))
pickle.dumps(d)

最后一行给出以下错误:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.5/pickle.py", line 1366, in dumps
    Pickler(file, protocol).dump(obj)
  File "/usr/lib/python2.5/pickle.py", line 224, in dump
    self.save(obj)
  File "/usr/lib/python2.5/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.5/pickle.py", line 725, in save_inst
    save(stuff)
  File "/usr/lib/python2.5/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.5/pickle.py", line 649, in save_dict
    self._batch_setitems(obj.iteritems())
  File "/usr/lib/python2.5/pickle.py", line 663, in _batch_setitems
    save(v)
  File "/usr/lib/python2.5/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.5/pickle.py", line 600, in save_list
    self._batch_appends(iter(obj))
  File "/usr/lib/python2.5/pickle.py", line 615, in _batch_appends
    save(x)
  File "/usr/lib/python2.5/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.5/pickle.py", line 562, in save_tuple
    save(element)
  File "/usr/lib/python2.5/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.5/pickle.py", line 562, in save_tuple
    save(element)
  File "/usr/lib/python2.5/pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "/usr/lib/python2.5/pickle.py", line 748, in save_global
    (obj, module, name))
pickle.PicklingError: Can't pickle <function bar at 0xb753fe2c>: it's not found as __main__.bar

有什么解决办法吗?也许我需要限制用户可以添加为回调的函数类型?

谢谢,
乔纳森

【问题讨论】:

  • 我想这里有两个单独的问题:(1) 是如何存储回调链 - 正如 Jean-Paul 建议的那样单独存储它们 - 以及 (2) 是如何存储回调本身 -使用仿函数,就像 vz0 建议的那样。我如何接受你的两个答案?

标签: python twisted pickle deferred


【解决方案1】:

不要试图腌制 Deferreds。 Twisted 不支持它。即使您设法构建了一些看起来可行的东西(这并非完全不可能),Twisted 的后续版本也可能会破坏您保存的所有状态。

延迟用于通过代码控制事件流。它们不是用于存储应用程序状态。如果您想持久化您的应用程序状态,请将其与任何 Deferred 分开并序列化只是它。

执行此操作时,您可能还希望避免使用 pickle 作为序列化格式。 Pickle 不是存储数据的好方法。它是一种高度复杂的格式,对 Python 版本和库版本的变化非常敏感。它无法定义模式,因此您永远无法真正确定要序列化的内容或已序列化的内容。单独检查一个泡菜和加载它是非常困难的,所以如果它坏了(如果你决定重命名一个你有泡菜实例的类,它会坏掉),恢复数据是一个主要的麻烦。

【讨论】:

    【解决方案2】:

    用可调用的类实例替换 foo/bar 函数:

    class foo(object):
        def __init__(self, *args):
            self.args = args
        def __call__(self):
            print self.args
    
    d = defer.Deferred()
    d.addCallback(foo("Hello", "world"))
    pickle.dumps(d)
    

    【讨论】:

    • 大概,腌制一个函数的想法是,你可以关闭你的程序或转移腌制的对象,并期望在其他地方调用该函数。这不起作用,因为函数地址是在运行时确定的。它可能会在程序的多次连续运行中起作用,但它并不安全。
    • 否决了我的反对意见,因为我想我意识到你在谈论函子。 +1,现在,尽管如果您尝试非常 OOP 和类型安全,这种类型的回调可能会导致设计问题。 :)
    • @San Jacinto:是的,它有效。 Pickle 文档清楚地说明了什么可以腌制,什么不能腌制。 docs.python.org/library/…
    • @vz0 是的,我在想你打算将函数对象传递给仿函数,然后以某种方式使用它来调用函数。显然,现在我再次看到它,这不是你的意图。您的示例确实不适合这种区别。
    • 在这里使用可调用实例并没有特别的帮助。您还可以使用绑定方法或全局函数。重要的一点是生成的对象完全由全局可寻址的代码对象组成,而不是内部函数。但请参阅 Jean-Paul 对真正解决方案的回答 - 不要腌制Deferreds。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-25
    • 2018-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多