美文网首页
day26-继承和派生

day26-继承和派生

作者: 天行_b6d0 | 来源:发表于2020-08-10 14:30 被阅读0次

一、常用三连

  1. 什么是继承
    继承是一种新建类的方式,新建的类称之为子类,被继承的类称之为父类、超类、基类。
    在python中支持多继承
  2. 为什么要用继承
    子类可以遗传父类的属性,使用继承可以解决类与类之间代码的冗余问题。
  3. 如何实现继承
class Parent1:
    pass

class Parent2:
    pass

class Sub1(Parent1):  # 在类名后加上括号,里面放被继承类的类名
    pass

class Sub2(Parent1,Parent2):
    pass

print(Sub1.__bases__)  # 查看该类的父类
print(Sub2.__bases__)

二、在子类派生的新方法中重用父类的功能

method_1:指名道姓地调用某个类的函数
特点:不依赖于继承关系

class OldboyPeople:
    school = "oldboy"
    #             空对象,"洛根",22,'male'
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender

    def f1(self):
        print('1111111')

class Student(OldboyPeople):
    #            空对象,"洛根",22,'male',1001,"python全栈开发"
    def __init__(self,name,age,gender,stu_id,course):
        OldboyPeople.__init__(self,name,age,gender)  # OldboyPeople.__init__(空对象,"洛根",22,'male')
        self.stu_id = stu_id
        self.course = course


    def choose(self):
        print('%s 正在选课' %self.name)

    def f1(self):
        OldboyPeople.f1(self)
        print("22222")

class Teacher(OldboyPeople):
    def score(self,stu,num):
        stu.num = num


stu1=Student("洛根",22,'male',1001,"python全栈开发")
# tea1=Teacher("egon",18,'male',2000,10)


stu1.f1()

method_2:super()

调用super()会得到一个特殊的对象,该对象专门用来引用父类的属性,且严格按照MRO规定的顺序向后查找
  • ps:在Python2中super的使用需要完整地写成super(自己的类名,self) ,而在python3中可以简写为super()。
  • 这两种方式的区别是:方式一是跟继承没有关系的,而方式二的super()是依赖于继承的,并且即使没有直接继承关系,super()仍然会按照MRO继续往后查找
>>> #A没有继承B
... class A:
...     def test(self):
...         super().test()
... 
>>> class B:
...     def test(self):
...         print('from B')
... 
>>> class C(A,B):
...     pass
... 
>>> C.mro() # 在代码层面A并不是B的子类,但从MRO列表来看,属性查找时,就是按照顺序C->A->B->object,B就相当于A的“父类”
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>,<class ‘object'>]
>>> obj=C()
>>> obj.test() # 属性查找的发起者是类C的对象obj,所以中途发生的属性查找都是参照C.mro()
from B

obj.test()首先找到A下的test方法,执行super().test()会基于MRO列表(以C.mro()为准)当前所处的位置继续往后查找(),然后在B中找到了test方法并执行。

三、属性查找

由对象引发的属性查找: 先从对象里面找,再去类里面找,再去父类里面找。
例一:

class Foo:
    def f2(self):
        print("Foo.f2")

    def f1(self):
        print('Foo.f1')
        self.f2()  # obj.f2()


class Bar(Foo):
    def f2(self):
        print("Bar.f2")


obj = Bar()
obj.f1()  # 输出的结果为:Foo.f1    Bar.f2

例二:父类如果不想让子类覆盖自己的方法,可以在方法名前加前缀__

class Foo:
    def __f2(self):  # _Foo__f2
        print("Foo.f2")

    def f1(self):
        print('Foo.f1')
        self.__f2()  # obj._Foo__f2()


class Bar(Foo):
    def __f2(self):  # _Bar__f2
        print("Bar.f2")


obj = Bar()

obj.f1()

四、继承的实现原理

1、两个种类的类
新式类:但凡是继承了object类的子类,以该子类子子孙孙类都称之为新式类
经典类:没有继承了object类的子类,以该子类子子孙孙类都称之为经典类
在python3中全是新式类,只有在python2 中才有经典类,在python3中没有继承任何类的类会默认继承object类。

