Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3569493
  • 博文数量: 205
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 7385
  • 用 户 组: 普通用户
  • 注册时间: 2013-01-23 18:56
个人简介

将晦涩难懂的技术讲的通俗易懂

文章分类

全部博文(205)

文章存档

2024年(8)

2023年(9)

2022年(4)

2021年(12)

2020年(8)

2019年(18)

2018年(19)

2017年(9)

2016年(26)

2015年(18)

2014年(54)

2013年(20)

分类: Python/Ruby

2016-07-15 22:09:03


Python中的evalexeccomplie用法

——lvyilong316


1. complie

complie(string, file, type)函数允许在运行时迅速生成代码对象。那么什么是代码对象呢?Python中要执行的代码,都必须先转换成字节码才能由python解释器执行,而一段代码对应转换成的字节码就可以理解为是一个代码对象。那complie生成的代码对象有什么用呢?这些代码对象可以进一步由exec或者eval函数来执行。

compile有三个参数,并且都是必须的,且这三个参数的类型都是字符串。第一个参数代表要编译的python代码;第二个参数通常被置为空串,该参数代表了存放代码对象的文件名字,由于compile的通常用法是动态的生成字符串形式的python代码,然后生成一个代码对象——代码显然没有存放在任何文件;最后一个参数用来说明代码对象的类型,有三个可能值:

eval :可求值表达式,和eval一起使用;

single :单一可执行语句,和exec一起使用;

exec : 可执行语句组,和exec一起使用;

这里注意三个词:表达式、语句、语句组。语句和语句组的区别比较好理解,语句组就是多个语句。但语句和表达式需要分清楚,这也是后面讲到的evalexec的根本不同之处。eval函数用来执行python表达式,而exec函数用来执行python语句。

1

点击(此处)折叠或打开

  1.   obj = compile('print "hello world!"', '', 'single')
  2.   exec obj #由于是single类型的代码对象,所以可以用exec执行

运行结果:

[long@localhost test]$ python ./compile1.py

hello world!

2

点击(此处)折叠或打开

  1. obj = compile("""
  2. print "hello world"
  3. print "mutiline"
  4. """, '', 'exec')
  5. exec obj #exec类型的代码对象,所以可以使用exec执行

运行结果:

[long@localhost test]$ python ./compile2.py

hello world

mutiline

3

obj = compile('2+4', '', 'eval')

eval(obj)  #eval类型的代码对象,所以可以使用eval执行

注意:由于只是执行一个表达式,所以不会有任何输出,当然如果交互式中会输出6.

另外,eval类型只能是表达式,如:obj=compile('a=2+4', '', 'eval'),执行就会报错,因为a=2+4是语句,而不是表达式。

4

点击(此处)折叠或打开

  1. def test_fun1():
  2.     obj = compile('a=123', '', 'exec')
  3.     exec obj
  4. test_fun1()
  5. print a

运行结果:

