2015/10/9Python基础:可调用和可执行对象

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

在Python中有多种运行外部程序的方法,比如,运行操作系统命令或另外的Python脚本,或执行一个磁盘上的文件,或通过网络来运行文件。这完全取决于想要干什么。特定的环境包括:

在当前脚本继续运行

创建和管理子进程

执行外部命令或程序

执行需要输入的命令

通过网络来调用命令

执行命令来创建需要处理的输出

执行其他的Python脚本

执行一系列动态生成的Python语句

导入Python模块

Python中,内建和外部模块都可以提供上述各种功能。程序员得根据实现的需要,从这些模块中选择合适的处理方法。

可调用对象

许多Python对象都是我们所说的可调用的,即是任何通过函数操作符()来调用的对象。Python有4中可调用对象:函数,方法,类,以及一些类的实例。

1.函数

Python有3中不同类型的函数对象,第一种是内建函数。

内建函数(BIFs)

BIF是用C/CPP写的,编译过后放入Python解释器,然后把它们作为第一(内建)名字空间的一部分加载进系统。这些函数在_builtin_模块里,并作为__builtins__模块导入到解释器中。

可以用dir()列出函数的所有属性:

>>>dir(type)

['__abstractmethods__','__base__','__bases__','__basicsize__','__call__','__class__','__delattr__','__dict__','__dictoffset__','__doc__','__eq__','__flags__','__format__','__ge__','__getattribute__','__gt__','__hash__','__init__','__instancecheck__','__itemsize__','__le__','__lt__','__module__','__mro__','__name__','__ne__','__new__','__reduce__','__reduce_ex__','__rePR__','__setattr__','__sizeof__','__str__','__subclasscheck__','__subclasses__','__subclasshook__','__weakrefoffset__','mro']

从内部机制来看,因为BIFs和内建方法(BIMs)属于相同类型,所以对BIF或者BIM调用type()的结果是:

>>>type(dir)<type'builtin_function_or_method'>

用户定义的函数(UDF)

UDF通常是用Python写的,定义在模块的最高级,因此会作为全局名字空间的一部分装置到系统中。函数也可以在其他函数体内定义,我们可以对多重嵌套作用域中的属性进行访问。

从内部机制来看,用户自定义的函数是“函数”类型的:

>>>deffoo():pass>>>type(foo)<type'function'>

lambda表达式

lambda表达式和用户自定义函数相比,略有不同。虽然它们也是返回一个函数对象,但是不是用def语句创建的,而是用lambda关键字:

因为lambda表达式没有给命名绑定的代码提供基础结构,所以要通过函数式编程接口来调用,或把它们的引用赋值给一个变量,然后就可以直接调用或者再通过函数来调用。变量仅是个别名,并不是函数对象的名字。

通过lambda来创建函数的对象除了没有命名之外,和UDF有相同的属性;__name__或者func_name属性给定位字符串"<lambda>"

>>> lambdaFunc =lambdax: x * 2

>>> lambdaFunc(12)24

>>>type(lambdaFunc)<type'function'>

>>> lambdaFunc.__name__'<lambda>'

如果是UDF的名字,则是这样:

>>>deffoo():pass>>> foo.__name__'foo'

以上是三种函数对象

2.方法

用户自定义方法是被定义为类的一部分的函数。许多Python的数据类型,比如列表和字典,也有方法,被称为内建方法。为了说明所有权的类型,方法通过对象的名字和句点属性标识符命名。

内建方法(BIMs)

刚刚我们说了BIF和BIM的类似之处。只有内建类型有内建方法。对于内建方法,type()工厂函数给出了和BIF一样的输出。

>>>type([].append)<type'builtin_function_or_method'>

此外BIM和BIF两者有相同属性。不同之处在于BIM的__self__属性指向一个Python对象,BIF指向None。

用户定义的方法(UDM)

UDM包含在类定义之中,只是拥有标准函数的包装,仅有定义他们的类可以使用。如果没有在子类定义中被覆盖,也可以通过子类实例来调用它们。

3.类

调用类的结果就是创建了实例,也就是实例化。

4.类的实例

Python给类提供了名为__call__的特别方法,该方法允许程序员创建可调用的对象(实例)。默认情况下,__call__()方法是没有实现的,这意味着大多数情况下实例是不可调用的。然而,如果在类中覆盖了这个方法,那么这个类的实例就成为可调用的了。调用这样的实例对象等同于调用__call__()方法。如:foo()和foo.__call__(foo)的效果相同,这里的foo也作为参数出现,因为是对自己的引用,实例将自动成为每次方法调用的第一个参数,如果__call__()有参数,那么foo(arg)就和foo.__call__(foo, arg)一样。