2、菱形问题
一个子类继承的多条件分支最终汇聚到一个非object类,在菱形继承下,新式类与经典类关于属性查找的方式不同。
新式类:广度优先
经典类:深度优先

例1:非菱形继承,经典类与新式类的属性查找顺序都一样


菱形继承1.png
class E:
    # def test(self):
    #     print('from E')
    pass

class F:
    def test(self):
        print('from F')


class B(E):
    # def test(self):
    #     print('from B')
    pass

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


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


class A(B, C, D):
    # def test(self):
    #     print('from A')
    pass

obj=A()
obj.test()

例2:菱形继承


菱形继承2.png
class G(object): # 在python2中,未继承object的类及其子类,都是经典类
    # def test(self):
    #     print('from G')
    pass

class E(G):
    # def test(self):
    #     print('from E')
    pass

class F(G):
    # def test(self):
    #     print('from F')
    pass

class B(E):
    # def test(self):
    #     print('from B')
    pass

class C(F):
    # def test(self):
    #     print('from C')
    pass

class D(G):
    # def test(self):
    #     print('from D')
    pass

class A(B,C,D):
    # def test(self):
    #     print('from A')
    pass

obj=A()
obj.test()

print(A.mro())

五、Python Mixins机制

一个子类可以同时继承多个父类,这样的设计常被人诟病,一来它有可能导致可恶的菱形问题,二来在人的世界观里继承应该是个”is-a”关系。 比如轿车类之所以可以继承交通工具类,是因为基于人的世界观,我们可以说:轿车是一个(“is-a”)交通工具,而在人的世界观里,一个物品不可能是多种不同的东西,因此多重继承在人的世界观里是说不通的,它仅仅只是代码层面的逻辑。不过有没有这种情况,一个类的确是需要继承多个类呢?

答案是有,我们用交通工具来举例:

​ 民航飞机、直升飞机、轿车都是一个(is-a)交通工具,前两者都有一个功能是飞行fly,但是轿车没有,所以如下所示我们把飞行功能放到交通工具这个父类中是不合理的。

class Vehicle:  # 交通工具
    def fly(self):
        '''
        飞行功能相应的代码        
        '''
        print("I am flying")


class CivilAircraft(Vehicle):  # 民航飞机
    pass


class Helicopter(Vehicle):  # 直升飞机
    pass


class Car(Vehicle):  # 汽车并不会飞,但按照上述继承关系,汽车也能飞了
    pass

但是如果民航飞机和直升机都各自写自己的飞行fly方法,又违背了代码尽可能重用的原则(如果以后飞行工具越来越多,那会重复代码将会越来越多)。

所以为了尽可能地重用代码,那就只好在定义出一个飞行器的类,然后让民航飞机和直升飞机同时继承交通工具以及飞行器两个父类,这样就出现了多重继承。这时又违背了继承必须是”is-a”关系。这个难题该怎么解决?
<<<<<<<<<>>>>>>>>>python提供了Mixins机制,简单来说Mixins机制指的是子类混合(mixin)不同类的功能,而这些类采用统一的命名规范(例如Mixin后缀),以此标识这些类只是用来混合功能的,并不是用来标识子类的从属"is-a"关系的,所以Mixins机制本质仍是多继承,但同样遵守”is-a”关系,如下

class Vehicle:  # 交通工具
    pass


class FlyableMixin:
    def fly(self):
        '''
        飞行功能相应的代码        
        '''
        print("I am flying")


class CivilAircraft(FlyableMixin, Vehicle):  # 民航飞机
    pass


class Helicopter(FlyableMixin, Vehicle):  # 直升飞机
    pass


class Car(Vehicle):  # 汽车
    pass

# ps: 采用某种规范(如命名规范)来解决具体的问题是python惯用的套路

可以看到,上面的CivilAircraft、Helicopter类实现了多继承,不过它继承的第一个类我们起名为FlyableMixin,而不是Flyable,这个并不影响功能,但是会告诉后来读代码的人,这个类是一个Mixin类,表示混入(mix-in),这种命名方式就是用来明确地告诉别人(python语言惯用的手法),这个类是作为功能添加到子类中,而不是作为父类,它的作用同Java中的接口。所以从含义上理解,CivilAircraft、Helicopter类都只是一个Vehicle,而不是一个飞行器。

