クロージャーを通じて再帰的な関数があるとします。
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
おそらく、言語は通常のコード内で再帰関数を構築するときに同様のことを行う必要があります。これも、構築されるまで関数オブジェクトをそのクロージャーに配置することはできません。私が見逃している再帰関数を構築するための魔法はありますか?