14.1 可调用对象

许多的Python对象都是我们所说的可调用的,即是任何能通过函数操作符“()”来调用的对象。要调用可调用对象,函数操作符得紧跟在可调用对象之后。比方说,用“foo()”来调用函数“foo”。可调用对象可以通过函数式编程接口来进行调用,如apply()、filter()、map()和reduce(),这4个接口我们都在11章讨论过了。Python有4种可调用对象:函数、方法、类、以及一些类的实例。记住这些对象的任何引用或者别名都是可调用的。

14.1.1 函数

我们介绍的第一种可调用的对象是函数。Python有三种不同类型函数对象。第一种是内建函数。

1.内建函数(BIF)

内建函数(Built-in Function, BIF)是用C/C++写的,编译过后放入Python解释器,然后把它们作为第一(内建)名称空间的一部分加载进系统。如前面章节所提到的,这些函数在bulitin模块里,并作为builtins模块导入到解释器中。

14.1 可调用对象 - 图1

BIF有基础类型属性,其中一些独特的属性已列在表14.1中。

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

14.1 可调用对象 - 图2

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

14.1 可调用对象 - 图3

注意这不能应用于工厂函数,因为type()正好会返回产生对象的类型:

14.1 可调用对象 - 图4

2.用户定义的函数(UDF)

用户定义的函数(User-Defined Function, UDF)通常是用Python写的,定义在模块的最高级,因此会作为全局名称空间的一部分(一旦创建好内建名称空间)装载到系统中。函数也可在其他的函数体内定义,并且由于在2.2中嵌套作用域的改进,我们现在可以对多重嵌套作用域中的属性进行访问。可以用func_closure属性来钩住在其他地方定义的属性。

如同上面的BIF,UDF也有许多的属性。UDF最让人感兴趣和最特殊的属性都列在下面的表14.2中。

14.1 可调用对象 - 图5

从内部机制来看,用户自定义的函数是“函数”类型的,如在下面的例子中用type()表明的一样:

14.1 可调用对象 - 图6

  1. lambda表达式(名为“<lambda>”的函数)

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

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

通过lambda来创建函数的对象除了没有命名之外,享有和用户自定义函数相同的属性;name或者func__name属性给定为字符串“<lambda>”。使用type()工厂函数,我们来演示下lambda表达式返回和用户自定义函数相同的函数对象。

14.1 可调用对象 - 图7

在上面的例子中,我们将表达式赋值给一个别名。我们也可以直接在一个lambda表达式上调用type():

14.1 可调用对象 - 图8

我们快速的来看看UDF名字,使用上面的lambda Func和先前小节中的foo():

14.1 可调用对象 - 图9

从11.9小节中我们可以看到,一旦函数声明以后(且函数对象可用),程序员也可以自定义函数属性。所有的新属性变成udf.dict对象的一部分。在本章的稍后内容中,我们将讨论获取含有Python代码的字符串并执行该代码。到了本章最后,会有一个组合例子,着重描写函数属性和Python代码(字符串)的动态求值和执行语句。

14.1.2 方法

在第13章中,我们研究了方法。用户自定义方法是被定义为类的一部分函数。许多Python数据类型,比如列表和字典,也有方法,这些被称为内建方法。为了进一步说明“所有权”的类型,方法通过对象的名字和句点属性标识进行命名。

14.1 可调用对象 - 图10

1.内建方法(BIM)

在前面的小节中,我们讨论了内建方法与内建函数的类似之处。只有内建类型(built-in type, BIT)有内建方法(built-in Method, BIM)。正如你在下面看到的,对于内建方法,type()工厂函数给出了和BIF相同的输出——注意,我们是如何提供一个内建对象来访问BIM:

14.1 可调用对象 - 图11

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

对于类和实例,都能以该对象为参数,通过内建函数dir()来获得他们的数据和方法属性。这也可以用在BIM上:

14.1 可调用对象 - 图12

然而,不用多久就会发现,从功能上看,用实际的对象去访问其方法并不是非常有用,如最后的例子。由于没有引用来保存这个对象,所以它立即被垃圾回收了。你处理这种访问的类型唯一的用处就是显示BIT有什么方法。

2.用户定义的方法(UDM)

UDM (User-defined method,用户定义的方法)包含在类定义之中,只是拥有标准函数的包装,仅有定义它们的类可以使用。如果没有在子类定义中被覆盖掉,也可以通过子类实例来调用它们。正如在13章解释的那样,UDM与类对象是关联的(非绑定方法),但是只能通过类的实例来调用(绑定方法)。无论UDM是否绑定,所有的UMD都是相同的类型——“实例方法”,如在下面例子看到的type()调用:

14.1 可调用对象 - 图13

表11.4中展示了UDM的属性。访问对象本身将会揭示你正在引用一个绑定方法还是非绑定方法。正如你从下面看到的,绑定的方法揭示了方法绑定到哪一个实例。

14.1 可调用对象 - 图14

14.1 可调用对象 - 图15

14.1 可调用对象 - 图16

14.1.3 类

我们可以利用类的可调用性来创建实例。“调用”类的结果便是创建了实例,即大家所知道的实例化。类有默认构造器,该函数什么都不做,基本上只有一个pass语句。程序员可以通过实现int()方法,来自定义实例化过程。实例化调用的任何参数都会传入到构造器里。

14.1 可调用对象 - 图17

我们已经很熟悉实例化过程以及它是如何完成的,在这里将不再赘述。不过,一个新的问题是如何让实例能够被调用。

14.1.4 类的实例

Python给类提供了名为call的特别方法,该方法允许程序员创建可调用的对象(实例)。默认情况下,call()方法是没有实现的,这意味着大多数实例都是不可调用的。然而,如果在类定义中覆盖了这个方法,那么这个类的实例就成为可调用的了。调用这样的实例对象等同于调用call()方法。自然地,任何在实例调用中给出的参数都会被传入到call()中。那么foo()就和foo.call(foo)的效果相同,这里foo也作为参数出现,因为是对自己的引用,实例将自动成为每次方法调用的第一个参数。如果call()有参数,比如(self, arg),那么foo(arg)就和调用foo.call(foo,arg)—样。这里我们给出一个可调用实例的例子,和前面小节的例子相似:

14.1 可调用对象 - 图18

14.1 可调用对象 - 图19

记住只有定义类的时候实现了call方法,类的实例才能成为可调用的。