動的に生成という言い方が正しいかはわからないけど、実行中に関数名を動的に決定したい。いくつかやり方はあるけど、例えば exec
を使用して、適当な dict
に入れてやればよい。
def dynamic_generated_fn():
created_fns = {}
fn_code = """def {name}(x):
return x + '_called_by_{name}'"""
fn_name1 = 'foo'
fn_name2 = 'bar'
exec(fn_code.format(name=fn_name1), {}, created_fns)
print(created_fns[fn_name1]('test'))
exec(fn_code.format(name=fn_name2), {}, created_fns)
print(created_fns[fn_name2]('test'))
$ python test.py
test_called_by_foo
test_called_by_bar
fn_name1
あるいは fn_name2
という変数を使って、関数を呼び出している。スタックトレースにも fn_name
が出力される。
def dynamic_generated_fn_error():
created_fns = {}
fn_code = """def {name}(x):
raise RuntimeError('{name}')"""
fn_name = 'foo'
exec(fn_code.format(name=fn_name), {}, created_fns)
print(created_fns[fn_name]('test'))
$ python test.py
Traceback (most recent call last):
File "test.py", line 31, in <module>
dynamic_generated_fn_error()
File "test.py", line 27, in dynamic_generated_fn_error
print(created_fns[fn_name]('test'))
File "<string>", line 2, in foo
RuntimeError: foo
ファイル名は "<string>"
となってしまうが、ちゃんと関数名が "foo"
になっている。
このままだと、外の関数を呼び出すことができない。
def dynamic_generated_fn_in_fn_error():
created_fns = {}
def add_phrase(x):
return x + '_add'
fn_code = """def {name}(x):
return add_phrase(x)"""
fn_name = 'foo'
exec(fn_code.format(name=fn_name), {}, created_fns)
print(created_fns[fn_name]('test'))
$ python test.py
Traceback (most recent call last):
File "test.py", line 46, in <module>
dynamic_generated_fn_in_fn_error()
File "test.py", line 42, in dynamic_generated_fn_in_fn_error
print(created_fns[fn_name]('test'))
File "<string>", line 2, in foo
NameError: name 'add_phrase' is not defined
外の関数を呼び出したいときは functools
で部分適用すればよい。ドキュメント → functools#partial
def dynamic_generated_fn_in_fn():
created_fns = {}
def add_phrase(x):
return x + '_add'
import functools
fn_code = """def {name}(fn, x):
return fn(x)"""
fn_name = 'foo'
exec(fn_code.format(name=fn_name), {}, created_fns)
generated_fn = functools.partial(created_fns[fn_name], add_phrase)
print(generated_fn('test'))
$ python test.py
test_add
内部で呼び出したい関数の個数分、適用する必要が出てくるが、まぁしょうがない。
フルコードは Gist に上げた。
2020-07-24