AI智能
改变未来

python 继承

[toc]

继承

继承简介

  1. 继承是一种创建新类的方式,新建的类可称为子类或派生类,父类可称为基类或超类
  2. python支持多继承,新建的类可以支持一个或多个父类
\'\'\'单继承和多继承简单定义\'\'\'class Parent1:passclass Parent2:passclass Sub1(Parent1): #单继承passprint(Sub1.__bases__)  # 查看自己的父类---->(<class \'__main__.Parent1\'>,)class Sub2(Parent1,Parent2): # 多继承passprint(Sub2.__bases__)    # 查看自己的父类---->(<class \'__main__.Parent1\'>, <class \'__main__.Parent2\'>)

经典类与新式类

在py2中有经典类和新式类的区别:

  • 新式类:继承了object类的子类,以及该子类的子类,子子类

  • 经典类:没有继承object类的子类,以及该子类的子类,子子类

\'\'\'py2中\'\'\'class Foo:pass     # 经典类class Bar(object):pass     # 新式类

注意:在py3中没有继承任何类,默认继承object类,所以python3中都是新式类

\'\'\'py3中\'\'\'class Foo():passprint(Foo.__bases__) # --->(<class \'object\'>,),默认继承object类class Sub(Foo):passprint(Sub.__bases__) # ---->(<class \'__main__.Foo\'>,)

类继承解决了什么问题

  • 类解决对象与对象之间代码冗余的问题,子类可以遗传父类的属性
  • 继承解决的是类与类之间代码冗余的问题
  • object类丰富了代码的功能

示例如下:

\'\'\'学生选课系统和老师打分功能\'\'\'# 学生类class Student():def __init__(self,name,age,gender,course = None):self.name = nameself.age = ageself.gender = genderself.course = course# 定义一个选课的方法def choose_course(self,course):if self.course is None:self.course = []self.course.append(course)print(f"Student choice class --->{self.course}")# 教师类class Teacher():def __init__(self,name,age,gender,level):self.name = nameself.age = ageself.gender = genderself.level = level# 定义一个打分方法def make_score(self,stu_obj,score):stu_obj.score = scoreprint(f\'Teacher{self.name} make {stu_obj.score} to {stu_obj.name}! \')\'\'\'有很多冗余的代码,优化一下,定义一个人的类整合一下重复的代码\'\'\'# 人类class Human():def __init__(self, name, age, gender):self.name = nameself.age = ageself.gender = gender# 学生类class Student(Human):def __init__(self, name, age, gender, score=None, course=None):Human.__init__(self, name, age, gender)self.score = scoreself.course = course# 定义一个选课的方法def choose_course(self, course):if self.course is None:self.course = []self.course.append(course)print(f"Student choice class --->{self.course}")# 教师类class Teacher(Human):def __init__(self, name, age, gender, level):Human.__init__(self, name, age, gender)self.level = level# 定义一个打分方法def make_score(self, stu_obj, score):stu_obj.score = scoreprint(f\'Teacher{self.name} make {stu_obj.score}marks to {stu_obj.name}! \')# 学生类实例化stu = Student(\'HammerZe\', 18, \'male\')stu.choose_course(\'python\')# 教师类实例化teacher = Teacher(\'li\', 18, \'male\', 10)teacher.make_score(stu, 90)Student choice class --->[\'python\']Teacherli make 90marks to HammerZe!

多继承的优缺点

  • 优点:子类可以同时遗传多个父类的属性,最大限度的重用代码

  • 缺点:违反人的思维习惯,一个人有两个爹,代码的可读性会变差,不建议使用多继承,如果不可避免多个父类的继承,应该使用

    Mixins机制

  • 继承表达的是一种“是”什么关系

Mixins机制

  • 多继承的正确打开方式:mixins机制

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

  • 让多继承满足人的思维习惯—>什么“是”什么

class Vehicle:passclass FlyableMixin:   # 规范多继承def fly(self):pass# 民航飞机class CiviAircraft(FlyableMixin,Vehicle)pass#直升机class Helicopter(FlyableMixin,Vehicle)passclass Car(Vehicle)pass\'\'\'表达是的关系,放在继承的末尾,特们都是交通工具\'\'\'\'\'\'eg:飞机有飞的功能,是交通工具\'\'\'

如果有多个功能就必须写多个Mixin类!

继承的查找顺序

  • 对象>子类>父类>父父类

  • 单继承背景下属性查找

示例如下:

