Python 如何序列化递归函数?
假设我有一个通过闭包递归的函数:Python 如何序列化递归函数?,python,reflection,recursion,closures,introspection,Python,Reflection,Recursion,Closures,Introspection,假设我有一个通过闭包递归的函数: def outer(): def fact(n): return 1 if n == 0 else n * fact(n - 1) return fact 现在我想序列化函数并使用类型重新构造它。FunctionType: import pickle, marshal, copyreg, types def make_cell(value): return (lambda: value).__closure__[0]
def outer():
def fact(n):
return 1 if n == 0 else n * fact(n - 1)
return fact
现在我想序列化函数并使用类型重新构造它。FunctionType
:
import pickle, marshal, copyreg, types
def make_cell(value):
return (lambda: value).__closure__[0]
def make_function(*args):
return types.FunctionType(*args)
copyreg.pickle(types.CodeType,
lambda code: (marshal.loads, (marshal.dumps(code),)))
copyreg.pickle(type((lambda i=0: lambda: i)().__closure__[0]),
lambda cell: (make_cell, (cell.cell_contents,)))
copyreg.pickle(types.FunctionType,
lambda fn: (make_function, (fn.__code__, {}, fn.__name__, fn.__defaults__, fn.__closure__)))
buf = pickle.dumps(outer())
fn = pickle.loads(buf)
这适用于普通闭包,但是对于fact
它会导致无限递归,因为pickle
尝试在其闭包内序列化fact
。在pickle
中处理递归数据结构的通常方法是在构造和初始化之间记忆对象,但是函数
对象是不可变的,正如fn.\uu闭包
(元组)和单元格对象:
>>> cell = (lambda i=0: lambda: i)().__closure__[0]
>>> cell.cell_contents = 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: attribute 'cell_contents' of 'cell' objects is not writable
>>单元格=(lambda i=0:lambda:i)()。\uuuu闭包\uuuu[0]
>>>cell.cell_内容=5
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
AttributeError:“单元格”对象的属性“单元格内容”不可写
在普通代码中构造递归函数时,该语言可能也要做类似的事情,因为函数对象在构造之前无法放入其闭包中。构建递归函数是否有我所缺少的魔力?闭包绑定自由变量,而不是它的值。对于自引用闭包,Python需要做的就是首先为自由的事实
名称创建闭包(尚未绑定到任何东西),使用闭包创建函数对象,然后将事实
绑定到该对象
因此,您需要将创建闭包和函数合并到同一个外部函数中,以便创建一个与函数将绑定到的名称对应的闭包:
def create_closure_and_function(*args):
func = None
def create_function_closure():
return func
closure = create_function_closure.__closure__
func = types.FunctionType(*args[:-1] + [closure])
return func
要使用unpickling实现这一点,您必须循环使用闭包参数(
args[-1]
)并检测哪里存在递归,然后用create\u function\u闭包替换该项。我想,闭包\uuu[0]
。这就是我在Python3中使用非本地完成此操作的原因:
def settable_cell():
if False:
x = None
def set_cell(y):
nonlocal x
x = y
return (lambda: x).__closure__[0], set_cell
在Python 2中,使用生成器:
def settable_cell():
def g():
while True:
x = (yield (lambda: x).__closure__[0])
set_cell = iter(g()).send
return set_cell(None), set_cell
这允许将创建闭包单元与设置其自由变量的值分离;解决方案的其余部分只需要对pickle
memosisation功能进行一些修改。我不是python专家,但序列化不是关于保存状态吗?这里没有要保存的状态。@RobertHarvey:父函数返回的函数对象是一个新对象。它的状态是闭包,可能包含更多数据。闭包只是对原始变量的引用。闭包首先可以引用一个空的名称,因此fact
首先是一个空单元格。然后在构建函数之后,fact
被绑定到新函数。就Python而言,没有问题;我试图将创建闭包与设置其单元格的值区分开来。在同一个函数范围内做任何事情都有点麻烦,但是您可以使用生成器或另一个嵌套函数(使用非本地
)捕获单元格的范围。