面向对象 继承 (inheritance)
面向对象 继承 (inheritance)
面向对象的继承
继承定义
- 继承(英语:inheritance)是面向对象软件技术当中的一个概念 / 一种创建新类的方式
- B类 继承 A类 , B类 叫做 A类的 子类 / 派生类 , A类 叫做 B类 的父类 / 基类 / 超类
- B类以及B类的对象 使用A类的所有的属性及方法
优点
- 增加了类的耦合性(耦合性不宜多,宜精)/ 耦合性太高是缺点
- 减少了重复代码 / 节省代码
- 使得代码更加规范化,合理化 / 规范化代码
- 重写父类代码 / 覆盖父类代码
class Person:
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex
class Cat:
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex
class Dog:
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex
# 继承的用法:
class Aniaml(object): # Aminal 叫做父类 / 基类 / 超类(对于下面的子类来说)
# object 是Animal的父类
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex
class Person(Aniaml): # Person Cat Dog 子类 / 派生类
pass
class Cat(Aniaml):
pass
class Dog(Aniaml):
pass
继承的分类
分类 : 单继承 / 多继承
单继承
子类 执行 / 调用 父类 的方法
对象 执行 父类 的一切 ***
- 注意: 子类以及子类对象只能调用父类的属性以及方法,不能操作(增删改).
# 类名,对象分别调用父类方法
class Aniaml(object): # Aminal 叫做父类 / 基类 / 超类
type_name = '动物类'
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex
def eat(self):
print(self)
print('吃东西')
class Person(Aniaml): # 子类 / 派生类 下面这三个都是属于子类调用父类
pass
class Cat(Aniaml): # 子类 / 派生类
pass
class Dog(Aniaml): # 子类 / 派生类
pass
# 子类 执行 (调用) 父类 的 属性 / 方法:
print(Person.type_name) # 可以调用父类的属性,方法。
Person.eat(111)
print(Person.type_name)
# 对象:
# 实例化对象
p1 = Person('春哥','男',18)
print(p1.__dict__)
# 对象执行类的父类的属性,方法。
print(p1.type_name)
p1.type_name = '666'
print(p1)
p1.eat()
# 注意: 子类以及子类对象只能调用父类的属性以及方法,不能操作(增删改).
执行顺序
class Aniaml(object):
type_name = '动物类'
def __init__(self,name,sex,age):
self.name = name
self.age = age
self.sex = sex
def eat(self):
print(self)
print('吃东西')
class Person(Aniaml):
def eat(self): # 子类将父类的方法覆盖了,(重写父类的方法)
print('%s 吃饭'%self.name)
class Cat(Aniaml):
pass
class Dog(Aniaml):
pass
p1 = Person('barry','男',18)
# 实例化对象时必须执行__init__方法,类中没有,从父类找,父类没有,从object类中找。
p1.eat()
# 先要执行自己类中的eat方法,自己类没有才能执行父类中的方法。
实例化对象时必须执行__init__
方法, 本类中没有,从父类找,父类没有,从object类中找
先要执行自己类中的方法,自己类没有才能执行父类中的方法。
同时执行类及父类方法
方法一
- 如果想执行父类的func方法,这个方法并且子类中也用,那么就在子类的方法中写上:
父类.func(对象,其他参数)
方法二
- 利用super,super().func(参数)
class Animal:
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
def eat(self):
print("动物都需要进食")
class Person(Animal):
def __init__(self,name,age,gender,hobby):
# 方法一
Animal.__init__(self,name,age,gender)
# 方法二
super().__init__(name,age,gender) # 相当于super(Person, self).__init__(name, age, sex)
def eat(self):
print("人类需要吃饭")
super().eat()
p1 = Person('怼怼哥', 23, '不详','吹牛逼')
p1.eat()
# "人类需要吃饭"
# "动物都需要进食"
多继承
class God: # 神仙
def fei(self):
print("神仙都会⻜")
class Monkey: # 猴
def chitao(self):
print("猴⼦喜欢吃桃⼦")
class SunWukong(God, Monkey): # 孙悟空是神仙, 同时也是⼀只猴 即 多继承
pass
sxz = SunWukong() # 孙悟空
sxz.chitao() # 会吃桃⼦
sxz.fei() # 会⻜
多继承中, 存在着这样⼀个问题. 当两个⽗类中出现了重名⽅法的时候 , 查找⽗类⽅法 , 即MRO(method resolution order) 问题. 在python中这是⼀个很复杂的问题. 因为在不同的python版本中使⽤的是不同的算法来完成MRO的.
python中类的种类(继承需要):
python2x版本中存在两种类
- ⼀个叫经典类. 在python2.2之前. ⼀直使⽤的是经典类. 经典类在基类的根如果什么都不写会报错
- 经典类: 基类不继承object,查询规则 依靠:深度优先的原则.
- 记住⼀个原则. 在经典类中采⽤的是 深度优先遍历⽅案 / 即: ⼀条路走到头. 然后再回来. 继续找下⼀个
- ⼀个叫新式类. 在python2.2之后出现了新式类. 新式类的特点是基类继承object类 , 新式类比经典类执行效率高
# python2x
class A: # 经典类
pass
class B(object): # 新式类
pass
python3x版本中只有一种类 / 新式类
- python3中使⽤的都是 新式类. 如果基类谁都不继承. 那这个类会默认继承 object
- object是最上面的类, python3以后默认了object,不用写了
# python3x:(默认继承object)
class C: # 新式类
pass
经典类的多继承
- python3中不存在经典类 , 经典类的MRO , 是一种树形结构遍历,默认 从左向右
- 记住⼀个原则. 在经典类中采⽤的是 深度优先遍历⽅案 / 即: ⼀条路走到头. 然后再回来. 继续找下⼀个
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
class E:
pass
class F(D, E):
pass
class G(F, D):
pass
class H:
pass
class Foo(H, G):
pass
print(Foo.mro())
# 类的MRO: Foo-> H -> G -> F -> E -> D -> B -> A -> C. 深度优先遍历 (经典类中采用的遍历方法)
mro序列
- 工作中用mro()方法研究新式类的继承顺序
- print(A.mro())
新式类的多继承 mro序列
- 新式类: 基类继承object,查询规则: mro算法.
mro序列
表头和表尾 (表尾可为None)
- 表头 列表的第一个元素
- 表尾 列表中表头以外的元素集合(可以为空)
列表之间的+操作
- [A,B,C] : 表头: A 表尾: [B,C]
- [A] : 表头: A 表尾: []
计算mro(A)方式
class O:
pass
class D(O):
pass
class E(O):
pass
class F(O):
pass
class B(D,E):
pass
class C(E,F):
pass
class A(B,C):
pass
# a = A()
# a.func()
mro(A) = mro(A(B,C))
= [A] + merge(mro(B), mro(C), [B,C])
mro(B) = mro(B(D,E))
= [B] + merge(mro(D), mro(E), [D,E])
= [B] + merge([D,O], [E,O], [D,E])
= [B,D] + merge([O], [E,O], [E])
= [B,D,E,O]
mro(C) = mro(C(E,F))
= [C] + merge(mro(E), mro(F),[E,F])
= [C] + merge([E,O],[F,O],[E,F])
= [C,E] + merge([O],[F,O],[F])
= [C,E,F,O]
mro(A) = mro(A(B,C))
= [A] + merge([B,D,E,O], [C,E,F,O], [B,C])
= [A,B] + merge([D,E,O], [C,E,F,O], [C])
= [A,B,D] + merge([E,O], [C,E,F,O], [C])
= [A,B,D,C] + merge([E,O], [E,F,O])
= [A,B,D,C,E] + merge([O], [F,O])
= [A,B,D,C,E,F,O]
# 解题过程:
1.先从上到下,列出每个类的线性表达式.
2.列出的线性表达式中如果有多个表达式的话,需要进行化简.
3.最终的子类也使用线性表达式表示,由于有多个父类,也需要进行化简.
线性表达式有两个部分组成:第一个类名称为头部,剩余的部分称为尾部.
化简:从多个线性表达式中第一个表达式的头部开始抽取,如果这个类名不在其他的线性表达式中的尾部出现的话,则可以抽取;若第一个表达式的头部的类名在其他的表达式中的尾部出现的话,则不能抽取;此时,从下一个表达式的头部开始抽取,前面的表达式保持原样.以备下一次抽取,下一次抽取的过程依然从第一个表达式的头部开始,重复上面的过程.