• Python 基础合集8:类的继承和多态


    一、前言

    本小节主要梳理类的继承和多态,继承包含三种形式:单继承、多层继承、多重继承。

    环境说明:Python 3.6、windows11 64位

    二、继承

    基础语法如下,class B(A)表示的含义就是B 继承A ,A 是B 的父类。

    class A():
        name='Xindata'
    class B(A):
        pass
    
    

    2.1 单继承

    子类继承父类之后,父类的所有属性和方法,子类都可以直接调用。

    class A():
        name='Xindata'
        @classmethod
        def func1(cls):
            print('My name is %s.'%cls.name)
    class B(A):
        pass
    
    # 子类调用父类属性和方法
    print(B.name)
    B.func1()
    # 结果为:
    # Xindata
    # My name is Xindata.
    

    子类继承父类之后,父类的所有实例属性和方法,子类实例化之后都可以调用。但是子类直接调用父类实例属性和方法则会报错,其实这点和父类本身调用父类实例属性和方法类似,实例的属性和方法不属于类本身,要实例化之后才能进行调用
    如果相关的基础不够扎实可以看看上一篇《类和实例》2.2.2 实例化调用部分内容。

    class A():
        name='Xindata'
        @classmethod
        def func1(cls):
            print('My name is %s.'%cls.name)
        def func2(self):
            print('I\'m instance.')
    class B(A):
        pass
    
    # 子类调用父类属性和方法
    print(B.name)  # 结果为:Xindata
    B.func1()      # 结果为:My name is Xindata.
    # B.func2()
    
    # 子类实例化后调用父类和父类的实例的属性和方法
    b = B()
    print(b.name)  # 结果为:Xindata
    b.func1()      # 结果为:My name is Xindata.
    b.func2()      # 结果为:I'm instance.
    

    继承带__init__()函数的父类,为了获得父类的初始化属性和方法,可以通过调用父类的初始函数实现,如下代码第8行,直接调用类A的初始函数,这样子对类B进行实例化的时候,就可以调用父类的初始化属性和方法,同时也可以在子类的初始函数新增属性。

    class A():
        def __init__(self,name):
            self.name = name
        def func1(self):
            print('My name is %s.'%self.name)
    class B(A):
        def __init__(self,name):
            A.__init__(self,name)   # 调用类A 的初始函数
            self.eye = 'black'
            
    b=B('Xindata')
    b.func1()      # 结果为:My name is Xindata.
    

    调用父类的初始化属性和方法,除了通过调用父类的初始化函数,还可以通过super()函数实现。使用super()函数时注意不需要给参数self传递值。

    super() 函数是用于调用父类(超类)的一个方法。主要是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承(后两小节介绍),会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。

    class A():
        def __init__(self,name):
            self.name = name
        def func1(self):
            print('My name is %s.'%self.name)
    class B(A):
        def __init__(self,name):
            super().__init__(name)   # 注意,不需要参数self 
            self.eye = 'black'
            
    b=B('Xindata')
    b.func1()      # 结果为:My name is Xindata.
    

    接下来看看一些从属关系,借助函数isinstance()issubclass()辅助理解。

    • isinstance()判断参数1的实例是否是参数2的类的实例。语法:isinstance([实例名], [类名])
    • issubclass()判断参数1的类是不是参数2的类的子类。语法:issubclass([类名1], [类名2])

    子类的实例属于父类,父类的实例不属于子类。

    class A():
        name='Xindata'
        @classmethod
        def func1(cls):
            print('My name is %s.'%cls.name)
        def func2(self):
            print('I\'m instance.')
    class B(A):
        pass
    
    a = A()
    b = B()
    
    # 实例和类
    print(isinstance(a,A)) # True,父类实例属于父类
    print(isinstance(a,B)) # False,父类实例不属于子类
    print(isinstance(b,B)) # True,子类实例属于类子
    print(isinstance(b,A)) # True,子类实例属于父类
    # 子类和父类,如果是实例会报错
    print(issubclass(B,A)) # True,B是A的子类
    print(issubclass(A,B)) # False
    # print(issubclass(a,B)) # 报错
    # print(issubclass(b,A)) # 报错
    

    2.2 多层继承

    多层继承,或者叫多级继承,就是父类继承祖父类,子类继承父类,孙类继承子类,一级一级传参。最后的一个类继承了前面所有类的属性和方法。

    class A():
        name='Xindata'
        @classmethod
        def func1(cls):
            print('My name is %s.'%cls.name)
        def func2(self):
            print('I\'m instance.')
    class B(A):
        @classmethod
        def func3(cls):
            print('class B')
    class C(B):
        @classmethod
        def func4(cls):
            print('class C')
    
    print(C.name)   # 结果为:Xindata
    C.func1()       # 结果为:My name is Xindata.
    C.func3()       # 结果为:class B
    C.func4()       # 结果为:class C
    

    多层继承遵循就近原则,如果父类和祖父类都有func1()方法,则子类从父类获取相关的属性和方法;如果父类没有func1()方法,再到祖父类中获取,如果祖父类也没有相关方法则报错。如果是子类也含有func1()方法,则直接取子类的。
    注意:如果父类和祖父类同时都有某个属性或方法,祖父类的相关属性方法会被父类覆盖,此时如果某属性在父类没有,但在祖父类中有,子类也不能调用该属性,具体看看下例的name属性,该属性仅在祖父类A存在,但是在父类B所在的方法被重写,在调用类C时,可以看做A.func1()不存在过,所以C.name不会到Afunc1()方法中寻找属性name,而是返回错误:ttributeError: type object ‘C’ has no attribute ‘name’。

    class A():
        @classmethod
        def func1(cls):
            cls.name = 'class A.func1.name'
            cls.hair = 'class A.func1.hair'
            print('class A.func1')
        
        @classmethod
        def func2(cls):
            print('class A.func2')  
    
    class B(A):
        @classmethod
        def func1(cls):       # 重写父类A 的func1()函数,新增功能
            cls.skill_1 = 'class B.func1.skill_1'
            cls.hair = 'class B.func1.hair'
            print('class B.func1')
    
    class C(B):
        @classmethod
        def func3(cls):
            print('class C.func3')
    
    
    C.func1() # 结果为:class B.func1
    C.func2() # 结果为:class A.func2
    C.func3() # 结果为:class C.func3
    
    print(C.hair)    # 结果为:class B.func1.hair
    print(C.skill_1) # 结果为:class B.func1.skill_1
    # print(C.name)    # 报错
    

    2.3 多重继承

    类的多重继承,即同时继承多个类。基本语法:

    class A():
        pass
    class B():
        pass
    class C(B,A):
        pass
    

    多重继承也有一个“就近原则”,就是靠近左侧的类优先调用。在调用子类的某个属性或方法的时候,如果子类本身没有,则从继承的父类中,依次从左到右找寻相关的属性和方法。
    当继承的类中有相同的属性或方法的时候,多重继承和多层继承一样有一个先后调用的顺序,只调用了排在靠左侧的类的相关属性和方法,忽略靠右侧的类的相关属性和方法。如下代码,父类B和父类A都有func1()方法,由于类B在左侧,所以子类C优先继承父类B的方法func1,而忽略类A的方法func1。而类属性name在类A的方法func1中,所以类C没有继承到类A的属性name,当执行C.name时报错:AttributeError: type object ‘C’ has no attribute ‘name’。

    class A():
        @classmethod
        def func1(cls):
            cls.name = 'class A.func1.name'
            cls.hair = 'class A.func1.hair'
            print('class A.func1')
        
        @classmethod
        def func2(cls):
            print('class A.func2')  
    
    class B():
        @classmethod
        def func1(cls):   
            cls.skill_1 = 'class B.func1.skill_1'
            cls.hair = 'class B.func1.hair'
            print('class B.func1')
    
    class C(B,A):
        @classmethod
        def func3(cls):
            print('class C.func3')
    
    
    C.func1() # 结果为:class B.func1
    C.func2() # 结果为:class A.func2
    C.func3() # 结果为:class C.func3
    
    print(C.hair)    # 结果为:class B.func1.hair
    print(C.skill_1) # 结果为:class B.func1.skill_1
    print(C.name)    # 报错
    

    三、多态

    多态,我的理解就是在不同的类中使用同样的方法名称,实现某一类功能,调用相关方法时,解释器把调用分派给正确的方法。
    不管对象属于哪个类,也不管声明的具体接口是什么,只要对象实现了相应的方法,函数就可以在对象上执行操作。
    具体看以下代码,对AnimalCatDog三个类分别实例化然后分别调用各个类的run()方法,虽然名称相同,但是解释器可以把相关的调用给正确的方法,打印出正确的值。

    class Animal():
        def run(self):
            print('Anaimal running')
    class Cat(Animal):
        def run(self):
            print('Cat running')
    class Dog(Animal):
        def run(self):
            print('Dog running')
    
    A = Animal()
    C = Cat()
    D = Dog()
    for animal in [A,C,D]:
        animal.run()
    # 结果为:
    # Anaimal running
    # Cat running
    # Dog running
    

    四、小结

    本节主要的知识点框架如下:
    类的继承和多态.png




    <下节预告:模块和包>


    - End -

  • 相关阅读:
    浅谈企业信息化安全建设中的三大误区
    js 谈谈Generator和[Symbol.iterator]
    Vite3.0都发布了,你还能卷得动吗(新特性一览)
    C#添加缓存,删除缓存,修改缓存
    字节码增强技术-ASM
    每日一练--IT冷知识&C/C++--第一天
    图像分割笔记(五):基于PaddleSeg使用Transfomer模型对航空遥感图像分割
    jenkins 拉取git仓库报错: stderr: fatal: not in a git directory
    Decoder-Only、Encoder-Only和Encoder-Decoder架构的模型区别、优缺点以及使用其架构的模型示例
    【linux学习】常用系统工作命令
  • 原文地址:https://blog.csdn.net/qq_45476428/article/details/127116783