一.继承介绍
1 什么是继承
继承一种新建类的方式,新建的类称之为子类/派生类,被继承的类称之为父类\基类\超类
python中继承的特点:
1. 子类可以遗传/重用父类的属性
2. python中一个子类可以同时继承多个父类
3. 在继承背景下去说,python中的类分为两种:新式类,经典类
新式类: 但凡继承了object的类Foo,以及该类的子类...都是新式类
在python3中一个类即便是没有显式地继承任何类,默认就会继承object
即python3中所有的类都是新式类
经典类:没有继承object的类,以及该类的子类...都是经典类
在python2中才区分新式类与经典类,
在python2中一个类如果没有显式地继承任何类,也不会继承object
二.用继承的目的:减少代码冗余
例如上节的OldboyStudent类,如果里面加上成绩(score)属性
再定义一个老师类(OldboyTeacher),里面加上老师的等级(level)
这样两个类里面都会出现self.name=name\self.age=age....
因此,我们可以像提取对象的相同属性来定义类一样来提取类的相同属性定义一个父类
例如:
猫可以:喵喵叫 吃 喝 拉 撒
猫可以:汪汪叫 吃 喝 拉 撒
如果我们要分别为猫和狗创建一个类,那么就需要为 猫 和 狗 实现他们所有的功能,伪代码如下:
class 猫:
def 喵喵叫(self):
print '喵喵叫'
def 吃(self):
# do something
def 喝(self):
# do something
def 拉(self):
# do something
def 撒(self):
# do something
class 狗:
def 汪汪叫(self):
print '汪汪叫'
def 吃(self):
# do something
def 喝(self):
# do something
def 拉(self):
# do something
def 撒(self):
# do something
上述代码不难看出,吃、喝、拉、撒是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次。如果使用 继承 的思想,如下实现:
动物:吃喝拉撒
猫:喵喵叫(猫继承动物的功能)
狗:汪汪叫(狗继承动物的功能)
伪代码如下:
class 动物:
def 吃(self):
# do something
def 喝(self):
# do something
def 拉(self):
# do something
def 撒(self):
# do something
# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class 猫(动物):
def 喵喵叫(self):
print'喵喵叫'
class 狗(动物):
def 汪汪叫(self):
print'喵喵叫'
代码实现:
class Animal:
def eat(self):
print("%s 吃 "%self.name)
def drink(self):
print("%s 喝 "%self.name)
def shit(self):
print("%s 拉 "%self.name)
def pee(self):
print("%s 撒 "%self.name)
class Cat(Animal):
def__init__(self, name):
self.name = name
self.breed ='猫'
def cry(self):
print('喵喵叫')
class Dog(Animal):
def__init__(self, name):
self.name = name
self.breed='狗'
def cry(self):
print('汪汪叫')
# ######### 执行 #########
c1 = Cat('小白家的小黑猫')
c1.eat()
c2 = Cat('小黑的小白猫')
c2.drink()
d1 = Dog('胖子家的小瘦狗')
d1.eat()
属性的查找
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')
b=Bar()
b.f2()
'''
输出结果:
Foo.f2
Bar.f1
'''
三.在子类派生的新方法中重用父类功能的方式一
在子类派生出的新方法中重用父类功能的方式一:
指名道姓地引用某一个类中的函数
总结:
1. 与继承无关
2. 访问是类的函数,没有自动传值的效果
class OldboyPeople:
school='oldboy'
def __init__(self,name,age,sex):
self.name=name
self.age=age
self.sex=sex
class OldboyStudent(OldboyPeople):
def __init__(self,name,age,sex,score):
OldboyPeople.__init__(self,name,age,sex)
self.score=score
def choose_course(self):
print('%s 正在选课'% self.name)
class OldboyTeacher(OldboyPeople):
def __init__(self,name,age,sex,leval):
OldboyPeople.__init__(self,name,age,sex)
self.leval=leval
def paly_score(self,stu,score):
stu.score=score
print('%s 老师给%s同学打了%s分'%(self.name,stu.name,score))
stu1=OldboyStudent('wzj','18','male',100)
print(stu1.__dict__)
stu1.choose_course()
tea=OldboyTeacher('egon','18','male',10)
print(tea.__dict__)
tea.paly_score(stu1,720)
print(stu1.score)
四 属性查找
1.在单继承背景下属性的查找优先级:对象->对象的类->父类->父类.....
2.在多继承背景下属性的查找优先级:
1).如果一个子类继承多个分支(多个分支没有共同继承一个非object的类)
此时属性的查找优先级是:对象->对象的类->按照从左往右的顺序一个分支一个分支的找下去
如下图,先从A的实例化的对象中找,如果没有,按照下图所示数字顺序查找
菱形继承问题:
新式类 : 广度优先查找,从左往右一个分支一个分支的查找,在最后一个分支才去查找顶级类
obj->A->B->E->C-F->D->G->object
经典类 : 深度优先查找,从左往右一个分支一个分支的查找,在第一个分支就查找顶级类
obj->A->B->E->G->C-F->D
mro:用来查看查找顺序的方法
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'>]
六 .在子类派生出的新方法中重用父类功能的方式二
super() 必须在类中用
在python2中:super(自己的类名,自己的对象)
在python3中:super()
调用该函数会得到一个特殊的对象,该对象专门用来访问父类中的属性,!!!完全参照mro列表!!!!
总结:
1. 严格依赖继承的mro列表
2. 访问是绑定方法,有自动传值的效果
class A:
def f1(self):
print('A.f1')
super().f2()
class B:
def f2(self):
print('B.f2')
class C(A,B):
def f2(self):
print('C.f2')
obj=C()
# print(C.mro())
obj.f1()#===>A.f1 B.f2
这里调用obj.f1(),找到了A类下的f1,打印了'A.f1 ',再执行了super().f2(),那么这个f2()是找B中的还是C中的
先看对象所在类的mro表为:[<class '__main__.C'>, <class'__main__.A'>, <class '__main__.B'>, <class 'object'>]
A中的super,所以找A的父类,这里的父类并不是明显的父类,二十mro表中A后面的类,即B,所以这里打印的是B.f2