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而言,没有问题;我试图将创建闭包与设置其单元格的值区分开来。在同一个函数范围内做任何事情都有点麻烦,但是您可以使用生成器或另一个嵌套函数(使用
非本地
)捕获单元格的范围。