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 -

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xin学数据

为你点亮一盏灯,愿你前进无阻。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值