day26
一.继承介绍
1.什么是继承
继承是一种新建类的方式,新建的类称之为子类,被继承的类称之为父类,基类,超类
python 支持多继承
2.为何要继承
子类会遗传父类的属性,所以继承是用来解决类与类之间代码冗余问题
3.如何实现继承
class Parent1: # 定义父类1
pass
class Parent2: # 定义父类2
pass
class Sub1(Parent1): # 单继承
pass
class Sub2(Parent1,Parent2): # 多继承
pass
'''
通过类的内置属性__bases__可以查看类继承的所有父类
'''
print(Sub1.__bases__) # (<class '__main__.Parent1'>,)
print(Sub2.__bases__) # (<class '__main__.Parent1'>, <class '__main__.Parent2'>)
继承案例:重复代码还是很多,可以再改进一下
class OldboyPeople:
school = 'oldboy'
class Student(OldboyPeople):
def __init__(self,name,age,gender,stud_id,course):
self.name=name
self.age=age
self.gender=gender
self.stu_id=stud_id
def choose(self):
print('%s 正在选课'%self.name)
class Teacher(OldboyPeople):
def __init__(self,name,age,gender,salary,level):
self.name=name
self.age=age
self.gender=gender
self.salary=salary
self.level=level
def score(self,stu,num):
stu.num=num
stu1=Student('alex',20,'male',1001,'python全栈开发')
tea1=Teacher('egon',18,'male',2000,10)
print(stu1.school) # oldboy
二.在子类派生的新方法中重用父类的功能方式之一
方式一:指名道姓地调用某一个类的函数
特点:不依赖于继承关系
改进后案例:
在子类派生的新方法中重用父类的功能,解决了代码冗余问题
class OldboyPeople:
school = 'oldboy'
# 空对象,'alex',20,'male'
def __init__(self,name,age,gender):
self.name=name
self.age=age
self.gender=gender
def f1(self):
print('111111')
class Student(OldboyPeople):
# 空对象,'alex',20,'male',1001,'python全栈开发'
def __init__(self,name,age,gender,stu_id,course):
OldboyPeople.__init__(self,name,age,gender) # OldboyPeople.__init__(空对象,'alex',20,'male')
self.stu_id=stu_id
self.course=course
def choose(self):
print('%s 正在选课'%self.name)
def f1(self):
OldboyPeople.f1(self)
print('2222')
class Teacher(OldboyPeople):
def __init__(self,name,age,gender,salary,level):
OldboyPeople.__init__(self,name,age,gender) # OldboyPeople.__init__(空对象,'alex',20,'male')
self.salary=salary
self.level=level
def score(self,stu,num):
stu.num = num
stu1 = Student('alex',20,'male',1001,'python全栈开发')
tea1 = Teacher('egon',18,'male',2000,10)
stu1.f1()
tea1.f1()
三.属性查找
有了继承关系,对象在查找属性时,先从对象自己的__dict__中找,如果没有则去子类中找,然后再去父类中找
例1:
class Foo:
def f2(self):
print('Foo.f2')
def f1(self):
print('Foo.f1')
self.f2() # obj.f2()
class Bar(Foo):
def f2(self):
print('Bar.f2')
obj = Bar()
obj.f1()
'''
先从对象中找f1,没找到,去对象所在的类Bar中找,没找到,去父类Foo中找,找到了
打印Foo.f1,运行self.f2,也就是obj.f2(),在类Bar中找到f2,
因而打印结果为Bar.f2
Foo.f1
Bar.f2
'''
例2:父类如果不想让子类覆盖自己的方法,可以在方法名前加前缀__,将方法设置为私有的
class Foo:
def __f2(self): # 变形为 _Foo__f2
print('Foo.f2')
def f1(self):
print('Foo.f1')
self.__f2() # 变形为obj._Foo__f2(),因此只会调用自己所在的类中的方法
class Bar(Foo):
def __f2(self): # 变形为 _Bar__f2
print('Bar.f2')
obj = Bar()
obj.f1()
'''
Foo.f1
Foo.f2
'''
四.继承的实现原理
1.补充知识:
新式类:但凡是继承了object类的子类,以该子类子子孙孙类都称之为新式类
经典类:没有继承object类的子类,以该类子子孙孙类都称之为经典类
在python3中没有继承任何类的会默认继承object类
所以python3中全都是新式类,python2中才有经典类:
class Foo():
pass
print(Foo.__bases__) # (<class 'object'>,)
'''
object类提供了一些常用内置方法的实现,
如用来在打印对象时返回字符串的内置方法__str__
'''
在python2中可以自己主动继承object类
2.继承的实现原理
菱形问题:
一个子类继承的多条件分支最终汇聚到一个非object类,在菱形继承下
新式类与经典类关于属性查找的方式不同
新式类:广度优先
经典类:深度优先
例1:非菱形继承,经典类与新式类的属性查找顺序都一样
都是一个分支一个分支地找下去,最后找object
class E:
# def test(self):
# print('from E')
pass
class F:
# def test(self):
# print('from F')
pass
class B(E):
# def test(self):
# print('from B')
pass
class C(F):
# def test(self):
# print('from C')
pass
class D:
def test(self):
print('from D')
class A(B,C,D):
def tset(self):
print('from A')
obj = A()
obj.test()
例2:菱形继承:经典类与新式类的属性查找顺序不一样
新式类:广度优先,按照顺序一个分支一个分支找,最后找大boss(大家的父类)
经典类:深度优先,会在检索第一条分支的时候就一条道走到黑,检索大boss(大家的父类)找不到,再检索另一条分支
class G(object):
def test(self):
print('from G')
class E(G):
# def test(self):
# print('from E')
pass
class F(G):
def test(self):
print('from F')
class B(E):
# def test(self):
# print('from B')
pass
class C(F):
def test(self):
print('from C')
class D(G):
def test(self):
print('from D')
class A(B,C,D):
# def test(self):
# print('from A')
pass
# obj = A()
# obj.test()
# MRO列表就是一个简单的所有基类的线性顺序列表
print(A.mro()) # 新式类内置了mro方法可以查看线性列表的内容,经典类没有该内置该方法
'''
[<class '__main__.A'>, <class '__main__.B'>,
<class '__main__.E'>, <class '__main__.C'>,
<class '__main__.F'>, <class '__main__.D'>,
<class '__main__.G'>, <class 'object'>]
'''
python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表.
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
多继承的属性查找顺序:
1.由对象发起的属性查找,会从对象自身的属性里检索,没有则会按照对象的类.mro()规定的顺序依次找下去,
2.由类发起的属性查找,会按照当前类.mro()规定的顺序依次找下去
五.mixins机制(了解)
大多数面向对象语言都不支持多继承,而在Python中,一个子类是可以同时继承多个父类的,这固然可以带来一个子类可以对多个不同父类加以重用的好处,但也有可能引发著名的 Diamond problem菱形问题.利用mixins机制可以有效避免菱形问题
例1:
class Vehicle: # 交通工具
def fly(self):
'''
飞行功能相应的代码
'''
print("I am flying")
class CivilAircraft(Vehicle): # 民航飞机
pass
class Helicopter(Vehicle): # 直升飞机
pass
class Car(Vehicle): # 汽车并不会飞,但按照上述继承关系,汽车也能飞了
pass
Mixins机制指的是子类混合(mixin)不同类的功能,而这些类采用统一的命名规范(例如Mixin后缀),以此标识这些类只是用来混合功能的,并不是用来标识子类的从属"is-a"关系的,所以Mixins机制本质仍是多继承,但同样遵守”is-a”关系
class Vehicle: # 交通工具
pass
class FlyableMixin:
def fly(self):
'''
飞行功能相应的代码
'''
print("I am flying")
class CivilAircraft(FlyableMixin, Vehicle): # 民航飞机
pass
class Helicopter(FlyableMixin, Vehicle): # 直升飞机
pass
class Car(Vehicle): # 汽车
pass
# ps: 采用某种规范(如命名规范)来解决具体的问题是python惯用的套路
使用Mixin类实现多重继承要非常小心
1.首先它必须表示某一种功能,而不是某个物品,python 对于mixin类的命名方式一般以 Mixin, able, ible 为后缀
2.其次它必须责任单一,如果有多个功能,那就写多个Mixin类,一个类可以继承多个Mixin,为了保证遵循继承的“is-a”原则,只能继承一个标识其归属含义的父类
3.然后,它不依赖于子类的实现
4.最后,子类即便没有继承这个Mixin类,也照样可以工作,就是缺少了某个功能。(比如飞机照样可以载客,就是不能飞了)
六.在子类派生的新方法中重用父类功能的方式二
在子类派生的新方法中重用父类的功能
方式一:指名道姓地调用某一个类的函数
特点:不依赖于继承关系
class OldboyPeople:
school = "oldboy"
# 空对象,"艾利克斯",73,'male'
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
def f1(self):
print('1111111')
class Student(OldboyPeople):
# 空对象,"艾利克斯",73,'male',1001,"python全栈开放"
def __init__(self,name,age,gender,stu_id,course):
OldboyPeople.__init__(self,name,age,gender) # OldboyPeople.__init__(空对象,"艾利克斯",73,'male')
self.stu_id = stu_id
self.course = course
def choose(self):
print('%s 正在选课' %self.name)
def f1(self):
OldboyPeople.f1(self)
print("22222")
stu1=Student("艾利克斯",73,'male',1001,"python全栈开放")
# tea1=Teacher("egon",18,'male',2000,10)
stu1.f1()
方式二:调用super(自己的类名,self)
调用super(自己的类名,self)会返回一个特殊的对象,super(自己的类名,self).属性,会参照属性查找发起的那个类的mro列表去它父类中查找属性
特点:严格依赖于继承关系
class OldboyPeople:
school = 'oldboy'
# 空对象,"艾利克斯",73,'male'
def __init__(self,name,age,gender):
self.name=name
self.age=age
self.gender=gender
def f1(self):
print('1111111')
class Student(OldboyPeople):
def __init__(self,name,age,gender,stu_id,course):
# OldboyPeople.__init__(self,name,age,gender) # OldboyPeople.__init__(空对象,"艾利克斯",73,'male')
super(Student,self).__init__(name,age,gender)
self.stu_id = stu_id
self.course = course
def choose(self):
print('%s 正在选课' %self.name)
def f1(self):
# OldboyPeople.f1(self)
# super().f1()
print("22222")
# print(Student.mro())
stu1=Student("艾利克斯",73,'male',1001,"python全栈开放")
# print(stu1.__dict__)
stu1.f1()
示例:
class A:
def test(self):
super().test()
class B:
def test(self):
print('from B')
class C(A,B):
pass
c=C()
print(C.mro()) # [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
c.test() # from B
# obj = A()
# obj.test() # 报错
class A:
def test(self):
print('A---->test')
super().aaa()
class B:
def test(self):
print('B---->test')
def aaa(self):
print('B---->aaa')
class C(A,B):
def aaa(self):
print('C----->aaa')
c=C()
print(C.mro())
c.test()
"""
A---->test
B---->aaa
"""
七.组合
组合:把另外一个类的对象赋值给当前对象的属性
组合表达的是一种有的关系
class Teacher:
def __init__(self,name,age,gender,level):
self.name = name
self.age = age
self.gender = gender
self.level = level
def tell(self):
print("%s:%s" % (self.name, self.age))
class Student:
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
class Course:
def __init__(self, name, price, period):
self.name = name
self.price = price
self.period = period
def tell(self):
print('<%s:%s:%s>' % (self.name, self.price, self.period))
tea1 = Teacher("egon", 18, "male", 10)
stu1 = Student("xxx", 19, "male")
python = Course("python开放", 30000, "3mons")
linux = Course("linux课程", 30000, "3mons")
tea1.courses = [python,linux]
stu1.course = python
# tea,stu # 超级对象
# stu1.course.tell()
for course_obj in tea1.courses:
course_obj.tell()