私は Python コードの動的評価を調べていて、関数とeval()
ステートメントに遭遇しました。compile()
exec
eval
誰か、との違いexec
と、 のさまざまなモードがどのようにcompile()
当てはまるかを説明していただけますか?
ベストアンサー1
短い答え、またはTL;DR
基本的に、eval
動的に生成された単一のPython式を評価するために使用され、exec
動的に生成された Python コードを副作用のためだけに実行するために使用されます。
eval
次の 2 つの違いがありますexec
。
eval
は単一の式のみを受け入れ、ループ、関数/メソッドの初期化などexec
の Python ステートメントを含むコード ブロックを取得できます。try: except:
class
def
Python の式は、変数割り当てで値として使用できるものすべてです。
a_variable = (anything you can put within these parentheses is an expression)
eval
は指定された式の値を返しますexec
が、 はコードからの戻り値を無視し、常に戻りますNone
(Python 2 ではステートメントであり、式として使用できないため、実際には何も返されません)。
バージョン 1.0 - 2.7 では、関数内で副作用にexec
使用される関数に対して、CPython は異なる種類のコード オブジェクトを生成する必要があったため、 というステートメントがありました。exec
Python 3 では、exec
は関数です。これを使用しても、それが使用されている関数のコンパイル済みバイトコードには影響しません。
つまり基本的には次のようになります。
>>> a = 5
>>> eval('37 + a') # it is an expression
42
>>> exec('37 + a') # it is an expression statement; value is ignored (None is returned)
>>> exec('a = 47') # modify a global variable as a side effect
>>> a
47
>>> eval('a = 47') # you cannot evaluate a statement
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
a = 47
^
SyntaxError: invalid syntax
inモードは、任意の数のステートメントを暗黙的に常に を返すバイトコードにコンパイルしますが、 incompile
モードは、単一の式をその式の値を返すバイトコードにコンパイルします。'exec'
None
'eval'
>>> eval(compile('42', '<string>', 'exec')) # code returns None
>>> eval(compile('42', '<string>', 'eval')) # code returns 42
42
>>> exec(compile('42', '<string>', 'eval')) # code returns 42,
>>> # but ignored by exec
モードでは(文字列が渡された場合は 関数'eval'
でも)、ソース コードにステートメントや単一の式以外のものが含まれていると、例外が発生します。eval
compile
>>> compile('for i in range(3): print(i)', '<string>', 'eval')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
for i in range(3): print(i)
^
SyntaxError: invalid syntax
実際には、 「evalは単一の式のみを受け入れる」という文は、文字列(Pythonソースコードを含む)がに渡された場合にのみ適用されますeval
。その後、内部的にバイトコードにコンパイルされます。compile(source, '<string>', 'eval')
違いが実際に生じるのはここです。
オブジェクト (Pythonバイトコードcode
を含む) がまたはに渡された場合、が戻り値を無視し、常に を返すという点を除けば、それらは同じように動作します。したがって、文字列として渡すのではなく、事前に d でバイトコードに変換しておけば、ステートメントを含む何かを実行するためにを使用できます。exec
eval
exec
None
eval
compile
>>> eval(compile('if 1: print("Hello")', '<string>', 'exec'))
Hello
>>>
コンパイルされたコードにステートメントが含まれていても、 は問題なく動作します。None
は から返されるコード オブジェクトの戻り値であるため、が返されますcompile
。
モードでは(文字列が渡された場合は 関数'eval'
でも)、ソース コードにステートメントや単一の式以外のものが含まれていると、例外が発生します。eval
compile
>>> compile('for i in range(3): print(i)', '<string>'. 'eval')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
for i in range(3): print(i)
^
SyntaxError: invalid syntax
より長い答え、つまり残酷な詳細
exec
そしてeval
のexec
機能(これはPython 2のステートメント) は、動的に作成されたステートメントまたはプログラムを実行するために使用されます。
>>> program = '''
for i in range(3):
print("Python is cool")
'''
>>> exec(program)
Python is cool
Python is cool
Python is cool
>>>
>>> a = 2
>>> my_calculation = '42 * a'
>>> result = eval(my_calculation)
>>> result
84
exec
どちらeval
も、実行するプログラム/式を、str
ソースコードを含む 、unicode
またはオブジェクトとして、あるいはPython バイトコードを含むオブジェクトとして受け入れます。bytes
code
str
ソースコードを含む/ unicode
/bytes
が に渡された場合exec
、次のコードと同等の動作になります。
exec(compile(source, '<string>', 'exec'))
同様に、eval
次のコードも同等に動作します:
eval(compile(source, '<string>', 'eval'))
Expr
Pythonではすべての式を文として使うことができるので( Pythonではノードと呼ばれます)、抽象文法; 逆は真ではありません)、exec
戻り値が必要ない場合は常に を使用できます。つまり、 または のどちらでも使用できますeval('my_func(42)')
がexec('my_func(42)')
、違いは はeval
によって返された値を返しmy_func
、exec
それを破棄することです。
>>> def my_func(arg):
... print("Called with %d" % arg)
... return arg * 2
...
>>> exec('my_func(42)')
Called with 42
>>> eval('my_func(42)')
Called with 42
84
>>>
2 つのうち、は、 、、、、または、代入文 (別名)、またはプログラム全体などexec
の文を含むソース コードのみを受け入れます。def
for
while
import
class
a = 42
>>> exec('for i in range(3): print(i)')
0
1
2
>>> eval('for i in range(3): print(i)')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
for i in range(3): print(i)
^
SyntaxError: invalid syntax
exec
とはどちらも、コードが参照するグローバル変数スコープとローカル変数スコープであるeval
2 つの追加位置引数と を受け入れます。これらは、またはを呼び出したスコープ内のとにデフォルト設定されますが、 には任意の辞書を使用でき、には任意の辞書を使用できます(もちろん も含みます)。これらは、コードが参照する変数を制限/変更するだけでなく、使用されるコードが作成する変数をキャプチャするためにも使用されることがよくあります。globals
locals
globals()
locals()
exec
eval
globals
mapping
locals
dict
exec
>>> g = dict()
>>> l = dict()
>>> exec('global a; a, b = 123, 42', g, l)
>>> g['a']
123
>>> l
{'b': 42}
( 全体の値を表示する場合、 が欠落している場合はおよび がグローバルに関する組み込みモジュールを自動的に追加するg
ため、 は非常に長くなります)。exec
eval
__builtins__
Python 2では、文の公式構文はexec
実際には でありexec code in globals, locals
、次のように表されます。
>>> exec 'global a; a, b = 123, 42' in g, l
ただし、代替構文exec(code, globals, locals)
も常に受け入れられています (以下を参照)。
compile
のcompile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)
組み込み関数を使用するとexec
、または事前にeval
ソースをオブジェクトにコンパイルすることで、同じコードの繰り返し呼び出しを高速化できます。パラメータは、関数が受け入れるコード フラグメントの種類と、関数が生成するバイトコードの種類を制御します。選択肢は、、およびです。code
mode
compile
'eval'
'exec'
'single'
'eval'
モードは単一の式を想定しており、実行するとその式の値を返すバイトコードを生成します。>>> dis.dis(compile('a + b', '<string>', 'eval')) 1 0 LOAD_NAME 0 (a) 3 LOAD_NAME 1 (b) 6 BINARY_ADD 7 RETURN_VALUE
'exec'
単一の式からコードのモジュール全体まで、あらゆる種類の Python 構造を受け入れ、それらをモジュールのトップレベルのステートメントであるかのように実行します。コード オブジェクトは次を返しますNone
。>>> dis.dis(compile('a + b', '<string>', 'exec')) 1 0 LOAD_NAME 0 (a) 3 LOAD_NAME 1 (b) 6 BINARY_ADD 7 POP_TOP <- discard result 8 LOAD_CONST 0 (None) <- load None on stack 11 RETURN_VALUE <- return top of stack
'single'
は、単一の文(または で区切られた複数の文)'exec'
を含むソース コードを受け入れるの限定された形式です。最後の文が式文である場合、結果のバイトコードは、その式の値の も標準出力に出力します(!) 。;
repr
if
-elif
-チェーン、、およびその、およびブロックをelse
含むループは、単一の文とみなされます。else
try
except
else
finally
2 つのトップレベル ステートメントを含むソース フラグメントは、 ではエラーになります。ただし、Python 2 では、コード内に複数のトップレベル ステートメントを許可するバグ
'single'
があり、最初のステートメントのみがコンパイルされ、残りは無視されます。Python 2.7.8 の場合:
>>> exec(compile('a = 5\na = 6', '<string>', 'single')) >>> a 5
Python 3.4.2 では次のようになります。
>>> exec(compile('a = 5\na = 6', '<string>', 'single')) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<string>", line 1 a = 5 ^ SyntaxError: multiple statements found while compiling a single statement
これは、対話型の Python シェルを作成する場合に非常に便利です。ただし、結果のコードを実行しても、式の値は返されません。
eval
したがって、 と の最大の違いはexec
、実際には機能とそのモードeval
から生じます。compile
ソースコードをバイトコードにコンパイルするだけでなくcompile
、抽象構文木(Python コードのツリーを解析) をcode
オブジェクトに変換し、ソース コードを抽象構文ツリーに変換します ( はast.parse
Python で記述されており、 を呼び出すだけですcompile(source, filename, mode, PyCF_ONLY_AST)
)。これらは、たとえばソース コードをオンザフライで変更する場合や、動的なコード作成に使用されます。複雑なケースでは、コードをテキスト行として扱うよりもノードのツリーとして扱う方が簡単な場合が多いためです。
ではeval
、単一の式を含む文字列のみを評価できますが、eval
ステートメント全体、またはcompile
バイトコードに変換されたモジュール全体を評価することもできます。つまり、Python 2 では、 はprint
ステートメントであり、eval
直接導くことはできません。
>>> eval('for i in range(3): print("Python is cool")')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
for i in range(3): print("Python is cool")
^
SyntaxError: invalid syntax
compile
これを'exec'
モード付きでオブジェクトに変換code
すれば、関数は戻りeval
値eval
を返しますNone
。
>>> code = compile('for i in range(3): print("Python is cool")',
'foo.py', 'exec')
>>> eval(code)
Python is cool
Python is cool
Python is cool
調べてみるとeval
そしてexec
CPython 3のソースコードを見ると、これは非常に明白です。どちらもPyEval_EvalCode
同じ引数で呼び出されますが、唯一の違いはexec
明示的に返すNone
。
exec
Python 2とPython 3の構文の違い
Python 2における大きな違いの 1 つは、がexec
文であり、 がeval
組み込み関数であることです(Python 3 では両方とも組み込み関数です)。Python exec
2 における の公式構文が であることはよく知られている事実ですexec code [in globals[, locals]]
。
Python 2-to-3の大半とは異なり移植 ガイド 思われる 提案する、 CPython 2のステートメントは、Python 3の関数呼び出しとまったく同じexec
構文でも使用できます。その理由は、Python 0.9.9に組み込み関数があったためです。そして、その組み込み関数はステートメントに置き換えられました。 exec
exec(code, globals, locals)
exec
Python 1.0 リリースより前のどこか。
Python 0.9.9との下位互換性を損なわないことが望ましいため、グイド・ヴァン・ロッサムは1993年に互換性ハックを追加した。: がcode
長さ2または3のタプルで、globals
と がlocals
ステートメントに渡されなかった場合exec
、 はタプルの2番目と3番目の要素がそれぞれと でcode
あるかのように解釈されます。互換性ハックはglobals
locals
Python 1.4 ドキュメント (オンラインで入手可能な最も古いバージョン); そのため、移植ガイドやツールの多くの作成者には知られていなかったが、文書化されたまた2012年11月:
最初の式は、長さ 2 または 3 のタプルにすることもできます。この場合、オプション部分は省略する必要があります。 形式は
exec(expr, globals)
と同等ですexec expr in globals
が、 形式はexec(expr, globals, locals)
と同等ですexec expr in globals, locals
。 のタプル形式はexec
Python 3 との互換性を提供します。ここで、exec
はステートメントではなく関数です。
はい、CPython 2.7 では、これは前方互換性オプションであると便利に言及されています (後方互換性オプションがあることで人々を混乱させる理由は何でしょうか)。実際には、これは20 年間後方互換性のために存在していました。
したがって、whileはexec
Python 1とPython 2では文であり、Python 3とPython 0.9.9では組み込み関数です。
>>> exec("print(a)", globals(), {'a': 42})
42
おそらくこれまでに広くリリースされたすべての Python バージョンで同一の動作をしており、Jython 2.5.2、PyPy 2.3.1 (Python 2.7.6)、IronPython 2.6.1 でも動作します (CPython の文書化されていない動作を厳密に追跡したことは称賛に値します)。
exec
互換性ハックのある Python 1.0 - 2.7 では、戻り値を変数に格納することはできません。
Python 2.7.11+ (default, Apr 17 2016, 14:00:29)
[GCC 5.3.1 20160413] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a = exec('print(42)')
File "<stdin>", line 1
a = exec('print(42)')
^
SyntaxError: invalid syntax
(これは Python 3 でも役に立ちません。exec
常に が返されるからですNone
)、または への参照を渡しますexec
。
>>> call_later(exec, 'print(42)', delay=1000)
File "<stdin>", line 1
call_later(exec, 'print(42)', delay=1000)
^
SyntaxError: invalid syntax
これは、誰かが実際に使用した可能性があるパターンですが、可能性は低いです。
または、リストの理解で使用します。
>>> [exec(i) for i in ['print(42)', 'print(foo)']
File "<stdin>", line 1
[exec(i) for i in ['print(42)', 'print(foo)']
^
SyntaxError: invalid syntax
これはリスト内包表記の乱用です (for
代わりにループを使用してください)。