对包装的部分函数进行序列化
我正在尝试创建一个可以被“序列化”的装饰器,使用的是部分函数。不过,每次尝试时,我总是遇到序列化错误。
第一个简单的例子如下:
def decorator(func):
def wrapper(**kwargs):
return partial(func, **kwargs)
return wrapper
@decorator
def decorated(x, y=1, z=2):
return x+y+z
y5 = decorated(y=5)
pickle.dumps(y5)
这里的 partial
是从 functools
这个模块里来的。
稍微复杂一点的尝试是在 def wrapper
之前加上 @wraps
,但这并没有解决问题。
我不太明白序列化到底是怎么回事。
1 个回答
11
问题出在你的装饰器上,而不是partial。一个partial对象应该可以正常被序列化:
>>> from pickle import *
>>> from functools import *
>>> f = partial(pow, 2)
>>> p = dumps(f)
>>> g = loads(p)
>>> g(5)
32
所以,你代码中的问题在于装饰器。它没有保留原始函数的名称。试试这个:
import pickle
from functools import *
def decorator(func):
def wrapper(**kwargs):
return partial(func, **kwargs)
return wrapper
def decorated(x, y=1, z=2):
return x+y+z
dd = decorator(decorated)
y5 = dd(y=5)
pickle.dumps(y5)
使用dd
的修改应该能让序列化逻辑通过名称找到底层函数。这就是序列化的工作原理。
要查看序列化中的函数名称,可以查看dumps的输出:
>>> print pickle.dumps(y5)
cfunctools
partial
p0
(c__main__
decorated
p1
tp2
Rp3
(g1
(t(dp4
S'y'
p5
I5
sNtp6
b.
单词“decorated”需要能够被找到,应该和底层函数相等,而不是被装饰器隐藏。记住,当函数被序列化时,只有它们的名称会被存储。函数的实际内容不会在序列化中。
有一些变通方法,但并不优雅。你可以使用__setstate__()来保存函数名称和源代码。然后添加一个__getstate__()方法,通过执行源代码来恢复函数。
另外,你也可以提取函数对象中的字节码并保存它们。在恢复时,编译代码对象并执行它。
简而言之,你使用@符号的装饰器目标与函数序列化的工作方式直接冲突。为了实现你的目标,你需要自定义函数的序列化,让它保存函数的实际操作,而不仅仅是名称。