14.1可调用对象
Python有四种可调用对象(可以通过函数操作符‘()’来调用的对象):函数、方法、类、类的实例
14.1.1函数
1.内建函数(BIF)
内建函数是用C/C++写的,编译过后放入Python解释器,然后把它们作为第一名称空间的一部分加载进系统。如之前章节所述,这些函数在_bulitin_模块里,并作为__builtins__模块导入到解释器中
属性 | 描述 |
bif.__doc__ | 文档字符串(或None) |
bif.__name__ | 字符串类型的文档名字 |
bif.__self__ | 设置为None(保留给内建方法) |
bif.__module__ | 存放bif定义的模块名字(或None) |
可以用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']
从内部机制来看,因为内建函数和内建方法属于相同的类型,所以对BIF或者BIM调用type()的结果是
>>> type(dir)
<type 'builtin_function_or_method'>
注意这不能应用于工厂函数,因为type()正好会返回产生对象的类型
2.用户定义的函数(UDF)
用户定义的函数通常是用Python写的,定义在模块的最高级,因此会作为全局名称空间的一部分装载到系统中。函数也可以在其他函数体内定义,并且由于在2.2中嵌套作用域的改进,我们现在可以对多重嵌套作用域中的属性进行访问。可以用func_closure属性来勾住在其他地方定义的属性。
UDF也有许多属性,其中最特殊和最令人感兴趣的属性列表如下
属性 | 描述 |
udf.__doc__ | 文档字符串,也可以用udf.func_doc |
udf.__name__ | 字符串类型的函数名字,也可以用udf.func_name |
udf.func_code | 字节编译的代码对象 |
udf.func_defaults | 默认的参数元组 |
udf.func_globals | 全局名称空间字典;和从函数内部调用globals(x)一样 |
udf.func_dict | 函数属性的名称空间 |
udf.func_doc | (见上面的udf.__doc__) |
udf.func_name | (见上面的udf.__name__) |
udf.func_closure | 包含了自由变量的引用的单元对象元组(自用变量在UDF中使用,但在别处定义) |
3.lambda表达式
通过lambda来创建函数的对象除了没有命名之外,享有和用户自定义函数相同的属性,__name__或者func_name属性给定为字符串"<lambda>"。
14.1.2方法
1.内建方法
对于内建方法,type()工厂函数给出了和内建函数相同的输出。此外,内建方法和内建函数两者也享有相同属性,不同之处在于内建方法的__self__属性指向一个Python对象,而内建函数指向None。
对于类和实例都可以通过dir来获取方法属性,对于内建方法也是如此。
2.用户定义的方法(UDM)
用户定义的方法包含在类定义之中,只是拥有标准函数的包装,仅有定义它们的类可以使用。如果没有在子类定义中被覆盖掉,也可以通过子类实例来调用它们。正如13章中解释的,UDM与类对象是关联的(非绑定方法),但是只能通过类的实例来调用(绑定方法)。
属性 | 描述 |
udm.__doc__ | 文档字符串(与udm.im_fuc.__doc__相同) |
udm.__name__ | 字符串类型的方法名字(与udm.im_fuc.__name__相同) |
udm.__module__ | 定义udm的模块的名字(或None |
udm.im_class | 方法相关联的类(如果是非绑定,那么为要求是udm的类 |
udm.im_func | 方法的函数对象 |
udm.im_self | 如果绑定的话为相关联的实例,否则为None |
14.1.3 类
>>>class C(object):
def __init__(self, *args):
print 'Instantiated with these arguments:
', args
>>>c1=C('foo',3)
Instantiated with these arguments:
('foo',3)
14.1.4 类的实例
Python给类提供了名为__call__的特别方法,该方法允许创建可调用的对象(实例)。默认情况下__call__()方法是没有实现的,这意味着大多数实例都是不可调用的。
---恢复内容结束---
14.1可调用对象
Python有四种可调用对象(可以通过函数操作符‘()’来调用的对象):函数、方法、类、类的实例
14.1.1函数
1.内建函数(BIF)
内建函数是用C/C++写的,编译过后放入Python解释器,然后把它们作为第一名称空间的一部分加载进系统。如之前章节所述,这些函数在_bulitin_模块里,并作为__builtins__模块导入到解释器中
属性 | 描述 |
bif.__doc__ | 文档字符串(或None) |
bif.__name__ | 字符串类型的文档名字 |
bif.__self__ | 设置为None(保留给内建方法) |
bif.__module__ | 存放bif定义的模块名字(或None) |
可以用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']
从内部机制来看,因为内建函数和内建方法属于相同的类型,所以对BIF或者BIM调用type()的结果是
>>> type(dir)
<type 'builtin_function_or_method'>
注意这不能应用于工厂函数,因为type()正好会返回产生对象的类型
2.用户定义的函数(UDF)
用户定义的函数通常是用Python写的,定义在模块的最高级,因此会作为全局名称空间的一部分装载到系统中。函数也可以在其他函数体内定义,并且由于在2.2中嵌套作用域的改进,我们现在可以对多重嵌套作用域中的属性进行访问。可以用func_closure属性来勾住在其他地方定义的属性。
UDF也有许多属性,其中最特殊和最令人感兴趣的属性列表如下
属性 | 描述 |
udf.__doc__ | 文档字符串,也可以用udf.func_doc |
udf.__name__ | 字符串类型的函数名字,也可以用udf.func_name |
udf.func_code | 字节编译的代码对象 |
udf.func_defaults | 默认的参数元组 |
udf.func_globals | 全局名称空间字典;和从函数内部调用globals(x)一样 |
udf.func_dict | 函数属性的名称空间 |
udf.func_doc | (见上面的udf.__doc__) |
udf.func_name | (见上面的udf.__name__) |
udf.func_closure | 包含了自由变量的引用的单元对象元组(自用变量在UDF中使用,但在别处定义) |
3.lambda表达式
通过lambda来创建函数的对象除了没有命名之外,享有和用户自定义函数相同的属性,__name__或者func_name属性给定为字符串"<lambda>"。
14.1.2方法
1.内建方法
对于内建方法,type()工厂函数给出了和内建函数相同的输出。此外,内建方法和内建函数两者也享有相同属性,不同之处在于内建方法的__self__属性指向一个Python对象,而内建函数指向None。
对于类和实例都可以通过dir来获取方法属性,对于内建方法也是如此。
2.用户定义的方法(UDM)
用户定义的方法包含在类定义之中,只是拥有标准函数的包装,仅有定义它们的类可以使用。如果没有在子类定义中被覆盖掉,也可以通过子类实例来调用它们。正如13章中解释的,UDM与类对象是关联的(非绑定方法),但是只能通过类的实例来调用(绑定方法)。
属性 | 描述 |
udm.__doc__ | 文档字符串(与udm.im_fuc.__doc__相同) |
udm.__name__ | 字符串类型的方法名字(与udm.im_fuc.__name__相同) |
udm.__module__ | 定义udm的模块的名字(或None |
udm.im_class | 方法相关联的类(如果是非绑定,那么为要求是udm的类 |
udm.im_func | 方法的函数对象 |
udm.im_self | 如果绑定的话为相关联的实例,否则为None |
14.1.3 类
1 >>> class C(object): 2 def __init__(self, *args): 3 print "instantiated with these arguments: ", args 4 >>> c1 = C() 5 instantiated with these arguments: 6 () 7 >>> c2 = C("the number of the counting shall be",3) 8 instantiated with these arguments: 9 ('the number of the counting shall be', 3)
14.1.4类的实例
Python给类提供了名为__call__的特别方法,该方法允许程序员创建可调用的对象实例。默认情况下,__call__()方法是没有实现的,这意味着大多数实例是不可调用的。foo()就相当于foo.__call__(foo), foo(arg)相当于foo.__call__(foo,arg)。
1 >>> class C(object): 2 def __call__(self, *args): 3 print "i am callable!called with args: ", args 4 >>> c = C() 5 >>> c 6 <__main__.C object at 0x02115E10> 7 >>> callable(c) 8 True 9 >>> c() 10 i am callable!called with args: 11 ()
14.2代码对象
每个可调用对象的核心都是代码对象,代码对象由语句statements、赋值assignments、表达式expressions、可调用对象组成。
如果要执行Python代码,那么该代码必须转换成字节编译的代码(字节码),这才是真正的代码对象。然而它们不包含任何关于它们执行环境的信息,这便是可调用对象存在的原因--包装一个代码对象并提供额外的信息。
14.3可执行的对象声明和内建函数
Python提供了大量的BIF来支持可调用/可执行对象
内建函数和语句 | 描述 |
calllable(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='')) |
14.3.1callable()
callable()是一个布尔函数,确定一个对象是否可以通过函数操作符()来调用。
14.3.2compile()
compile()函数允许程序员在程序运行时生成代码对象(字符串形式),然后用exec和eval()来执行代码。compile()的第三个参数表明代码对象的类型,有三个可能的值'eval'--可求值的表达式(与eval()搭配),'single'--单一可执行语句(与exec搭配),'exec'--可执行语句组(与exec搭配)。
1.可求值表达式
>>>eval_code = compile('100+200', '', 'eval')
>>>eval(eval_code)
300
2.单一可执行语句
>>> single_code = compile('print "Hello world!"', '','single')
>>> single_code
<code object <module> at 02085D58, file "", line 1>
>>> exec single_code
Hello world!
3.可执行语句组
>>> exec_code = compile("""
req = input('Count how many numbers?')
for eachNum in range(req):
print eachNum
""", '', 'exec')
>>> exec exec_code
Count how many numbers?5
0
1
2
3
4
14.3.3 eval()
eval()的第一个参数可以是字符串或者compile()创建的预编译代码对象,第二个第三个参数都是可选的,分别是全局和局部名称空间中的对象,如果给出了这两个参数,那么global必须是个字典,locals可以是任意的映射对象,比如一个实现了__getitem__()方法的对象。如果只传入了一个全局字典,那么该字典也作为locals传入。
14.3.4 exec
exec的语法是exec obj
1 >>> f = open("xcount.py") 2 >>> exec f 3 x is currently: 0 4 incrementing x to: 1 5 incrementing x to: 2 6 incrementing x to: 3 7 incrementing x to: 4 8 incrementing x to: 5 9 >>> exec f 10 >>> f.close() 11 >>> f = open("xcount.py") 12 >>> exec f 13 x is currently: 0 14 incrementing x to: 1 15 incrementing x to: 2 16 incrementing x to: 3 17 incrementing x to: 4 18 incrementing x to: 5
当obj为文件对象时,要记得关闭打开了的文件对象
14.3.5 input()
内建函数input()是eval()和raw_input()的组合,等价于eval(raw_input()),input()有一个可选的参数,用于给用户一个字符串提示。
14.3.6使用Python在运行时生成和执行Python代码
loopmake.py脚本是一个简单的、迅速生成和执行循环的计算机辅助软件工程(computer-aided software engineering, CASE)。它提示用户给出各种参数,生成代码并执行。
1 dashes = " " + "-"*50 2 exec_dict = { 3 "f":""" 4 for %s in %s: 5 print %s 6 """, 7 "s":""" 8 %s=0 9 %s = %s 10 while %s < len(%s): 11 print %s[%s] 12 %s = %s + 1 13 """, 14 "n":""" 15 %s = %d 16 while %s < %d: 17 print %s 18 %s = %s + %d 19 """ 20 } 21 def main(): 22 ltype = raw_input("loop type?(for/while)") 23 dtype = raw_input("data type?(number/seq)") 24 if dtype == "n": 25 start = input("starting value?") 26 stop = input("ending value(non-inclusive)?") 27 step = input("stepping value?") 28 seq = str(range(start, stop, step)) 29 else: 30 seq = raw_input("enter sequence:") 31 var = raw_input("iterative variable name?") 32 if ltype == "f": 33 exec_str = exec_dict["f"] % (var, seq, var) 34 elif ltype == "w": 35 if dtype == "s": 36 svar = raw_input("enter sequence name?") 37 exec_str = exec_dict["s"] % 38 (var, svar, seq, var, svar, svar, var,var,var) 39 elif dtype == "n": 40 exec_str = exec_dict["n"] % 41 (var, start, var, stop, var, var, var, step) 42 print dashes 43 print "your custom-generated code:" + dashes 44 print exec_str + dashes 45 print "test execution of the code:" + dashes 46 exec exec_str 47 print dashes 48 49 if __name__ == "__main__": 50 main()
第二个例子着重描写了在第11章引入的函数属性,它是PEP232中的例子得到的灵感,可以用来进行单元测试
1 def foo(): 2 return True 3 def bar(): 4 "bar() does not do much" 5 return True 6 foo.__doc__ = "foo() does not do much" 7 foo.tester = """ 8 if foo(): 9 print "passed" 10 else: 11 print "failed" 12 """ 13 for eachAttr in dir(): 14 obj = eval(eachAttr) 15 if isinstance(obj, type(foo)): 16 if hasattr(obj, "__doc__"): 17 print " function '%s' has a doc string: %s" % (eachAttr, obj.__doc__) 18 if hasattr(obj, "tester"): 19 print "function '%s' has a tester...executing" % eachAttr 20 exec obj.tester 21 else: 22 print "function '%s' has no tester...skipping" % eachAttr 23 else: 24 print "%s is not a function" % eachAttr
14.4 执行其他Python程序
核心笔记:模块会在导入时执行所有代码,如不希望这样应该在代码中加入 if __name__ == '__main__'。
14.4.2 execfile()
显然,导入模块不是在Python脚本中执行其它Python脚本的最好方法,我们可以使用:
f = open(filename, 'r')
exec f
f.close()
这等价于 execfile(filename)
虽然上述代码执行了一个模块,但是仅仅是在现有的执行环境下运行(比如它自己的全局和局部的名称空间)。在某些情况下可能需要不同全局和局部的名称空间。execfile(filename, globals=globals(), locals=locals())中的globals和locals参数是可选的,如果只给出了globals的值那么locals默认值也与globals一致。如果提供了locals的值,那么这个值必须是一个字典。(由于execfile不会确保不修改局部名称空间,所以在修改时的可靠做法是穿入一个虚假的locals字典以进行检查)。
14.4.3 将模块作为脚本执行
(略)
14.5 执行其他非Python程序
(略)
14.6受限执行
(略)
14.7结束执行
Python中主要有两种方法应对错误,其一是通过异常和异常处理,其二是把处理错误的代码放入if中。
14.7.1 sys.exit() and SystemExit
在调用sys.exit()时,就会引发systemExit()异常。除非对异常进行监控(放在try和except之中),否则异常通常是不会被捕捉或处理的,解释器会用给定的状态参数推出,如果没有给出这个状态参数,该参数默认为0。SystemExit是唯一不被看作错误的异常。
14.7.2 sys.exitfunc()
sys.exitfunc()默认是不可用的,但你可以改写它以提供额外的功能。
14.7.3os._exit()函数
os模块的_exit()函数不应该在一般应用中使用,这个函数的状态参数是必须的,并且不会执行任何清理便立即推出Python。
以下略。