Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

自由変数の値を変更する

More than 1 year has passed since last update.

関数の自由変数(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')
fumitoh
Pythonの言語仕様や実装に関する備忘録が中心です。たまに便利なTipsなども投稿します。
https://github.com/fumitoh
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away