使用Mixin类实现多重继承要非常小心

  • 首先它必须表示某一种功能,而不是某个物品,python 对于mixin类的命名方式一般以 Mixin, able, ible 为后缀
  • 其次它必须责任单一,如果有多个功能,那就写多个Mixin类,一个类可以继承多个Mixin,为了保证遵循继承的“is-a”原则,只能继承一个标识其归属含义的父类
  • 然后,它不依赖于子类的实现
  • 最后,子类即便没有继承这个Mixin类,也照样可以工作,就是缺少了某个功能。(比如飞机照样可以载客,就是不能飞了)

六、组合

在一个类中以另外一个类的对象作为数据属性,称为类的组合。组合与继承都是用来解决代码的重用性问题。不同的是:继承是一种“是”的关系,比如老师是人、学生是人,当类之间有很多相同的之处,应该使用继承;而组合则是一种“有”的关系,比如老师有生日,老师有多门课程,当类之间有显著不同,并且较小的类是较大的类所需要的组件时,应该使用组合,如下示例

class Course:
    def __init__(self,name,period,price):
        self.name=name
        self.period=period
        self.price=price
    def tell_info(self):
        print('<%s %s %s>' %(self.name,self.period,self.price))

class Date:
    def __init__(self,year,mon,day):
        self.year=year
        self.mon=mon
        self.day=day
    def tell_birth(self):
       print('<%s-%s-%s>' %(self.year,self.mon,self.day))

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

#Teacher类基于继承来重用People的代码,基于组合来重用Date类和Course类的代码
class Teacher(People): #老师是人
    def __init__(self,name,sex,age,title,year,mon,day):
        super().__init__(name,age,sex)
        self.birth=Date(year,mon,day) #老师有生日
        self.courses=[] #老师有课程,可以在实例化后,往该列表中添加Course类的对象
    def teach(self):
        print('%s is teaching' %self.name)


python=Course('python','3mons',3000.0)
linux=Course('linux','5mons',5000.0)
teacher1=Teacher('lili','female',28,'博士生导师',1990,3,23)

# teacher1有两门课程
teacher1.courses.append(python)
teacher1.courses.append(linux)

# 重用Date类的功能
teacher1.birth.tell_birth()

# 重用Course类的功能
for obj in teacher1.courses: 
    obj.tell_info()

相关文章

  • day26-继承和派生

    一、常用三连 什么是继承继承是一种新建类的方式,新建的类称之为子类,被继承的类称之为父类、超类、基类。在pytho...

  • 继承和派生

    我们知道C++里面有很多机制是为了提高软件的重用性来设计的, 什么叫重用性好呢?就是你有一部分代码实现了一个功能,...

  • 实现继承

    1. 继承 1.1 继承和派生的关系 基类和派生类的关系就是,派生类从基类派生而来。 1.2 派生语法 1.3 访...

  • 2017.9.18

    继承和派生 派生的格式: class 派生类名:继承方式 基类名 {派生类中的新成员} 例: class Mast...

  • OC学习总结2 派生、重写和多态

    继承和派生继承是指一个新类拥有全部被继承类的属性和方法,通过继承产生新类的过程也称作派生。派生: 由子类创建的方法...

  • C++ 一篇搞懂继承的常见特性

    继承和派生 01 继承和派生的概念 继承: 在定义一个新的类 B 时,如果该类与某个已有的类 A 相似(指的是 B...

  • C++派生类

    派生类的概念和定义 ① 定义格式 派生类定义的语法为: class 派生类名:继承方式1 基类名1, 继承方式2 ...

  • CPP:04类的继承和派生

    CPP04:类的继承和派生

  • 2017 09 18

    相关知识点的学习与运用: 一.继承和派生: 1.继承: 1-1基本结构: class派生类名:继承方式 基类名 {...

  • C++继承和派生

    C++通过继承关系,实现了代码的可重用性。 public表示继承方式,也表示访问权限 一、继承方式和访问权限的影响...

网友评论

      本文标题:day26-继承和派生

      本文链接:https://www.haomeiwen.com/subject/bdftdktx.html