4.9课堂

一. 继承介绍

"""
 # 什么是继承?
    继承是一种创建新类的方式, 在python中, 新创建的类可以继承一个或多个父类, 那么这时这个新建的类就被称之为子类或派生类, 父类就被成之为基类或超类, 且子类会遗传父类的属性.
    需要注意的是: python支持多继承, 在python中新创建的类可以继承一个或多个父类(补充: 只有python中支持多继承, 其它语言不支持.)

# 类的作用: 解决对象与对象之间的代码重用性问题.
# 继承的作用: 解决类与类之间代码重用性问题.

# 查看当前类的所继承的父类: 通过内置属性"类名.__bases__"可以查看类继承的所有父类

# 多继承的优缺点: 不建议使用多继承, 有可能会引发可恶的菱形问题. 如果真的涉及到一个子类不可避免地重用多个父类的属性, 也不应该直接使用多继承, 而是应该使用Mixins机制.
    优点:
        子类可以同时遗传多个父类的属性, 最大限度的解决类与类之间的代码重用问题
    缺点: 继承表达的是一种什么"是"什么的关系.
        1. 多继承违背了人的思维逻辑习惯
        2. 代码的可读性会变差
        3. 扩展性变差(可读性和扩展性是相辅相成的)

# 补充: object类提供了一些常用的内置方法的实现. 如用来打印对象时返回字符串的内置方法__str__
"""

class Parent1(object):
    x = 1111


class Parent2(object):
    pass


class Sub1(Parent1):  # 单继承
    pass


class Sub2(Parent1, Parent2):  # 多继承
    pass


print(Sub1.__bases__)  # (<class '__main__.Parent1'>,)
print(Sub2.__bases__)  # (<class '__main__.Parent1'>, <class '__main__.Parent2'>)

print(Sub2.x)  # 1111

二. 继承与抽象

"""
如何寻找类与类之间的关系?
    先抽象, 再继承.
    抽象: 指的是总结相似之处, 比如总结对象之间的相似之处得到类, 那么我们也可以总结类与类之间的相似之处得到父类.
继承的好处: 用来解决类与类之间的代码重用性问题.
"""

# 示范1:类与类之间存在冗余问题
class Student:
    school = 'OLDBOY'

    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    def choose_course(self):
        print('学生%s 正在选课' % self.name)


class Teacher:
    school = 'OLDBOY'

    def __init__(self, name, age, sex, salary, level):
        self.name = name
        self.age = age
        self.sex = sex
        self.salary = salary
        self.level = level

    def score(self):
        print('老师 %s 正在给学生打分' % self.name)

# 示范2:基于继承解决类与类之间的冗余问题
class People:
    school = 'OLDBOY'

    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex


class Student(People):
    def choose_course(self):
        print(f'学生{self.name}正在选课')


class Teacher(People):
    #            老师的空对象,'tank',20, 'male',30000,  9
    def __init__(self, name, age, sex, salary, level):
        #         老师的空对象,'tank', 20, 'male'
        People.__init__(self, name, age, sex)  # "指明道姓"调用继承的父类People, 调用类下的__init__, 基于类调用这是这是一个普通函数, 应该准守函数的传参规则.
        self.salary = salary
        self.level = level

    def score(self):
        print(f'老师{self.name}正在给学生打分')


# 实例化学生对象
stu_obj = Student('egon', 19, 'male')
print(stu_obj.__dict__) # {'name': 'egon', 'age': 19, 'sex': 'male'}
stu_obj.choose_course()  # 学生egon正在选课

# 实例化老师对象
tea_obj = Teacher('tank', 20, 'male', 30000, 9)
print(tea_obj.__dict__)  # {'name': 'tank', 'age': 20, 'sex': 'male', 'salary': 30000, 'level': 9}
tea_obj.score()  # 老师tank正在给学生打分

三. 属性查找

"""
对象查找属性: 先从对象自己的__dict__中找, 如果没有则去子类中找, 然后再去父类中找....
类属性查找:   先从自己中类的__dict__中找, 如果没有则去父类中找....
父类如果不想让子类覆盖自己的方法: 
	1. 可以采用"指明道姓"的方式访问自己内部的方法
	2. 可以采用封装的思想使用双下划线开头的方式将方法设置为私有的.
"""

1. 示范一: 注意, 谁来调用的执行查找方式就以谁作为起始位置开始查找

class Foo:
    def f1(self):
        print('Foo.f1')

    def f2(self):
        print('Foo.f2')
        self.f1()  # obj.f1() # 谁来调用的执行查找方式就以谁作为起始位置开始查找


class Bar(Foo):
    def f1(self):
        print('Bar.f1')


obj = Bar()

# 查找顺序: obj.f2 -> Bar.f2 -> Foo.f2 --> Foo中找到了f2 -> obj.f1 -> Bar.f1 -> Bar中找到了f1
obj.f2()
'''
Foo.f2
Bar.f1
'''

2. 示范二: 父类如果不想让子类覆盖自己的方法, 可以采用"指明道姓"的方式访问自己内部的方法

