eval,exec和compile之间有什么区别?

时间:2010-02-08 09:56:44

标签: python dynamic eval exec

我一直在研究Python代码的动态评估,并遇到eval()compile()函数以及exec语句。

有人可以解释evalexec之间的区别,以及compile()的不同模式如何适应?

3 个答案:

答案 0 :(得分:421)

简答,或TL; DR

基本上,eval用于 eval 用于创建单个动态生成的Python表达式,而exec用于 exec 用于动态生成的Python代码只是为了它的副作用。

evalexec有两个不同之处:

  1. eval只接受单个表达式exec可以使用包含Python语句的代码块:loops,try: except:,{{1 }和函数/方法class initions等等。

    Python中的表达式是变量赋值中的值:

    def
  2. a_variable = (anything you can put within these parentheses is an expression) 返回给定表达式的值,而eval忽略其代码的返回值,并始终返回exec(in Python 2它是一个语句,不能用作表达式,因此它实际上不会返回任何内容。)

  3. 在版本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 字节码)传递给evalcode它们的行为相同,除了exec忽略返回值这一事实,仍然总是返回eval。因此,有可能使用exec执行具有语句的内容,如果您只是None将其转换为字节码而不是将其作为字符串传递:

    eval
    即使编译的代码包含语句,

    也可以顺利运行。它仍然返回compile,因为这是从>>> eval(compile('if 1: print("Hello")', '<string>', 'exec')) Hello >>> 返回的代码对象的返回值。

    None模式下(如果传入字符串,则使用compile函数),如果源代码包含语句或除了a之外的任何其他内容,'eval'会引发异常单一表达:

    eval

    答案越长,a.k.a血淋淋的细节

    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 都接受程序/表达式作为包含源代码的execevalstr对象运行,或者作为包含Python字节码的 unicode对象

    如果将bytes / code / str包含的源代码传递给unicode,则其行为与以下内容相同:

    bytes

    exec同样表现为:

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

    由于所有表达式都可以用作Python中的语句(这些语句在Python abstract grammar中称为eval节点;反之亦然),如果使用eval(compile(source, '<string>', 'eval')) ,则可以始终使用Expr你不需要返回值。也就是说,您可以使用execeval('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 >>> 接受包含语句的源代码,例如execdefforwhile或{{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个额外的位置参数 - execeval - 它们是代码看到的全局和局部变量作用域。这些默认为调用globalslocals范围内的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)内置函数来加速compileexec对相同代码的重复调用。 eval参数控制code函数接受的代码片段的类型以及它产生的字节码的类型。选项包括modecompile'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

    compileprint模式放入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中查看evalexec源代码,这是非常明显的;他们都用相同的参数调用eval,唯一的区别是exec explicitly returns None

    Python 2和Python 3之间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个元素分别是codeglobals。甚至在Python 1.4 documentation (the earliest available version online)中也没有提到兼容性黑客攻击;因此很多作者都不知道移植指南和工具,直到它再次documented in November 2012

      

    第一个表达式也可以是长度为2或3的元组。在这种情况下,必须省略可选部分。表单locals相当于exec,而表单code相当于globalslocals的元组形式提供与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)

  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.
    
  2. 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.
    
  3. compileexeceval的较低级别版本。它不会执行或评估您的语句或表达式,而是返回可以执行此操作的代码对象。模式如下:

    1. compile(string, '', 'eval')返回在您完成eval(string)后已执行的代码对象。请注意,不能在此模式下使用语句;只有(单个)表达式有效。
    2. compile(string, '', 'exec')返回在您完成exec(string)后已执行的代码对象。你可以在这里使用任意数量的陈述。
    3. compile(string, '', 'single')exec模式类似,但除了第一个语句外,它将忽略所有内容。请注意,if / else语句及其结果被视为单个语句。

答案 2 :(得分:49)

exec用于声明,不返回任何内容。 eval用于表达式并返回表达式的值。

表达意味着“某事”而声明意味着“做某事”。