1. ホーム
  2. python

[解決済み] eval、exec、compileの違いは何ですか?

2022-03-18 02:45:04

質問

Pythonのコードの動的評価について調べていて、以下のものに出会いました。 eval()compile() 関数、および exec ステートメントを使用します。

の違いについて、どなたか教えてください。 evalexec のモードがどのように異なるのか、また compile() が合うのでしょうか?

解決方法は?

短い答え、またはTL;DR

基本的には eval は、以下のように使用されます。 評価 動的に生成された1つのPython式を生成し exec エグゼクティブ 動的に生成された Python コードを、その副作用のためにのみ使用します。

evalexec は、この2つの違いがあります。

  1. eval のみを受け付けます。 単一表現 , exec は、Pythonのステートメント:ループを持つコードブロックを取ることができます。 try: except: , class と関数/メソッド def といった具合になります。

    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 を関数内の副作用のために使用します。

Python3では 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


compile'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') ここが本当に違うところです。

もし code オブジェクト(Python バイトコード に渡されます。 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

より長い答え、つまり血なまぐさい詳細です。

execeval

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

execeval は、実行されるプログラム/式を str , unicode または bytes ソースコードを含むオブジェクトとして、または code オブジェクト で、Pythonのバイトコードを含んでいます。

もし str / unicode / bytes にソースコードが渡されました。 exec と等価に動作する。

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

eval と同等の振る舞いをする。

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


Pythonではすべての式が文として使えるので(これらは Expr ノードは、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


両方 execeval は、さらに2つの位置引数を受け付けます。 globalslocals - は、コードが見ることのできるグローバル変数とローカル変数のスコープです。これらのデフォルトは 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 のため、もっと長くなります。 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 ソースをコンパイルして 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 その式の値を標準出力に出力する(!) .

    An if - elif - else を持つループ、チェーン else を、そして try とその except , elsefinally ブロックは1つの文とみなされます。

    トップレベルのステートメントを2つ含むソースフラグメントは 'single' ただし、Python 2 では バグ で、コード内に複数のトップレベルステートメントが存在する場合がありますが、最初の1つだけがコンパイルされ、残りは無視されます。

    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 を実行すると、結果のコードが表示されます。

このように execeval は、実際には compile 関数とそのモードです。


ソースコードをバイトコードにコンパイルするだけでなく。 compile はコンパイルに対応しています。 抽象構文木 (Pythonコードのパースツリー)を code オブジェクトに、ソースコードは抽象的なシンタックスツリー ( ast.parse はPythonで書かれており、単に compile(source, filename, mode, PyCF_ONLY_AST) 複雑なケースでは、コードをテキストの行ではなく、ノードのツリーとして扱う方が簡単なことが多いからです。


一方 eval は単一の式を含む文字列を評価することしかできませんが eval 文全体、あるいはモジュール全体が compile バイトコードに変換されます。 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の大きな違いの1つ 2 というのは exec は文であり eval は組み込み関数です(Python 3ではどちらも組み込み関数です)。 よく知られた事実ですが、公式の構文では exec は、Python 2 では exec code [in globals[, locals]] .

Python 2-to-3の大部分とは異なり ポーティング ガイド 見える を提案する は、その exec という構文で使用することもできます。 見た目 まさに のように exec 関数の呼び出しは、Python 3では 理由は、Python 0.9.9では exec(code, globals, locals) 組み込み関数! そして、そのビルトイン関数は exec ステートメント Python 1.0 リリース以前のどこかで .

Python 0.9.9との後方互換性を壊さないことが望ましかったため。 Guido van Rossum は 1993 年に互換性ハックを追加しました。 : もし code が長さ2または3のタプルであり、かつ globalslocals に渡されることはありませんでした。 exec ステートメントを使用する場合、そうでなければ code は、タプルの 2 番目と 3 番目の要素が globalslocals をそれぞれ作成しました。にも互換性ハックは記載されていませんでした。 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との互換性を確保するため、Python 3では exec は文ではなく、関数です。

そう、CPython 2.7では、前方互換性オプションであることが手際よく言及されています(なぜ後方互換性オプションがあることで人々を混乱させるのでしょうか)。 というのは、以前からあったのですが 20年来の後方互換性 .

このように、一方 exec は、Python 1とPython 2では文、Python 3とPython 0.9.9では組み込み関数です。

>>> exec("print(a)", globals(), {'a': 42})
42

Jython 2.5.2, PyPy 2.3.1 (Python 2.7.6), IronPython 2.6.1 でも動きます (CPythonの文書化されていない挙動に忠実に従ったことに感謝します)。

Python 1.0 - 2.7 の互換性ハックではできないこと、それは exec を変数に格納します。

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 ループの代わりに!)。