関数の自由変数(Free variable)とそれを調べる方法は、以前こちらの記事で紹介しましたが、自由変数の値を無理やり置換した関数を作るということをやってみました。
通常、そのようなことは出来ませんが、ctypes
という標準モジュールを用いると、
CPython
のAPIにアクセス出来ます。
これを用いて自由変数の新しい値をラップするcell
オブジェクトを作成し、自由変数の定義順にcell
を並べたtuple
が新しいclosure
です。
types
標準モジュールの中にFunctionType
型が定義されており、最初の引数に元々の関数のコードオブジェクトと、closure
引数に先ほど作成した新しいclosure
を渡せば、元々の関数の自由変数の値は新しい値に置き換わっています。
もちろんCPythonだけに適用できる方法です。
import ctypes
from types import FunctionType
def outer():
x, y = 1, 'y' # 外側のスコープで定義
def temp_func():
return x, y # 内側の関数では自由変数となる
return temp_func
def _create_cell(value): # valueの値を持つ新しいcellオブジェクトを作成
PyCell_New = ctypes.pythonapi.PyCell_New
PyCell_New.argtypes = (ctypes.py_object,)
PyCell_New.restype = ctypes.py_object
return PyCell_New(value)
def _create_closure(*values): # 複数の値からcellのtupleを作る
return tuple(_create_cell(val) for val in values)
closure = _create_closure(2, 'z')
func = outer() # 置換前の関数
altfunc = FunctionType(func.__code__, globals(), closure=closure) # 置換した関数を作成
実行結果
In[2]: func()
Out[2]: (1, 'y')
In[3]: altfunc()
Out[3]: (2, 'z')