Fork me on GitHub

day 20

1. 类的继承

1.1 什么是继承

  • 继承是一种新建类的方式,新建的类称为子类,被继承的类称为父类
  • 继承的特性是:子类会遗传父类的属性,并且可以派生出自己的属性
  • 继承是类与类之间的关系

在 python 中,一个子类可以继承多个父类,其他语言只能一个子类继承一个父类。

1.2 为什么要有继承

目的:减少代码冗余(减少重复代码)

1.3 如何实现继承

  1. 先确定谁是子类,谁是父类

  2. 在定义类时,子类 + (),括号内填父类的名字,实现继承

    使用 __bases__ 方法,可以获取当前类所继承的类

    class Father1:
        pass
    
    
    class Father2:
        pass
    
    
    class Son1(Father1):
        pass
    
    
    class Son2(Father1, Father2):
        pass
    
    
    print(Son1.__bases__) # 查看父类
    
    print(Son2.__bases__)
    

    运行结果:

    (<class '__main__.Father1'>,)
    (<class '__main__.Father1'>, <class '__main__.Father2'>)
    
    Process finished with exit code 0
    

1.4 类与抽象

  • 继承的关系
    • 对象是特征与技能的结合体
    • 类是一系列对象相同的特征与技能的结合体
    • 继承是一系列类相同的特征与技能的结合体

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

抽象分成两个层次:

  1. 将奥巴马和梅西这俩对象比较像的部分抽取成类;
  2. 将人,猪,狗这三个类比较像的部分抽取成父类。

抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度),如下图所示:

继承:基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。

抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类,如下图所示:

1.5 查找顺序

在继承背景下,对象属性的查找顺序:

  1. 对象查找属性会先从对象的名称空间中查找
  2. 若对象没有,则会去类里面找
  3. 若当前类是子类,并且没有对象找的属性,会去父类中查找

注意:对象查找属性,若子类有,不管父类有没有,以子类的为准。

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

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


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


# 对象查找属性的顺序:对象自己-》对象的类-》父类-》父类。。。
obj = Bar()  # self是obj本身,即找到Bar的f1()
obj.f2()

运行结果:

Foo.f2
Bar.f1

Process finished with exit code 0

2. 派生

2.1 什么是派生

子类中新定义的属性的这个过程叫做派生,并且需要记住子类在使用派生的属性时始终以自己的为准。

2.2 派生方法

2.2.1 类调用

指名道姓访问某一个类的函数:该方式与继承无关

# 直接通过 父类.(调用)__init__,把__init__当做普通函数使用,传入对象与继承的属性.
class Father:
    def __init__(self, name, age):
        self.name = name
        self.age = age


class Son(Father):
    def __init__(self, name, age, weight, height):
        Father.__init__(self, name, age)
        self.weight = weight
        self.height = height

2.2.2 super() 方法

  • 严格依赖继承属性查找关系
  • super() 会得到一个特殊的对象,该对象就是专门用来访问父类中的属性的(按照继承的关系)
  • super().__init__ (不用为self传值)
  • super() 的完整用法是 super(自己的类名,self) ,在python2中需要写完整,而python3中可以简写为 super()
class Father:
    def __init__(self, name, age):
        self.name = name
        self.age = age


class Son(Father):
    def __init__(self, name, age, weight, height):
        super().__init__(name, age)
        self.weight = weight
        self.height = height

3. 类的分类(了解)

在 python2 中,才有才会有新式类与经典类之分;在 python3 中,所有的类都是新式类。

3.1 新式类

继承 object 的类都称之为新式类。

在 python3 中,如果子类没有继承自定义的类,都默认继承 object 。

class Foo():
    pass


class Goo(Foo):
    pass


print(Foo.__bases__) 
print(Goo.__bases__) 

运行结果:

(<class 'object'>,)	# 默认继承 object
(<class '__main__.Foo'>,)

Process finished with exit code 0

3.2 经典类

在 python2 中,凡是没有继承 object 的类都是经典类。

4. 菱形继承问题

4.1 菱形继承问题

在Java和C#中子类只能继承一个父类,而Python中子类可以同时继承多个父类,如A(B,C,D)

如果继承关系为非菱形结构,则会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到我们想要的属性

如果继承关系为菱形结构,即子类的父类最后继承了同一个类,那么属性的查找方式有两种:

  • 经典类下:深度优先
  • 广度优先:广度优先

经典类:一条路走到黑,深度优先

新式类:不找多各类最后继承的同一个类,直接去找下一个父类,广度优先

可以使用下面的代码逐一进行验证:

# 验证
class A:
    def test(self):
        print('from A')

    pass


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

    pass


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

    pass


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

    pass


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

    pass


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

    pass


f1 = F()
f1.test()

结果:

# 新式类: F-D-B-E-C-A-object
# 经典类: F-D-B-A-E-C

4.2 mro() 方法

对于多继承的类,python 提供了 mro() 方法,便捷的查看继承的查找顺序。

对于上述代码,我们可以使用 mro() 方法:

# print(F.mro())
# 新式类下:
for i in F.mro():
    print(i)

运行结果:

<class '__main__.F'>
<class '__main__.D'>
<class '__main__.B'>
<class '__main__.E'>
<class '__main__.C'>
<class '__main__.A'>
<class 'object'>

Process finished with exit code 0
posted @ 2019-10-10 20:05  Yugaliii  阅读(131)  评论(0编辑  收藏  举报