3.1.2 继承与派生 在子类中重用父类的方法
继承是类与类之间的关系, 什么是什么
总结对象之间的相似点成类
总结类之间的相似点成父类
类从(子)下到(父)上,是抽象。类从上到下,是继承,类到对象实例化。
# class ParentClass1: # pass # # class ParentClass2: # pass # # class SubClass1(ParentClass1): # pass # # class SubClass2(ParentClass1,ParentClass2): # pass # # print(SubClass1.__bases__) # print(SubClass2.__bases__) class Hero: x=3 def __init__(self,nickname,life_value,aggresivity): self.nickname=nickname self.life_value=life_value self.aggresivity=aggresivity def attack(self,enemy): enemy.life_value-=self.aggresivity class Garen(Hero): # x=2 pass class Riven(Hero): pass g1=Garen('刚们',29,30) # print(g1.nickname,g1.life_value,g1.aggresivity) # g1.x=1 print(g1.x) #属性查找小练习 class Foo: def f1(self): print('from Foo.f1') def f2(self): print('from Foo.f2') self.f1() #b.f1() 继续从对象本身开始找 class Bar(Foo): def f1(self): print('from Bar.f1') b=Bar() # print(b.__dict__) b.f2()
输出:
from Foo.f2
from Bar.f1
属性查找: 先从对象自己这里查找,没有再去类里去找,类再没有,去父类找。
派生:
子类可以添加自己新属性或者在自己这里重新定义这些属性(不会影响父类),
需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新的属性时,就以自己为准了。
class Hero: def __init__(self,nickname,life_value,aggresivity): self.nickname=nickname self.life_value=life_value self.aggresivity=aggresivity def attack(self,enemy): enemy.life_value-=self.aggresivity class Garen(Hero): camp='Demacia' def attack(self,enemy): print('from Garen Class') class Riven(Hero): camp='Noxus' g=Garen('草丛伦',100,30) r=Riven('锐雯雯',80,50) # print(g.camp) # g.attack(r) # print(r.life_value) g.attack(r)
继承的实现原理:
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1. 子类会先于父类检查
2 多个父类会根据它们在列表中的顺序被检查
3 如果下一个类存在两个合法的选择,选择第一个父类
新式类中每个类都有一个mro方法。可以查出继承的顺序。
但经典类没有mro这个方法。
如:f = F()
print(f.__mro__) #只有新式类才有
在Java和C#中子类中能继承一个父类,而Python中子类可以同时继承多个父类,如果继承了多个父类,那么属性的查找方式有两种,分别是:深度优先和广度优先。
Python2中有分经典类和新式类。Python3都是新式类。
python2中 经典类: 没有继承object类以及子类,都称为经典类。这个类的子类也叫经典类。
class Foo: pass class Bar(Foo): pass
python2:新式类: 继承了object类以及子类,都称为新式类。
class Foo(object): pass class Bar(Foo): pass print(Bar.__base__)
Python3:默认都继承了object类
class Foo(): pass print(Foo.__base__) 输出:<class: object>
当类是经典类时,多继承情况下,在要查找属性不存在时,会按照深度优先的方式查找下去。
#验证多继承情况下的属性查找 class A: # def test(self): # print('from A') pass class B(A): # def test(self): # print('from B') pass class C(A): # def test(self): # print('from C') pass class D(B): # def test(self): # print('from D') pass class E(C): # def test(self): # print('from E') pass class F(D,E): # def test(self): # print('from F') pass #F,D,B,E,C,A print(F.mro()) # f=F() # f.test()
实验:如果C没有继承A,新式类会不会是F D B A E C ??
在子类中重用父类的方法
在子类派生出的新的方法中重用父类的方法,有两种实现方式
方式一:指名道姓(不依赖继承)
方式二:super() (依赖继承)
在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该是调用普通函数的方式,即:类名.func(),此时就与调用函数无异了,因此即便是self参数也要为其传值 。
方式一示例:
class Hero: def __init__(self,nickname,life_value,aggresivity): self.nickname=nickname self.life_value=life_value self.aggresivity=aggresivity def attack(self,enemy): enemy.life_value-=self.aggresivity class Garen(Hero): camp='Demacia' def __init__(self,nickname,life_value,aggresivity,weapon): # self.nickname=nickname # self.life_value=life_value # self.aggresivity=aggresivity Hero.__init__(self,nickname,life_value,aggresivity) #也是指名道姓,不依赖继承 self.weapon=weapon def attack(self,enemy): Hero.attack(self,enemy) #指名道姓,不依赖于继承。如果没有继承Hero也是可以的。 print('from Garen Class') g=Garen('草丛伦',100,30,'金箍棒') print(g.__dict__)
方式二示例:
class Hero: # def __init__(self,nickname,life_value,aggresivity): # self.nickname=nickname # self.life_value=life_value # self.aggresivity=aggresivity # def attack(self,enemy): # enemy.life_value-=self.aggresivity # # # class Garen(Hero): # camp='Demacia' # # def __init__(self,nickname,life_value,aggresivity,weapon): # # self.nickname=nickname # # self.life_value=life_value # # self.aggresivity=aggresivity # # # super(Garen,self).__init__(nickname,life_value,aggresivity) #python2 # super().__init__(nickname,life_value,aggresivity) #python3可以这样简写 # self.weapon=weapon # # def attack(self,enemy): # super(Garen,self).attack(enemy) #依赖继承, super(Garen,self)是对象,所以self参数不用传。 # print('from Garen Class') # # # g=Garen('草丛伦',100,30,'金箍棒') # # print(g.__dict__)
super()沿着MRO列表,是基于调用对象的MRO往后找。而不是调用对象本身再开始找。
class A: def f1(self): print('from A') super().f1() #c对象有MRO往后找。 class B: def f1(self): print('from B') class C(A,B): pass print(C.mro()) #[<class '__main__.C'>, # <class '__main__.A'>, # <class '__main__.B'>, # <class 'object'>] c=C() c.f1()
输出:
from A
from B