我一直在研究Python代码的动态评估,并遇到eval()
和compile()
函数以及exec
语句。
有人可以解释eval
和exec
之间的区别,以及compile()
的不同模式如何适应?
答案 0 :(得分:421)
基本上,eval
用于 eval 用于创建单个动态生成的Python表达式,而exec
用于 exec 用于动态生成的Python代码只是为了它的副作用。
eval
和exec
有两个不同之处:
eval
只接受单个表达式,exec
可以使用包含Python语句的代码块:loops,try: except:
,{{1 }和函数/方法class
initions等等。
Python中的表达式是变量赋值中的值:
def
a_variable = (anything you can put within these parentheses is an expression)
返回给定表达式的值,而eval
忽略其代码的返回值,并始终返回exec
(in Python 2它是一个语句,不能用作表达式,因此它实际上不会返回任何内容。)
在版本1.0 - 2.7中,None
是一个语句,因为CPython需要为函数内部使用exec
的函数生成不同类型的代码对象。
在Python 3中,exec
是一个函数;它的使用对使用它的函数的编译字节码没有影响。
因此基本上:
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
模式下,它编译单< / em>表达式为返回该表达式的值的字节码。
'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'
函数),如果源代码包含语句或除了a之外的任何其他内容,eval
会引发异常单一表达:
compile
实际上语句&#34; eval只接受一个表达式&#34; 仅在字符串(包含Python 源代码)传递给{{ 1}}。然后使用compile(source, '<string>', 'eval')
在内部编译为字节码。这就是差异的真正来源。
如果>>> 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
或code
,它们的行为相同,除了exec
忽略返回值这一事实,仍然总是返回eval
。因此,有可能使用exec
执行具有语句的内容,如果您只是None
将其转换为字节码而不是将其作为字符串传递:
eval
即使编译的代码包含语句,也可以顺利运行。它仍然返回compile
,因为这是从>>> eval(compile('if 1: print("Hello")', '<string>', 'exec'))
Hello
>>>
返回的代码对象的返回值。
在None
模式下(如果传入字符串,则使用compile
函数),如果源代码包含语句或除了a之外的任何其他内容,'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
函数(a statement in Python 2)用于执行动态创建的语句或程序:
exec
eval
函数对single expression执行相同操作,和返回表达式的值:
eval
>>> program = '''
for i in range(3):
print("Python is cool")
'''
>>> exec(program)
Python is cool
Python is cool
Python is cool
>>>
和>>> a = 2
>>> my_calculation = '42 * a'
>>> result = eval(my_calculation)
>>> result
84
都接受程序/表达式作为包含源代码的exec
,eval
或str
对象运行,或者作为包含Python字节码的 unicode
对象。
如果将bytes
/ code
/ str
包含的源代码传递给unicode
,则其行为与以下内容相同:
bytes
和exec
同样表现为:
exec(compile(source, '<string>', 'exec'))
由于所有表达式都可以用作Python中的语句(这些语句在Python abstract grammar中称为eval
节点;反之亦然),如果使用eval(compile(source, '<string>', 'eval'))
,则可以始终使用Expr
你不需要返回值。也就是说,您可以使用exec
或eval('my_func(42)')
,区别在于exec('my_func(42)')
返回eval
返回的值,而my_func
会丢弃它:
exec
在2中,只有>>> 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
>>>
接受包含语句的源代码,例如exec
,def
,for
,while
或{{1} },赋值语句(又名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
都接受2个额外的位置参数 - exec
和eval
- 它们是代码看到的全局和局部变量作用域。这些默认为调用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
如果缺少全局变量)。
在Python 2中,eval
语句的官方语法实际上是__builtins__
,如
exec
但是,替代语法exec code in globals, locals
也一直被接受(见下文)。
>>> exec 'global a; a, b = 123, 42' in g, l
通过预先将源代码编译为exec(code, globals, locals)
对象,可以使用compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)
内置函数来加速compile
或exec
对相同代码的重复调用。 eval
参数控制code
函数接受的代码片段的类型以及它产生的字节码的类型。选项包括mode
,compile
和'eval'
:
'exec'
模式需要单个表达式,并且会生成字节码,在运行时将返回该表达式的值:
'single'
'eval'
接受从单个表达式到整个代码模块的任何类型的python构造,并执行它们就像它们是模块顶级语句一样。代码对象返回>>> 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'
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
的{{1}},{{1} }和else
块被视为单个语句。
包含2个顶级语句的源片段是try
的错误,除了在Python 2中有错误,有时允许代码中有多个顶级语句;只编译第一个;其余的都被忽略了:
在Python 2.7.8中:
except
在Python 3.4.2中:
else
这对于制作交互式Python shell非常有用。但是,表达式的值未返回,即使您finally
生成的代码也是如此。
因此'single'
和>>> exec(compile('a = 5\na = 6', '<string>', 'single'))
>>> a
5
的最大区别实际上来自>>> 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
函数及其模式。
除了将源代码编译为字节码之外,eval
还支持将abstract syntax trees(解析Python代码树)编译为exec
个对象;和源代码到抽象语法树(eval
是用Python编写的,只调用compile
);这些用于动态修改源代码,也用于动态代码创建,因为在复杂情况下,通常更容易将代码作为节点树而不是文本行处理。
虽然compile
只允许您评估包含单个表达式的字符串,但您可以code
整个语句,甚至可以将ast.parse
整个模块转换为字节码;也就是说,对于Python 2,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
如果在CPython 3中查看eval
和exec
源代码,这是非常明显的;他们都用相同的参数调用eval
,唯一的区别是exec
explicitly returns None
。
eval
的语法差异 Python 2 的主要区别之一是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
是一个内置函数(两者都是Python 3中的内置函数) )。
众所周知,Python 2中PyEval_EvalCode
的官方语法是exec
。
与大多数Python 2对3 porting guides seem to suggest不同,CPython 2中的exec
语句也可以与语法一起使用看起来 完全类似于Python 3中的eval
函数调用。原因是Python 0.9.9具有exec
内置函数!并且内置函数已替换为exec code [in globals[, locals]]
语句somewhere before Python 1.0 release。
由于不希望向后兼容Python 0.9.9,Guido van Rossum added a compatibility hack in 1993:如果exec
是长度为2或3的元组,exec
和{{1如果没有传递给exec(code, globals, locals)
语句,exec
将被解释为元组的第2和第3个元素分别是code
和globals
。甚至在Python 1.4 documentation (the earliest available version online)中也没有提到兼容性黑客攻击;因此很多作者都不知道移植指南和工具,直到它再次documented in November 2012:
第一个表达式也可以是长度为2或3的元组。在这种情况下,必须省略可选部分。表单
locals
相当于exec
,而表单code
相当于globals
。locals
的元组形式提供与Python 3的兼容性,其中exec(expr, globals)
是函数而不是语句。
是的,在CPython 2.7中,它被轻而易举地称为前向兼容性选项(为什么人们会因为存在向后兼容性选项而感到困惑), 当它实际上已经存在向后兼容二十年。
因此,exec expr in globals
是Python 1和Python 2中的语句,而Python 3和Python 0.9.9中的内置函数,
exec(expr, globals, locals)
在所有广泛发布的Python版本中都有相同的行为;并且也在Jython 2.5.2,PyPy 2.3.1(Python 2.7.6)和IronPython 2.6.1中工作(对CPython的未记录行为密切关注他们。)
Pythons 1.0 - 2.7及其兼容性黑客无法做到的是将exec expr in globals, locals
的返回值存储到变量中:
exec
(这在Python 3中也不会有用,因为exec
始终返回exec
),或者将引用传递给>>> exec("print(a)", globals(), {'a': 42})
42
:
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
滥用列表推导(改为使用exec
循环!)。
答案 1 :(得分:189)
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 :(得分:49)
exec用于声明,不返回任何内容。 eval用于表达式并返回表达式的值。
表达意味着“某事”而声明意味着“做某事”。