0.说明
作为《Python核心编程》核心部分的最后一章,这篇的内容也相当重要。对于高级部分的整理,将采用《Python核心编程》第三版,但是,方式会以之前的完全不一样了。
1.可调用对象
可调用对象即可通过函数操作符“()”来调用的对象,也可以通过函数式编程接口来进行调用,如apply()、filter()、map()和reduce()。Python有4种可调用对象:函数、方法、类和一些类的实例。
(1)函数
Python中有三种不同类型的函数:内建函数(BIF)、用户定义的函数(UDF)和lambda表达式。
- 内建函数(BIF)
用C/C++写的,编译过后放入Python解释器中,然后把它们作为第一(内建)名称空间的一部分加载进系统,这些函数在__builtin__模块里,并作为__builtins__模块导入到解释器中。
可以使用dir()来列出函数的所有属性,另外从内部机制来看,BIF和内建方法属于相同的类型,所以全长type()调用的结果是:
>>> type(dir) <type ‘builtin_function_or_method‘> >>> >>> type(‘xpleaf‘.upper) <type ‘builtin_function_or_method‘>
但是应用于工厂函数,得到的结果是不一样的,虽然这些工厂函数本身是内建函数:
>>> type(int) <type ‘type‘>
那是因为,本质上这些工厂函数是类。
- 用户定义的函数(UDF)
用户定义的函数通常是用Python写的,调用type()的结果如下:
>>> def foo():pass ... >>> type(foo) <type ‘function‘>
UDF本身也有很多属性:
>>> dir(foo) [‘__call__‘, ‘__class__‘, ‘__closure__‘, ‘__code__‘, ‘__defaults__‘, ‘__delattr__‘, ‘__dict__‘, ‘__doc__‘, ‘__format__‘, ‘__get__‘, ‘__getattribute__‘, ‘__globals__‘, ‘__hash__‘, ‘__init__‘, ‘__module__‘, ‘__name__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘func_closure‘, ‘func_code‘, ‘func_defaults‘, ‘func_dict‘, ‘func_doc‘, ‘func_globals‘, ‘func_name‘]
- lambda表达式(名为“<lambdda>”的函数)
lambda返回一个函数对象,只是lambda表达式没有给命令绑定的代码提供基础结构,所以要通过函数式编程接口来调用,或把它们的引用赋值给一个变量,通过该变量来进行调用,只是需要注意的是,这个变量仅仅是个别名,并不是函数对象的名字。
通过lambda来创建的函数对象除了没有命名外,和用户定义的函数是有相同的属性的,当然,对于__name__属性,值为"<lambda>":
>>> lambdaFunc = lambda x: x*2 >>> lambdaFunc(100) 200 >>> type(lambdaFunc) <type ‘function‘> >>> >>> lambdaFunc.__name__ ‘<lambda>‘ >>> >>> foo.__name__ ‘foo‘
(2)方法
用户自定义方法是被定义为类的一部分函数,而许多Python数据类型本身也有方法,这些方法被称为内建方法。
- 内建方法(BIM)
只有内建类型有内建方法,如下:
>>> type([].append) <type ‘builtin_function_or_method‘>
BIM和BIF两者享有相同的属性,只是不同的是,BIM的__self__属性指向一个Python对象,而BIF指向None:
>>> dir([].append) [‘__call__‘, ‘__class__‘, ‘__cmp__‘, ‘__delattr__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__le__‘, ‘__lt__‘, ‘__module__‘, ‘__name__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__self__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘] >>> [].append.__self__ [] >>> dir.__self__ >>> print [].append.__self__ [] >>> print dir.__self__ None
- 用户定义的方法(UDM)
所有的UDM都是相同的类型,即“实例方法”,无论该方法是否有绑定的实例:
>>> class C(object): ... def foo(self): ... pass ... >>> c = C() >>> >>> type(C.foo) <type ‘instancemethod‘> >>> type(c.foo) <type ‘instancemethod‘>
只是访问对象本身会揭示该方法是否绑定:
>>> c.foo <bound method C.foo of <__main__.C object at 0x7f37a3c45810>> >>> C.foo <unbound method C.foo>
(3)类
调用类,其实就是创建一个实例对象。类有默认构造器,但是这个函数什么都不做,可以通过实现__init__()方法来自定义实例化过程。
(4)类的实例
Python给类提供了名为__call__的特别方法,该方法允许程序创建可调用的对象(实例)。默认情况下没有实现该方法,因此实例是不可调用的。
在定义类时覆盖__call__方法即可达到实例可调用的效果,这样,调用实例对象就等同于调用__call__()方法,参数的设置跟类的构造器原理是一样的:
>>> class C(object): ... def __call__(self, *args): ... print "I‘m callable! Called with args:\n", args ... >>> c = C() >>> c <__main__.C object at 0x7f37a3c2d090> >>> callable(c) # 实例是可调用的 True >>> c() # 调用实例 I‘m callable! Called with args: () >>> c(3) # 传入参数 I‘m callable! Called with args: (3,) >>> c(3, ‘no more , no less‘) I‘m callable! Called with args: (3, ‘no more , no less‘) >>> >>> c.__call__(3) # 与c(3)的调用原理是一样的 I‘m callable! Called with args: (3,) >>> c.__call__(3, ‘no more, no less‘) I‘m callable! Called with args: (3, ‘no more, no less‘)
2.代码对象
每个可调用物的核心都是代码对象,由语句、赋值、表达式和其他可调用物组成。查看一个模块意味着观察一个较大的、包含了模块中所有代码的对象。然后代码可以分成语句、赋值、表达式,以及可调用物。可调用物又可以递归分解到下一层,那儿有自己的代码对象。
一般来说,代码对象可以作为函数或者方法调用的一部分来执行(比如语句或赋值等),也可用exec语句或内建函数eval()来执行。从整体上看,一个Python模块的代码对象是构成该模块的全部代码。
如果要执行Python代码,那么该代码必须先要转换成字节编译的代码(又称字节码)。这才是真正的代码对象。然而,它们不包含任何关于它们执行环境的信息,这便是可调用物存在的原因,它被用来包装一个代码对象并提供额外的信息(如属性等相关信息)。
函数对象仅是代码对象的包装,方法则是给函数对象的包装,只是不同于单纯的代码对象,它们还提供了一些额外的信息。当研究到最底层,便会发现这是一个代码对象。
在函数对象中,有一个func_code属性,其实就是指代码对象:
>>> def foo(): pass ... >>> foo.func_code <code object foo at 0x7f37a5daceb0, file "<stdin>", line 1>