13.7 绑定和方法调用

现在我们需要再次阐述Python中绑定(binding)的概念,它主要与方法调用相关连。我们先来回顾一下与方法相关的知识。首先,方法仅仅是类内部定义的函数(这意味着方法是类属性而不是实例属性)。

其次,方法只有在其所属的类拥有实例时,才能被调用。当存在一个实例时,方法才被认为是绑定到那个实例了。没有实例时方法就是未绑定的。

最后,任何一个方法定义中的第一个参数都是变量self,它表示调用此方法的实例对象。

13.7 绑定和方法调用 - 图1核心笔记:self是什么?

self变量用于在类实例方法中引用方法所绑定的实例。因为方法的实例在任何方法调用中总是作为第一个参数传递的,self被选中用来代表实例。你必须在方法声明中放上self(你可能已经注意到了这点),但可以在方法中不使用实例(self)。如果你的方法中没有�用到self,那么请考虑创建一个常规函数,除非你有特别的原因。毕竟,你的方法代码没有使用实例,没有与类关联其功能,这使得它看起来更像一个常规函数。在其他面向对象语言中,self可能被称为this。

13.7.1 调用绑定方法

方法,不管绑定与否,都是由相同的代码组成的。唯一的不同在于是否存在一个实例可以调用此方法。在很多情况下,程序员调用的都是一个绑定的方法。假定现在有一个MyClass类和此类的一个实例mc,而你想调用MyClass.foo()方法。因为已经有一个实例,你只需要调用mc.foo()就可以。记得self在每一个方法声明中都是作为第一个参数传递的。当你在实例中调用一个绑定的方法时,self不需要明确地传入了。这算是“必须声明self作为第一个参数”对你的报酬。当你还没有一个实例并且需要调用一个非绑定方法的时候你必须传递self参数。

13.7.2 调用非绑定方法

调用非绑定方法并不经常用到。需要调用一个还没有任何实例的类中的方法的一个主要的场景是:你在派生一个子类,而且你要覆盖父类的方法,这时你需要调用那个父类中想要覆盖掉的构造方法。这里是一个本章前面介绍过的例子:

13.7 绑定和方法调用 - 图2

EmplAddrBookEntry是AddrBookEntry的子类,我们重载了构造器init()。我们想尽可能多地重用代码,而不是去从父类构造器中剪切,粘贴代码。这样做还可以避免bug传播,因为任何修复都可以传递给子类。这正是我们想要的——没有必要一行一行地复制代码。只需要能够调用父类的构造器即可,但该怎么做呢?

我们在运行时没有AddrBookEntry的实例。那么我们有什么呢?我们有一个EmplAddrBookEntry的实例,它与AddrBookEntry是那样地相似,我们难道不能用它代替呢?当然可以!

当一个EmplAddrBookEntry被实例化,并且调用init()时,其与AddrBookEntry的实例只有很少的差别,主要是因为我们还没有机会来自定义我们的EmplAddrBookEntry实例,以使它与AddrBookEntry不同。

这是调用非绑定方法的最佳地方了。我们将在子类构造器中调用父类的构造器并且明确地传递(父类)构造器所需要的self参数(因为我们没有一个父类的实例)。子类中init()的第一行就是对父类init()的调用。我们通过父类名来调用它,并且传递给它self和其他所需要的参数。一旦调用返回,我们就能定义那些与父类不同的仅存在我们的(子)类中的(实例)定制。