继承及属性查找+super()和mro()+多态

继承及属性查找+super()和mro()+多态

一、 ★继承

1. 什么是继承?
     继承就是新建类的一种方式,新建的类我们称为子类或者叫派生类,被继承的类我们称为父类或者基类
     子类可以使用父类中的属性或者方法
2. 为什么要用继承?
    类解决了对象与对象之间的代码冗余问题
    继承解决的是类与类之间的代码冗余问题

3. 如何使用继承?
    新式类:继承了object类的子子孙孙类都是新式类
    经典类:没有继承了object类的子子孙孙类都是经典类

★新式类/经典类

Python3 中:
新式类: 继承object类的子类, 以及该类的子类,子子类。
经典类: 没有继承 object类的子类, 以及该类的子类,子子类。

Python2 中:
不区分新式类与经典类

Python 3 没有任何继承任何类,那么会默认继承object类, 所有都是新式类
object类: Python内置的类, 存放了一些常用的功能

新式类和经典类只有在python2中区分:Python2 中没有__base__

分为单继承和多继承:        单继承继承一个父类                         多继承继承多个父类

class ParentClass1: #定义父类
    pass

class ParentClass2: #定义父类
    pass

class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
    pass

class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
    pass

查看继承:

print(Parent1.__bases__) # (<class 'object'>,)
print(sub1.__bases__)  # (<class '__main__.Parent1'>, <class '__main__.Parent2'>)

查看继承:

SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
# <class '__main__.ParentClass1'>
SubClass2.__bases__
# class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>

提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。

 二、继承与抽象(先抽象再继承)

继承了哪些类 类名.__base__ 查看单继承 , 看多继承只显示一个

类名.__bases__ 查看多继承 , 能看单继承

抽象即抽取类似或者说比较像的部分。

抽象分成两个层次: 

1.将奥巴马和梅西这俩对象比较像的部分抽取成类; 

2.将人,猪,狗这三个类比较像的部分抽取成父类。

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

 

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

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

示例:

三、继承与重用性

  1 ==========================第一部分
  2 例如
  3 
  4   猫可以:吃、喝、爬树
  5 
  6   狗可以:吃、喝、看家
  7 
  8 如果我们要分别为猫和狗创建一个类,那么就需要为 猫 和 狗 实现他们所有的功能,伪代码如下:
  9 
 10 
 11 #猫和狗有大量相同的内容
 12 class 猫:
 13 
 14     def 吃(self):
 15         # do something
 16 
 17     def 喝(self):
 18         # do something
 19 
 20     def 爬树(self):
 21         # do something
 22 
 23 
 24 
 25 class 狗:
 26 
 27     def 吃(self):
 28         # do something
 29 
 30     def 喝(self):
 31         # do something
 32 
 33     def 看家(self):
 34         #do something
 35 
 36 
 37 ==========================第二部分
 38 上述代码不难看出,吃、喝是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次。如果使用 继承 的思想,如下实现:
 39 
 40   动物:吃、喝
 41 
 42      猫:爬树(猫继承动物的功能)
 43 
 44      狗:看家(狗继承动物的功能)
 45 
 46 伪代码如下:
 47 class 动物:
 48 
 49     def 吃(self):
 50         # do something
 51 
 52     def 喝(self):
 53         # do something
 54 
 55 # 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
 56 class 猫(动物):
 57 
 58     def 爬树(self):
 59         print '喵喵叫'
 60 
 61 # 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
 62 class 狗(动物):
 63 
 64     def 看家(self):
 65         print '汪汪叫'
 66 
 67 
 68 ==========================第三部分
 69 #继承的代码实现
 70 class Animal:
 71 
 72     def eat(self):
 73         print("%s 吃 " %self.name)
 74 
 75     def drink(self):
 76         print ("%s 喝 " %self.name)
 77 
 78 class Cat(Animal):
 79 
 80     def __init__(self, name):
 81         self.name = name
 82         self.breed = ''
 83 
 84     def climb(self):
 85         print('爬树')
 86 
 87 class Dog(Animal):
 88 
 89     def __init__(self, name):
 90         self.name = name
 91         self.breed=''
 92 
 93     def look_after_house(self):
 94         print('汪汪叫')
 95 
 96 
 97 # ######### 执行 #########
 98 
 99 c1 = Cat('小白家的小黑猫')
