python之面向对象三大特性——继承

1、继承
1.1、什么是继承
继承是一种新建类的方式,新建的类称之为子类,被继承的类称之为父类、基类、超类

python支持多继承
1.2、为何要继承
子类会遗传父类的属性,所以继承是用来解决类与类之间代码冗余问题
1.3、如何实现继承
# 定义两个基类
class Parent1:
    pass

class Parent2:
    pass

# 定义两个子类,分别继承Parent1和Parent1, Parent2
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 Student:
    
    def __init__(self, name, age, gender, stu_id, course):
        self.name = name
        self.age = age
        self.gender = gender
        self.stu_id = stu_id
        self.course = course

    def choose_course(self):
        print('{}正在选课'.format(self.name))

# 定义老师类
class Teacher:

    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, score):
        print('{}的分数是{}'.format(stu, score))

学生类和老师类中存在相同属性,因此需要定义Person类提取共同属性

1.3.1、子类派生新方法重用父类功能方式一
方式一: 指名道姓地调用某一个类的函数
特点: 不依赖于继承关系

# 定义基类,提取公用属性
class Person:

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

# 定义学生类
class Student:

    def __init__(self, name, age, gender, stu_id, course):
        # 指名道姓的和Person类要name,age,gender三个属性
        Person.__init__(self, name, age, gender)
        # 学生类独有的属性
        self.stu_id = stu_id
        self.course = course

    def choose_course(self):
        print('{}正在选课'.format(self.name))


# 定义老师类
class Teacher:
	
    def __init__(self, name, age, gender, salary, level):
        # 指名道姓的和Person类要name,age,gender三个属性
        Person.__init__(self, name, age, gender)
        # 老师类独有的属性
        self.salary = salary
        self.level = level

    def score(self, stu, score):
        print('{}的分数是{}'.format(stu, score))
1.3.2、子类派生新方法重用父类功能方式二
方式二: 调用super(自己的类名,self)会返回一个特殊的对象,super(自己的类名,self).属性会参照属性查找发起的那个类的mro列表去它父类中查找属性
特点: 严格依赖于继承关系

# 定义基类,提取公用属性
class Person:

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

# 定义学生类
class Student(Person):

    def __init__(self, name, age, gender, stu_id, course):
        # 使用super为__init__传递name,age,gender三个属性,python3中可以直接写super()
        super(Student, self).__init__(name, age, gender)
        # 学生类独有的属性
        self.stu_id = stu_id
        self.course = course

    def choose_course(self):
        print('{}正在选课'.format(self.name))

# 定义老师类
class Teacher(Person):

    def __init__(self, name, age, gender, salary, level):
        # 使用super为__init__传递name,age,gender三个属性,python3中可以直接写super()
        super(Teacher, self).__init__(name, age, gender)
        # 老师类独有的属性
        self.salary = salary
        self.level = level

    def score(self, stu, score):
        print('{}的分数是{}'.format(stu, score))
1.3.3、小案例
# 案例1
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

# 案例2
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())  # [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
c.test()  # A---->test  B---->aaa
1.4、关于属性查找
有了继承关系,对象在查找属性时,先从对象自己的__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()  # Foo.f1  Bar.f2


# 示例二:
# 父类如果不想让子类覆盖自己的方法,可以在方法名前加前缀__

class Foo:
    def __f2(self):  # _Foo__f2
        print("Foo.f2")

    def f1(self):
        print('Foo.f1')
        self.__f2()  # _Foo__f2()

class Bar(Foo):
    def __f2(self):  # _Bar__f2
        print("Bar.f2")

obj = Bar()
obj.f1()  # Foo.f1  Foo.f2
1.5、继承的实现原理
1.5.1、新式类与经典类
新式类: 但凡是继承了object类的子类,则该子类子子孙孙类都称之为新式类
经典类: 没有继承了object类的子类,则该子类子子孙孙类都称之为经典类
python3中全都是新式类,python2中才有经典类
在python3中没有继承任何类的类会默认继承objectclass Foo(object):
    pass

