继承及属性查找+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 """

继承及属性查找+super()和mro()+多态的更多相关文章

  1. 继承&派生 属性查找

    # 在单继承背景下,无论是新式类还是经典类属性查找顺序都一样 # 先object->类->父类->... 实例: class Foo: def f1(self): print('Fo ...

  2. python面向编程:类继承、继承案例、单继承下属性查找、super方法

    一.类的继承 二.基于继承解决类与类的代码冗余问题 三.在单继承背景下属性的查找 四.super的方法 一.类的继承 1.什么是继承? 在程序中继承是一种新建子类的方法的方式,新创建的类成为子类\派生 ...

  3. day 24 面向对象之继承及属性查找顺序

    组合 组合:自定义类的对象作为另外一个类的属性 class Teacher: def init(self, name, age): self.name = name self.age = age t1 ...

  4. 详解 继承(下)—— super关键字 与 多态

    接上篇博文--<详解 继承(上)-- 工具的抽象与分层> 废话不多说,进入正题: 本人在上篇"故弄玄虚",用super();解决了问题,这是为什么呢? 答曰:子类中所有 ...

  5. Python全栈--9.1--面向对象进阶-super 类对象成员--类属性- 私有属性 查找源码类对象步骤 类特殊成员 isinstance issubclass 异常处理

    上一篇文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使用(可以讲多函数中公用的变量封装到对象中) 对象 ...

  6. 面向对象:继承(经典类&新式类继承原理、属性查找)、派生

    继承: 继承是指类与类之间的关系,是一种“什么”是“什么”的关系. 继承的功能之一就是用来解决代码重用问题 继承是一种创建新类的方式,在Python中,新建的类可以继承一个或多个父类,父类又可以称为基 ...

  7. python基础-9.1 面向对象进阶 super 类对象成员 类属性 私有属性 查找源码类对象步骤 类特殊成员 isinstance issubclass 异常处理

    上一篇文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使用(可以讲多函数中公用的变量封装到对象中) 对象 ...

  8. Python大神必须掌握的技能:多继承、super和MRO算法

    本文主要以Python3.x为例讲解Python多继承.super以及MRO算法. 1. Python中的继承 任何面向对象编程语言都会支持继承,Python也不例外.但Python语言却是少数几个支 ...

  9. super之mro列表牵引出c3算法

    目录 一:super的使用 二:super之mro列表牵引出c3算法 三:mro列表总结使用 一:super的使用 class Person(object): def __init__(self, n ...

随机推荐

  1. mysql 的 if 和 SQL server 的 iif

    在sql语句中,mysql 使用 if 而SQL server 使用iif 如 mysql : SELECT IF(1<2,'yes ','no'); sql server: SELECT II ...

  2. linux 设置root 密码

    指令意思: sudo -i  是 切换到root权限 ,如果没有密码,则直接可以操作,有密码则会要求输入密码 sudo passwd root  是修改密码指令 ,回车后 提示输入新密码 新密码需要输 ...

  3. Redisson-关于使用订阅数问题

    一.前提 最近在使用分布式锁redisson时遇到一个线上问题:发现是subscriptionsPerConnection or subscriptionConnectionPoolSize 的大小不 ...

  4. X-Forwarded-for漏洞解析

    首先了解X-Forwarded-for(简称:XFF) X-Forwarded-for:简称XFF,它代表客户端,也就是HTTP的请求真实的IP,只有在通过了HTTP代理或者负载均衡器时才会添加该项. ...

  5. MySQL数据库学习打卡 DAY2

    今天学习了MySQL的DML操作,完成了关于增删改查所有基本内容的学习.

  6. 【一个小实验】腾讯云的redis的主从结构的特性

    使用腾讯云上的redis,可以添加多个备机的分片,并且可以选择不同的账号来设定主从读写的策略. 现在设置两个账号:primary-主节点写,主节点读:secondary-主节点写,从节点读. 研究出了 ...

  7. Spring Boot Starter 和 ABP Module

    Spring Boot 和 ABP 都是模块化的系统,分别是Java 和.NET 可以对比的框架.模块系统是就像乐高玩具一样,一块一块零散积木堆积起一个精彩的世界.每种积木的形状各不相同,功能各不相同 ...

  8. C++虚函数和静态函数调用方式

    简单情况: #include<iostream> using namespace std; class A { public: virtual void foo() { cout < ...

  9. DispatcherServlet的init源代码

    springmvc执行过程源代码分析 1,tomcat启动,创建容器的过程 通过load-on-start标签指定的1,创建DispatcherServlet对象, DispatcherServlet ...

  10. iptables的概念与底层原理(详解)

    目录 一:iptables 1.iptables简介 2.什么是防火墙? 3.防火墙种类 二:iptables基本介绍 1.解析内容 三:iptables流程(讲解) 1.流入本机 2.解析(流入本机 ...