继承
一、什么是继承
继承是一种新建类的方式,继承了一个类,类中的属性和方法就在子类中了。
1.1各种类介绍
基类:在面向对象设计中,被定义为包含所有实体共性的class类型,被称为“基类”。经常用于封装。
派生类:派生类必须通过使用派生类列表明确指出它是从哪个(哪些)基类继承而来的
父类:被子类继承的类
子类:继承父类的类
1.2 如何使用继承
class 类名(父类1,父类2)
pass
1.3 新式类和经典类
新式类:只要继承了object类,就是新式类,在python3中都是新式类。
python3 中,默认继承object类
class A: #此处就默认他继承了object类
pass
python2 中,需要显示指定的继承object类
经典类:没有继承object类,就是经典类
二、利用继承减少代码冗余
2.1 多层继承
class D: #D继承object
print('ddd')
class C(D): #c继承D
print('ccc')
class B(C): #B继承c
print('bbb')
class A(B): #A继承B
print('aaa')
2.2 多继承
class D: #D继承object
print('ddd')
class C:
print('ccc')
class B:
print('bbb')
class A(B,C,D): #A继承B,C,D
print('aaa')
2.3 多继承的多层继承
class G: #G继承object
print('ddd')
class F(G):
print('ccc')
class E(G):
print('bbb')
class D(G):
print('ddd')
class C(F):
print('ccc')
class B(E):
print('bbb')
class A(B,C,D): #A继承B,C,D
print('aaa')
当我们写到上面的代码时,不料代码的继承写为了如下图继承的样子,我们称他为继承的菱形问题
继承的菱形问题:新式类和经典类的查找顺序不一样
A-->B-->E-->G-->C-->F-->D
A-->B-->E-->C-->F-->D-->G
--继承的菱形问题(显示的都继承一个类,不是object类):新式类和经典类的查找顺序是不一样的
- 新式类(py3中全是新式类):广度优先---从左侧开始,一直往上找,找到菱形的顶点结束(不包括菱形顶点),继续下一个继承的父类往上找,找到菱形的顶点结束(不包括菱形顶点),最后找到菱形顶点
- 经典类(只有py2中才有):深度优先---从左侧开始,一直往上找,找到菱形的顶点结束(包括菱形顶点)继续下一个继承的父类往上找,找到菱形的顶点结束(不包含菱形定点)
--属性的查找顺序是什么?
先找对象----》类中找-----》父类中找(多继承)-----》报错
三、重用父类方法
3.1 重用父类方法方式一
指名道姓的用,跟继承没有关系
-
指名道姓的方式在什么情况下用?
-
没有继承关系
-
如果继承了多个父类,super是按照mro列表找,现在想指名道姓的用某个父类的某个方法,就需要指名道姓的使用
-
class Person: #父类 #默认继承object类
school = 'oldboy'
def __init__(self,name,age)
self.name = name
self.age = age
def study(self):
print('This is study method')
class Student(Person):
school = 'yyyyy' #自己在里面面添加的属性
def __init__(self,name,age,course):
#如何重用父类的__init__方法
person.__init__(self,name,age) #重用Person,就会调用父类里面的所有东西
self.course = course
def study(self):
Person.study(self)
print(f"{self.name} is learning)
Stu1 = Student('yjy',20,'python')
stu1.school='xxx'
print(stu1.school)
stu1.study()
-----------------------------------------------------------
yyyy
This is study method
yjy is learning
3.2 重用父类方法方式二
通过super关键字,跟继承有关系
- 有继承关系的时候,通常用super
class Person: #父类 #默认继承object类
school = 'oldboy'
def __init__(self,name,age)
self.name = name
self.age = age
def study(self):
print('This is study method')
class Student(Person):
school = 'yyyyy' #自己在里面面添加的属性
def __init__(self,name,age,course):
#如何重用父类的__init__方法
#super() 会按照mro列表拿到父类对象,对象来调用绑定方法,不需要传递第一个参数
#super()相当于得到了一个特殊对象,第一个参数不需要传,调用绑定方法,会把自己传过去
super().__init__(name,age) #经典类中写法:super(Student,self).__init__(name,age),为了兼容py2
self.course = course
def study(self): #定义一个study方法
Person.study(self)
print(f"{self.name} is learning)
--------------------------------------------------------------
yyyy
This is study method
yjy is learning
ps:A.mro
,A.__mro__
是继承查找顺序的列表(mro只在新式类中有mro)
super很重要
四、回顾绑定方法
#定义一个学生类
class Student:
def __init__(self,name,age):
self.name = name
self.age = age
#定义一个study方法
def study(self):
print(self.name)
print('study.....')
#定义一个改变名字的方法
def change_name(self,new_name):
print(f"原来的名字是{self.name}")
self.name = new_name
print(f"现在的名字是{self.name}")
#方式一:类调用对象的绑定方法(写在类中的函数,没加装饰器),有几个参数就需要传几个参数
Student.__init__(123,'yjy',20) #会报错AttributeError: 'str' object has no attribute 'name'
#方式二:类实例化对象,会自动调用__init__完成初始化操作,象的绑定方法的特殊之处,会把对象本身当做第一个参数传入
stu = Student('yjy',18)
print(stu.name) #会输出yjy
stu.study() #会输出yjy study.....
#修改学生姓名的四种方式
stu = Student('aaa',18)
#方式一(直接进行修改属性)
stu.name = 'bbb'
print(stu.name)
#方式二(对象调用change_name方法进行修改)
stu.change_name('bbb')
print(stu.name)
#方式三(函数调用change_name方法进行修改)
Student.change_name(stu,'bbb')
print(stu.name)
#方式四(定义一个普通的函数)
def chang_name(obj,name):
print(f"原来的名字是{obj.name}")
obj.name = name
print(f"现在的名字是{obj.name}")
chang_name(stu,'bbb')
print(stu.name)
#其实函数调用change_name方法进行修改属性,就是去定义一个函数去修改,没有什么特别的