面向对象3

-------------------面向对象的第二大特性:继承-------------------------------------------------------继承-----------------------------------
1.什么是继承:新建类的方式,新建的类可称为子类或者派生类,父类又可称为基类或者超类,子类会遗传父类的属性,所以说继承会减少代码的冗余
需要注意的是:在python当中是支持多继承的,在Python中,新建的类可以继承一个或者多个父类
python多继承:
优点:子类可以同时遗传多个父类的属性,最大限度的重用代码
缺点:
1.违背了人的思维习惯:继承表达的是一种什么'是'什么的关系
2.代码可读性会变差
3.不建议使用多继承,有可能会引发菱形问题扩展性变差
如果真的涉及到一个子类不可避免地重用多个父类的属性,应该使用Mixins机制
类名括号里面的是用来继承的
class Parent1:
pass
class Parent2:
pass
class Sub1(Parent1): # 单继承
pass
class Sub2(Parent1,Parent2): # 多继承
pass
print(sub1.__bases__) # 可以查看该类继承了多少个父类
print(sub2.__bases__)
在python2当中有经典类和新式类之分
新式类:继承了object类的子类,以及该子类的子类,子子类...
经典类:没有继承了object类的子类,以及该子类的子类,子子类...
也就是说只要没有继承object类的就是经典类
在python3当中,没有继承任何类,那么会默认继承了object类,所以python3中所有的类都是新式类
2.为何要用继承:用来解决类与类之间代码冗余问题
object类是一个内置的类,在python3中会默认继承给所有类,他是所有类的父类

类与类之间存在冗余问题
基于继承解决类与类之间的冗余问题

3.继承基础下的属性查找:
单继承下的属性查找:自己对象调用一个属性,obj--自己类--父类-...
案例一:注意看清楚是类调用还是对象调用,他们属性查找的顺序都不同
class Foo:
def f1(self):
print('Foo.f1')
def f2(self):
print('Foo.f2')
self.f1()
class Bar(Foo):
def f1(self):
print('Bar.f1')
obj = Bar()
obj.f2() # Foo.f2 和Bar.f1
案例二:如果非要调用自己的f1可以用类调用属性,是从类找,类没有去父类去找的特点,他跟对象不同,对象是找不到,在从类找,然后在从父类找
class Foo:
def f1(self):
print('Foo.f1')
def f2(self):
print('Foo.f2')
Foo.f1(self) # 调用当前类的f1 <----------
class Bar(Foo):
def f1(self):
print('Bar.f1')
obj = Bar()
obj.f2() # Foo.f2 和Foo.f1
案例三:如果非要调用自己的f1可以用隐藏属性对内不对外的特点可以实现
class Foo:
def __f1(self):
print('Foo.f1')
def f2(self):
print('Foo.f2')
self.__f1()
class Bar(Foo):
def __f1(self):
print('Bar.f1')
obj = Bar()
obj.f2() # Foo.f2 和Foo.f1

4.多继承下的属性查找:
菱形问题:一个子类继承了多个父类,最终汇聚到一个点,这个点是非object上的类。
菱形问题的问题是因为这种结构会带来一些问题,并不是指的是这种结构一定会出现问题。
4.1继承原理:
每次定义一个类,不管你继承不继承,python都会通过c3算法,解析出来一个顺序列表,该列表就是MRO列表,在查找属性的时候会根据mro列表从左到右依次查找。类名.mro = [查找属性的顺序的各个类]
如果你以类A为起始开始查找,那么就必须要按照类A的MRO属性查找。
也就是说以某某类为起始发起的属性查找就按照某某类的MRO属性查找。
类相关的属性查找(类名.属性 , 该类的对象.属性),都是参照的该类的MRO属性查找
MRO在python2和python3中算出来的顺序是不一样的。
非菱形继承的背景下属性查找顺序:python2和python3查找顺序都是一样的,也就是说新式类和经典类在非菱形继承背景下属性查找顺序是一样的,但是经典类下没有MRO查找.但是原理都是跟新式类一样
都是一个分支一个分支找下去,最后分支找完了,才会找object类
具体查找:A-B-E然后在C-F接着D最后才会找object类
菱形继承下的背景下的属性查找顺序:深度优先和广度优先
如果你是经典类,深度优先。会一个分支一个分支走,但是会在第一个分支走到头,也就是说必须要x找到他们那个'点'.
如果你是新式类,广度优先。会一个分支一个分支走,但是不会每一条道走那个'点'.(比如说他们最终的父辈汇聚成一个点G类,也就是说他不会检索G,会在每个分支走完才会最后检索G)
目前我们用的都是新式类,也就是说无论是菱形问题还是非菱形问题,属性查找顺序都是根据MRO的广度优先原则。
多继承可以用但是避免以下几点:
1.继承结构尽量不要过于复杂
2.要在多继承的背景下满足继承的什么'是'什么的关系
3.如果真的要用多继承,最好用Mixins机制
5.Mixins机制
Mixins机制是用来解决多继承的问题的
多继承会让程序的可读性变差,进而导致扩展性变差。
但是我们既要用多继承又不想让他的缺点那么明显,Mixins机制可以帮我们提升可读性,进而提升扩展性。所以Mixins机制的核心就是
Mixins机制的核心:就是在多继承背景下尽可能提升多继承的可读性
ps:让多继承满足人的思维习惯(什么'是'什么)
为了使得子类不用继承本没
class Vehicle:
pass
class FlyMixin: # Mixin,able,ible后缀的类名用来混合功能的
并不是属于什么'是'什么的关系,用来给子类混入东西的
def fly(self):
pass
class C(FlyMixin, Vehicle):
pass
class D(FlyMixin, Vehicle):
pass
class B(Vehicle):
pass



在子类派生的新方法中如何重用父类的功能
第一种:指名道姓 类名.__init__(self,参数) # 类调用函数属性,需要传参
第二种:super()调用父类提供给自己的方法,严格依赖继承关系
super(自己的类,对象).__init__() # super()会把自己的类传进来以及对象也传进来,super(自己的类,对象)然后会得到一个特殊的对象,该对象会参照发起属性查找类的MRO去当前类的父类中找属性,这是他的特殊之处
当前类的父类(发起属性类A的MRO中当前类B后面的类C,其中类C就称为当前类B的父类)
在python3中可以简写不用加自己的类和对象直接用一下形式
super().__init__(参数) # 因为super()是一个特殊的对象,所以调用函数属性的时候,不用自己传self,他会默认传self,只需要正常传参就行
class A:
def test(self):
super().test()
class B:
def test(self):
PRINT('from B')
class C(A,B):
pass
obj = C()
obj.test() # 很明显这个发起属性查找的是C类,所以应该按照C的MRO查找属性顺序,所以在A中的super()会按照C的MRO。
假如C的MRO是[C-A-B],也就是说super()会按照发起属性查找的MRO也就是C的MRO查找属性,当前的父类并不是字面意义上的父类,super()当前的类是A,但是A我们看起来是没有父类(不算object),其实他的父类并不是我们理解的那个父类,而是说是在MRO中B就是A的父类。特别注意,MRO属性查找,不会重复寻找,也就是说找过A就不会在找A。
6.组合:让一个对象拥有一个属性,但是这个属性是另外一个对象
obj1 = Student('egon','18')
obj1.course = python # 这个就相当于给对象新建一个值
而且这个值还是另一个类中的对象。这就叫做组合,也就相当于我们给学生直接添加了一门课程对象,课程对象里面有各种,什么课程名字,课程周期,课程价格等等。

posted @ 2021-07-18 11:36  点滴180  阅读(23)  评论(0编辑  收藏  举报