假设我有一个通过闭包递归的函数:
def outer():
def fact(n):
return 1 if n == 0 else n * fact(n - 1)
return fact
我现在想序列化函数并使用以下方法重建它types.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
是在构造和初始化之间记忆对象,但function
对象是不可变的,fn.__closure__
(元组)和单元对象也是如此:
>>> 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
大概在普通代码中构造递归函数时,该语言必须做类似的事情,因为函数对象在构造之前不能放置在其闭包中。构建我缺少的递归函数有什么魔力吗?