<h2>简短的回答,或TL;博士</h2>
<p>基本上,<a href="https://docs.python.org/3/library/functions.html#eval" rel="noreferrer">^{<cd1>}</a>用于<strong>eval</strong>评估单个动态生成的Python表达式,而<a href="https://docs.python.org/3/library/functions.html#exec" rel="noreferrer">^{<cd2>}</a>用于<strong>exec</strong>ute动态生成的Python代码,仅用于其副作用</p>
<p><code>eval</code>和<code>exec</code>有以下两个区别:</p>
<ol>
<li><p><code>eval</code>只接受<strong>单个表达式</strong>,<code>exec</code>可以接受包含Python语句的代码块:循环、<code>try: except:</code>、<code>class</code>和函数/方法<code>def</code>初始化等等</p>
<p>Python中的表达式是变量赋值中的值:</p>
<pre><code>a_variable = (anything you can put within these parentheses is an expression)
</code></pre></li>
<li><p><code>eval</code><strong>返回给定表达式的值</strong>,而<code>exec</code>忽略其代码中的返回值,并始终返回<code>None</code>(在Python 2中,它是一个语句,不能用作表达式,因此它实际上不返回任何内容)</p></li>
</ol>
<p>在版本1.0-2.7中,<code>exec</code>是一个语句,因为CPython需要为函数生成一种不同类型的代码对象,这些函数使用<code>exec</code>作为函数内部的副作用</p>
<p>在Python3中,<code>exec</code>是一个函数;它的使用对使用它的函数的编译字节码没有影响</p>
<hr/>
<p>因此,基本上:</p>
<pre><code>>>> 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
</code></pre>
<hr/>
<p><code>'exec'</code>模式中的<code>compile</code>将任意数量的语句编译成总是隐式返回<code>None</code>的字节码,而在<code>'eval'</code>模式中,它将<em>单个</em>表达式编译成<em>返回该表达式值的字节码</p>
<pre><code>>>> 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
</code></pre>
<p>在<code>'eval'</code>模式下(如果传入字符串,则使用<code>eval</code>函数),如果源代码包含语句或单个表达式以外的任何内容,<code>compile</code>将引发异常:</p>
<pre><code>>>> 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
</code></pre>
<hr/>
<p>实际上,语句<em>“eval只接受一个表达式”</em>仅在字符串(包含Python<em>源代码</em>)被传递到<code>eval</code>时才适用。然后使用<a href="https://docs.python.org/3/library/functions.html#compile" rel="noreferrer">^{<cd24>}</a>在内部将其编译为字节码,这就是差异的真正来源</p>
<p>如果一个<code>code</code>对象(包含Python<em>字节码</em>)被传递给<code>exec</code>或<code>eval</code>,<em>它们的行为相同</em>,除了<code>exec</code>忽略返回值,仍然总是返回<code>None</code>这一事实。因此,可以使用<code>eval</code>执行包含语句的内容,前提是在执行之前<code>compile</code>将其转换为字节码,而不是将其作为字符串传递:</p>
<pre><code>>>> eval(compile('if 1: print("Hello")', '<string>', 'exec'))
Hello
>>>
</code></pre>
<p>即使编译后的代码包含语句,也可以正常工作。它仍然返回<code>None</code>,因为这是从<code>compile</code>返回的代码对象的返回值</p>
<p>在<code>'eval'</code>模式下(如果传入字符串,则使用<code>eval</code>函数),如果源代码包含语句或单个表达式以外的任何内容,<code>compile</code>将引发异常:</p>
<pre><code>>>> 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
</code></pre>
<h2>答案越长,又称血淋淋的细节</h2>
<h2><code>exec</code>和<code>eval</code></h2>
<p><a href="https://docs.python.org/3/library/functions.html#exec" rel="noreferrer">^{<cd2>}</a>函数(即<a href="https://docs.python.org/2/reference/simple_stmts.html#exec" rel="noreferrer">a statement in Python 2</a>)用于执行动态创建的语句或程序:</p>
<pre><code>>>> program = '''
for i in range(3):
print("Python is cool")
'''
>>> exec(program)
Python is cool
Python is cool
Python is cool
>>>
</code></pre>
<p><a href="https://docs.python.org/3/library/functions.html#eval" rel="noreferrer">^{<cd1>}</a>函数对<a href="https://docs.python.org/3/reference/expressions.html" rel="noreferrer">single expression</a>、<em>和</em>执行相同的操作,返回表达式的值:</p>
<pre><code>>>> a = 2
>>> my_calculation = '42 * a'
>>> result = eval(my_calculation)
>>> result
84
</code></pre>
<p><code>exec</code>和<code>eval</code>都接受程序/表达式作为包含源代码的<code>str</code>、<code>unicode</code>或<code>bytes</code>对象运行,或者作为包含Python字节码的<em>{<cd25>}对象运行</p>
<p>如果包含源代码的<code>str</code>/<code>unicode</code>/<code>bytes</code>被传递给<code>exec</code>,则其行为等同于:</p>
<pre><code>exec(compile(source, '<string>', 'exec'))
</code></pre>
<p>和<code>eval</code>的行为类似于:</p>
<pre><code>eval(compile(source, '<string>', 'eval'))
</code></pre>
<hr/>
<p>由于所有表达式都可以用作Python中的语句(在Python <a href="https://docs.python.org/3/library/ast.html#abstract-grammar" rel="noreferrer">abstract grammar</a>中称为<code>Expr</code>节点;反之则不成立),因此如果不需要返回值,则始终可以使用<code>exec</code>。也就是说,您可以使用<code>eval('my_func(42)')</code>或<code>exec('my_func(42)')</code>,区别在于<code>eval</code>返回值由<code>my_func</code>返回,并且<code>exec</code>丢弃它:</p>
<pre><code>>>> 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
>>>
</code></pre>
<p>其中,只有<code>exec</code>接受包含语句的源代码,如<code>def</code>、<code>for</code>、<code>while</code>、<code>import</code>、或<code>class</code>、赋值语句(又称<code>a = 42</code>)或整个程序:</p>
<pre><code>>>> 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
</code></pre>
<hr/>
<p><code>exec</code>和<code>eval</code>都接受2个额外的位置参数<code>globals</code>和<code>locals</code>,这是代码看到的全局和局部变量范围。它们默认为<code>globals()</code>和<code>locals()</code>在调用<code>exec</code>或<code>eval</code>的范围内,但是任何字典都可以用于<code>globals</code>,任何<code>mapping</code>都可以用于<code>locals</code>(当然包括<code>dict</code>)。它们不仅可用于限制/修改代码看到的变量,还可用于捕获<code>exec</code>已执行代码创建的变量:</p>
<pre><code>>>> g = dict()
>>> l = dict()
>>> exec('global a; a, b = 123, 42', g, l)
>>> g['a']
123
>>> l
{'b': 42}
</code></pre>
<p>(如果显示整个<code>g</code>的值,则会长得多,因为<code>exec</code>和<code>eval</code>如果缺少内置模块,会自动将其作为<code>__builtins__</code>添加到全局中)</p>
<p>在Python 2中,<code>exec</code>语句的官方语法实际上是<code>exec code in globals, locals</code>,如</p>
<pre><code>>>> exec 'global a; a, b = 123, 42' in g, l
</code></pre>
<p>然而,替代语法<code>exec(code, globals, locals)</code>也一直被接受(见下文)</p>
<h2><code>compile</code></h2>
<p>通过预先将源代码编译成<code>code</code>对象,可以使用<a href="https://docs.python.org/3/library/functions.html#compile" rel="noreferrer">^{<cd87>}</a>内置函数加速对<code>exec</code>或<code>eval</code>相同代码的重复调用。<code>mode</code>参数控制<code>compile</code>函数接受的代码片段类型及其生成的字节码类型。选择是<code>'eval'</code>、<code>'exec'</code>和<code>'single'</code>:</p>
<ul>
<li><p><code>'eval'</code>模式需要一个表达式,并将生成字节码,运行时将返回该表达式的值<strong>:</p>
<pre><code>>>> dis.dis(compile('a + b', '<string>', 'eval'))
1 0 LOAD_NAME 0 (a)
3 LOAD_NAME 1 (b)
6 BINARY_ADD
7 RETURN_VALUE
</code></pre></li>
<li><p><code>'exec'</code>接受从单个表达式到整个代码模块的任何类型的python构造,并像执行模块顶级语句一样执行它们。代码对象返回<code>None</code>:</p>
<pre><code>>>> 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
</code></pre></li>
<li><p><code>'single'</code>是<code>'exec'</code>的一种有限形式,它接受包含一个<strong>单个<strong>语句(或由^{<cd101>分隔的多个语句)的源代码。如果最后一个语句是一个表达式语句,则生成的字节码也<em>将该表达式的值的<code>repr</code>打印到标准输出(!)</em></p>
<p>一个<code>if</code>-<code>elif</code>-<code>else</code>链,一个带有<code>else</code>的循环,以及带有<code>except</code>、<code>else</code>和<code>finally</code>块的<code>try</code>被认为是一个语句</p>
<p>包含2个顶级语句的源片段对于<code>'single'</code>来说是一个错误,除了在Python 2中有一个bug</em>,它有时允许代码中有多个顶级语句;只有第一个被编译;其余的则被忽略:</p>
<p>在Python 2.7.8中:</p>
<pre><code>>>> exec(compile('a = 5\na = 6', '<string>', 'single'))
>>> a
5
</code></pre>
<p>在Python 3.4.2中:</p>
<pre><code>>>> 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
</code></pre>
<p>这对于制作交互式Python shell非常有用。但是,表达式的值<em>不会返回</em>,即使您<code>eval</code>返回结果代码也是如此</p></li>
</ul>
<p>因此<code>exec</code>和<code>eval</code>的最大区别实际上来自<code>compile</code>函数及其模式</p>
<hr/>
<p>除了将源代码编译成字节码之外,<code>compile</code>还支持将<a href="https://docs.python.org/3/library/ast.html#abstract-grammar" rel="noreferrer"><em>abstract syntax trees</em></a>(Python代码的解析树)编译成<code>code</code>对象;将源代码转换成抽象语法树(用Python编写<code>ast.parse</code>,只调用<code>compile(source, filename, mode, PyCF_ONLY_AST)</code>);例如,它们用于动态修改源代码,也用于动态代码创建,因为在复杂情况下,将代码作为节点树而不是文本行处理通常更容易</p>
<hr/>
<p>虽然<code>eval</code>只允许您计算包含单个表达式的字符串,但您可以<code>eval</code>整个语句,甚至是已<code>compile</code>转换为字节码的整个模块;也就是说,对于Python 2,<code>print</code>是一条语句,不能直接<code>eval</code>引导:</p>
<pre><code>>>> 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
</code></pre>
<p><code>compile</code>用<code>'exec'</code>mode转换成一个<code>code</code>对象,您可以<strong><code>eval</code>它</strong>;<code>eval</code>函数将返回<code>None</code></p>
<pre><code>>>> 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
</code></pre>
<p>如果我们研究cpython3中的<a href="https://hg.python.org/cpython/file/ec6ed10d611e/Python/bltinmodule.c#l805" rel="noreferrer">^{<cd1>}</a>和<a href="https://hg.python.org/cpython/file/ec6ed10d611e/Python/bltinmodule.c#l882" rel="noreferrer">^{<cd2>}</a>源代码,这是非常明显的;它们都使用相同的参数调用<code>PyEval_EvalCode</code>,唯一的区别是<a href="https://hg.python.org/cpython/file/ec6ed10d611e/Python/bltinmodule.c#l903" rel="noreferrer">^{<cd2>} explicitly returns ^{<cd12>}</a></p>
<h2>Python 2和Python 3之间<code>exec</code>的语法差异</h2>
<p>Python<strong>2</strong>的一个主要区别是<code>exec</code>是一个语句<code>eval</code>是一个内置函数(两者都是Python 3中的内置函数)。
众所周知,python2中<code>exec</code>的官方语法是<code>exec code [in globals[, locals]]</code></p>
<p>与大多数Python 2-to-3 <a href="http://python3porting.com/differences.html#exec" rel="noreferrer">porting</a>{a15}{a16}{a17}不同,CPython 2中的<code>exec</code>语句还可以与语法一起使用,<em>看起来像Python 3中的<code>exec</code>函数调用一样。原因是Python 0.9.9具有<code>exec(code, globals, locals)</code>内置函数!这个内置函数被替换为<code>exec</code>语句<a href="https://hg.python.org/cpython/file/fccd415e2eb8/Python/ceval.c" rel="noreferrer">somewhere before Python 1.0 release</a></p>
<p>由于不希望破坏与Python 0.9.9的向后兼容性,<a href="https://hg.python.org/cpython/file/fccd415e2eb8/Python/ceval.c#l2521" rel="noreferrer">Guido van Rossum added a compatibility hack in 1993</a>:如果<code>code</code>是长度为2或3的元组,并且<code>globals</code>和<code>locals</code>没有传递到<code>exec</code>语句中,否则<code>code</code>将被解释为元组的第二和第三个元素分别是<code>globals</code>和^<cd69>}。甚至在<a href="https://docs.python.org/release/1.4/ref/ref6.html#HDR10" rel="noreferrer">Python 1.4 documentation (the earliest available version online)</a>中也没有提到兼容性黑客;因此,许多关于移植指南和工具的作者并不知道,直到{a21}再次{a22}:</p>
<blockquote>
<p>The first expression may also be a tuple of length 2 or 3. In this case, the optional parts must be omitted. The form <code>exec(expr, globals)</code> is equivalent to <code>exec expr in globals</code>, while the form <code>exec(expr, globals, locals)</code> is equivalent to <code>exec expr in globals, locals</code>. The tuple form of <code>exec</code> provides compatibility with Python 3, where <code>exec</code> is a function rather than a statement.</p>
</blockquote>
<p>是的,在CPython 2.7中,它被方便地称为向前兼容选项(为什么人们会对向后兼容选项感到困惑呢),
当它实际上已经存在了20年的<em>向后兼容性</em></p>
<p>因此,虽然<code>exec</code>在Python 1和Python 2中是一个语句,在Python 3和Python 0.9.9中是一个内置函数</p>
<pre><code>>>> exec("print(a)", globals(), {'a': 42})
42
</code></pre>
<p>可能在所有广泛发布的Python版本中都有相同的行为;并在Jython 2.5.2、PyPy 2.3.1(Python 2.7.6)和IronPython 2.6.1中工作(他们密切关注CPython未记录的行为,对此表示感谢)</p>
<p>在Pythons 1.0-2.7中,由于兼容性问题,您无法将<code>exec</code>的返回值存储到一个变量中:</p>
<pre><code>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
</code></pre>
<p>(这在Python 3中也没有用处,因为<code>exec</code>总是返回<code>None</code>),或者传递对<code>exec</code>的引用:</p>
<pre><code>>>> call_later(exec, 'print(42)', delay=1000)
File "<stdin>", line 1
call_later(exec, 'print(42)', delay=1000)
^
SyntaxError: invalid syntax
</code></pre>
<p>这是一种可能有人实际使用过的模式,尽管不太可能</p>
<p>或者在列表中使用它:</p>
<pre><code>>>> [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
</code></pre>
<p>这是对列表理解的滥用(改用<code>for</code>循环!)</p>