如何序列化递归函数?

2024-05-20 11:54:57 发布

您现在位置:Python中文网/ 问答频道 /正文

假设我有一个通过闭包递归的函数:

def outer():
    def fact(n):
        return 1 if n == 0 else n * fact(n - 1)
    return fact

现在我想序列化函数并使用types.FunctionType重新构造它:

^{pr2}$

这对于普通闭包来说很好,但是对于fact它会导致无限递归,因为pickle试图在其闭包中序列化{}。在pickle中处理递归数据结构的通常方法是在构造和初始化之间记住对象,但是function对象是不可变的,正如{}(元组)和单元格对象一样:

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

假设在普通代码中构造递归函数时,语言必须做一些类似的事情,因为函数对象在构造完成之前不能放在它的闭包中。构建递归函数是否有我所缺少的魔力?在


Tags: 对象lambda函数returnif序列化defcontents
2条回答

闭包绑定到一个自由变量,而不是它的值。对于自引用闭包,Python需要做的就是首先为自由的fact名称创建一个闭包(尚未绑定到任何东西),用闭包创建函数对象,然后将fact绑定到该对象。在

因此,您需要将创建一个闭包和一个函数合并到同一个外部函数中,以便为该函数将要绑定到的名称创建一个闭包:

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

我想,要使这项工作与取消pickle一起工作,您必须循环闭包参数(args[-1])并检测哪里有递归,并用create_function_closure.__closure__[0]替换这一项。在

在Python3中,我就是这样做的,使用nonlocal

def settable_cell():
    if False:
        x = None
    def set_cell(y):
        nonlocal x
        x = y
    return (lambda: x).__closure__[0], set_cell

在python2中使用生成器:

^{pr2}$

这允许将创建闭包单元格与设置其自由变量的值分开;解决方案的其余部分只需要对pickle记忆工具进行一些修改。在

相关问题 更多 >