eval,exec和compile有什麼區別?

我一直在研究Python代碼的動態評估,並遇到eval()compile()函數以及exec語句。 html

有人能夠解釋一下evalexec之間的區別,以及不一樣的compile()模式如何適應嗎? python


#1樓

exec用於語句,不返回任何內容。 eval用於表達式,並返回表達式的值。 linux

表達式表示「某事」,而語句表示「作某事」。 express


#2樓

  1. exec不是表達式:Python 2.x中的語句和Python 3.x中的函數。 它編譯並當即評估字符串中包含的一條語句或一組語句。 例: c#

    exec('print(5)') # prints 5. # exec 'print 5' if you use Python 2.x, nor the exec neither the print is a function there exec('print(5)\\nprint(6)') # prints 5{newline}6. exec('if True: print(6)') # prints 6. exec('5') # does nothing and returns nothing.
  2. eval是一個內置函數( 不是語句),該函數對一個表達式求值並返回該表達式產生的值。 例: app

    x = eval('5') # x <- 5 x = eval('%d + 6' % x) # x <- 11 x = eval('abs(%d)' % -100) # x <- 100 x = eval('x = 5') # INVALID; assignment is not an expression. x = eval('if 1: x = 4') # INVALID; if is a statement, not an expression.
  3. compileexeceval的較低版本。 它不會執行或評估您的語句或表達式,但會返回能夠執行此操做的代碼對象。 模式以下: ide

    1. compile(string, '', 'eval')返回若是完成eval(string)將會執行的代碼對象。 請注意,您不能在這種模式下使用語句。 僅(單個)表達式有效。
    2. compile(string, '', 'exec')返回若是執行完exec(string)後將要執行的代碼對象。 您能夠在此處使用任意數量的語句。
    3. compile(string, '', 'single')相似於exec模式,可是它將忽略除第一條語句之外的全部內容。 請注意,帶有結果的if / else語句被視爲單個語句。

#3樓

簡短答案,即TL; DR

基本上, eval用於EVAL審視大家單個動態生成的Python表達式,以及exec用於動態生成的Python代碼只爲它的反作用EXEC尤特。 函數

evalexec有如下兩個區別: 工具

  1. eval只接受單個表達exec能夠採起具備Python語句代碼塊:循環, try: except:class和函數/方法def initions等。 ui

    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

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

實際上,只有將字符串(包含Python 源代碼 )傳遞給eval時,語句「 eval只接受一個表達式」才適用。 而後使用compile(source, '<string>', 'eval')在內部將其編譯爲字節碼。這纔是真正的區別。

若是將code對象(包含Python bytecode )傳遞給execeval ,則它們的行爲相同 ,除了exec忽略返回值的事實外,始終始終返回None 。 所以,若是您只是將compile爲字節碼而不是將其做爲字符串傳遞,則可使用eval執行具備語句的內容:

>>> 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都接受程序/表達式以包含源代碼的strunicodebytes對象或包含Python字節碼的code對象運行

若是將包含源代碼的str / unicode / bytes傳遞給exec ,則其行爲等效於:

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

eval相似地等效於:

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

因爲全部表達式均可以用做Python中的語句(在Python 抽象語法中 ,這些表達式稱爲Expr節點;反之則不成立),若是不須要返回值,則始終可使用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接受包含語句的源代碼,例如defforwhileimportclass ,賦值語句(aka 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 -這是全局和局部變量的做用域,該代碼看到。 它們默認爲execeval範圍內的globals()locals() ,可是任何字典均可以用於globals以及任何locals mapping (固然包括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)能夠經過預先將源code編譯爲code對象來使用execeval來加快相同代碼的重複調用。 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使用其exceptelsefinally塊被視爲單個語句。

    包含2個頂級語句的源代碼片斷是'single'的錯誤,除了在Python 2中存在一個錯誤該錯誤有時會在代碼中容許多個頂級語句。 只有第一個被編譯; 其他的將被忽略:

    在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 Shell很是有用。 可是,即便eval結果代碼,也不會返回表達式的值。

所以, execeval最大區別實際上來自於compile功能及其模式。


除了將源代碼編譯爲字節碼外, compile支持將抽象語法樹 (Python代碼的解析樹)編譯爲code對象。 並將源代碼轉換成抽象語法樹( ast.parse用Python編寫,僅調用compile(source, filename, mode, PyCF_ONLY_AST) ); 它們被用於例如動態修改源代碼,以及用於動態代碼建立,由於在複雜狀況下,將代碼做爲節點樹而不是文本行來處理一般會更容易。


雖然eval僅容許您評估包含單個表達式的字符串,可是您能夠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

'exec'模式將其compilecode對象,而後能夠進行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

若是在CPython 3中研究evalexec源代碼,這是很是明顯的; 它們都使用相同的參數調用PyEval_EvalCode ,惟一的區別是exec顯式返回None

Python 2和Python 3之間exec語法差別

Python 2的主要區別之一是exec是一個語句, eval是一個內置函數(這兩個都是Python 3中的內置函數)。 這是一個衆所周知的事實,正式語法exec在Python 2 exec code [in globals[, locals]]

與大多數Python 2到3 移植 指南 彷佛 建議的不一樣 ,CPython 2中的exec語句也能夠與看起來 徹底像Python 3中的exec函數調用的語法一塊兒使用。緣由是Python 0.9.9具備exec(code, globals, locals)內置函數! 而且該內置函數在python 1.0發佈以前的某個地方exec語句替換了。

因爲但願不破壞與Python 0.9.9的向後兼容性,所以Guido van Rossum在1993年添加了一個兼容性黑客 :若是code是長度爲2或3的元組,不然不會將globalslocals傳遞給exec語句,該code將被解釋爲好像元組的第2個元素和第3個元素分別是globalslocals 。 即便在Python 1.4文檔(在線最先可用的版本)中也沒有提到兼容性hack; 所以對於移植指南和工具的許多做者並不瞭解,直到2012年11月再次對其進行了記錄

第一個表達式也能夠是長度爲2或3的元組。在這種狀況下,必須省略可選部分。 形式exec(expr, globals)等同於exec expr in globals ,而形式exec(expr, globals, locals)等同於exec expr in globals, localsexec的元組形式提供了與Python 3的兼容性,其中exec是函數而不是語句。

是的,在CPython 2.7中它被方便地稱爲前向兼容選項(爲何令人們感到困惑,由於根本就沒有向後兼容選項),而實際上它已經存在了二十年

所以,雖然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的未記錄的行爲表示敬意)。

在具備兼容性問題的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循環!)。

相關文章
相關標籤/搜索