class Foo():def f1(self):print(\'Foo.f1\')def f2(self):print(\'Foo.f2\')self.f1()class Bar(Foo):def f1(self):print(\'Bar.f1\')obj = Bar()obj.f2()# 结果Foo.f2Bar.f1\'\'\'查找顺序:1.obj先从obj名称空间找,再从Bar名称空间中找,没有f2去他爹(Foo)中找2.执行Foo中得f2,遇到self.f1()此时self是obj,是Bar的对象3.执行Bar中的f1\'\'\'# 区别下:父类不想让子类的方法覆盖,可以私有化class Foo:def __f1(self):  # _Foo__f1()print(\'Foo.f1\')def f2(self):#print(\'Foo.f2\')self.__f1()  # _Foo__f1()class Bar(Foo):def __f1(self):  # # _Bar__f1()print(\'Bar.f1\')obj = Bar()obj.f2()# 结果Foo.f2Foo.f1\'\'\'Foo中f1私有化,所以输出的是Foo中的f1\'\'\'

多继实现原理

菱形结构

在python中可以继承多个类,这样就会引发下面的结构:

  • 当D继承B和C,B、C分别继承A就会组成一个菱形的继承关系,这样就会涉及到查找属性的顺序问题,A、B、C、中如果方法重名,输出的顺序是按
    mro

    列表输出的顺序继承

示例如下:

\'\'\'py3中\'\'\'class A():def out_text(self):print(\'from A\')class B(A):def out_text(self):print(\'from B\')class C(A):def out_text(self):print(\'from C\')class D(B,C):passobj = D()obj.out_text() # 结果---->from B\'\'\' 可以打印出mro列表查看顺序\'\'\'print(D.mro())# [<class \'__main__.D\'>,# <class \'__main__.B\'>,# <class \'__main__.C\'>,# <class \'__main__.A\'>,# <class \'object\'>]\'\'\'这样看来查找顺序就显而易见了,1、从D中找out_text方法,没有直接去B2、B中有out_text方法,直接输出停止查找\'\'\'
  • mro列表查找准则:

      子类先查,再查父类

    1. 当继承多个父类的时候,按mro列表顺序被检查

    2. 如果继承多个类,被继承类内具有相同的方法,先输出mro列表左边类的方法

  • 注意:mro列表可以写成

    __mro__

    也可以,调用mro方法的必须是起始类,obj是D的对象,所以用D.mro()

  • mro列表是通过一个C3线性算法来实现的

非菱形结构

代码实现如下:

\'\'\'py3中\'\'\'class E:passclass F:passclass B(E):passclass C(F):passclass D:def test(self):print(\'from D\')class A(B, C, D):passprint(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 D

深度优先和广度优先

深度优先:

  • 经典类:按深度优先查询

经典类查找顺序如下:

在py2中,没有继承object的类及其子类都是经典类

代码实现:

\'\'\'py2中\'\'\'class G: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):passobj = A()obj.test()  # 查找顺序为:obj->A->B->E->G->C->F->D->object# 结果from B

广度优先:

  • 新式类:按广度优先顺序查找

新式类查找顺序如下:

在py3中,默认为新式类

代码实现如下:

\'\'\'py3中\'\'\'class G:def test(self):print(\'from G\')class E(G):passclass F(G):passclass B(E):passclass C(F):passclass D(G):def test(self):print(\'from D\')class A(B, C, D):passobj = A()obj.test() # 查找顺序为:obj->A->B->E->C->F->D->G->object# 结果from D

super()方法

super()方法的存在就是为了解决多重继承的问题,在一个父类中使用super()方法用于调用下一个父类的方法

  • super方法
class A:def test(self):print(\'from A\')super().test()\'\'\'用于调用下一个父类的方法B.test\'\'\'class B:def test(self):print(\'from B\')class C(A, B):passc = C()c.test()print(C.mro())# 查找顺序如下#[<class \'__main__.C\'>, <class \'__main__.A\'>, <class \'__main__.B\'>, <class \'object\'>]# 结果from Afrom B

抽象类

python的抽象类需要借助模块实现,抽象类是一个特殊的类,它只能被继承,不能被实例化

  • 作用:在不同的模块中通过抽象基类来调用,可以用最精简的方式展示出代码之间的逻辑关系,让模块之间的依赖清晰简单,使得代码的可读性变高。
  • 注意:子类继承抽象类的时候,必须定义相同方法对抽象类的方法进行覆盖
import abc# 抽象类: 抽象类只能被继承,不能被实例化class Animal(metaclass=abc.ABCMeta):@abc.abstractmethod  # 该方法已经是抽象方法了def speak(self): pass@abc.abstractmethoddef login(self):passclass People(Animal):def speak(self):print(\'嗷嗷嗷\')def login(self):passclass Pig(Animal):def speak(self):print(\'哼哼哼\')class Dog(Animal):def speak(self):print(\'汪汪汪\')obj = People()obj.speak()

方法补充:

  1. sel.__class__

    查看对象所属类

  2. 类名/对象名.__dict__

    查看类/对象名称空间

  3. 类名/对象名.__bases__

    查看父类

  4. 起始类名.__mro__

    打印继承顺序,py3从左到右查找

  5. locals()

    查看局部名称空间

  6. globals()

    查看全局名称空间

  7. dirs(str)

    查看字符串所搭配的内置方法有哪些,查看内容可换

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » python 继承