代码对象
概念
可调用的对象是python执行环境里最重要的部分,然而它们只是冰山一角。Python语句、赋值、表达式,甚至还有模块构成了更宏大的场面。这些可执行对象无法像可调用物那样被调用。更确切的说,这些对象只是构成可执行代码块的拼图的很小一部分,而这些代码块被称为代码对象。
每个可调用物的核心都是代码对象,由语句、赋值、表达式和其他可调用物组成。查看一个模块意味着观察一个较大、包含了模块中所以代码的对象。然后代码可以分成语句、赋值、表达式,以及可调用物。可调用物又可以递归分解到下一层,那儿有自己的代码对象。
一般来说,代码对象可以作为函数或者方法调用的一部分来执行,也可用exec语句或内建函数eval()来执行。整体上看,一个python模块的代码对象是构成该模块的全部代码。
如果要执行python代码,那么该代码必须要先转换成字节编译的代码(又称字节码)。这才是真正的代码对象。然而,它们不包含任何关于它们执行环境的信息,这便是可调用物存在的原因,它被用来包装一个代码对象并提供额外的信息。
函数对象仅是代码对象的包装,方法则是给函数对象的包装,你可以到处看看。当你研究到最底层,你会发现便是一个代码对象。
可执行的对象声明和内建函数
Python提供了大量的BIF来支持可调用/可执行对象, 其中包括exec语句。这些函数帮助程序员执行代码对象,也可以用内建函数compile()来生成代码对象。
可执行的对象声明和内建函数
内建函数和语句 描述
callable(obj) 如果obj可调用,返回True,否则返回False
compile(string,file,type) 从type类型中创建代码对象;file存放代码(通常设””)
eval(obj,globals=globals(),locals=locals()) 对obj求值,obj为已编译代码对象表达式,或是一个字符串表达式,可以给出全局或者局部的名称空间。
exec obj 执行obj,单一python语句或者语句集合,也就是说格式是代码对象或者字符串,obj也可以是文件对象(已打开的有效python脚本)
input(prompt=’’) 等同于eval(raw_input(prompt=’’))
1, callable()
布尔函数,确定一个对象是否可以通过函数操作符”()”来调用。若是返回True,否则False ,(2.2以前返回1和0)
>>> callable(dir) # 内建函数
True
>>>
>>> callable(1) # 整型
False
>>>
>>> def foo():pass
...
>>> callable(foo) #自定义函数
True
>>>
>>> callable('bar') # 字符串
False
>>>
>>> class C(object): pass
...
>>> callable(C) # 类
True
>>>
>>>
2, compile()
compile()函数允许程序员在允许时刻迅速生成代码对象,然后就可以用exec语句或者eval()来执行这些对象或者对它们求值。一个很重要的观点: exec和eval()都可以执行字符串格式的python代码。当执行字符串形式的代码时,每次都必须对这些代码进行字节编译处理。compile()函数提供了一次性字节代码预编译,以后每次调用的时候,都不用编译了。
compile()三个参数都是必须的,第一个参数代表了要编译的代码。第二个字符串虽然是必须的,但通常被置为空串。该参数代表了存放代码对象的文件的名字(字符串类型)。compile的通常用法是动态生成字字符串形式的python代码,然后生成一个代码对象(代码显然没有存放在任何文件)。最后一个参数是个字符串,它用来表明代码对象的类型。有三个可能值:
‘eval’ 可求值的表达式(和eval()一起使用)
‘single’ 单一可执行语句(和exec一起使用)
‘exec’ 可执行语句组(和exec一起使用)
a), 可求值表达式
>>>eval_code = compile('100 + 200','','eval')
>>> eval(eval_code)
300
>>>
b), 单一可执行语句
>>> single_code = compile('print "hello world!"','' ,'single')
>>>
>>> single_code
<code object <module> at 0x7fd1de13d7b0, file "",line 1>
>>>
>>> exec single_code
hello world!
>>>
>>>
c), 可执行语句
>>> exec_code =compile("""
... req = input('Count howmany numbers? ')
... for eachNum inrange(req):
... print eachNum
... """, '','exec')
>>>
>>> exec exec_code
Count how many numbers? 6
0
1
2
3
4
5
>>>
3, eval()
eval()对表达式求值,可以为字符串或内建函数compile()创建的预编译代码对象。这是eval()第一个也是最重要的参数。第二个第三个都是可选的,分别代表了全局和局部空间中的对象。如果给出这两个参数,globals必须是个字典,locals可以是任意的映射对象,比如,一个实现了__getitem__方法的对象。(在2.4之前,local必须是一个字典),如果没有给出,分别默认为globals()和locals()返回的对象,如只传入一个全局字典,则该字典也作为locals传入。
>>>eval('932')
932
>>>
>>>int('932')
932
>>>
两者结果相同,但采用方式不同,内建函数eval()接收引号内的字符串并把它作为表达式进行求值。内建函数int()接收代表整数的字符串转为整数。当我们用纯字符串表达式的时候,两者便不再相同了:
>>>eval('100 + 200')
300
>>>int('100 + 200')
Traceback (mostrecent call last):
File "<stdin>", line 1, in<module>
ValueError:invalid literal for int() with base 10: '100 + 200'
>>>
4, exec
和eval()相似,exec语句执行代码对象或字符串形式的python代码。类似地,用compile()预编译重复代码有助于改善性能,因为在调用时不必经过字节编译处理。Exec语句只接收一个参数,下面是通用用法:
exec obj
被执行的对象(obj)可以只是原始的字符串,比如单一语句或是语句组,它们也可以预编译成一个代码对象(分别用’single’和’exec’参数)。
>>> exec """
... x = 0
... print 'x is currently:', x
... while x < 5:
... x += 1
... print'incrementing x to:', x
... """
x is currently: 0
incrementing x to: 1
incrementing x to: 2
incrementing x to: 3
incrementing x to: 4
incrementing x to: 5
>>>
>>>
最后,exec还可以接受有效的python文件对象,将上面代码创建为一个xcount.py文件:
>>>
>>> f = open('xcount.py')
>>> exec f
x is currently: 0
increment x to: 1
increment x to: 2
increment x to: 3
increment x to: 4
increment x to: 5
>>>
注意:一旦执行完毕,继续对exec调用就会失败,因为exec已从文件中读取了全部数据且停留在文件末尾(EOF),当用相同文件对象调用时已没有可执行代码了。我们用文件对象的tell()方法告诉我们处于文件的何处,然后用os.path.getsize()告诉我们xcount.py脚本大小,你会发现,两个数字完全一样:
>>> f.tell()
111
>>>
>>> f.close()
>>>
>>> from os.path import getsize
>>>
>>> getsize('xcount.py')
111
>>>
可以使用seek()再次运行
>>>
>>> f.seek(0)
>>> f.tell()
0
>>>
>>> exec f
x is currently: 0
increment x to: 1
increment x to: 2
increment x to: 3
increment x to: 4
increment x to: 5
>>> f.close()
5, input()
内建函数input()是eval()和raw_input()的组合,等价于eval(raw_input).类似于raw_input(),input()有一个可选参数,代表给用户的字符串提示。不给定默认为空串。
从功能上看,input()不同于raw_input(),因为raw_input()总是以字符串形式,逐字返回用户的输入。Input()履行相同任务;而且它还把输入作为python表达式进行求值。这意味着input()返回的数据是对输入表达式求值的结果:一个python对象。下面例子会让人更加清楚:当用户输入一个列表时,raw_input()返回一个列表的字符串描绘,而input()返回实际的列表:
>>> aString = raw_input('Enter a list:')
Enter a list:[ 123, 'xyz', 45.67]
>>>
>>> aString
"[ 123, 'xyz', 45.67]"
>>>
>>> type(aString)
<type 'str'>
>>> aList = input('Enter a list: ')
Enter a list: [ 123, 'xyz', 45.67 ]
>>> aList
[123, 'xyz', 45.670000000000002]
>>> type(aList)
<type 'list'>
>>>
可见,input()把输入作为python对象来求值并返回表达式的结果。