Python进阶之继承

【一】什么是继承

  • 继承是一种创建新类的方式,新建的类可以继承一个或多个父类

  • 父类又可称为基类或超类,新建的类称为派生类或子类

  • 子类通过继承父类,减少了重复代码的使用

【二】继承的分类

【1】单继承

  • 单继承就是只继承一个父类的子类
# 父类
class School(object):
    school = "北京大学"

# 子类:继承了父类School
class Student(School):

    def __init__(self, name):
        self.__name = name

    def __read(self):
        print(f"学生{self.__name}的学校是{self.school}")

    def read(self):
        self.__read()


student = Student('ligo')
student.read()
# 学生ligo的学校是北京大学

【2】多继承

  • 多继承就是继承多个父类的子类
# 父类1
class Person(object):
    age = 18

# 父类2
class School(object):
    school = "北京大学"

# 子类:继承了父类School和Person
class Student(School,Person):

    def __init__(self, name):
        self.__name = name

    def __read(self):
        print(f"{self.age}岁的学生{self.__name}的学校是{self.school}")

    def read(self):
        self.__read()


student = Student('ligo')
student.read()
# 18岁的学生ligo的学校是北京大学

【三】查看继承父类的方式

# 父类1
class Person(object):
    age = 18

# 父类2
class School(object):
    school = "北京大学"

# 子类1:继承了父类School
class Student1(School):

    def __init__(self, name):
        self.__name = name

    def __read(self):
        print(f"学生{self.__name}的学校是{self.school}")

    def read(self):
        self.__read()

# 子类2:继承了父类School和Person
class Student2(School, Person):

    def __init__(self, name):
        self.__name = name

    def __read(self):
        print(f"{self.age}岁的学生{self.__name}的学校是{self.school}")

    def read(self):
        self.__read()


# __base__只查看从左到右继承的第一个子类,如果继承多个父类,默认只打印第一个继承的父类
print(Student1.__base__)  # <class '__main__.School'>
print(Student2.__base__)  # <class '__main__.School'>
# __bases__查看当前所有继承的父类,打印成一个元祖输出
print(Student1.__bases__)  # (<class '__main__.School'>,)
print(Student2.__bases__)  # (<class '__main__.School'>, <class '__main__.Person'>)

【四】经典类与新式类

  • 经典类和新式类的区别在于Python版本的不同
  • 经典类:在python2中,没有显示继承object的类,以及该类的子类,都是经典类
  • 新式类:在python2中,显示继承object的类,以及该类的子类,都是新式类
  • 在python3中,无论是否继承object,都默认继承object,即python3中所有类都默认为新式类

【五】继承和抽象

  • 继承描述的是子类与父类之间的关系,是一种什么是什么的关系
  • 要找出这种关系,必须先抽象再继承
  • 抽象即抽取类似或者说比较像的部分

【1】什么是继承和抽象

(1)抽象

  • 抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)
  • C罗,杜兰特这类对象属于人,小猪佩奇,猪猪侠这类对象属于猪,黑猫警长,波比这类对象属于猫
  • 人、猪、猫可以归类成动物

(2)继承

  • 继承:是基于抽象的结果,一定是先抽象再继承
  • 继承是由少变多,抽象是由多变少

【2】继承的实现

(1)没有继承和抽象,单独成类

  • 猫和狗各自有一个单独的类
class Dog(object):
    def run(self):
        print("狗可以到处跑!")

    def eat(self):
        print("狗可以吃饭!")

    def drink(self):
        print("狗可以喝水!")


class Cat(object):
    def run(self):
        print("猫可以到处跑!")

    def eat(self):
        print("猫可以吃饭!")

    def drink(self):
        print("猫可以喝水!")

(2)抽象成类

  • 将猫和狗总结成一个类,可以跑,可以吃饭,可以喝水
class Animal(object):
    def run(self):
        print(f"{self.name}可以到处跑!")

    def eat(self):
        print(f"{self.name}可以吃饭!")

    def drink(self):
        print(f"{self.name}可以喝水!")

