• 面向对象三大特性之继承


    面向对象三大特性之继承

    一、继承初体验

    • 什么是继承
        继承是一种新建类的方式,新建的类称之为子类或派生类,继承的父类称之为基类或超类。
    
        - 在Python中,一个子类可以继承多个父类。(面试可能会问)
        - 在其它语言中,一个子类只能继承一个父类。
    
    • 继承的作用
    减少代码冗余
    
    • 如何实现继承法?
       1) 先确认谁是子类,谁是父类。
       2) 在定义类子类时, 子类名(父类名)。
    
    • 小例子
    #父类
    class Father1:
        x = 1
        pass
    
    class Father2:
        pass
    
    class Father3:
        pass
    
    #子类
    class sub(Father1,Father2,Father3):
        pass
    
    #子类.__bases__查看父类
    print(sub.__bases__)
    #(<class '__main__.Father1'>, <class '__main__.Father2'>, <class '__main__.Father3'>)
    print(sub.x)
    #1
    

    二、如何寻找继承关系

    • 如何寻找继承关系
        - 确认谁是子类
            - 小小明对象 ---> 人子类 ---> 动物父类
            - 猪坚强对象 ---> 猪子类 ---> 动物父类
            - 哈士奇对象 ---> 狗子类 ---> 动物父类
    
            - 人、猪、狗 都是子类
    
        - 确认谁是父类
            - 动物类是父类
    
            - 得先抽象,再继承
                - 抽取对象之间相似的部分,总结出类
                - 抽取类之间相似的部分,总结出父类。
    
    
    • 传统写法,代码冗余
    #老师类
    class Teacher:
        school = 'oldboy'
        country = 'china'
        def __init__(self, name, age, sex):
            self.name = name
            self.age = age
            self.sex = sex
    
        #老师改分
        def change_limit(self):
            print(f'老师{self.name}开始批改分数...')
    
    
    #学生类
    class Student:
        school = 'oldboy'
        country = 'china'
    
        def __init__(self, name, age, sex):
            self.name = name
            self.age = age
            self.sex = sex
    
        #学生选课
        def choose_course(self, name, age, sex):
            self.name = name
            self.age = age
            self.sex = sex
    
    tea1 = Teacher('tank', 28, 'male')
    print(tea1.name, tea1.age, tea1.sex)  #tank 28 male
    
    stu1 = Student('yafeng', 18, 'male')
    print(stu1.name, stu1.age, stu1.sex)  #yafeng 18 male
    
    '''这样写虽然也可以拿到我想要的信息,但是代码很长,且有很多代码重复,下面的父类就是解决这种情况'''
    
    • 用父类去书写,解决代码冗余
    #父类
    class OldPeople:
        school = 'oldboy'
        country = 'china'
    
        def __init__(self, name, age, sex):
            self.name = name
            self.age = age
            self.sex = sex
    
    #子类
    #学生类
    class OldStudent(OldPeople):
    
        def chose_course(self):
            print(f'学生{self.name}正在选课!')
    
    
    #老师类
    class OldTeacher(OldPeople):
    
        def change_score(self):
            print(f'老师{self.name}正在修改分数')
    
    obj1 = OldStudent('yafeng', 18, 'male')
    print(obj1.name, obj1.age, obj1.sex)  #yafeng 18 male
    
    
    obj2 = OldTeacher('tank', 25, 'male')
    print(obj2.name, obj2.age, obj2.sex)  #tank 25 male
    

    三、在继承属性下对象属性的查找属性

    • 注意
    '''
    注意: 程序的执行顺序是由上到下,父类必须定义在子类的上方。
    - 在继承背景下,对象属性的查找顺序:
        1.先从对象自己的名称空间中查找
        2.对象中没有,从子类的名称空间中查找。
        3.子类中没有, 从父类的名称空间中查找,若父类没有,则会报错!
    
    '''
    
    • 查找顺序演示
    #父类
    class Father:
        x = 10
        pass
    
    
    #子类
    class Sub(Father):
        x = 100
        pass
    obj1 = Sub()
    # print(obj1.x)  #100  先从对象自己的名称空间中查找
    # print(obj1.x)  #10,此时把x=100注释掉,对象中没有,从子类的名称空间中查找。
    # print(obj1.x)  #将父类的x=10也注释掉AttributeError: 'Sub' object has no attribute 'x'
    
    
    #注意:
    obj1.x = 1000#这是给对象添加属性的操作,并不是修改子类的属性
    print('子类的名称空间', Sub.__dict__)
    #子类的名称空间 {'__module__': '__main__', 'x': 100, '__doc__': None}
    
    print('对象的名称空间', obj1.__dict__)
    #对象的名称空间 {'x': 1000}
    
    print('父类的名称空间', Father.__dict__)
    #父类的名称空间 {'__module__': '__main__', 'x': 10, '__dict__': <attribute '__dict__' of 'Father' objects>,
    # '__weakref__': <attribute '__weakref__' of 'Father' objects>, '__doc__': None}
    

    四、派生

    • 派生
    '''
    派生:
        指的是子类继承父类的属性与方法,并且派生出自己独有的属性与方法。
        若子类中的方法名与父类的相同,优先用子类的。
    '''
    
    # #父类
    # class Foo:
    #     def f1(self):
    #         print('from Foo.f1...')
    #
    #     def f2(self):
    #         print('from Foo.f2...')
    #
    #
    # #子类
    # class Goo(Foo):
    #
    #     #重写(其实python中根本没有重写一说,姑且就称之为重写)
    #     def f1(self):
    #         print('from Goo.f1...')
    #
    #
    #     def func(self):
    #         print('from Goo.func...')
    #
    # obj = Goo()
    # print(obj.f1())  #from Goo.f1...
    # print(obj.f2())  #from Foo.f2...
    # print(obj.func())  #from Goo.func...
    
    
    #父类
    class Foo:
        def f1(self):
            print('from Foo.f1...')
    
        def f2(self):
            print('from Foo.f2...')
            self.f1()
    
    
    #子类
    class Goo(Foo):
    
        #重写(其实python中根本没有重写一说,姑且就称之为重写)
        def f1(self):
            print('from Goo.f1...')
    
    
        def func(self):
            print('from Goo.func...')
    
    obj = Goo()
    obj.f2()   #答案是多少?
    #'from Foo.f2...'  'from Goo.f1...'
    
    

    五、子类继承父类并重用父类的属性的方法

    • 代码
    '''
    - 子类继承父类,派生出自己的属性与方法,并且重用父类的属性与方法。
    '''
    
    '''需求:此时要给子类老师类添加新的属性薪水,给子类中的学生类添加女票'''
    #方法一:直接在子类中添加ps(那我还要你父类干嘛)
    #父类
    # class OldPeople:
    #     school = 'oldboy'
    #     country = 'china'
    #
    #     def __init__(self, name, age, sex):
    #         self.name = name
    #         self.age = age
    #         self.sex = sex
    #
    #
    # #子类
    # #学生类
    # class OldStudent(OldPeople):
    #
    #     def __init__(self, name, age, sex, girl_friend):
    #         self.name = name
    #         self.age = age
    #         self.sex = sex
    #         self.girl_friend = girl_friend
    #
    #     def chose_course(self):
    #         print(f'学生{self.name}正在选课!')
    #
    #
    # #老师类
    # class OldTeacher(OldPeople):
    #
    #     def __init__(self, name, age, sex, sal):
    #         self.name = name
    #         self.age = age
    #         self.sex = sex
    #         self.sal = sal
    #
    #     def change_score(self):
    #         print(f'老师{self.name}正在修改分数')
    #
    #
    #
    # obj1 = OldStudent('yafeng', 18, 'male', '热巴')
    # print(obj1.name, obj1.age, obj1.sex, obj1.girl_friend)  #yafeng 18 male 热巴
    #
    #
    # obj2 = OldTeacher('tank', 25, 'male', 15000)
    # print(obj2.name, obj2.age, obj2.sex, obj2.sal)  #tank 25 male 15000
    
    
    '''
    解决需求: 子类重用父类的属性,并派生出新的属性。
        两种方式:
            1.直接引用父类的__init__为其传参,并添加子类的属性。
            2.通过super来指向父类的属性。
                - super()是一个特殊的类,调用super得到一个对象,该对象指向父类的名称空间。
    
            注意: 使用哪一种都可以,但不能两种方式混合使用。
    '''
    
    #方式一:直接引用父类的__init__为其传参,并添加子类的属性。
    
    # #父类
    # class OldPeople:
    #     school = 'oldboy'
    #     country = 'china'
    #
    #     def __init__(self, name, age, sex):
    #         self.name = name
    #         self.age = age
    #         self.sex = sex
    #
    # #子类
    # #学生类
    # class OldStudent(OldPeople):
    #
    #     def __init__(self, name, age, sex, girl_friend):
    #
    #         # 类调用类内部的__init__,只是一个普通函数
    #         OldPeople.__init__(self, name, age, sex)
    #         self.girl_friend = girl_friend
    #
    #     def chose_course(self):
    #         print(f'学生{self.name}正在选课!')
    #
    #
    # #老师类
    # class OldTeacher(OldPeople):
    #
    #     def __init__(self, name, age, sex, sal):
    #         OldPeople.__init__(self, name, age, sex)
    #         self.sal= sal
    #
    #     def change_score(self):
    #         print(f'老师{self.name}正在修改分数')
    #
    # obj1 = OldStudent('yafeng', 18, 'male', '热巴')
    # print(obj1.name, obj1.age, obj1.sex, obj1.girl_friend)  #yafeng 18 male 热巴
    #
    #
    # obj2 = OldTeacher('tank', 25, 'male', 15000)
    # print(obj2.name, obj2.age, obj2.sex, obj2.sal)  #tank 25 male 15000
    
    
    #方式二:2.通过super来指向父类的属性。
    #- super()是一个特殊的类,调用super得到一个对象,该对象指向父类的名称空间。
    
    # 父类
    class OldPeople:
        school = 'oldboy'
        country = 'china'
    
        def __init__(self, name, age, sex):
            self.name = name
            self.age = age
            self.sex = sex
    
    
    #子类
    #学生类
    class OldStudent(OldPeople):
    
        def __init__(self, name, age, sex, girl_friend):
            super().__init__(name, age, sex)#注意此时使用super()方法,不用传self
            self.girl_friend = girl_friend
        def chose_course(self):
            print(f'学生{self.name}正在选课!')
    
    
    #老师类
    class OldTeacher(OldPeople):
    
        def __init__(self, name, age, sex, sal):
            super().__init__(name, age, sex)
            self.sal = sal
    
        def change_score(self):
            print(f'老师{self.name}正在修改分数')
    
    
    
    obj1 = OldStudent('yafeng', 18, 'male', '热巴')
    print(obj1.name, obj1.age, obj1.sex, obj1.girl_friend)  #yafeng 18 male 热巴
    
    
    obj2 = OldTeacher('tank', 25, 'male', 15000)
    print(obj2.name, obj2.age, obj2.sex, obj2.sal)  #tank 25 male 15000
    
    

    六、经典类与新式类

    • 了解
    #!/usr/bin/u/ubv/a python
    #_*_ coding:utf8 _*_
    '''
    经典类与新式类: (了解)
        - 工作中遇不到
        - 面试有可能会问
    
    - 新式类:
        1.凡是继承object的类或子孙类都是新式类。
        2.在python3中所有的类都默认继承object。
    
    - 经典类:
        1.在python2中才会有经典类与新式类之分。
        2.在python2中,凡是没有继承object的类,都是经典类。
    
    '''
    
    
    class User(object):
        pass
    
    class User():
        x = 10
        pass
    
    class Sub(User):
        pass
    
    print(User.__dict__)
    
    

    七、super严格遵循mro继承顺序原则

    • 了解
    '''
    调用mro返回的是一个继承序列: (了解知识点)
        super的继承顺序严格遵循mro继承序列。
    '''
    
    class Father1:
        x = 10
        pass
    
    class Father2:
        x = 100
        pass
    
    class Sub(Father1, Father2):
        pass
    
    print(Sub.mro())  #[<class '__main__.Sub'>, <class '__main__.Father1'>, <class '__main__.Father2'>, <class 'object'>]
    obj = Sub()
    print(obj.x)  #10
    
    
    '''
    在python3中提供了一个查找新式类查找顺序的内置方法.
        mro(): 会把当前类的继承关系列出来。
    '''
    # 注意: super()会严格按照mro列表的顺序往后查找
    class A:
        def test(self):
            print('from A.test')
            super().test()
    
    
    class B:
        def test(self):
            print('from B.test')
    
    
    class C(A, B):
        pass
    
    
    c = C()
    # 检查super的继承顺序
    print(C.mro())
    
    # # 去A找,有的话打印,然后super又执行了test,根据mro中查找打印B类中test。
    
    # '''
    # from A.test
    # from B.test
    # '''
    

    八、钻石继承(菱形继承)

    • 了解
    '''
    多继承情况下造成 “钻石继承”
    
        mro的查找顺序:
            - 新式类:
                - 广度优先
    
            - 经典类:
                - 深度优先
    
    面试注意细节:
        - 遇到一个技术 知道是什么,但是不会用,一定要贬低这个技术,觉得很简单,让面试官误以为你很会。
        - 遇到一个技术,不知道是什么,要说我见过,但是忘记怎么用了,我博客里面,回头找一下就行了。
        
        -新式类:广度优先
        - 经典类:深度优先
    
    '''
    
    # 了解:
    # 新式类:
    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(B):
        # def test(self):
        #     print('from D')
        pass
    
    class E(C):
        # def test(self):
        #     print('from E')
        pass
    
    class F(D, E):
        # def test(self):
        #     print('from F')
        pass
    
    # F-->D-->B-->E-->C-->A-->object
    # print(F.mro())
    obj = F()
    obj.test()
    
    
  • 相关阅读:
    最近花了几个夜晚帮师妹整了一个企业网站
    英文学习网站
    Visual Studio 常用快捷键 (二)
    Visual Studio 常用快捷键
    学习英文之社区,博客及源码
    CodeForces 676D代码 哪里有问题呢?
    线程中调用python win32com
    Python 打包工具cx_freeze 问题记录及解决办法
    HDU1301 Jungle Roads
    HDU 1875 畅通工程再续
  • 原文地址:https://www.cnblogs.com/yafeng666/p/11936514.html
Copyright © 2020-2023  润新知