将晦涩难懂的技术讲的通俗易懂
分类: Python/Ruby
2016-07-15 22:09:03
Python中的eval、exec、complie用法
complie(string, file, type)函数允许在运行时迅速生成代码对象。那么什么是代码对象呢?Python中要执行的代码,都必须先转换成字节码才能由python解释器执行,而一段代码对应转换成的字节码就可以理解为是一个代码对象。那complie生成的代码对象有什么用呢?这些代码对象可以进一步由exec或者eval函数来执行。
compile有三个参数,并且都是必须的,且这三个参数的类型都是字符串。第一个参数代表要编译的python代码;第二个参数通常被置为空串,该参数代表了存放代码对象的文件名字,由于compile的通常用法是动态的生成字符串形式的python代码,然后生成一个代码对象——代码显然没有存放在任何文件;最后一个参数用来说明代码对象的类型,有三个可能值:
‘eval’ :可求值表达式,和eval一起使用;
‘single’ :单一可执行语句,和exec一起使用;
‘exec’ : 可执行语句组,和exec一起使用;
这里注意三个词:表达式、语句、语句组。语句和语句组的区别比较好理解,语句组就是多个语句。但语句和表达式需要分清楚,这也是后面讲到的eval和exec的根本不同之处。eval函数用来执行python表达式,而exec函数用来执行python语句。
l 例1
点击(此处)折叠或打开
运行结果:
[long@localhost test]$ python ./compile1.py
hello world!
l 例2
点击(此处)折叠或打开
运行结果:
[long@localhost test]$ python ./compile2.py
hello world
mutiline
l 例3
obj = compile('2+4', '', 'eval')
eval(obj) #eval类型的代码对象,所以可以使用eval执行
注意:由于只是执行一个表达式,所以不会有任何输出,当然如果交互式中会输出6.
另外,eval类型只能是表达式,如:obj=compile('a=2+4', '', 'eval'),执行就会报错,因为”a=2+4”是语句,而不是表达式。
l 例4
点击(此处)折叠或打开
运行结果:
[long@localhost test]$ python ./compile4.py
Traceback (most recent call last):
File "./compile4.py", line 6, in
print a
NameError: name 'a' is not defined
我们本打算在函数test_fun1中通过compile执行赋值语句给变量a赋值,然后在全局作用域输出,但却因找不到定义而出错。这个结果其实很好理解,因为实在test_fun1中通过compile执行赋值语句,所以所执行的语句就相当于在test_fun1中执行的一样,其作用域当然也是test_fun1的局部作用域,在全局作用域中当然找不到a的值。那么我们能否在test_fun1中通过compile定义全局变量呢?当然可以,只需如下改动:
l 例5
点击(此处)折叠或打开
运行结果:
[long@localhost test]$ python ./compile4.py
123
这种用法其实不能算compile的,而是exec的用法,具体看下一节exec的介绍。
exec的函数原型为exec(object[, globals[, locals]]),参数object是一个字符串的语句或者一个编译过的语句的对象名称。参数globals是全局命名空间,用来指定执行语句时可以访问的全局命名空间;参数locals是局部命名空间,用来指定执行语句时可以访问的局部作用域的命名空间。要注意本函数不会返回任何值,不管函数或语句有任何的返回值语句,比return或yield语句。
如果参数globals和locals忽略,就会使用调用时所处的命名空间。这两个参数都要求是字典形式来说明命名空间。
当然后两个参数还有另外一种用法:
exec object in globals, locals
也就是我们之前将compile时见到的用法。其实说白了,global和locals就是给exec要执行的语句或者执行对象指定运行的全局空间和局部空间。
l 例6.
点击(此处)折叠或打开
运行结果:
[long@localhost test]$ python ./exec1.py
456
可以看到由于给exec指定了全局作用域,所以全局作用域不在是原来的globals(),a的值也会在全局作用域中查找,如果查找不到则出错。
l 例7
点击(此处)折叠或打开
运行结果:
[long@localhost test]$ python ./exec2.py
789
知道上个例子的原理,这个例子就不难理解了,因为同时给exec指定了全局作用域和局部作用域,所以查找从局部作用域开始,找不到才会查找全局作用域。下面在看一个例子。
l 例8
点击(此处)折叠或打开
运行结果:
[long@localhost test]$ python ./exec3.py
{}
hello
{'__builtins__': {'bytearray': , 'IndexError': , 'all': , 'help': Type help() for interactive help, or help(object) for help about object., 'vars': , 'SyntaxError': , 'unicode': , 'UnicodeDecodeError': , 'isinstance': , 'copyright': Copyright (c) 2001-2010 Python Software Foundation.
All Rights Reserved.
Copyright (c) 2000 BeOpen.com.
All Rights Reserved.
Copyright (c) 1995-2001 Corporation for National Research Initiatives.
All Rights Reserved.
Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
All Rights Reserved., 'NameError': , 'BytesWarning': , 'dict': , 'input': ......(省略)}}
这个结果就很令人费解了,怎么g是一个空的dict,执行了一个无关紧要的print语句一下多出了这么多东西。其实这是由于g的身份发生了变化,在执行exec之前,g只是一个dict。但在执行exec时,我们把g这个dict当做了一个作用域,或者说是名称空间来使用,那么执行完exec后g的身份就有一个普通的dict变为了一个名称空间了,当然他本身还是dict类型,只是具备了一个名称空间的所有基本属性。
有了上面一个例子的基础,我们再来看下如何利用compile和exec动态的生成一个模块。
l 例9
假如当前目录下有一个config文件,内容如下:
点击(此处)折叠或打开
我们的代码为exec4.py,内容如下:
点击(此处)折叠或打开
运行结果:
[long@localhost test]$ python ./exec4.py
{'__name__': 'my_module', '__doc__': None}
{'hobbys': ['football', 'basketball', 'pingpang'], 'name': 'lvyilong316', '__builtins__': {'bytearray': , 'IndexError': , 'all': , 'help': Type help() for interactive help, or help(object) for help about object., 'vars': , 'SyntaxError': , 'unicode': , 'UnicodeDecodeError': , 'isinstance': , 'copyright': Copyright (c) 2001-2010 Python Software Foundation.
All Rights Reserved.
Copyright (c) 2000 BeOpen.com.
All Rights Reserved.
......(省略名称空间的基本属性)......
'age': 27, '__name__': 'my_module', '__doc__': None}
--------------------------------------------------
name:lvyilong316, age:27, hobbys:['football', 'basketball', 'pingpang']
当然上面的函数也可以使用execfile简化如下:
点击(此处)折叠或打开
介绍到这里不知道你有没有想过一个问题:既然exec这么强大,可以直接执行字符串语句,为什么还需要compile编译成代码对象呢?一个很重要的观点是:exec和eval都可以执行字符串格式的python代码。当执行字符串形式的代码时,每次都必须对这些代码进行字节编译处理。Compile函数提供一次性字节代码编译,以后每次调用的时候就不用编译了。
eval的功能是执行表达式,而表达式的可以是字符串形式的python代码,也可以是compile创建的代码对象。
eval的函数原型为:eval(obj, globals=globals(),locals=locals())
eval和exec相比处理一个是执行表达式,一个是执行语句,其他没什么区别,这里就不再过多介绍了。
下面做一下三个函数的总结:
(1) compile函数是只编译字符串代码,而不作任何的执行,但它可以编译表达式或语句。
(2) eval函数是只执行表达式字符串代码,而不执行语句代码。
x = eval('%d + 6' % x)
(3) exec函数是只执行语句代码,而不执行表达式代码,因为它没有任何返回值。
exec('if True: print(6)')