Python基础之类的继承与派生

一、继承

新创建一个类,这个类可以继承一个或多个父类,新创建的类及哦啊做子类或派生类,被继承的类叫做父类或基类。

例:

class ParentClass_01:   # 定义父类
    pass

class ParentClass_02:   # 定义父类
    pass

class SonClass_01(ParentClass_01):    # 继承一个父类
    pass

class SonClass_02(ParentClass_01, ParentClass_02):    # 继承多个父类
    pass

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

print(ParentClass_01.__bases__)

# (<class 'object'>,)

print(SonClass_02.__bases__)

# (<class '__main__.ParentClass_01'>, <class '__main__.ParentClass_02'>)

二、继承与抽象

要找出类与类之间的继承关系,需要先抽象,再继承。抽象就是总结相似之处,总结对象之间的相处之处得到类,总结类与类之间的相似之处就可以得到父类。如图

基于抽象的结果,可以找到继承的关系

 由上图我们可以看出类与类之间的继承指的是:什么‘是’什么的关系(比如:人类、猪类、熊猫类都是动物类)。

子类可以继承父类的所有属性,因此继承用来解决类与类之间的代码重用性。

例如:定义一个Student类,一个Teacher类

class Student:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex
        
    def choose_course(self):
        print(f'{self.name} is choosing a course')
        
class Teacher:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex
        
    def teaching(self):
        print(f'{self.name} is teaching')

Student类与Teacher类之间存在重复的代码,老师与学生都是人类,所以可以定义一个People类,实现代码重用。

class People:
    school = '四川大学'
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

class Student(People):
    def choose_course(self):
        print(f'{self.name} is choosing a course')

class Teacher(People):
    def teaching(self):
        print(f'{self.name} is teaching')

 Student类与Teacher类中没有定义__init__方法,会从父类中找到__init__方法,从而进行正常实例化。

teacher = Teacher('zhang', 24, 'male')
print(teacher.school, teacher.name,teacher.age, teacher.sex)
# 四川大学 zhang 24 male

三、属性查找

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

class Foo:
    def f1(self):
        print('from Foo.f1')
    def f2(self):
        print('from Foo.f2')
        self.f1()

class Bar(Foo):
    def f1(self):
        print('from Bar.f1')

b = Bar()
b.f2()

# from Foo.f2
# from Bar.f1

查找顺序:b.f2()会现在子类Bar中找f2,没有找到,再去父类Foo中找到f2,先打印from Foo.f2,然后执行到self.f1(),即b.f1(),仍会按照:对象本身->类Bar->父类Foo的顺序依次找下去,在类Bar中找到f1,因而打印结果为from Bar.f1。

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

class Foo:
    def __f1(self):    # 变形为:Foo__f1
        print('from Foo.f1')
    def f2(self):
        print('from Foo.f2')
        self.__f1()    # 变形为:self.Foo__f1,因此只会调用自己所在的类中的方法

class Bar(Foo):
    def f1(self):
        print('from Bar.f1')

b = Bar()
b.f2()

# from Foo.f2
# from Foo.f1

四、继承的先后顺序

一个子类可以同时继承多个父类,那么在查找元素属性的时候,是根据什么方式来进行的。

查找方式分为:深度优先和广度优先

新式类采用广度优先,经典类采用深度优先

先来了解一下什么是新式类,什么是经典类

在python2中才有经典类与新式类的说法,在python3中只有新式类。

新式类:由任意内置类型派生出来的类,如:str,list,dic,object

经典类:不是由任意内置类型派生出来的类

# 在python2中
class A(object):   # 新式类
    pass

class B:    # 经典类
    pass

class C:   # 经典类
    pass

class D(C):    # 经典类
    pass
# 在python3中
class A:     # 新式类,继承了object类
    pass
print(A.__bases__)     
# (<class 'object'>,)

例:

class P1:
    def foo(self):
        print('P1---foo')
    
class P2:
    def foo(self):
        print('P2---foo')
    def bar(self):
        print('P2---bar')
        
class C1(P1, P2):
    pass

class C2(P1, P2):
    def bar(self):
        print('C2---bar')
        
class D(C1, C2):
    pass

d = D()
d.foo()
d.bar()

继承关系:

在python2中,使用深度优先查找,查找顺序为:

D->C1->P1->P2->C2->P1->P2

输出结果为:

P1---foo
P2---bar

在python3中,使用广度优先查找,查找顺序为:

D->C1->C2->P1->P2

输出结果为:

P1---foo
C2---bar

五、派生与方法重用

子类可以派生出自己新的属性,在进行属性查找时,子类中的属性名会优先于父类被查找,例如每个老师还有职称这一属性,我们就需要在Teacher类中定义该类自己的__init__覆盖父类的

class People:
    school = '四川大学'
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

class Teacher(People):
    def __init__(self, name, age, sex, title):
        self.name = name
        self.age = age
        self.sex = sex
        self.title = title

    def teaching(self):
        print(f'{self.name} is teaching')
teacher = Teacher('lili',18,'female','特级讲师')
print(teacher.name, teacher.age, teacher.sex, teacher.title)
# lili 18 female 特级讲师

子类的__init__中有重复代码,可以使用下面的方式实现在子类派生出的方法中重用父类的功能。

1:“指名道姓”地调用某一个类的函数

class People:
    school = '四川大学'
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

class Teacher(People):
    def __init__(self, name, age, sex, title):
        People.__init__(self,name, age, sex)   # 调用的是函数,需要传入self
        self.title = title

    def teaching(self):
        print(f'{self.name} is teaching')

2:使用super()

调用super()会得到一个特殊的对象,该对象专门用来引用父类的属性。

class People:
    school = '四川大学'
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

class Teacher(People):
    def __init__(self, name, age, sex, title):
        super().__init__(name, age, sex)   # 调用的是绑定方法,自动传入self
        self.title = title

    def teaching(self):
        print(f'{self.name} is teaching')

使用这两种方法时,需要注意传参的差别。这两种方式的区别是:第一种是跟继承没有关系的,第二种的super()是依赖于继承的,并且即使没有直接继承关系,super()也会按照MRO继续往后找。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值