第7.14节 Python类中的实例方法详析
一、 实例方法的定义
在本章前面章节已经介绍了类的实例方法,实例方法的定义有三种方式:
1. 类体中定义实例方法
第一种方式很简单,就是在类体中定义,所有在类中定义的方法默认是实例方法。定义实例方法与定义函数基本相同,只是Python要求实例方法的第一个形参必须为self,也就是实例对象本身,因此实例方法至少应该有一个self参数。关于self的说明,请大家参考老猿前面的章节《第7.5节 揭开Python类中self的面纱》。如下例子:
class MethodTest():
def method1(self):print("in method1")
2. 在类体外将一个函数直接赋值给一个对象实例
Python 是动态语言,允许为对象动态增加方法,相关步骤如下:
1) 按照实例方法定义形式在类体外定义一个函数,函数需要将self作为第一个参数;
2) 使用“实例对象名.方法名=函数”方式在实例中增加方法。
老猿认为,这种动态定义实例方法,本质上就是动态增加实例变量,只是这个实例变量比较特殊,是个函数类型,对应的赋值也是一个函数。因此上述方法定义的实例方法,与类体中定义的方法还是有差别的,对这种方法Python 不会将实例对象自动绑定到方法的第一个参数,即使将第一个参数命名为 self 也没有用。
3. 在类体外将一个函数绑定到对象实例
这种方法与第二种方法本质上是一致的,只是能解决调用时无法自动将实例对象作为第一个实参处理的问题。具体绑定方法如下:
1) 按照实例方法定义形式在类体外定义一个函数,函数需要将self作为第一个参数;
2) 使用“实例对象名.方法名 = MethodType(函数, 对象)” 方式在实例中增加方法。
其中MethodType是从Python的types模块import进来的,通过MethodType可以将函数与实例对象的某个方法进行绑定,调用时就无需再传递实例对象到第一个参数。
上述第二种和第三种方法都是动态方法,绑定的“实例对象名.方法名”可以是一个已经存在的实例方法,也可以是新定义的一个属性方法,如果是一个已经存在的实例方法,新绑定的函数将替换原实例的方法。
二、 实例方法的使用
1. 实例方法可以在类体的实例方法内调用,此时调用方使用“self.方法”方式调用,并在调用传递实参时不需要使用self。请看如下类的定义和调用:
class MethodTest():
def method1(self):print("in method1")
def method2(self):
print("in method2")
self.method1()
上述代码中method2调用了方法method1,使用self方式调用。
2. 在类体外面调用类体内直接定义的实例方法,直接用“实例名.方法名”方式调用,对于类体内定义的实例方法传实参时无需传递self形参对应的实参,由Python在编译时自动添加实例对象作为第一个实参。请看上面的类定义实例对象后调用method2:
m1=MethodTest()
m1.method2()
上述样例代码执行截图:
注意:在类外调用实例方法时,无需也不能传递self的实参,由Python解释器自动为其在后面执行时添加。如果定义的方法没有以类方法的方式定义,也没有在方法定义时将self作为第一个形参,编译时并不会报错,但调用该实例方法时会有运行时错误,因为Python会强行绑定实例对象作为第一个参数,因此调用者的实参与定义的形参是一样时,此时会多出一个实参self,导致执行时报参数个数不对的错误。
3. 在类体外面调用通过函数直接赋值定义的实例方法时,其调用方式与类体内定义的模式不同,要求第一个实参必须是调用的对象本身;
以上面的类MethodTest为例,如果要增加一个method3的实例方法,其代码如下:
def dynamicmethond(self):print("in DynamicMethond")
m1.method3=dynamicmethond
这样m1这个实例就增加了一个method3方法,调用代码如下:
m1.method3(m1) #注意必须在第一个参数将实例自身传递进去
4. 在类体外面调用通过MethodType绑定的实例方法时,其调用方式与类体内定义的模式相同,第一个形参self对应的实参无需传值,由Python自动添加对应的实例对象;
以上面的类MethodTest为例,如果要增加一个method4的实例方法进行绑定,其代码如下:
def dynamicmethond(self):print("in DynamicMethond")
m1.method4= MethodType(dynamicmethond,m1)
这样m1这个实例就增加了一个method4方法,且实现了方法与实例的绑定,调用代码如下:
m1.method4()
三、 构造方法
之所以单独介绍构造方法,是因为构造方法是一个特殊的实例方法,具体特殊性包括如下:
1. Python的构造方法对所有自定义类名字都固定为__init__,且第一个参数必须为self;
2. 可以允许开发者没有在类中定义构造方法,此时Python 会自动为该类定义一个只包含一个 self 参数的默认的构造方法;
3. 构造方法如果有多个形参,除了第一个参数必须是self外,其他参数都是创建实例时在类名后面的括号中给出,也就构造方法的参数就是创建实例时传入的参数,也必须是创建实例时传入;
4. 构造方法是每个实例定义时自动执行;
5. 在子类重写构造方法时,必须调用超类(继承的类)的构造方法,否则可能无法正确地初始化对象。
1) Python3中所有类都继承于object类,构造函数的重写不用考虑object类的情况,即无需调用object类的构造函数;
2) 在构造方法中调用父类的构造方法时,可以使用“父类.类名.__init__(self,其他参数)”的方式调用;
3) 在构造方法中调用父类的构造方法时,可以使用“super().__init__ (参数)”的方式调用,注意此时的参数中不能传递self参数。但这种情况如果存在多个超类时,执行的是第一个超类的构造方法,其实参必须按照第一个超类构造方法的形参来传递,其他超类的构造方法必须通过“父类.类名.__init__(self,其他参数)”的方式调用。
理解了这几点,结合其他语言的构造方法,就理解了Python的构造方法。
四、 本节相关代码的完整执行截屏
本节结合案例详细介绍了实例方法的定义和使用方法,需要注意,动态定义实例方法只影响单个实例自身,对其他实例没有影响。老猿认为动态定义实例方法的使用范围比较有限,如果可能尽量使用类体定义实例方法,仅在特定场景(如动态扩展功能)下使用动态实例方法。
老猿Python(https://blog.csdn.net/LaoYuanPython)系列文章用于逐步介绍老猿学习Python后总结的学习经验,这些经验有助于没有接触过Python的程序员可以很容易地进入Python的世界。
欢迎大家批评指正,谢谢大家关注!