如何序列化回调链

3 投票
2 回答
3011 浏览
提问于 2025-04-16 10:48

我在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

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

谢谢,
Jonathan

2 个回答

0

把 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)
2

不要尝试对Deferreds进行序列化(也就是“腌制”)。Twisted不支持这个。即使你能搞出一些看起来能用的东西(这并不是完全不可能),但将来Twisted更新后,你保存的状态可能会全部坏掉。

Deferreds是用来控制你代码中事件流动的工具。它们并不是用来存储应用程序状态的。如果你想保存应用程序的状态,应该把它和Deferreds分开,只对状态进行序列化。

在这样做的时候,你可能还想避免使用pickle作为序列化格式。因为pickle并不是一个好的数据存储方式。它的格式非常复杂,对Python版本和库版本的变化非常敏感。它没有定义数据结构的方式,所以你永远无法真正确定你在序列化什么或者你已经序列化了什么。想要单独检查一个pickle文件而不加载它是非常困难的,所以如果它坏掉了(比如你重命名了一个已经序列化的类),恢复数据就会变得非常麻烦。

撰写回答