面向对象编程之——继承(介绍,子类派生的新方法中重用父类功能,属性查找,继承实现原理,Mixins机制)

一、继承介绍

1、什么是继承
继承是一种新建类的方式,新建的类称之为子类,
被继承的类称之为父类、基类、超类
    python支持多继承,用逗号分隔开多个继承的类
 查看继承:派生类名.__bases__ #__base__只查看从左到右继承的第一个父类,__bases__则是查看所有继承的父类
2、为何要继承
子类会遗传父类的属性,所以继承是用来解决类与类之间代码冗余问题
  PS:类是为了解决对象与对象之间代码冗余问题

3、如何实现继承
ps:单继承能更好的反应什么是什么的关系
# 继承案列:
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
        self.course = course

    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("艾利克斯",73,'male',1001,"python全栈开放")
tea1=Teacher("egon",18,'male',2000,10)

print(stu1.school)

 

二、在子类派生的新方法中重用父类的功能

方式一:指名道姓地调用某一个类的函数
特点:不依赖于继承关系
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")

class Teacher(OldboyPeople):
    def score(self,stu,num):
        stu.num = num

stu1=Student("艾利克斯",73,'male',1001,"python全栈开放")
# tea1=Teacher("egon",18,'male',2000,10)

stu1.f1()

方式二:调用super(自己的类名,self)会返回一个特殊的对象,super(自己的类名,self).属性,会参照属性查找发起的那个类的mro列表去它父类中查找属性

 特点:严格依赖于继承关系

#1
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()


#2: 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()) c.test()

 obj=A() obj.test()

#3:
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()

 

三、属性查找

# 例1:有了继承关系,对象在查找属性时,先从对象自己的__dict__中找,如果没有则去子类中找,然后再去父类中找
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()

# 例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):#父类为Foo
    def __f2(self):  # _Bar__f2
        print("Bar.f2")

obj = Bar()
obj.f1()

 

四、继承的实现原理

# coding:utf-8 (在python2中要写文件头才能保证不乱码,python3底层会有优化机制不用写)
1、 补充知识:
新式类:但凡是继承了object类的子类,以该子类子子孙孙类都称之为新式类
经典类:没有继承了object类的子类,以该子类子子孙孙类都称之为经典类

python3中无论是否继承object,都默认继承object,即python3中全都是新式类。
python2中才有经典类
python2中没有显式的继承object的类,以及该类的子类,都是经典类;
python2中显式地声明继承object的类,以及该类的子类,都是新式类。
pyhon2中新式类要主动继承,手动加上object,加上文件头

class Foo(object):
pass
print(Foo.__bases__) #通过类的内置属性__bases__可以查看类继承的所有父类

2、 继承的实现原理
继承(先抽象再继承):是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
对于你定义的每一个类,Python都会计算出一个方法解析顺序(MRO)列表,该MRO列表就是一个简单的所有基类的线性顺序列表
python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止
查找顺序:(1)子类会先于父类被检查
     (2)
多个父类会根据它们在列表中的顺序被检查
     (3)
如果对下一个类存在两个合法的选择,选择第一个父类
ps:
1.由对象发起的属性查找,会从对象自身的属性里检索,没有则会按照对象的类.mro()规定的顺序依次找下去, 2.由类发起的属性查找,会按照当前类.mro()规定的顺序依次找下去

2.1 菱形问题(有时也称死亡钻石):一个子类继承的多条件分支最终汇聚到一个非object类,在菱形继承下
新式类与经典类关于属性查找的方式不同

新式类:广度优先
经典类:深度优先
# 例1:非菱形继承,经典类与新式类的属性查找顺序都一样
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')

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

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

obj=A()
obj.test()

 

# 例2:菱形继承
class G(object): # 在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')
    pass

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

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

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

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

obj=A()
obj.test()

print(A.mro())

五、mixins机制

mixins机制 遵循'is-a'关系
#不合理
class Vehicle:  # 交通工具
    def fly(self):
        '''
        飞行功能相应的代码        
        '''
        print("I am flying")

class CivilAircraft(Vehicle):  # 民航飞机
    pass

class Helicopter(Vehicle):  # 直升飞机
    pass

class Car(Vehicle):  # 汽车并不会飞,但按照上述继承关系,汽车也能飞了
    pass

 

#Mixins机制
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类实现多重继承要非常小心

  • 首先它必须表示某一种功能,而不是某个物品,python 对于mixin类的命名方式一般以 Mixin, able, ible 为后缀
  • 其次它必须责任单一,如果有多个功能,那就写多个Mixin类,一个类可以继承多个Mixin,为了保证遵循继承的“is-a”原则,只能继承一个标识其归属含义的父类
  • 然后,它不依赖于子类的实现
  • 最后,子类即便没有继承这个Mixin类,也照样可以工作,就是缺少了某个功能。(比如飞机照样可以载客,就是不能飞了)

​ Mixins是从多个类中重用代码的好方法,但是需要付出相应的代价,我们定义的Minx类越多,子类的代码可读性就会越差,并且更恶心的是,在继承的层级变多时,代码阅读者在定位某一个方法到底在何处调用时会晕头转向


你可能感兴趣的:(面向对象编程之——继承(介绍,子类派生的新方法中重用父类功能,属性查找,继承实现原理,Mixins机制))