print(Foo.__bases__)  # (<class 'object'>,)
1.5.2、菱形问题
一个子类继承的多条件分支最终汇聚到一个非object类,这就是菱形问题

大多数面向对象语言都不支持多继承,而在Python中,一个子类是可以同时继承多个父类的,这固然可以带来一个子类可以对多个不同父类加以重用的好处,但也有可能引发著名的 Diamond problem菱形问题(或称钻石问题,有时候也被称为“死亡钻石”)

A类在顶部,B类和C类分别位于其下方,D类在底部将两者连接在一起形成菱形
如下:

在这里插入图片描述

1.5.3、继承实现原理
对于你定义的每一个类,Python都会计算出一个方法解析顺序(MRO)列表,该MRO列表就是一个简单的所有基类的线性顺序列表
类名.mro()  # 新式类内置了mro方法可以查看线性列表的内容,经典类没有该内置该方法

# 非菱形继承,经典类与新式类的属性查找顺序都一样
class E:
    def test(self):
        print('from E')

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

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

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


print(A.mro())
'''
[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.F'>, <class '__main__.D'>, <class 'object'>]
'''

obj = A()
obj.test() # 结果为:from B

结论:
   	1.由对象发起的属性查找,会从对象自身的属性里检索,没有则会按照对象的类.mro()规定的顺序依次找下去
	2.由类发起的属性查找,会按照当前类.mro()规定的顺序依次找下去
1.5.4、深度优先和广度优先

深度优先图例:
在这里插入图片描述
广度优先图例:
在这里插入图片描述

# 在菱形继承下新式类与经典类关于属性查找的方式不同

# 深度优先

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

class E(G):
    def test(self):
        print('from E')

class F(G):
    def test(self):
        print('from F')

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

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()  # 查找顺序为:obj->A->B->E->G->C->F->D->object



# 广度优先
class G(object):
    def test(self):
        print('from G')

class E(G):
    def test(self):
        print('from E')

class F(G):
    def test(self):
        print('from F')

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

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()  # 查找顺序为:obj->A->B->E->C->F->D->G->object
1.6、Pyton Mixins机制
 一个子类可以同时继承多个父类,这样的设计常被人诟病,一来它有可能导致可恶的菱形问题,二来在人的世界观里继承应该是个”is-a”关系
    
# 民航飞机、直升飞机、轿车都是一个(is-a)交通工具,前两者都有一个功能是飞行fly,但是轿车没有,所以如下所示我们把飞行功能放到交通工具这个父类中是不合理的

class Vehicle:  # 交通工具
    def fly(self):
        '''
        飞行功能相应的代码        
        '''
        print("I am flying")

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

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

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


# Python语言可没有接口功能,但Python提供了Mixins机制,简单来说Mixins机制指的是子类混合(mixin)不同类的功能,而这些类采用统一的命名规范(例如Mixin后缀),以此标识这些类只是用来混合功能的,并不是用来标识子类的从属"is-a"关系的,所以Mixins机制本质仍是多继承,但同样遵守”is-a”关系

class Vehicle:  # 交通工具
    pass

class FlyMixin:
    def fly(self):
        '''
        飞行功能相应的代码        
        '''
        print("I am flying")

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

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

class Car(Vehicle):  # 汽车
    pass
1.6.1、使用Mixin类的注意点
1、必须表示某一种功能,而不是某个物品,python 对于mixin类的命名方式一般以 Mixin, able, ible 为后缀
2、必须责任单一,如果有多个功能,那就写多个Mixin类,一个类可以继承多个Mixin,为了保证遵循继承的“is-a”原则,只能继承一个标识其归属含义的父类
3、它不依赖于子类的实现
4、子类即便没有继承这个Mixin类,也照样可以工作,就是缺少了某个功能。(比如飞机照样可以载客,就是不能飞了)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值