eval、exec、compileの違いは何ですか? 質問する

eval、exec、compileの違いは何ですか? 質問する

私は Python コードの動的評価を調べていて、関数とeval()ステートメントに遭遇しました。compile()exec

eval誰か、との違いexecと、 のさまざまなモードがどのようにcompile()当てはまるかを説明していただけますか?

ベストアンサー1

短い答え、またはTL;DR

基本的に、eval動的に生成された単一のPython式を評価するために使用され、exec動的に生成された Python コードを副作用のためだけに実行するために使用されます。

eval次の 2 つの違いがありますexec

  1. evalは単一の式のみを受け入れ、ループ、関数/メソッドの初期化などexecの Python ステートメントを含むコード ブロックを取得できます。try: except:classdef

    Python の式は、変数割り当てで値として使用できるものすべてです。

    a_variable = (anything you can put within these parentheses is an expression)
    
  2. 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'でも)、ソース コードにステートメントや単一の式以外のものが含まれていると、例外が発生します。evalcompile

>>> 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 でバイトコードに変換しておけば、ステートメントを含む何かを実行するためにを使用できます。execevalexecNoneevalcompile

>>> eval(compile('if 1: print("Hello")', '<string>', 'exec'))
Hello
>>>

コンパイルされたコードにステートメントが含まれていても、 は問題なく動作します。Noneは から返されるコード オブジェクトの戻り値であるため、が返されますcompile

モードでは(文字列が渡された場合は 関数'eval'でも)、ソース コードにステートメントや単一の式以外のものが含まれていると、例外が発生します。evalcompile

>>> 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
>>> 

eval関数は、単一の表現、次式の値を返します。

>>> a = 2
>>> my_calculation = '42 * a'
>>> result = eval(my_calculation)
>>> result
84

execどちらevalも、実行するプログラム/式を、strソースコードを含む 、unicodeまたはオブジェクトとして、あるいはPython バイトコードを含むオブジェクトとして受け入れます。bytescode

strソースコードを含む/ unicode/bytesが に渡された場合exec、次のコードと同等の動作になります。

exec(compile(source, '<string>', 'exec'))

同様に、eval次のコードも同等に動作します:

eval(compile(source, '<string>', 'eval'))

ExprPythonではすべての式を文として使うことができるので( Pythonではノードと呼ばれます)、抽象文法; 逆は真ではありません)、exec戻り値が必要ない場合は常に を使用できます。つまり、 または のどちらでも使用できますeval('my_func(42)')exec('my_func(42)')、違いは はevalによって返された値を返しmy_funcexecそれを破棄することです。

>>> 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の文を含むソース コードのみを受け入れます。defforwhileimportclassa = 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とはどちらも、コードが参照するグローバル変数スコープとローカル変数スコープであるeval2 つの追加位置引数と を受け入れます。これらは、またはを呼び出したスコープ内のとにデフォルト設定されますが、 には任意の辞書を使用でき、には任意の辞書を使用できます(もちろん も含みます)。これらは、コードが参照する変数を制限/変更するだけでなく、使用されるコードが作成する変数をキャプチャするためにも使用されることがよくあります。globalslocalsglobals()locals()execevalglobalsmappinglocalsdictexec

>>> g = dict()
>>> l = dict()
>>> exec('global a; a, b = 123, 42', g, l)
>>> g['a']
123
>>> l
{'b': 42}

( 全体の値を表示する場合、 が欠落している場合はおよび がグローバルに関する組み込みモジュールを自動的に追加するgため、 は非常に長くなります)。execeval__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ソースをオブジェクトにコンパイルすることで、同じコードの繰り返し呼び出しを高速化できます。パラメータは、関数が受け入れるコード フラグメントの種類と、関数が生成するバイトコードの種類を制御します。選択肢は、、およびです。codemodecompile'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含むループは、単一の文とみなされます。elsetryexceptelsefinally

    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.parsePython で記述されており、 を呼び出すだけです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すれば、関数は戻りevalevalを返します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そしてexecCPython 3のソースコードを見ると、これは非常に明白です。どちらもPyEval_EvalCode同じ引数で呼び出されますが、唯一の違いはexec明示的に返すNone

execPython 2とPython 3の構文の違い

Python 2における大きな違いの 1 つは、がexec文であり、 がeval組み込み関数であることです(Python 3 では両方とも組み込み関数です)。Python exec2 における の公式構文が であることはよく知られている事実ですexec code [in globals[, locals]]

Python 2-to-3の大半とは異なり移植 ガイド 思われる 提案する、 CPython 2のステートメントは、Python 3の関数呼び出しまったく同じexec構文でも使用できます。その理由は、Python 0.9.9に組み込み関数があったためです。そして、その組み込み関数はステートメントに置き換えられました。 execexec(code, globals, locals)execPython 1.0 リリースより前のどこか

Python 0.9.9との下位互換性を損なわないことが望ましいため、グイド・ヴァン・ロッサムは1993年に互換性ハックを追加した。: がcode長さ2または3のタプルで、globalsと がlocalsステートメントに渡されなかった場合exec、 はタプルの2番目と3番目の要素がそれぞれと でcodeあるかのように解釈されます。互換性ハックはglobalslocalsPython 1.4 ドキュメント (オンラインで入手可能な最も古いバージョン); そのため、移植ガイドやツールの多くの作成者には知られていなかったが、文書化されたまた2012年11月:

最初の式は、長さ 2 または 3 のタプルにすることもできます。この場合、オプション部分は省略する必要があります。 形式はexec(expr, globals)と同等ですexec expr in globalsが、 形式はexec(expr, globals, locals)と同等ですexec expr in globals, locals。 のタプル形式はexecPython 3 との互換性を提供します。ここで、execはステートメントではなく関数です。

はい、CPython 2.7 では、これは前方互換性オプションであると便利に言及されています (後方互換性オプションがあることで人々を混乱させる理由は何でしょうか)。実際には、これは20 年間後方互換性のために存在していました。

したがって、whileはexecPython 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代わりにループを使用してください)。

おすすめ記事