class Foo:
    def f1(self):
        print('Foo.f1')

    def f2(self):
        print('Foo.f2')
        Foo.f1(self)  # 采用"指明道姓"的方式访问自己内部的方法


class Bar(Foo):
    def f1(self):
        print('Bar.f1')


obj = Bar()
# 查找顺序: obj.f2 -> Bar.f2 -> Foo.f2 --> Foo中找到了f2 -> Foo.f1 --> Foo中找到了f1
obj.f2()
"""
Foo.f2
Foo.f1
"""

3. 示范三: 父类如果不想让子类覆盖自己的方法, 可以采用封装的思想使用双下划线开头的方式将方法设置为私有的.

class Foo:
    def __f1(self):  # _Foo__f1
        print('Foo.f1')

    def f2(self):
        print('Foo.f2')
        self.__f1()  # 采用封装的思想使用双下划线开头的方式将方法设置为私有


class Bar(Foo):
    def __f1(self):  # _Bar__f1
        print('Bar.f1')


obj = Bar()
# 查找顺序: obj.f2 -> Bar.f2 -> Foo.f2 --> Foo中找到了f2 -> obj._Foo__f1 --> Bar._Foo__f1 --> Foo._Foo__f1 --> Foo中找到了_Foo__f1
obj.f2()
"""
Foo.f2
Foo.f1
"""

四. 继承的实现原理

1. 菱形问题/钻石问题 + 继承原理(MRO)

"""
前言补充: 
    大多数面向对象语言都不支持多继承, 而在python中是可以同时继承多个父类的.这样固然可以带来一个子类可以对多个不同的父类加以重用的好处, 但也有可能引发著名的菱形问题(Diamond problem 或称钻石问题, 有时候也被称之为"死亡钻石")
什么是菱形问题?
    多个子类最终会继承到一个非object类中, 这种继承关系就会引发菱形问题.(注意: 菱形问题指的是, 多个子类 以及 最终继承的类是非object的类.)
"""

class A(object):
    def test(self):
        print('from A')
    pass


class B(A):
    # def test(self):
    #     print('from B')

    pass


class C(A):
    # def test(self):
    #     print('from C')
    pass


class D(C, B):
    # def test(self):
    #     print('from D')
    pass

# 类D以及类D的对象访问属性都是参照该类的mro列表
print(D.mro())  # [<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]

d_obj = D()
d_obj.test()
print(D.test)  

# 类C以及类C的对象访问属性都是参照该类的mro列表
print(C.mro())  # [<class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
c_obj = C()
c_obj.test()

2. 什么是经典类与新式类?

"""
经典类:
    python2中, 没有显示继承object的类, 以及该类的子类.
新式类:
    python2中, 显示继承object的类, 以及该类的子类.
    python3中, 即使没有显示继承object类, 默认也是继承该类.(提示:Python3中统一都是新式类)
"""

3. 如果多继承是非菱形继承

强调!!: 无论是什么结构, object都是最后一个被查找. 当然前提要找得到, 在python2中经典类没有继承object, 这个时候就找不到.

"""
查找顺序: 经典类与新式类的属性查找顺序都是按照类括号中继承类的先后顺序依次按照一个分支一个分支的查找下去(注意: 每个分支的最终继承的非object类会作为当前分支结束的条件)
"""

class E:
    # def test(self):
    #     print('from E')
    pass


class F:
    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')
    pass


class D:
    def test(self):
        print('from D')


class A(B, C, D):
    # def test(self):
    #     print('from A')
    pass


# 新式类
print(A.mro())  # A -> B -> E -> C -> F -> D -> object

obj = A()
obj.test()  # 打印结果: from F

4. 如果多继承是菱形继承

强调!!: 无论是什么结构, object都是最后一个被查找. 当然前提要找得到, 在python2中经典类没有继承object, 这个时候就找不到.

"""
查找顺序: 经典类深度优先, 新式类广度优先
提示1: 只有python2中有经典类的概念, python3中默认继承或不继承object都是新式类. 
提示2: python2中没有继承object的类及其子类都没有"类名.mro()"方法, 只有显示继承object类才有. 而python3因为无论有没有显示继承都是默认继承object类, 所以都有"类名.mro()"方法.
提示3: 深度优先, 广度优先都是基于当前分支最终继承的非object类当作参考点. 如果是按照新式类则会在直接在第一分支下就找最终继承的非object类. 如果是广度优先则会在最后分支中才去找最终继承的非object类.
"""

class G:  # 在python2中,未继承object的类及其子类,都是经典类
    def test(self):
        print('from G')
    pass


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


# 新式类
# print(A.mro())  # A -> B -> E -> C -> F -> D -> G -> object

# 经典类: 要放到python2中执行.
# print(A.mro()) # A -> B -> E -> G -> C -> F -> D

obj = A()
obj.test()   # 打印结果: python2中"from G". python3中"from C"
posted @ 2020-04-09 23:44  Python-feng  阅读(150)  评论(0编辑  收藏  举报