在Python中,val、exec和 compile 有什么区别?

基本上 eval 上用来评估一个动态生成的 Python 表达式;exec 额外的用于执行动态生成的 python 代码。

eval 和 exec 有以下两个差异

eval 只接受一个表达式,exec 可以接受一个包含了 python 语句的代码块: loops, try: except:, class 以及定义的函数和方法

Python 中的表达式是任何可以作为变量赋值中的值的表达式:

a_variable = (任何你可以放在这个括号内的都是一个表达式)

evale 返回表达式的值,而 exec 忽略返回值的代码,并且使用返回 None(在 Python 2 中这是一个声明,而不是一个表达式,所以没有任何返回)

在 1.0 - 2.7 版本中,exec 是一个声明,它总是为函数产生相同类型的代码对象,因为需要使用 exec 在函数中产生一个不同的编码对象。

在 python3 中,exec 上一个函数,它总是产生相同类型的代码对象,不管是在函数内部还是模块范围内调用的。

因此,基本上是这样的

>>> a = 5
>>> eval(‘37 + a‘)   # 这是一个表达式
42
>>> exec(‘37 + a‘)   # 这时一个表达式声明,返回值被忽略了(因为返回的是 None )
>>> exec(‘a = 47‘)   # 修改一个全局变量
>>> a
47
>>> eval(‘a = 47‘)  # 你无法评估一个表达式
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1
    a = 47
          ^
SyntaxError: invalid syntax

在‘exec‘模式下编译,将任意数量的语句编译成 总是返回None的字节码,而在‘eval‘模式下,它将单个表达式编译成返回该表达式值的字节码。

>>> eval(compile(‘42‘, ‘<string>‘, ‘exec‘))  # 代码返回 None
>>> eval(compile(‘42‘, ‘<string>‘, ‘eval‘))  # 代码返回42
42
>>> exec(compile(‘42‘, ‘<string>‘, ‘eval‘))  #  代码返回 42,但是被 exec 忽略了