(3)继承总类

class Animal(object):
    def run(self):
        print(f"{self.name}可以到处跑!")

    def eat(self):
        print(f"{self.name}可以吃饭!")

    def drink(self):
        print(f"{self.name}可以喝水!")


class Dog(Animal):
    def __init__(self, name):
        self.name = "小狗" + name


class Cat(Animal):
    def __init__(self, name):
        self.name = "小猫" + name


dog = Dog("小七")
dog.run()  # 小狗小七可以到处跑!
cat = Cat('波比')
cat.eat()  # 小猫波比可以吃饭

【六】属性查找顺序

【1】不隐藏属性的查找顺序

  • 有了继承关系,查找属性时先从对象自己的__dict__中找,如果没有则去子类中找,然后再去父类中找
class Foo:
    def f1(self):
        print('Foo.f1')

    # 【3】在父类Foo中找到f2
    def f2(self):
        # 【4】先打印 Foo.f2
        print('Foo.f2')
        # 【5】
        # 但是这里犯了难,这个 self 到底是 Foo 还是 Bar ?
        # 我们要时刻记得,源头是谁,这个self就是谁
        # 我们是从 对象 b 来的,而对象 b 又是 Bar 的对象
        # 我们从 Bar 找到了 Foo 类里面,所以我们的源头就是 Bar
        # 那这个 self 就是 Bar , 而 Bar 类里面有 f1 方法
        # 所以我们就会回到 Bar 类里面
        self.f1()


class Bar(Foo):
    # 【2】在Bar中找f2,找不到f2,去Foo找
    # 【6】返回到bar中的f1
    def f1(self):
        # 【7】打印Bar.f1
        print('Bar.f1')


# 实例化类得到对象
b = Bar()
# 【1】对象调用方法
b.f2()
# 所以打印的顺序是Foo中的f2,Bar中的f1
# Foo.f2
# Bar.f1

【2】隐藏属性的查找顺序

class Foo:
    # 变形为_Foo__fa
    def __f1(self):
        print('Foo.f1')
        
    # 在父类Foo中找到f2
    def f2(self):
        # 打印 Foo.f2
        print('Foo.f2')
        # 调用__f1,又输出Foo.f1
        self.__f1()


class Bar(Foo):
    # 变形为_Bar__f1,找不到f2,去Foo找
    def __f1(self):
        print('Bar.f1')


b = Bar()
b.f2()
# Foo.f2
# Foo.f1

【3】总结

  • 如果属性不封装的情况下,谁实例化得到的self 就去谁里面找
  • 如果属性封装的情况下 , 谁实例化得到的self无效,只能在当前所在的类的民称空间里面找

【七】继承实现的原理

【1】非菱形结构继承顺序

  • 在Java和C#中子类只能继承一个父类,而Python中子类可以同时继承多个父类,如A(B,C,D)
  • 如果继承关系为非菱形结构,则会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到我们想要的属性

【2】菱形结构继承顺序

  • 继承关系为菱形结构时属性的查找方式为:深度优先和广度优先

(1)深度优先

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

image-20240506171412

(2)广度优先

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

image-20240506171243305

(3)继承原理

class A(object):
    def test(self):
        print('from A')


class B(A):
    def test(self):
        print('from B')


class C(A):
    def test(self):
        print('from C')


class D(B):
    def test(self):
        print('from D')


class E(C):
    def test(self):
        print('from E')


class F(D, E):
    # def test(self):
    #     print('from F')
    pass


f1 = F()
f1.test()
# 只有新式才有这个属性可以查看线性列表,经典类没有这个属性
print(F.__mro__)
# [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

# 新式类继承顺序:F->D->B->E->C->A
# 经典类继承顺序:F->D->B->A->E->C
posted @ 2024-05-10 16:32  Ligo6  阅读(28)  评论(0)    收藏  举报