代码对象

可调用对象是Python执行环境里最重要的部分,然而这并不是全部。Python语句,赋值,表达式,甚至还有模块构成了更宏大的场面。这些可执行对象无法像可调用物那样被调用。这些代码块被称为代码对象。

每个可调用物的核心都是代码对象,由语句,赋值,表达式,以及其他可调用物组成。查看一个模块意味着观察一个较大的、包含了模块中所有代码的对象。然后代码分成语句,赋值,表达式,以及可调用物。可调用物又可以递归分解到下一层,那里有它自己的代码对象。

一般来说,代码对象可以作为函数或者方法调用的一部分来执行,也可用exec语句或内建函数eval()来执行。从整体上看,一个Python模块的代码对象是构成该模块的全部代码。

如果要执行Python代码,那么该代码必须先要转换成字节编译的代码(又称字节码)。这才是真正的代码对象。然而,它们不包含任何关于它们执行环境的信息,这便是可调用物存在的原因,它被用来包装一个代码对象并提供额外的信息。

UDF有 udf.func_code 属性就是代码对象。UDM的udm.im_func也是一个函数对象,他同样有它自己的udm.im_func.func_code代码对象。这样的话,你会发现,函数对象仅是代码对象的包装,方法则是给函数对象的包装。当研究到最底层,便是一个代码对象。

可执行的对象声明和内建函数

Python提供了大量的BIF来支持可调用/可执行对象。

1.callable()

callable()是一个布尔函数,确定一个对象是否可以用函数操作符()来调用。如果可调用便返回True,否则便是False。

2.compile()

compile()函数允许程序员在运行时刻迅速生成代码对象,然后就可以用exec语句或者内建函数eval()来执行这些对象或者他们进行求值。

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

'eval'可求值的表达式[和eval()一起使用]

'single'单一可执行语句[和exec一起使用]

'exec'可执行与剧组[和exec一起使用]

可求值表达式

>>> eval_code = compile('10-2','','eval')>>>eval(eval_code)8

单一可执行语句

>>> single_code = compile('print "Hello world"','','single')>>>single_code<code object <module> at 024DC698, file"", line 1>

>>>execsingle_code

Hello world

可执行语句组

>>> exec_code = compile("""req = input('Count how many numbers?')

for eachNum in range(req):

print eachNum""",'','exec')>>>execexec_code

Count how many numbers?601

2

3

4

5

3.eval()

eval()对表达式求值,表达式可以为字符串或内建函数compile()创建的预编译代码对象。这个对象是第一个也是最重要的参数。第二个和第三个参数是可选的,分别代表了全局和局部名称空间中的对象。如果给出了这两个参数,全局必须是个字典,局部可以是任意的映射对象。如果没有给出这两个参数,分别默认为globals()和locals()返回的对象。如果只传入了一个全局字典,那么该字典也作为局部参数传入。

这是eval()的一个例子。

>>> eval('123')123

>>> int('123')123

>>> eval('123+234')357

>>> int('123+234')

Traceback (most recent call last):

File"<pyshell#10>", line 1,in<module>int('123+234')

ValueError: invalid literalforint() with base 10:'123+234'

开始,我们传入'123'给eval()和int()的时候,返回了相同的结果,但是方式是不尽相同的,eval()接受引号内的字符串把它作为Python表达式求值,int()接受代表整数的字符串并把它转换为整数。而当我们输入'123+234'时,情况就不一样了。int()调用就失败了。可以认为eval()函数对表达式两端的引号视而不见,将它执行在解释器上,返回结果。

4.exec

和eval()相似,exec语句执行代码对象或字符串形式的Python代码。类似地,用compile()预编译重复代码有助于改善性能,因为在调用时不必经过字节编译处理。exec只接受一个参数,语法是:

execobj

obj可以是原始的字符串,比如单一语句或语句组,也可以预编译层一个代码对象。

>>>exec"""x = 0

print 'x is currently:',x

while x < 5:

x += 1

print 'incrementing x to:',x"""xiscurrently: 0

incrementing x to:1incrementing x to:2incrementing x to:3incrementing x to:4incrementing x to:5