100 c1.eat()
101 
102 c2 = Cat('小黑的小白猫')
103 c2.drink()
104 
105 d1 = Dog('胖子家的小瘦狗')
106 d1.eat()
107 
108 使用继承来解决代码重用的例子
继承的例子

在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时

我们不可能从头开始写一个类B,这就用到了类的继承的概念。

通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用。

 1 class People():
 2     school = 'sh'
 3 
 4     def __init__(self, name, age, gender, source=None):
 5         self.name = name
 6         self.age = age
 7         self.gender = gender
 8 
 9 
10 class Student(People):
11 
12     def __init__(self, name, age, gender, source=None):
13         if source==None:
14             source = []
15             People.__init__(self, name, age, gender)
16         self.sources = source
17 
18     def choose_source(self, source):
19         self.sources.appen(source)
20         print(f'用户{self.name}选择了{self.sources}课程')
21 
22 stu = Student('jason', 18, 'male', )
23 
24 
25 
26 class Teacher(People):
27 
28     def __init__(self, name, age, gender, level):
29         People.__init__(self, name, age, gender)
30         self.level = level
31 
32     def score(self, stu_obj, score):
33         stu_obj.score = score
34         print(f'{self.name}老师修改了学生{stu_obj.name}的成绩{score}')
35 
36 tea = Teacher('ly', 20, 'female', 10)
37 print(stu.name, tea.name)
代码重用的例子

提示:用已经有的类建立一个新的类,这样就重用了已经有的软件中的一部分设置大部分,大大生了编程工作量,这就是常说的软件重用,不仅可以重用自己的类,也可以继承别人的,比如标准库,来定制新的数据类型,这样就是大大缩短了软件开发周期,对大型软件开发来说,意义重大。

四、派生类&子类(了解)

当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。

 1 class Animal:
 2     '''
 3     人和狗都是动物,所以创造一个Animal基类
 4     '''
 5     def __init__(self, name, aggressivity, life_value):
 6         self.name = name  # 人和狗都有自己的昵称;
 7         self.aggressivity = aggressivity  # 人和狗都有自己的攻击力;
 8         self.life_value = life_value  # 人和狗都有自己的生命值;
 9 
10     def eat(self):
11         print('%s is eating'%self.name)
12 
13 class Dog(Animal):
14     '''
15     狗类,继承Animal类
16     '''
17     def bite(self, people):
18         '''
19         派生:狗有咬人的技能
20         :param people:  
21         '''
22         people.life_value -= self.aggressivity
23 
24 class Person(Animal):
25     '''
26     人类,继承Animal
27     '''
28     def attack(self, dog):
29         '''
30         派生:人有攻击的技能
31         :param dog: 
32         '''
33         dog.life_value -= self.aggressivity
34 
35 egg = Person('jason',10,1000)
36 ha2 = Dog('二愣子',50,1000)
37 print(ha2.life_value)
38 print(egg.attack(ha2))
39 print(ha2.life_value)
帮助理解-例子

注意:像ha2.life_value之类的属性引用,会先从实例中找life_value然后去类中找,然后再去父类中找...直到最顶级的父类。

 

在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数功能,应该是用调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,因此即便是self参数也要为其传值.

在python3中,子类执行父类的方法也可以直接用super方法。

 1 class A:
 2     def hahaha(self):
 3         print('A')
 4 
 5 class B(A):
 6     def hahaha(self):
 7         super().hahaha()
 8         #super(B,self).hahaha()
 9         #A.hahaha(self)
