继承及属性查找+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
通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。
当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如教授是老师。
如下图:
五、
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 """