我一直在研究Python代码的动态评估,并遇到eval()
和compile()
函数以及exec
语句。 html
有人能够解释一下eval
和exec
之间的区别,以及不一样的compile()
模式如何适应吗? python
exec用于语句,不返回任何内容。 eval用于表达式,并返回表达式的值。 linux
表达式表示“某事”,而语句表示“作某事”。 express
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.
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.
compile
是exec
和eval
的较低版本。 它不会执行或评估您的语句或表达式,但会返回能够执行此操做的代码对象。 模式以下: ide
compile(string, '', 'eval')
返回若是完成eval(string)
将会执行的代码对象。 请注意,您不能在这种模式下使用语句。 仅(单个)表达式有效。 compile(string, '', 'exec')
返回若是执行完exec(string)
后将要执行的代码对象。 您能够在此处使用任意数量的语句。 compile(string, '', 'single')
相似于exec
模式,可是它将忽略除第一条语句之外的全部内容。 请注意,带有结果的if
/ else
语句被视为单个语句。 基本上, eval
用于EVAL审视大家单个动态生成的Python表达式,以及exec
用于动态生成的Python代码只为它的反作用EXEC尤特。 函数
eval
和exec
有如下两个区别: 工具
eval
只接受单个表达 , exec
能够采起具备Python语句代码块:循环, try: except:
, class
和函数/方法def
initions等。 ui
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
在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 )传递给exec
或eval
,则它们的行为相同 ,除了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
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
或bytes
对象或包含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
接受包含语句的源代码,例如def
, for
, while
, import
或class
,赋值语句(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
双方exec
和eval
接受2个额外的位置参数- globals
和locals
-这是全局和局部变量的做用域,该代码看到。 它们默认为exec
或eval
范围内的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
的值,则将花费更长的时间,由于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)
能够经过预先将源code
编译为code
对象来使用exec
或eval
来加快相同代码的重复调用。 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个顶级语句的源代码片断是'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
结果代码,也不会返回表达式的值。
所以, exec
和eval
最大区别实际上来自于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'
模式将其compile
为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
若是在CPython 3中研究eval
和exec
源代码,这是很是明显的; 它们都使用相同的参数调用PyEval_EvalCode
,惟一的区别是exec
显式返回None
。
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的元组,不然不会将globals
和locals
传递给exec
语句,该code
将被解释为好像元组的第2个元素和第3个元素分别是globals
和locals
。 即便在Python 1.4文档(在线最先可用的版本)中也没有提到兼容性hack; 所以对于移植指南和工具的许多做者并不了解,直到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中它被方便地称为前向兼容选项(为何令人们感到困惑,由于根本就没有向后兼容选项),而实际上它已经存在了二十年 。
所以,虽然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
循环!)。