10         print('B')
11 
12 a = A()
13 b = B()
14 b.hahaha()
15 super(B,b).hahaha()
16 
17 帮你了解super
帮助了解--super

通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。

当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如教授是老师。

如下图:

 五、单继承/多继承

5.1 单继承:

对象先从自己的.__dict__中查找,找不到,再去子类中查找,还没有去父类中找.父类中没有就去object中查找,都没有就会报错。

# 新式类:按照广度优先查询
# 经典类:按照深度优先查询

 1 class Foo:
 2     def f1(self):
 3         print('Foo.f1')
 4 
 5     def f2(self):
 6         #
 7         print('Foo.f2')
 8         self.f1()
 9 
10 
11 class Bar(Foo):
12     def f1(self):
13         print('Bar.f1')
14 
15 
16 obj = Bar()  # {}
17 obj.f2()
18 
19 
20 # 练习
21 class Foo:
22     def __f1(self):  # _Foo__f1()
23         print('Foo.f1')
24 
25     def f2(self):
26         #
27         print('Foo.f2')
28         self.__f1()  # _Foo__f1()
29 
30 
31 class Bar(Foo):
32     def __f1(self):  # # _Bar__f1()
33         print('Bar.f1')
34 
35 
36 obj = Bar()  # {}
37 obj.f2()
单继承属性查找-练习

 5.2 多继承之菱形问题

一个类可以继承多个父类.

* 菱形问题
基类A 被父类B 与 父类C继承
子类继承 父类B 父类C
 1 # 新式类:按照广度优先查询
 2 # 经典类:按照深度优先查询
 3 class A(object):
 4     def test(self):
 5         print('from A')
 6 
 7 
 8 class B(A):
 9     # def test(self):
10     #     print('from B')
11     pass
12 
13 class C(A):
14     # def test(self):
15     #     print('from C')
16     pass
17 
18 
19 class D(B):
20     # def test(self):
21     #     print('from D')
22     pass
23 
24 class E(C):
25     # def test(self):
26     #     print('from E')
27     pass
28 
29 
30 class F(D, E):
31     # def test(self):
32     #     print('from F')
33     pass
34 
35 
36 f1 = F()
37 f1.test()
多继承属性查找-练习

示例:

class A:
    pass


class B(A):
    pass


class C(A):
    pass


class D(B, C):
    pass

Python 解决方案:
每定义一个类,Python都会计算出一个所有基类的线性MRO列表,
继承的查找顺序按列表的顺序从左往右查找.

Python3中新式类 内置mro方法.
查询某个类的继承查找顺序,就以那个类为起点展开这个列表.
使用:
类名.__mor__
类名.mro()
Python2没有.mro() 方法

子类会优先父类被检测到(子子类>子类>父类>基类),多个父类会依据它们在列表中的顺序查找。
class A:
    pass


class B(A):
    pass


class C(A):
    pass


class D(B, C):
    pass
    
    
print(D.mro())
print(D.__mro__)

"""
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>] 列表
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) 元组
"""

5.3深度优先

非菱形:
经典类与新式类的属性查找顺序一样,
按深度优先的方式查找,条分支找到底,
找完一条之后,再查另一条分支,最后是object.


Python2       B(E)   E()         第一分支找到底E
A(B, C, D)    C(F)   F()          第二分支找到底F  
             D()                 第三分支找到底D
             最后找object() 
             
Python3       B(E)   E(object)    第一分支找到底E
A(B, C, D)    C(F)   F(object)    第二分支找到底F  
             D(object)           第三分支找到底D
             最后找object() 
 1 # Python3 
 2 class E():
 3     pass
 4 
 5 
 6 class B(E):
 7     pass
 8 
 9 
10 class F():
11     pass
12 
13 
14 class C(F):
15     pass
16 
17 
18 class D():
19     pass
20 
21 
22 class A(B, C, D):
23     pass
24 
25 
26 print(A.mro())
27 
28 """
29 [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class 'object'>]
30 """
程序示例