[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定义全局变量呢?当然可以,只需如下改动:

5

点击(此处)折叠或打开

  1. def test_fun1():
  2.     obj = compile('a=123', '', 'exec')
  3.     exec obj in globals() #或 exec compile('a=123', '', 'exec') in globals()
  4. test_fun1()
  5. print a

运行结果:

[long@localhost test]$ python ./compile4.py

123

这种用法其实不能算compile的,而是exec的用法,具体看下一节exec的介绍。

2. exec

    exec的函数原型为exec(object[, globals[, locals]]),参数object是一个字符串的语句或者一个编译过的语句的对象名称。参数globals是全局命名空间,用来指定执行语句时可以访问的全局命名空间;参数locals是局部命名空间,用来指定执行语句时可以访问的局部作用域的命名空间。要注意本函数不会返回任何值,不管函数或语句有任何的返回值语句,比returnyield语句。

    如果参数globalslocals忽略,就会使用调用时所处的命名空间。这两个参数都要求是字典形式来说明命名空间。

当然后两个参数还有另外一种用法:

exec object in globals, locals

也就是我们之前将compile时见到的用法。其实说白了,globallocals就是给exec要执行的语句或者执行对象指定运行的全局空间和局部空间。

6.

点击(此处)折叠或打开

  1. a = 123
  2. g = {'a':456}
  3. exec "print a" in g

运行结果:

[long@localhost test]$ python ./exec1.py

456

可以看到由于给exec指定了全局作用域,所以全局作用域不在是原来的globals()a的值也会在全局作用域中查找,如果查找不到则出错。

7

点击(此处)折叠或打开

  1. a = 123
  2. g = {'a':456}
  3. l = {'a':789}
  4. exec "print a" in g,l

运行结果:

[long@localhost test]$ python ./exec2.py

789

知道上个例子的原理,这个例子就不难理解了,因为同时给exec指定了全局作用域和局部作用域,所以查找从局部作用域开始,找不到才会查找全局作用域。下面在看一个例子。

8

点击(此处)折叠或打开

  1. g = {}
  2. print g
  3. exec 'print "hello"' in g
  4. print g

运行结果:

[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当做了一个作用域,或者说是名称空间来使用,那么执行完execg的身份就有一个普通的dict变为了一个名称空间了,当然他本身还是dict类型,只是具备了一个名称空间的所有基本属性。

    有了上面一个例子的基础,我们再来看下如何利用compileexec动态的生成一个模块。

9

假如当前目录下有一个config文件,内容如下:

点击(此处)折叠或打开

  1. name = 'lvyilong316'
  2. age = 27
  3. hobbys = ['football','basketball','pingpang']

我们的代码为exec4.py,内容如下:


点击(此处)折叠或打开

  1. import os
  2. import sys
  3. def load_config():
  4.     config_path = "./config"
  5.     text = open(config_path).read() #读取配置文件
  6.     module = type(sys) #获取module类型
  7.     m = module("my_module") #创建一个模块(module),名称为”my_module”
  8.     print m.__dict__ #此时模块除了名称外没有其他成员
  9.     exec compile(text, '', 'exec') in m.__dict__ #以模块的__dict__为名字空间中执行配置文件中的语句
  10.     print m.__dict__
  11.     return m
  12. #之后就可以正常使用模块,而配置文件的内容已经加载到了模块中
  13. mo = load_config()
  14. print "-"*50
  15. print "name:%s, age:%d, hobbys:%s" % (mo.name, mo.age, mo.hobbys)


运行结果:

[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简化如下:


点击(此处)折叠或打开

  1. def load_config():
  2.     config_path = "./config"
  3.     module = type(sys)
  4.     m = module("my_module")
  5.     print m.__dict__
  6.     execfile(config_path, m.__dict__)
  7.     print m.__dict__
  8.     return m
  9.     execfile函数的原型为execfile(filename, globals=globals(), locals=locals())
  10. execfile(filename) 等价于一下三行:
  11. f = open(filename, 'r')
  12. exec f
  13. f.close()


介绍到这里不知道你有没有想过一个问题:既然exec这么强大,可以直接执行字符串语句,为什么还需要compile编译成代码对象呢?一个很重要的观点是:execeval都可以执行字符串格式的python代码。当执行字符串形式的代码时,每次都必须对这些代码进行字节编译处理。Compile函数提供一次性字节代码编译,以后每次调用的时候就不用编译了

3. eval

eval的功能是执行表达式,而表达式的可以是字符串形式的python代码,也可以是compile创建的代码对象。

eval的函数原型为:eval(obj, globals=globals(),locals=locals())

evalexec相比处理一个是执行表达式,一个是执行语句,其他没什么区别,这里就不再过多介绍了。

下面做一下三个函数的总结:

(1) compile函数是只编译字符串代码,而不作任何的执行,但它可以编译表达式或语句。

(2) eval函数是只执行表达式字符串代码,而不执行语句代码。

   x = eval('%d + 6' % x)

(3) exec函数是只执行语句代码,而不执行表达式代码,因为它没有任何返回值。

   exec('if True: print(6)')


阅读(5319) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~