zoukankan      html  css  js  c++  java
  • 面向对象三大特性之继承

    面向对象三大特性之继承

    1 继承的定义

    继承是一种创建新类的方式,继承之后子类可以遗传父类的属性

    新建的类可称为子类或派生类,父类又可称为基类或超类

    继承可以用来解决类与类之间代码冗余问题

    tips: python支持多继承

    在Python中,新建的类可以继承一个或多个父类

    `

    在python2中有经典类与新式类之分
    新式类:继承了object类的子类,以及该子类的派生类
    经典:没有继承object类的子类,以及该子类的派生类

    `

    在python3中没有继承任何类,那么会默认继承object类,所以python3中所有的类都是新式类

    class Parent1(object):	
        # 如果python3中写的继承想兼容python2要将所有无继承的类	继承object
        x=1111
    
    class Parent2(object):
        pass
    
    class Sub1(Parent1): # 单继承
        pass
    
    class Sub2(Parent1,Parent2): # 多继承
        pass
    
    print(Sub1.__bases__)
    print(Sub2.__bases__)
    
    print(Sub1.x)
    

    2 多继承的优缺点

    优点:子类可以同时遗传多个父类的属性,最大限度地重用代码
    缺点:
    1、违背人的思维习惯:继承表达的是一种什么"是"什么的关系
    2、代码可读性变差
    3、不建议使用多继承,有可能会引发菱形问题,使扩展性变差,
    如果涉及到一个子类不可避免地要重用多个父类的属性,应该使用Mixins机制

    3 继承的使用

    3.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)
    
    # 基于继承解决类与类之间的冗余问题
    class OldboyPeople:
        school = 'OLDBOY'
    
        def __init__(self, name, age, sex):
            self.name = name
            self.age = age
            self.sex = sex
    
    
    class Student(OldboyPeople):
        def choose_course(self):
            print('学生%s 正在选课' % self.name)
    stu_obj = Student('lili', 18, 'female')
    print(stu_obj.__dict__)
    print(stu_obj.school)
    stu_obj.choose_course()
    
    
    class Teacher(OldboyPeople):
        #           老师的空对象,'egon',18,'male',3000,10
        def __init__(self, name, age, sex, salary, level):
            # 调用父类OldboyPeople的__init__函数
            # 注意:要传入所有参数
            OldboyPeople.__init__(self,name,age, sex)
            self.salary = salary
            self.level = level
    
        def score(self):
            print('老师 %s 正在给学生打分' % self.name)
    
    tea_obj=Teacher('egon',18,'male',3000,10)
    print(tea_obj.__dict__)
    print(tea_obj.school)
    
    tea_obj.score()
    

    3.2 单继承背景下的属性查找

    class Foo:
        def f1(self):
            print('Foo.f1')
    
        def f2(self):
            print('Foo.f2')
            self.f1() 	# 本质上是obj.f1(),即Bar.f1()
    
    class Bar(Foo):
        def f1(self):
            print('Bar.f1')
    
    obj=Bar()
    obj.f2()
    # 结果:
    # Foo.f2
    # Bar.f1
    
    class Foo:
        def f1(self):
            print('Foo.f1')
    
        def f2(self):
            print('Foo.f2')
            Foo.f1(self) 	# 调用Foo类中的f1
    
    class Bar(Foo):
        def f1(self):
            print('Bar.f1')
    
    obj=Bar()
    obj.f2()
    
    # Foo.f2
    # Foo.f1
    
    class Foo:
        def __f1(self): # _Foo__f1
            print('Foo.f1')
    
        def f2(self):
            print('Foo.f2')
            self.__f1() 	# self._Foo__f1, 调用当前类中的f1
    
    class Bar(Foo):
        def __f1(self): # _Bar__f1
            print('Bar.f1')
    
    obj=Bar()
    obj.f2()
    
    # Foo.f2
    # Foo.f1
    

    3.3 多继承中的菱形问题

    A继承了B和C,B和C继承了D

    如果A中没有foo函数,B,C和D中都有各自的foo函数,

    此时调用A.foo()该选择谁的foo函数?

    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
    
    print(D.mro()) # 类D以及类D的对象访问属性都是参照该类的mro列表
    
    obj = D()		# 顺序为OBJ-D-C-B-A
    obj.test()
    
    print(D.test)
    
    print(C.mro()) # 类C以及类C的对象访问属性都是参照该类的mro列表
    c=C()
    c.test()
    

    总结一:

    类相关的属性查找(类名.属性,该类的对象.属性),都是参照该类的mro列表顺序

    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
    

    总结二:

    如果多继承是非菱形继承,即没有汇合处,

    则经典类与新式的属性查找顺序一样:
    都是一个分支一个分支地找下去,然后最后找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
    
    # 经典类:A->B->E->G->C->F->D
    obj = A()
    obj.test() #
    

    总结三:

    如果多继承是菱形继承,经典类与新式类的属性查找顺序不一样:
    经典类:深度优先,会在检索第一条分支的时候就直接一条道走到底,会检索共同的父类

    `

    新式类:广度优先,会在检索最后一条分支的时候才检索共同的父类

    总结四:

    多继承到底要不用???
    要用,但是注意几点问题
    1、继承结构尽量不要过于复杂
    2、推荐使用mixins机制:在多继承的背景下满足继承的什么"是"什么的关系

    3.4 多继承mixins规范

    在python中使用多继承时,应该遵守mixins的规范

    mixins机制核心就是在多继承背景下尽可能地提升多继承的可读性,

    将添加功能的类名写成able或ible或mixin结尾,而主体的类名没有后缀

    即名词只有一个,其他为形容词,如苹果是红的,甜的,有核的,有皮的水果

    ps:让多继承满足人的思维习惯=>什么"是"什么

    通常Mixin结果的类放在左边

    # 多继承的正确打开方式:mixins机制
    
    class Vehicle:	(交通工具)
        pass
    
    class FlyableMixin:
        def fly(self):
            pass
    
    class CivilAircraft(FlyableMixin,Vehicle):  # 民航飞机
        pass
    
    class Helicopter(FlyableMixin,Vehicle):  # 直升飞机
        pass
    
    class Car(Vehicle):  # 汽车并不会飞,但按照上述继承关系,汽车也能飞了
        pass
    
    
    
    

    3.5 在子类中重用父类的功能

    方式一:添加前缀,调用某一个类下的函数(不依赖于继承关系)

    class OldboyPeople:
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
    
        def f1(self):
            print('%s say hello' %self.name)
    
    
    class Teacher(OldboyPeople):
        def __init__(self,name,age,sex,level,salary):
            OldboyPeople.__init__(self,name,age,sex)
    
            self.level = level
            self.salary=salary
    
    tea_obj=Teacher('egon',18,'male',10,3000)
    print(tea_obj.__dict__)
    

    方式二:super()

    调用父类提供给自己的方法=>严格依赖继承关系
    调用super()会得到一个特殊的对象,该对象会参照发起属性查找的那个类的mro列表,去当前类的父类中找属性
    `

    在python2中super的使用需要完整地写成super(自己的类名,self) ,

    在python3中可以简写为super()。

    class OldboyPeople:
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
    
        def f1(self):
            print('%s say hello' %self.name)
    
    
    class Teacher(OldboyPeople):
        def __init__(self,name,age,sex,level,salary):
            super().__init__(name,age,sex) # 调用该函数的是方法,自动传入对象
    		# super()得到的是Teacher的mro列表中排在teacher类后的类
            # [<class '__main__.Teacher'>, <class '__main__.OldboyPeople'>, <class 'object'>]
            # 查找顺序为<class '__main__.OldboyPeople'>, <class 'object'>
            self.level = level
            self.salary=salary
    
    print(Teacher.mro())
    # [<class '__main__.Teacher'>, <class '__main__.OldboyPeople'>, <class 'object'>]
    tea_obj=Teacher('egon',18,'male',10,3000)
    print(tea_obj.__dict__)
    # {'name': 'egon', 'age': 18, 'sex': 'male', 'level': 10, 'salary': 3000}
    
    
    # super()案例
    class A:
        def test(self):
            print('from A')
            super().test()
    
    class B:
        def test(self):
            print('from B')
    
    class C(A,B):
        pass
    
    
    obj=C()
    obj.test()
    # from A
    # from B
    
    # C中没test方法,从父类中找,多继承,按C的mro顺序查找[C,A,B,O]
    # A中存在test方法,调用,输出from A,执行super().test()
    # super().test()是在C的mro列表中[C,A,B,O],在排在A后面的类中寻找test,即B->O
    # B中存在test,执行
    
    
    print(C.mro())
    # [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
    
    
    class A:
        def test(self):
            print('from A')
            super().test1()
    
    class B:
        def test(self):
            print('from B')
    
    class C(A,B):
        def test1(self):
            print('from C')
    
    obj=C()
    obj.test()
    # C中没test方法,从父类中找,多继承,按C的mro顺序查找[C,A,B,O]
    # A中存在test方法,调用,输出from A,执行super().test1()
    # super().test1()是在C的mro列表中[C,A,B,O],在排在A后面的类中寻找test1,即B->O
    # B中不存在,O中不存在,报错
    
    
    
  • 相关阅读:
    人到中年,奋斗了十几年结果却是负债累累,还要继续创业吗?
    迁移学习简述
    迁移学习简述
    关于mysql数据库的表概况 ,查看表状态
    关于mysql数据库的表概况 ,查看表状态
    关于mysql数据库的表概况 ,查看表状态
    关于mysql数据库的表概况 ,查看表状态
    一个简易的SocketIM
    一个简易的SocketIM
    一个简易的SocketIM
  • 原文地址:https://www.cnblogs.com/achai222/p/12669109.html
Copyright © 2011-2022 走看看