day29---面向对象编程之继承

一、什么是继承


# 继承是一种创建新类的方式。
新建的类可称为子类派生类,父类又可称为基类超类,子类会遗传父类的属性

注意:python支持多继承,新建的类可以继承一个或多个父类

class ParentClass1: #定义父类
    pass

class ParentClass2: #定义父类
    pass

class SubClass1(ParentClass1): #单继承
    pass

class SubClass2(ParentClass1,ParentClass2): #多继承
    pass

通过类的内置属性__bases__可以查看类继承的所有父类

print(SubClass2.__bases__)


>> (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

二、经典类与新式类


在python2中有新式类和经典类之分

#新式类:继承了object类的子类,以及该子类的子类子子类
#经典:没有继承object类的子类,以及该子类的子类子子类

在python3中没有继承任何类,那么会默认继承object类,所以python3中所有的类都是新式类

三、多继承


python多继承的优缺点

I 优点:子类可以同时遗传多个父类的属性,最大限度地重用代码
II缺点:1、违背人的思维习惯:继承表达的是一种什么""什么的关系
           2、代码可读性会变差
           3、不建议使用多继承,有可能会引发可恶的菱形问题,扩展性变差,
          如果真的涉及到一个子类不可避免地要重用多个父类的属性,应该使用Mixins

继承主要是用来解决类与类之间的代码冗余问题

# 3、如何实现继承
# # 示范1:类与类之间存在冗余问题
# class Student:
#     school='OLDBOY'
#
#     def __init__(self,name,age,sex):
#         self.name=name
#         self.age=age
#         self.sex=sex
#
#     def choose_course(self):
#         print('学生%s 正在选课' %self.name)
#
#
# class Teacher:
#     school='OLDBOY'
#
#     def __init__(self,name,age,sex,salary,level):
#         self.name=name
#         self.age=age
#         self.sex=sex
#         self.salary=salary
#         self.level=level
#
#     def score(self):
#         print('老师 %s 正在给学生打分' %self.name)
#


# 示范2:基于继承解决类与类之间的冗余问题
class OldboyPeople:
    school = 'OLDBOY'

    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex


class Student(OldboyPeople):
    def choose_course(self):
        print('学生%s 正在选课' % self.name)
# stu_obj = Student('lili', 18, 'female')
# print(stu_obj.__dict__)
# print(stu_obj.school)
# stu_obj.choose_course()


class Teacher(OldboyPeople):
    #           老师的空对象,'egon',18,'male',3000,10
    def __init__(self, name, age, sex, salary, level):
        # 指名道姓地跟父类OldboyPeople去要__init__
        OldboyPeople.__init__(self,name,age, sex)
        self.salary = salary
        self.level = level

    def score(self):
        print('老师 %s 正在给学生打分' % self.name)

tea_obj=Teacher('egon',18,'male',3000,10)
# print(tea_obj.__dict__)
# print(tea_obj.school)

tea_obj.score()

四、属性查找


有了继承关系,对象在查找属性时,先从对象自己的__dict__中找,如果没有则去子类中找,然后再去父类中找

>>> 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')
... 
>>> b=Bar()
>>> b.f2()
Foo.f2
Bar.f1

父类如果不想让子类覆盖自己的方法,可以采用双下划线开头的方式将方法设置为私有的

>>> class Foo:
...     def __f1(self): # 变形为_Foo__fa
...         print('Foo.f1') 
...     def f2(self):
...         print('Foo.f2')
...         self.__f1() # 变形为self._Foo__fa,因而只会调用自己所在的类中的方法
... 
>>> class Bar(Foo):
...     def __f1(self): # 变形为_Bar__f1
...         print('Bar.f1')
... 
>>> 
>>> b=Bar()
>>> b.f2() #在父类中找到f2方法,进而调用b._Foo__f1()方法,同样是在父类中找到该方法
Foo.f2
Foo.f1

五、菱形问题


大多数面向对象语言都不支持多继承,而在Python中,一个子类是可以同时继承多个父类的,这固然可以带来一个子类可以对多个不同父类加以重用的好处,但也有可能引发著名的
Diamond problem菱形问题

如下图:

A类在顶部,B类和C类分别位于其下方,D类在底部将两者连接在一起形成菱形。

继承原理:

python到底是如何实现继承的呢? 对于你定义的每一个类,Python都会计算出一个方法解析顺序(MRO)列表,该MRO列表就是一个简单的所有基类的线性顺序列表

python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。 而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:

1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类

对于对象

1.由对象发起的属性查找,会从对象自身的属性里检索,没有则会按照对象的类.mro()规定的顺序依次找下去,
2.由类发起的属性查找,会按照当前类.mro()规定的顺序依次找下去,

深度优先和广度优先


如果继承关系为菱形结构,那么经典类与新式类会有不同MRO,分别对应属性的两种查找方式:深度优先和广度优先

1、经典类

#当类是经典类时,多继承的情况下,要查找的属性不存在时,会按照深度优先的方式查找下去

查找顺序:A->B->E->G->C-F-D

2、新式类

#当类是新式类时,多继承的情况下,要查找的属性不存在时,会按照广度优先的方式查找下去

查找顺序:A->B->E->C->F-D->object

总结:

# 多继承到底要不用???
# 要用,但是规避几点问题
# 1、继承结构尽量不要过于复杂
# 2、推荐使用mixins机制:在多继承的背景下满足继承的什么"是"什么的关系
原文地址:https://www.cnblogs.com/surpass123/p/12667089.html