在“eval”模式下(如果传入字符串,则使用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

实际上, 这个声明 “eval 只接受一个表达式” 只有当 一个包含python代码的字符串传入进来是,他才会内部进行编译,生成字节码,compile(source, ‘<string>‘, ‘eval‘),这是差异真正来自的地方。

如果一个包含了 python 字节码的编码对象被传递给了 exec 和 eval ,他们的行为是一样的,区别仅仅在于 exce 忽略返回值,仍然返回 None。

所以如果你只想编译城字节码而不是作为字符串传递的化,可以使用 eval 来执行包含语句的东西。

>>> eval(compile(‘if 1: print("Hello")‘, ‘<string>‘, ‘exec‘))

Hello

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

在 eval 模式下(如果传入字符串,则使用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

更长的版本

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

如果将包含源代码的str / unicode / bytes传递给exec,则它的行为等同于:

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

和eval类似的表现等同于:

eval(compile(source, ‘<string>‘, ‘eval‘))

由于所有表达式都可以在Python中用作语句(这些语句在Python抽象语法中被称为Expr节点;反之亦然),如果不需要返回值,则可以始终使用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

>>>

在 Python 2 中,只有exec接受包含语句(如def,for,while,import或class),赋值语句(a.k.a 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个额外的位置参数 - 全局变量和局部变量 - 这是代码所看到的全局和局部变量范围。 这些默认为调用exec或eval的范围内的globals()locals(),但任何字典都可以用于全局变量和任何本地映射(当然包括字典)。 这些不仅可以用来限制/修改代码所看到的变量,还可以用于捕获执行代码创建的变量:

>>> 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语句的官方语法实际上是globals,locals中的exec代码,如

>>> 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对同一代码的重复调用。mode参数控制编译函数接受的代码片段的类型以及它产生的字节码的类型。 选择是‘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 到标准输出(!)。

一个 if-elif-else 链,一个带有 else 的循环,和一个 try except,else 和 finally 块被视为是一个单独的语句。

在 “single”中包含2个顶级语句的源代码片段是错误的,除了在Python 2中有一个错误,有时候允许代码中有多个顶层语句; 只有第一个是编译的; 其余的被忽略:

In Python 2.7.8:

>>> exec(compile(‘a = 5\na = 6‘, ‘<string>‘, ‘single‘)) >>> a 5

And in 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的最大区别实际上来自编译函数及其模式。

编译器除了将源代码编译成字节码之外,还支持将抽象语法树(将Python代码解析树)编译成代码对象; 和源代码到抽象语法树(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

用‘exec‘模式compile成代码对象,然后你可以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

如果在CPython 3中查看eval和exec源代码,这是非常明显的; 它们都使用相同的参数调用PyEval_EvalCode,唯一的区别是exec显式地返回None。

Python 2和Python 3之间exec的语法差异

一个在Python 2的主要差异是,exec 一个声明和eval是一个内置函数(无论是在Python 3的内置函数)。这是一个众所周知的事实,在Python 2执行正式的语法是全局执行代码 exec code [in globals[, locals]]

不像大多数Python 2-3移植指南似乎表明,在CPython 2 EXEC语句也可以使用语法,看起来就像在Python 3 exec函数调用。原因是,Python 0.9.9有exec(代码、全局变量、局部变量)内置函数!在Python 1发布之前,内置的函数被替换了。

因为它是可取的不是Python 0.9.9打破向后兼容性,Guido van Rossum说1993的兼容性的问题:如果代码是一个元组长度为2或3,与全球和当地人不传递给exec语句否则,代码将被解释为如果元组的第二元和第三元的全局变量和当地人的分别。即使在Python 1.4文档(最早可用的在线版本)中也没有提到兼容性破解程序,因此许多移植指南和工具的作者都不知道,直到2012年11月再次被证明:

第一表达式也可以是长度为2或3的元组。在这种情况下,必须省略可选部件。 exec(expr, globals) 相当于执行于全局,而形式exec(expr,全局变量、当地人)相当于执行于全局,当地人。执行器的元组形式提供了与Python 3的兼容性,在这里Python是一个函数而不是一个语句。

是的,在当前的2.7,是轻易被提到的向前兼容性选项(为什么迷惑人,都是一个向后兼容性选项),当它实际上已经有二十多年的向后兼容性。

因此,执行是Python 1和Python 2的声明,并在Python 3和Python 0.9.9内置函数,

>>> exec("print(a)", globals(), {‘a‘: 42})

42

是的,在当前的2.7,是轻易被提到的向前兼容性选项(为什么迷惑人,都是一个向后兼容性选项),当它实际上已经有二十多年的向后兼容性。

因此,执行是Python 1和Python 2的声明,并在Python 3和Python 0.9.9内置函数,

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:

哪一种模式可能是某人实际使用过的,虽然不太可能;

或者在列表理解中使用它:

>>> [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

这是列表解析滥用(使用循环取反!)。

原文地址:https://www.cnblogs.com/klausage/p/10123628.html

时间: 2024-08-05 22:22:56

在Python中,val、exec和 compile 有什么区别?的相关文章

python中_、__和__xx__的区别

python中_.__和__xx__的区别 本文为译文,版权属于原作者,在此翻译为中文分享给大家. 英文原文地址:Difference between _, __ and __xx__ in Python 在学习Python时,很多人都弄不清楚各种下划线的意思,而且在这之前已经给其他人解释过很多遍了,是时候把它记录下来. "_"单下划线 Python中不存在真正的私有方法.为了实现类似于c++中私有方法,可以在类的方法或属性前加一个“_”单下划线,意味着该方法或属性不应该去调用,它并不

python中eval, exec, execfile,和compile(转载)

此日志为转载,原文链接:http://skandgjxa.blog.163.com/blog/static/1415298201010262403483/ eval(str [,globals [,locals ]])函数将字符串str当成有效Python表 达式来求值,并返回计算结果. 同样地, exec语句将字符串str当成有效Python代码来执行.提供给exec的代码的名称空间和exec语句的名称空间相同. 最后,execfile(filename [,globals [,locals

python中eval, exec, execfile,和compile

eval(str [,globals [,locals ]])函数将字符串str当成有效Python表达式来求值,并返回计算结果. 同样地, exec语句将字符串str当成有效Python代码来执行.提供给exec的代码的名称空间和exec语句的名称空间相同. 最后,execfile(filename [,globals [,locals ]])函数可以用来执行一个文件,看下面的例子: >>> eval('3+4') 7 >>> exec 'a=100' >>

python中的exec和eval

exec 描述 exec 执行储存在字符串或文件中的 Python 语句,相比于 eval,exec可以执行更复杂的 Python 代码. 返回值 exec 返回值永远为 None. 需要说明的是在 Python2 中exec不是函数,而是一个内置语句(statement),但是Python 2中有一个 execfile() 函数. 可以理解为 Python 3 把 exec 这个 statement 和 execfile() 函数的功能够整合到一个新的 exec() 函数中去了. eval 描

Python中元组,列表,字典的区别

Python中,有3种内建的数据结构:列表.元组和字典.1.列表     list是处理一组有序项目的数据结构,即你可以在一个列表中存储一个序列的项目.列表中的项目.列表中的项目应该包括在方括号中,这样python就知道你是在指明一个列表.一旦你创建了一个列表,你就可以添加,删除,或者是搜索列表中的项目.由于你可以增加或删除项目,我们说列表是可变的数据类型,即这种类型是可以被改变的,并且列表是可以嵌套的.实例:#coding=utf-8animalslist=['fox','tiger','ra

Python基础学习-Python中最常见括号()、[]、{}的区别

Python中最常见括号的区别: 在Python语言中最常见的括号有三种,分别是:小括号().中括号[].花括号{}:其作用也不相同,分别用来代表不同的Python基本内置数据类型. Python中的小括号(): 代表tuple元祖数据类型,元祖是一种不可变序列.创建方法很简单,大多数时候都是小括号括起来的. 1 >>> tup = (1,2,3) 2 >>> tup 3 (1, 2, 3) 4 >>> () #空元祖 5 () 6 >>&

关于python中赋值、浅拷贝、深拷贝之间区别的深入分析

大家都知道,在python中复制一个对象有多种方法,其中常用的是赋值.浅拷贝和深拷贝,这三者之间有哪些区别和哪些坑呢? 首先,定义一下: 赋值:  a =1    b =a    a赋值给了b 浅拷贝: a = []  b = a.copy() 或者import copy             b = copy.copy(a) 深拷贝:import copy  a = []   b = copy.deepcopy(a) 未完待续

python中使用eval() 和 ast.literal_eval()的区别

eval函数在python中做数据类型的转换还是很有用的.它的作用就是把数据还原成它本身或者是能够转化成的数据类型. 那么eval和ast.literal_val()的区别是什么呢? eval在做计算前并不知道需要转化的内容是不是合法的(安全的)python数据类型.只是在调用函数的时候去计算.如果被计算的内容不是合法的python类型就会抛出异常. ast.literal则会判断需要计算的内容计算后是不是合法的python类型,如果是则进行运算,否则就不进行运算. --------------

python中函数参数*args和**kw的区别

1.函数与参数(实参) 在python中创建函数是def,创建函数名是def f(),f函数名字,def f(a,b),这里的a,b是两个参数,函数名是自定义的,参数也是自定义,随意就好.看图如下效果: 这里f(1,2)是实参,然后调用上面的函数. 下面讲解下*args和**kw的区别. 2.*args 这里的*后面的值是自定义的,只要不是数字就行,定义成*abc,*ccc都可以,len()是函数,它的意思是返回字符串长度.然后前面的a,b是普通参数,print a print b就是回显1,2