问题:eval,exec和compile有什么区别?
我一直在研究Python代码的动态评估,并遇到eval()
和compile()
函数,以及exec
语句。
有人可以解释之间的区别eval
和exec
怎样的不同模式,compile()
适应吗?
回答 0
简短答案,即TL; DR
基本上,eval
用于EVAL审视你们单个动态生成的Python表达式,并exec
用于EXEC动态生成的Python代码仅针对其副作用尤特。
eval
并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
的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
答案越长,又称血腥细节
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
对象包含源代码,或者作为一个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
双方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
到标准output(!)上。一个
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
使用整个语句,甚至可以是已被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是exec
一个声明,eval
是一个内置的功能(两者都内置函数在Python 3)。众所周知exec
,Python 2 中的正式语法为exec code [in globals[, locals]]
。
与大多数Python 2到3 移植 指南 似乎并不像 建议的那样,exec
CPython 2中的语句也可以与看起来 完全像exec
Python 3中的函数调用的语法一起使用。原因是Python 0.9.9具有exec(code, globals, locals)
内置的在功能上!并且该内置函数在Python 1.0发布之前的某处被exec
语句替换。
由于这是可取的不破与Python 0.9.9向后兼容性,吉多·范罗苏姆在1993年增加了兼容性劈:如果code
是长度为2或3的元组,并globals
与locals
未传递到exec
声明,否则,code
将被解释就像元组的第二个元素和第三个元素分别是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的兼容性,Python 3exec
是函数而不是语句。
是的,在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的未记录的行为表示敬意)。
在Pythons 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
改为使用循环!)。
回答 1
exec
不是表达式:Python 2.x中的语句和Python 3.x中的函数。它编译并立即评估字符串中包含的一条语句或一组语句。例: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
是一个内置函数(不是语句),该函数对一个表达式求值并返回该表达式产生的值。例: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
。它不会执行或评估您的语句或表达式,但会返回可以执行此操作的代码对象。模式如下:compile(string, '', 'eval')
返回如果您完成将执行的代码对象eval(string)
。请注意,您不能在这种模式下使用语句。仅(单个)表达式有效。compile(string, '', 'exec')
返回如果您完成将执行的代码对象exec(string)
。您可以在此处使用任意数量的语句。compile(string, '', 'single')
类似于exec
模式,但是它将忽略除第一条语句以外的所有内容。请注意,带有结果的if
/else
语句被视为单个语句。
回答 2
exec用于语句,不返回任何内容。eval用于表达式,并返回表达式的值。
表达式表示“某事”,而语句表示“做某事”。