面向对象特性之 继承

面向对象三大特性

继承

封装

多态

继承

一、什么是继承

继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类。父类的属性和方法,子类中也有。

格式就是子类名加括号,括号内是父类名:

class A:
    pass

class B(A):  # B就继承了A
    pass

这里提一下新式类和经典类的区别

新式类:只要继承了object类,就是新式类,在python3中,默认都是新式类

​ 在python2中,需要显示指定继承object才是新式类

经典类:在python2中,没有继承object的类,就是经典类

​ python3中没有经典类

二、类的继承分为单继承和多继承

class A(object):
    pass
class B(A):
    pass
#  B继承了A这个类,单继承

class C(A,B):
    pass
# C继承了A和B,多继承

类的其他内置属性:__名字__

print(B.__dict__) # 类的属性和方法

print(B.__name__) # 类名

print(B.__bases__) # 类的父类

继承中属性的查找顺序:

先在对象中找---》子类中找---》父类中找(多继承父类的话,从左到右,先从第一个父类找)---》父类中找不到就报错

三、利用继承减少代码冗余

# 这里首先利用面向对象写一个老师和学生的类,他们的基类都是人
# 这里不用继承
class Person:
    school = 'oldboy'

class Teacher(Person):
    def __init__(self,name,age,level):
        self.name=name
        self.age=age
        self.level=level

class Student(Person):
    def __init__(self,name,age,course):
        self.name=name
        self.age=age
        self.course=course
# 以上代码老师和学生的类都有共同属性:name和age,那就可以把这两个属性放到人的类中,然后老师和学生的类来继承人的类

class Person(object):
    school = 'oldboy'
    def __init__(self, name, age):
        self.name = name
        self.age = age

class Teacher(Person):
    pass

class Student(Person):
    pass

# 这样就减少了代码的冗余



注意:子类实例化会自动调用__init__方法,如果子类中没有,会去父类中找,父类中有几个参数就要传几个参数

四、多层继承和多继承

# 多层继承:一层一层的继承下去
class A:
    a="AAAA"
    
class B(A):
    a="BBB"

class C(B):
    a="CCC"
    pass

class D(C):
    a = "DDD"
    pass

# 多继承 
class A:
    a="AAAA"
    
class B:
    a="BBB"

class C:
    a="CCC"
    pass

class D(A,B,C):   # 类 D 继承了上面多个类
    a = "DDD"
    pass

# 多继承的多层
class A:
    a="AAAA"
    
class B(A):
    a="BBB"

class C(B):
    a="CCC"
    pass

class D(A,B,C):
    a = "DDD"
    pass

五、继承属性查找顺序

5.1 菱形问题

继承的菱形问题(显示的都继承一个类,不是object类):新式类和经典类的查找顺序是不一样的。

新式类(py3中全是新式类):广度优先---从左侧开始,一直往上找,找到菱形的顶点结束(不包括菱形顶点),继续下一个继承的父类往上找,找到菱形的顶点结束(不包括菱形顶点),最后找到菱形顶点。

经典类(只有py2中才有):深度优先---从左侧开始,一直往上找,找到菱形的顶点结束(包括菱形顶点)继续下一个继承的父类往上找,找到菱形的顶点结束(不包含菱形定点)。

不出现菱形问题:正常查找

#继承的菱形问题:新式类和经典类的查找顺序是不一样的
#新式类的查找属性:广度优先
#经典类:深度优先

class G(object):
    a = "GGG"
    pass
class F(G):
    # a = "FFF"
    pass
class E(G):
    # a = "EEE"
    pass
class D(G):
    # a = "DDD"
    pass
class C(F):
    # a="CCC"
    pass
class B(E):
    # a="BBB"
    pass
class A(B,C,D):
    # a="AAAA"
    pass

a=A()
print(a.a)

以上代码就出现新式类的菱形问题,查找顺序就是广度优先:

img

经典类的菱形问题,查找顺序就是深度优先:

img

5.2 根据mro列表查找

mro列表,继承顺序查找列表(只在新式类中有)

print(A.mro())
print(A.__mro__)
#  两种方式都可以

六、继承重用父类方法得两种方式

6.1 方式一:指名道姓的使用,跟继承关系无关

# 父类中object写与不写,在py3中没有区别,
# 有的人在py3中这么写,为了向下兼容
# 调用父类方法的第一种方式 :指名道姓的方式,跟继承关系无关
class Person(object):
    def __init__(self,name,age):
        self.name=name
        self.age=age

def init_1(self,name,age):
    self.name=name
    self.age=age

class Student:
    school = 'yyyy'
    
    def __init__(self,name,age,course):
        Person.__init__(self,name,age)    #指名道姓的使用Person的__init__方法,Preson中有三个参数就必须传三个
        init_1(self,name,age)
        self.course=course

stu=Student('nick',19,'python')
print(stu.name)

6.2 方式二:通过super关键字,跟继承关系有关

class Person(object):
    def __init__(self,name,age):
        self.name=name
        self.age=age

class Student(Person):
    school = 'yyyy'
    def __init__(self,name,age,course):
        #super()相当于得到了一个特殊对象,第一个参数不需要传,调用绑定方法,会把自己传过去
        ##########    self不需要传,不需要传
        super().__init__(name,age)
        #看到别人这么写:super(类名,对象)  在py3中为了兼容py2
        #在py3中这么写和省略写法完全一样
        #在py2中必须super(Student,self)写
        # super(Student,self).__init__(name,age)
        self.course=course

stu=Student('nick',19,'python')
print(stu.name)
print(stu.age)
print(stu.course)

6.3 总结

有继承关系的时候,通常用super关键字方法;

指名道姓的方法在以下情况下用:

  • 没有继承关系
  • 如果继承了多个父类,super是按照mro列表找,现在想指名道姓的用某个父类的某个方法,就需要指名道姓的使用
原文地址:https://www.cnblogs.com/zhuangyl23/p/11420981.html