python
で関数を動的に実行する。
eval()やast.literal_eval()を使う
動的な実行といえば、eval()
。
>>> eval('1+4')
5
>>> def f(): print('hello func')
...
>>> eval('f()')
hello func
>>>
このような感じで、式の評価や関数の実行に使える。
ast.literal_eval()
は、使える状況はeval()
よりも限られるが、下の引用によると安全に使えるようだ。
この関数は Python の式を含んだ信頼出来ない出どころからの文字列を、値自身を解析することなしに安全に評価するのに使えます。 この関数は、例えば演算や添え字を含んだ任意の複雑な表現を評価するのには使えません。
しかし、自分の用途には適合していないので今回は割愛。
安全を考慮する
外部の文字列によるコードを実行することになるので、安全性は必ず考える必要がある。くれぐれもそのまま実行は避けたいところ。
今回の用途では、バッチによる実行で引数ごとに異なる関数を実行したいという目的なので、あまり過剰に気にしなくてもいいかとは思うが、ある程度の対策はしておく。
特定文字列のみ実行可能にする
単純にeval()
実行前に、渡す引数の文字列をチェックする。今回はいくつかの文字列である必要があるだけなので、定数のタプルにその文字列たちを格納して、引数で渡された文字列がその中に含まれるかどうかを確認することにした。
# 受け入れる文字列のタプル
ACCEPT_ARGS = ('AAA', 'BBB', 'CCC')
...
arg = get_arg() # 何らかの方法で引数を取得
if arg in ACCEPT_ARGS:
# ここまで来れれば実行可能。
eval(generate_eval_string(arg)) # argを使って実行する文字列を作成してeval
else:
print('無効な文字列')
このような実装でいいと思う。やっていることは、難しいことではなく、
sys.argv
やargparse
などで、python起動時に渡される引数を取得(get_arg()
の中身で行う)ACCEPT_ARGS
に含まれる文字列かどうか判定。- 含まれていなければ、特に何もしない。
- 含まれていれば、
arg
を使って、実行する関数名を生成する。 - 最後に、生成した関数名を
eval
する。
ということだけ。
受入可能な文字列をタプルにしておくことで実行可能な値が分かりやすいというメリット(?)も一応あると思う。
環境変数とかを駆使したほうがいいかもしれないけれど。
とりあえずこれでいいだろう。
以上です。