Python构造函数详解及使用方法
构造函数
构造函数就是为了让我们再实例化对象的时候能够实现个性化定制。
1. __init__()
大家可以理解为在实例化对象之前就会自动执行的函数,如:
class A(): #定义类A
def __init__(self,x,y): #创建构造方法__init__()
#等号左边是绑定到实例化对象里面的属性值,等号右边是传进来的参数
self.x = x
self.y = y
def add(self): #返回x,y的和
return self.x + self.y
def mul(self): #返回x,y的积
return self.x * self.y
a = A(2,3)
a.add()
# 5
a.mul()
# 6
a.__dict__ #查看实例对象a中的数据
# {'x': 2, 'y': 3}
b = A(4,5)
b.add()
# 9
b.mul()
# 20
b.__dict__ #查看实例对象b中的数据
# {'x': 4, 'y': 5}
上述代码中,我们并没有调用__init__()方法,然而我们却得到了传进来的值,说明在我们调用方法之前就已经运行__init__()方法了。
2. 重写
有构造函数的类的继承:
class A(): #定义类A
def __init__(self,x,y): #创建构造方法__init__()
#等号左边是绑定到实例化对象里面的属性值,等号右边是传进来的参数
self.x = x
self.y = y
def add(self): #返回x,y的和
return self.x + self.y
def mul(self): #返回x,y的积
return self.x * self.y
class B(A): #定义类B,并且继承类A
def __init__(self,x,y,z):
A.__init__(self,x,y) #继承父类的方法
self.z = z
def add(self):
return A.add(self) + self.z
def mul(self):
return A.add(self) * self.z
b = B(1,2,3)
b.add()
# 6
b.mul()
# 9
通过上述代码可以看出我们直接通过 父类类名.方法名 重新父类的方法是可以达到效果的,不过这种方法会导致钻石继承。
3. 钻石继承
所谓钻石继承是指,两个类同时继承了一个父类,并且这两个类又同时被一个子类继承。
如下图,B1、B2 同时继承类A,而B1、B2 又同时被被C继承。
可是钻石继承会导致什么后果呢,我们举个例子:
class Grandmother: #定义Grandmother类
def __init__(self):
print("我是奶奶,我进动物园了,我要买票!")
class Father(Grandmother): #定义Father类,并且继承类Grandmother
def __init__(self):
Grandmother.__init__(self)
print("我是爸爸,我进动物园了,我要买票!")
class Uncle(Grandmother): #定义Uncle类,并且继承类Grandmother
def __init__(self):
Grandmother.__init__(self)
print("我是叔叔,我进动物园了,我要买票!")
class Me(Father,Uncle): #定义Me类,并且继承类Father和Uncle
def __init__(self):
Father.__init__(self)
Uncle.__init__(self)
print("我是小明,我进动物园了,我要买票!")
m = Me()
运行结果:
我是奶奶,我进动物园了,我要买票!
我是爸爸,我进动物园了,我要买票!
我是奶奶,我进动物园了,我要买票!
我是叔叔,我进动物园了,我要买票!
我是小明,我进动物园了,我要买票!
因为Father和Uncle同时继承了Grandmother类,导致Grandmother类中的__init__()在整个程序中被访问了两次,对于变成来说变得不严谨了,就相当于因为奶奶同时使爸爸和叔叔的妈妈,所以奶奶要买两张票才能进动物园一样。
有什么可以钻石继承的问题呢,这里就使用到了super()函数。
4. super()
super()可以在父类中搜索指定的方法,并自动绑定好self参数,避免重复调用的问题。
class Grandmother:
def __init__(self):
print("我是奶奶,我进动物园了,我要买票!")
class Father(Grandmother):
def __init__(self):
super().__init__() #使用super函数
print("我是爸爸,我进动物园了,我要买票!")
class Uncle(Grandmother):
def __init__(self):
super().__init__() #使用super函数
print("我是叔叔,我进动物园了,我要买票!")
class Me(Father,Uncle):
def __init__(self):
super().__init__() #使用super函数
print("我是小明,我进动物园了,我要买票!")
m = M()
运行结果:
这里奶奶只买了一次票,说明使用super可以避免重复调用的问题。
我是奶奶,我进动物园了,我要买票!
我是叔叔,我进动物园了,我要买票!
我是爸爸,我进动物园了,我要买票!
我是小明,我进动物园了,我要买票!
5.MRO顺序
prthon类是支持(多)继承的,一个类的方法和属性可能定义在当前类,也可能定义在基类。针对这种情况,当调用类方法或类属性时,就需要对当前类以及它的基类进行搜索,以确定方法或属性的位置,而搜索的顺序就称为方法解析顺序。
对于初学者来说,MRO顺序比较难理解,这里我们可以使用两种方法来查找MRO。
1. 通过mro()方法
Me.mro()
[<class '__main__.Me'>, <class '__main__.Father'>, <class '__main__.Uncle'>, <class '__main__.Grandmother'>, <class 'object'>]
根据运行结果我们可以清晰地看出搜索顺序,这里注意,对于<class 'object'>,我们在上述代码中并没有定义,可是通过mro方法却能检测出来,这是因为object使所有类的基类,无论什么情况,都会自动调用的。
2. 通过mro属性 __mro__
Me.__mro__
(<class '__main__.Me'>, <class '__main__.Father'>, <class '__main__.Uncle'>, <class '__main__.Grandmother'>, <class 'object'>)