一.什么是继承
1. 继承一种新建类的方式,新建的类称之为子类/派生类,被继承的类称之为父类\基类\超类
2.python中继承的特点:
1).子类可以遗传/重用父类的属性
2).python中一个子类可以同时继承多个父类
3).在继承背景下去说,python中的类分为两种:新式类,经典类
新式类:但凡继承了object的类,以及该类的子类,都是新式类,在python3中一个类即便是没有显式地继承任何类,默认就会 继承object, 即python3中所有的类都是新式类
经典类:没有继承object的类,以及该类的子类,都是经典类,在python2中才区分新式类与经典类,在python2中一个类如果没 有显式地继承任何类,也不会继承object类
3.为何要用继承
减少类与类之间代码冗余
4.如何用继承
class Parent1():
pass
class Parent2():
pass
class Sub1(Parent1):
pass
class Sub2(Parent1,Parent2):
pass
print(Parent1.__bases__)
print(Parent2.__bases__)
print(Sub1.__bases__)
print(Sub2.__bases__)
class Parent1():
xxx=333
class Sub1(Parent1):
pass
obj=Sub1()
# obj.xxx=222
print(obj.xxx)
# 问题:
# 1 子类如何重用父类的属性
# 2 在继承背景下,属性查找的优先级
# 3 新式类与经典类在属性查找上的区别
二. 属性查找
1.在单继承背景下的属性查找优先级:对象-->对象所在的类-->父类-->父类的父类......
class Foo:
def f1(self):
print('Foo.f1')
def f2(self):
print('Foo.f2')
self.f1()
class Bar(Foo):
def f1(self):
print('Foo.f1')
b=Bar()
b.f2()
"""
Foo.f2
Foo.f1
"""
2.在多继承背景下属性查找的优先级:
如果一个子类继承多个分支(多个分支没有共同继承一个非object的类)此时属性的查找优先级是:对象->对象的类->按 照从左往右的顺序一个分支一个分支的找下去
# 第四层:
class G:
# x = 'G'
pass
# 第三层
class E(G):
# x = 'E'
pass
class F:
# x = 'F'
pass
# 第二层
class B(E):
# x = 'B'
pass
class C(F):
# x = 'C'
pass
class D:
# x = 'D'
pass
# 第一层
class A(B, C, D):
# x = 'A'
pass
obj = A()
# obj.x = 111
# print(obj.x)
print(A.mro())
"""
<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.G'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class 'object'>
"""
3.菱形继承问题
新式类:广度优先查找,从左往右一个分支一个分支的查找,在最后一个分支才去查找顶级类
经典类:深度优先查找,从左往右一个分支一个分支的查找,在第一个分支就查找顶级类
class G(object):
# x = 'G'
pass
# 第三层
class E(G):
# x = 'E'
pass
class F(G):
# x = 'F'
pass
# 第二层
class B(E):
# x = 'B'
pass
class C(F):
# x = 'C'
pass
class D(G):
# x = 'D'
pass
# 第一层
class A(B, C, D):
# x = 'A'
pass
obj=A()
# obj.x=111
print(obj.x)
#新式类(广度优先): obj->A->B->E->C-F->D->G->object
#经典类(深度优先): obj->A->B->E->G->C-F->D
python专门为新式类内置了一个mro的方法,用来查看c3算法的计算结果,结果是print(A.mro())
三.子类中调用父类的方法
1.方法一:指名道姓地引用某一个类中的函数,即父类名.父类方法()
总结: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=0):
OldboyPeople.__init__(self,name,age,sex)
self.score = score
def choose_course(self):
print('%s choosing course' % self.name)
class OldboyTeacher(OldboyPeople):
def __init__(self,name,age,sex,level):
OldboyPeople.__init__(self,name,age,sex)
self.level=level
def score(self,stu,num):
stu.score=num
stu1=OldboyStudent('刘二蛋',38,'male')
print(stu1.__dict__)
tea1=OldboyTeacher('egon',18,'male',10)
print(tea1.__dict__)
2.方法二:super() 必须在子类中用
在python2中:super(自己的类名,自己的对象)
在python3中:super()
调用该函数会得到一个特殊的对象,该对象专门用来访问父类中的属性,!!!完全参照mro列表!!!!
总结:1.严格依赖继承的mro列表 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=0):
super(OldboyStudent,self).__init__(name,age,sex)
self.score = score
def choose_course(self):
print('%s choosing course' % self.name)
class OldboyTeacher(OldboyPeople):
def __init__(self,name,age,sex,level):
super().__init__(name,age,sex)
self.level=level
def score(self,stu,num):
stu.score=num
stu1=OldboyStudent('刘二蛋',38,'male')
print(stu1.__dict__)
tea1=OldboyTeacher('egon',18,'male',10)
print(tea1.__dict__)
强调:两者使用哪一种都可以,但最好不要混合使用 即使没有直接继承关系,super()仍然会按照mro继续往后找
#A没有继承B,但是A内super会基于C.mro()继续往后找
class A:
def test(self):
super().test()
class B:
def test(self):
print('from B')
class C(A,B):
pass
c=C()
c.test() #打印结果:from B
print(C.mro())
#[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
指名道姓与super()的区别
#指名道姓
class A:
def __init__(self):
print('A的构造方法')
class B(A):
def __init__(self):
print('B的构造方法')
A.__init__(self)
class C(A):
def __init__(self):
print('C的构造方法')
A.__init__(self)
class D(B,C):
def __init__(self):
print('D的构造方法')
B.__init__(self)
C.__init__(self)
pass
f1=D() #A.__init__被重复调用
'''
D的构造方法
B的构造方法
A的构造方法
C的构造方法
A的构造方法
'''
#使用super()
class A:
def __init__(self):
print('A的构造方法')
class B(A):
def __init__(self):
print('B的构造方法')
super(B,self).__init__()
class C(A):
def __init__(self):
print('C的构造方法')
super(C,self).__init__()
class D(B,C):
def __init__(self):
print('D的构造方法')
super(D,self).__init__()
f1=D() #super()会基于mro列表,往后找
'''
D的构造方法
B的构造方法
C的构造方法
A的构造方法
'''
当你使用super()函数时,Python会在MRO列表上继续搜索下一个类。只要每个重定义的方法统一使用super()并只调用它一次,那么控制流最终会遍历完整个MRO列表,每个方法也只会被调用一次(注意注意注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表)