exec还可以接受有效的Python文件对象。如果我们用上面的多行代码创建一个xcount.py的文件,那么也可以这样执行相同代码:

>>> f = open('xcount.py')#open the file>>>execf#execute the filexiscurrently: 0

incrementing x to:1incrementing x to:2incrementing x to:3incrementing x to:4incrementing x to:5

上面我们调用了文件f,如果在完成后继续调用它

>>>execf>>>

调用会失败。并不是真正的失败,只是不再做任何事。事实上,exec已从文件中读取了全部数据且停留在文件末尾(EOF)。当用相同的文件对象对exec进行调用的时候,没有可执行的代码了,所以exec什么都不做。

我们可以用tell()方法来告诉我们处于文件的何处,然后用os.path.getsize()来告诉我们脚本由多大。然后就会发现,这两个数字完全一样:

>>>f.tell()116

>>>f.close()>>>fromos.pathimportgetsize>>> getsize('xcount.py')116

如果想在不关闭和重新打开文件的情况下再次运行它,可以用seek()到文件最开头并再次调用exec。假定我们还没有调用f.close(),那么:

>>>f.seek(0)>>>execf

xiscurrently: 0

incrementing x to:1incrementing x to:2incrementing x to:3incrementing x to:4incrementing x to:5

>>> f.close()

5.input()

之前用到的内建函数input()是eval()和raw_input()的组合,等价于eval(raw_input()),input()和raw_input()一样有一个可选的参数给用户字符串提示。

input不同于raw_input(),input()返回的数据是对输入表达式求值的结果,是一个Python对象。

6.使用Python在运行时生成和执行Python代码

书上提供了两个例子,这两个例子在运行时吧Python代码作为字符串并执行。

第一个例子是loopmake.py脚本。一个简单迅速和执行循环的计算机辅助软件工程。提示用户给出各种参数,生成代码字符串,并执行它。

dashes ='\n'+'-'* 50exec_dict={'f':'''#for loop

for %s in %s:

print %s''','s':'''# sequence while loop

%s = 0

%s = %s

while %s < len(%s):

print %s[%s]

%s = %s + 1''','n':'''# counting while loop

%s = %d

while %s < %d:

print %s

%s = %s + %d'''}defmain():

ltype= raw_input('Loop type? (For/While)')

dtype= raw_input('Data type? (Number/Sequence)')ifdtype =='n':

start= input('Starting value?')

stop= input('Ending value (non-inclusive)?')

step= input('Stepping value?')

seq=str(range(start, stop, step))else:

seq= raw_input('Enter sequence:')

var= raw_input('Iterative variable name?')ifltype =='f':

exec_str= exec_dict['f'] %(var, seq, var)elifltype =='w':ifdtype =='s':

svar= raw_input('Enter sequence name?')

exec_str= exec_dict['s'] %\

(var, svar, seq, var, svar, svar, var, var, var)elifdtype =='n':

exec_str= exec_dict['n'] %\

(var, start, var, stop, var, var, var, step)printdashesprint'The custom-generated code for you is:'+dashesprintexec_str +dashesprint'The execution of the code:'+dashesexecexec_strprintdashesif__name__=='__main__':

main()

有兴趣的人可以执行一下这段代码,十分有趣,可以帮助你生成代码并执行。反正我写这段代码的时候感觉到了exec和input的强大。

第二个例子是有条件地执行代码

这是代码:

deffoo():returnTruedefbar():'bar() does not do much'returnTrue

foo.__doc__='foo() does not do much'foo.tester='''if foo():

print 'PASSED'

else:

print 'FAILED''''foreachAttrindir():

obj=eval(eachAttr)ifisinstance(obj, type(foo)):ifhasattr(obj,'__doc__'):print'\nFunction "%s" has a doc string:\n\t%s'\% (eachAttr, obj.__doc__)ifhasattr(obj,'tester'):print'Function "%s" has a tester... executing'\%eachAttrexecobj.testerelse:print'Function "%s" has no tester... skipping'\%eachAttrelse:print'"%s" is not a function'% eachAttr

下面是执行后的结果:

>>>"__builtins__"isnota function"__doc__"isnota function"__file__"isnota function"__name__"isnota function"__package__"isnota function

Function"bar"has a doc string:

bar() doesnotdo much

Function"bar"has no tester... skipping

Function"foo"has a doc string:

foo() doesnotdo much

Function"foo"has a tester... executing

PASSED

代码并不难理解,但其所做的事的确很有趣不是么?

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航