Python Language
`exec`と` eval`を使った動的コード実行
サーチ…
構文
- eval(式[、globals = None [、locals = None]])
- exec(オブジェクト)
- exec(オブジェクト、グローバル)
- exec(オブジェクト、グローバル、ローカル)
パラメーター
引数 | 詳細 |
---|---|
expression | 文字列としての式コード、またはcode オブジェクト |
object | 文字列としてのステートメントコード、またはcode オブジェクト |
globals | グローバル変数に使用する辞書。 localsが指定されていない場合、これは地元の人々にも使用されます。省略すると、呼び出しスコープのglobals() が使用されます。 |
locals | ローカル変数に使用されるマッピングオブジェクト。省略すると、代わりにglobals 渡されたものが使用されます。両方を省略すると、呼び出しスコープのglobals() およびlocals() がそれぞれglobals およびlocals 使用されglobals 。 |
備考
exec
では、 globals
がlocals
(つまり、同じオブジェクトを参照する)場合、コードはモジュールレベルにあるかのように実行されます。 globals
とlocals
が異なるオブジェクトである場合、コードはクラス本体内にあるかのように実行されます。
globals
オブジェクトが渡されても__builtins__
キーが指定されていない場合、Pythonの組み込み関数と名前は自動的にグローバルスコープに追加されます。実行されたスコープ内でのprint
やisinstance
などの機能の使用を抑止するには、 globals
__builtins__
というキーを値None
マップさせます。ただし、これはセキュリティ機能ではありません。
Python 2固有の構文は使用しないでください。 Python 3の構文はPython 2で動作します。したがって、以下の形式は推奨されません:<s>
-
exec object
-
exec object in globals
-
exec object in globals, locals
execでの文の評価
>>> code = """for i in range(5):\n print('Hello world!')"""
>>> exec(code)
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
evalで式を評価する
>>> expression = '5 + 3 * a'
>>> a = 5
>>> result = eval(expression)
>>> result
20
式を複数回プリコンパイルして評価する
compile
組み込み関数を使用して、式をコード・オブジェクトにプリコンパイルすることができます。このコードオブジェクトをevalに渡すことができます。これは、評価されたコードの繰り返し実行をスピードアップします。 compile
する3番目のパラメータは、文字列'eval'
必要があります。
>>> code = compile('a * b + c', '<string>', 'eval')
>>> code
<code object <module> at 0x7f0e51a58830, file "<string>", line 1>
>>> a, b, c = 1, 2, 3
>>> eval(code)
5
カスタムグローバルを使用してevalで式を評価する
>>> variables = {'a': 6, 'b': 7}
>>> eval('a * b', globals=variables)
42
プラスとして、このコードでは、誤って外部で定義された名前を参照することはできません。
>>> eval('variables')
{'a': 6, 'b': 7}
>>> eval('variables', globals=variables)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
NameError: name 'variables' is not defined
defaultdict
使用すると、たとえば未定義変数をゼロに設定することができます。
>>> from collections import defaultdict
>>> variables = defaultdict(int, {'a': 42})
>>> eval('a * c', globals=variables) # note that 'c' is not explicitly defined
0
ast.literal_evalを使用してPythonリテラルを含む文字列を評価する
文字列、浮動小数点数などのPythonリテラルを含む文字列がある場合、 ast.literal_eval
を使用してeval
ではなくその値を評価できます。これには、特定の構文のみを許可するという追加機能があります。
>>> import ast
>>> code = """(1, 2, {'foo': 'bar'})"""
>>> object = ast.literal_eval(code)
>>> object
(1, 2, {'foo': 'bar'})
>>> type(object)
<class 'tuple'>
しかし、これは信頼できないユーザによって提供されるコードの実行には安全ではなく、慎重に作成された入力で通訳者をクラッシュさせるのは簡単ではありません
>>> import ast
>>> ast.literal_eval('()' * 1000000)
[5] 21358 segmentation fault (core dumped) python3
ここで、入力は100万回繰り返された()
文字列で、CPythonパーサーでクラッシュを引き起こします。 CPython開発者は、パーサのバグをセキュリティの問題とはみなしません。
exec、eval、ast.literal_evalを使用して信頼できないユーザーが提供するコードを実行する
信頼できないユーザーのコードを安全に実行するためにeval
やexec
を使用することはできません。 ast.literal_eval
でもパーサーでクラッシュする傾向があります。悪意のあるコードの実行を防ぐことは時々ありますが、パーサーやトークナイザでのクラッシュの可能性を排除するものではありません。
信頼できないユーザーがコードを評価するには、サードパーティのモジュールに目を向けるか、独自のパーサーと独自の仮想マシンをPythonで記述する必要があります。