关于python:eval,exec和compile之间有什么区别?

What's the difference between eval, exec, and compile?

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

有人能解释一下evalexec的区别,以及compile()的不同模式是如何适应的吗?


简短的回答,或tl;dr

基本上,eval用于计算单个动态生成的python表达式,而exec仅用于执行动态生成的python代码,这是因为它的副作用。好的。

evalexec有这两个区别:好的。

  • eval只接受一个表达式,exec可以接受具有python语句的代码块:循环、try: except:class和函数/方法def初始化等等。好的。

    python中的表达式可以作为变量赋值中的值:好的。

    1
    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是一个函数;它的使用对使用它的函数的编译字节码没有影响。好的。

    因此,基本上:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    >>> 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

    'exec'模式中的compile将任意数量的语句编译成一个字节码,该字节码隐含地总是返回None,而在'eval'模式中,它将一个表达式编译成返回该表达式值的字节码。好的。

    1
    2
    3
    4
    5
    >>> 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会引发异常:好的。

    1
    2
    3
    4
    5
    6
    7
    >>> 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, '', 'eval')在内部编译成字节码,这就是差异的真正来源。好的。

    如果将包含python字节码的code对象传递给execeval时,它们的行为相同,但exec忽略返回值的事实除外,始终返回None。因此,如果您只需在将某个具有语句的内容放入字节码之前将其作为字符串传递,那么可以使用eval来执行它:好的。

    1
    2
    3
    >>> eval(compile('if 1: print("Hello")', '<string>', 'exec'))
    Hello
    >>>

    即使编译的代码包含语句,也可以毫无问题地工作。它仍然返回None,因为这是从compile返回的代码对象的返回值。好的。

    'eval'模式中(因此,如果传入字符串,则使用eval函数),如果源代码包含语句或单个表达式之外的任何其他语句,则compile会引发异常:好的。

    1
    2
    3
    4
    5
    6
    7
    >>> 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

    。答案越长,A.K.A.就越详细。execeval

    exec函数(在python 2中是一个语句)用于执行动态创建的语句或程序:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    >>> program = '''
    for i in range(3):
        print("Python is cool")
    '''

    >>> exec(program)
    Python is cool
    Python is cool
    Python is cool
    >>>

    eval函数是否同样的方法一个单一的表达,及时返回的值的表达式:

    /好的。

    1
    2
    3
    4
    5
    >>> a = 2
    >>> my_calculation = '42 * a'
    >>> result = eval(my_calculation)
    >>> result
    84

    execeval都接受的程序/表达的是either运行作为一个strunicodebytes面向含源代码,或作为一种面向code这包含Python字节码。

    /好的。

    如果一个str/ unicode/ bytes氟源代码是通过对exec,它behaves等价的:

    /好的。

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

    evalsimilarly behaves等价的:

    /好的。

    1
    eval(compile(source, '<string>', 'eval'))

    由于所有的表达式可以用来作为statements在Python(这些都是被称为"Expr节点在Python的抽象文法;对面是不是真的),你可以总是使用exec,如果你不需要的返回值。这是"说的,你可以使用either eval('my_func(42)')exec('my_func(42)'),差分,被认为eval时返回的值returned用my_func,和execdiscards它:

    /好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    >>> 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,只读execaccepts源代码,包含statements,def状,forwhileimport,或class,指派statement(a.k.a a = 42),或整个程序:

    /好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    >>> 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

    execeval接受额外的2 positional arguments - globalslocals-这是全球和本地变量scopes承认,欧洲sees代码。这些默认的globals()locals()内的范围,称为execeval,但任何字典可以用于globals和任何mapping方法locals(包括dict当然)。这些可以用于非只读的restrict /修改变量的代码,这是sees,但也常常准备用于capturing变量的承认,欧洲executed创源代码:

    /好的。

    1
    2
    3
    4
    5
    6
    7
    >>> g = dict()
    >>> l = dict()
    >>> exec('global a; a, b = 123, 42', g, l)
    >>> g['a']
    123
    >>> l
    {'b': 42}

    (如果你显示的值在整个g,它将是更自然的,因为execeval添加的内置模块为__builtins__的全局自动如果它is missing)。

    /好的。

    在Python中,官方的语法的exec声明是真的在为exec code in globals, locals

    /好的。

    1
    >>> exec 'global a; a, b = 123, 42' in g, l

    然而在alternate语法有exec(code, globals, locals)总是被接受了(见下面)。

    /好的。 compile

    compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)嵌入式CAN被用来调用速度上重复同样的代码与execeval通过编写的源code面向beforehand的积极性。在mode参数控制该种编码的现象在compile函数accepts和种字节码,它产生的。在choices是'eval''exec''single'

    /好的。

    • 'eval'模式expects一个单一的表达,和将产生的字节码,运行时,将返回的值,这表示:

      /好的。

      1
      2
      3
      4
      5
      >>> 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'accepts任何生物从单建筑的Python表达式的全部模块的代码,和executes为他们,如果他们的顶级statements模块。面向None时返回的代码:

      /好的。

      1
      2
      3
      4
      5
      6
      7
      >>> 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'接受源代码语句(含单或多用if语句分隔;)最后的语句表达的语句是一个字节码的产生,也无论是在repr的值到标准输出(表达)。。。。。。。。

      if- elif- else环链,它具有与elsetryexceptelsefinally块是一个单一的语句。

      的源程序,含2顶层语句片段是一个错误的'single'这样,Python中的2有一个错误,也允许多toplevel语句在代码是只读的第一;编译;其余是ignored:

      2.7.8在Python的:

      1
      2
      3
      4
      >>> exec(compile('a = 5
      a = 6'
      , '<string>', 'single'))
      >>> a
      5

      3.4.2和Python中的:

      1
      2
      3
      4
      5
      6
      7
      8
      >>> exec(compile('a = 5
      a = 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

      这是非常有用的用于制作交互式解释器shell。然而,表达的是不返回值的,即使你的eval产生的代码。

    因此,最大的区别是:execeval实际上从compile函数及其模式。

    此外,compiling到字节码的源代码,支持compiling compile抽象语法树的语法分析树code对象到Python代码);和对抽象语法树的源代码(用的是Python中的ast.parse只是调用compile(source, filename, mode, PyCF_ONLY_AST));这些都是用于修改的源代码的例子是飞行,和动态代码也被创造,它通常更容易处理的代码到该树的节点而不是条文本在复杂的案件。

    而只让你来评价eval字符串是包含一个单一的表达,你可以eval整个语句,或甚至是整个模块已进入compiled Python字节码;和2),这是print,是不能直接与evalled语句:

    1
    2
    3
    4
    5
    6
    7
    >>> 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'模式为对象,你可以evalcode和它的函数将返回Noneeval;。。。。。。。

    1
    2
    3
    4
    5
    6
    >>> 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

    如果一个看起来在源代码中的实现和execeval3,这是一个非常PyEval_EvalCode揭;他们具有相同的参数调用,这是唯一的人Noneexec明确收益差。

    协会之间的语法差异execPython Python 2和3

    一个主要的区别是,Python中的一个表2和evalexec是内置函数(无论是内置函数在Python 3)。这是一个好的大学,事实上官方的语法execexec code [in globals[, locals]]是Python中的2。

    不像大多数的Python 2到3的移植指南似乎suggest exec语句,同时也可以用在2与语法,函数调用的exec样外观是Python中的3。那是有原因的0.9.9 exec(code, globals, locals)Python的内置函数。这是和内置函数被替换与一execPython语句在1.0发布。

    由于不希望破坏与python 0.9.9的向后兼容性,guido van rossum在1993年添加了一个兼容性黑客:如果code是长度为2或3的元组,并且globalslocals没有传递到exec语句中,否则,code将被解释为f这两个元组分别是globalslocals。即使在python 1.4文档(最早的在线可用版本)中也没有提到兼容性黑客;因此,许多移植指南和工具的作者并不知道兼容性黑客,直到2012年11月再次记录:好的。

    The first expression may also be a tuple of length 2 or 3. In this case, the optional parts must be omitted. The form exec(expr, globals) is equivalent to exec expr in globals, while the form exec(expr, globals, locals) is equivalent to exec expr in globals, locals. The tuple form of exec provides compatibility with Python 3, where exec is a function rather than a statement.

    Ok.

    是的,在CPython 2.7中,它被简单地称为一个前向兼容性选项(为什么人们会因为有一个后向兼容性选项而感到困惑),实际上,它已经存在了20年的向后兼容性。好的。

    因此,虽然exec是python 1和python 2中的语句,是python 3和python 0.9.9中的内置函数,好的。

    1
    2
    >>> exec("print(a)", globals(), {'a': 42})
    42

    可能在所有广泛发布的Python版本中都有相同的行为;在Jython 2.5.2、Pypy2.3.1(Python2.7.6)和Ironpython 2.6.1中也有相同的行为(他们对CPython的未记录行为赞不绝口)。好的。

    在pythons 1.0-2.7中,您不能用兼容性hack将exec的返回值存储到变量中:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    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的引用:好的。

    1
    2
    3
    4
    5
    >>> call_later(exec, 'print(42)', delay=1000)
      File"<stdin>", line 1
        call_later(exec, 'print(42)', delay=1000)
                      ^
    SyntaxError: invalid syntax

    这是一种可能有人实际使用的模式,尽管可能性不大;好的。

    或者用它来理解列表:好的。

    1
    2
    3
    4
    5
    >>> [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循环!).好的。好啊。


  • exec不是表达式:python 2.x中的语句和python 3.x中的函数。它编译并立即计算字符串中包含的语句或语句集。例子:

    1
    2
    3
    4
    5
    6
    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)
    print(6)'
    )  # prints 5{newline}6.
    exec('if True: print(6)')  # prints 6.
    exec('5')                 # does nothing and returns nothing.
  • eval是一个内置函数(不是语句),它计算表达式并返回表达式生成的值。例子:

    1
    2
    3
    4
    5
    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.

  • compileexeceval的较低版本。它不执行或计算您的语句或表达式,但返回可以执行该操作的代码对象。模式如下:

  • compile(string, '', 'eval')返回执行eval(string)时本应执行的代码对象。请注意,在此模式下不能使用语句;只有(单个)表达式有效。
  • compile(string, '', 'exec')返回执行exec(string)时本应执行的代码对象。您可以在这里使用任意数量的语句。
  • compile(string, '', 'single')exec模式类似,但除了第一条语句外,它将忽略所有内容。请注意,带结果的if/else语句被视为单个语句。

  • exec是for语句,不返回任何内容。eval用于表达式,并返回表达式的值。

    表达式的意思是"某物",而语句的意思是"做某物"。