5.4 广度优先

菱形结构:
经典类按深度优先查找.
新式类按广度优先方式查找.

广度优点按mro列表的顺序镜像查找.
检索第一条分支的之后不会访问到底, 
之后在检索第二条分支,
当分支都被检测完之后,在查找所有分支的同一个基类.

Python3       B(E)   E(G)               分支直接查到E()
A(B, C, D)    C(F)   F(G)      G()      E()没有就放回其他分支...
             D(G)                      最后在找G()
 1 # python2.7下测试
 2 class G():
 3     # def test(self):          # 4
 4     #     print('from G')
 5     pass
 6 
 7 class F(G):
 8     # def test(self):          # 3
 9     #     print('from F')
10     pass
11 
12 class E(G):
13     # def test(self):          # 2
14     #     print('from E')
15     pass
16 
17 class B(E):
18     # def test(self):
19     #     print('from B')      # 1
20     pass
21 
22 class C(F):
23     # def test(self):          # 5
24     #     print('from C')
25     pass
26 
27 class D(G):                  
28     def test(self):             # 6
29          print('from D')
30     
31 class A(B, C, D):
32     pass
33 
34 obj = A()
35 obj.test()
程序示例
# Python 3.8
print(A.mro())

[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class '__main__.G'>, <class 'object'>]

六、继承推导★

6.1 推导

提取对象之间的相同数据,得一个新的类.
在一个类中调用另一个类.
 1 class School:
 2     school = '桂电'
 3 
 4     def __init__(self, name, age, gender):
 5         self.name = name
 6         self.age = age
 7         self.gender = gender
 8 
 9 
10 class Student:
11     school = School.school
12     def __init__(self, name, age, gender, courses):
13         School.__init__(self, name, age, gender)
14         # 课程
15         self.courses = courses
16 
17     # 学生信息
18     def stu_info(self):
19         print('< %s >' % self.courses)
20 
21 
22 # 教师类
23 class Teacher:
24     school = School.school
25     def __init__(self, name, age, gender, title):
26         School.__init__(self, name, age, gender)
27         # 教什么
28         self.title = title
29 
30     # 老师的信息
31     def Tea_info(self):
32         print('< %s >' % self.title)
33 
34 
35 stu1 = Student('kid', 18, 'male', 'python')
36 tea1 = Teacher('xx', 30, 'male', 'python')
37 print(stu1.__dict__)  # {'name': 'kid', 'age': 18, 'gender': 'male', 'courses': 'python'}
38 print(tea1.__dict__)  # {'name': 'xx', 'age': 30, 'gender': 'male', 'courses': 'python'}

6.2 推导

   提取类与类之间的相同数据,得出一个类,然后去继承这个类.。

  在类名后的括号内写上继承的名称. eg: 类名(继承谁就写谁的名字),对象可以得到类的所有属性,子类可以继承父类的所有的属性。

  子类中没有定义__init__方法, 但是会从父类中找__init__方法,找到变初始化数据。

 1 # 学校类
 2 class School:
 3     school = 'XXX'
 4 
 5     def __init__(self, name, age, gender):
 6         self.name = name
 7         self.age = age
 8         self.gender = gender
 9 
10         
11 # 学生类
12 class Student(School):
13     pass
14 
15 
16 # 教师类
17 class Teacher(School):
18     pass
19 
20 
21 stu1 = Student('kid', 18, 'male')
22 tea1 = Teacher('xx', 30, 'male')
23 print(stu1.__dict__)
24 print(tea1.__dict__)
25 print(stu1.school)
26 print(tea1.school)
27 """
28 {'name': 'kid', 'age': 18, 'gender': 'male'}
29 {'name': 'xx', 'age': 30, 'gender': 'male'}
30 XXX
31 XXX
32 """

 

 

 

 

posted @ 2021-12-06 20:17  Panda_Xin  阅读(61)  评论(0编辑  收藏  举报