继承的介绍


继承的作用: 减少代码的重复

抽象: 抽象即抽取类似或者说比较像的部分。

父类 也称之为 基类 超类
子类 也称之为 派生类

class 类名(所要继承的类名)

  • 经典类: 不传所要继承的类名
  • 新式类: 传入 object 即 class a(object)
  • 在 python 2.7 中 经典类和新式类共存且如果要创建新式类就要传入object
  • 在 python 3+ 中 只有新式类 所以在 py3+ 中 object 是不用传的 且 class a 等价于 class a(object) 

调用类的属性或方法的顺序: 先从子类找,找不到再到父类

单继承


1. 单继承的写法

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

    def fn(self, data):
        print('父级方法', data)

class Child(Father):
    pass

child = Child('Kevin', 18)

print(child.name)  # 'Kevin'
child.fn('参数')

2. 调用父类的方法

  • 父类名.方法名(self, 参数1, 参数2)

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

    def fn(self):
        print('父级方法')

class Child(Father):
    def c_fn(self):
Father.fn(self) # 调用父级方法

c = Child('Kevin', 18)
c.c_fn()

  • super().方法名(参数1, 参数2) -> super 不传参写法

    • super() 表示父类
    • 注意: super 传参和不传参的效果都是一样的,只是写法不同

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

    def fn(self):
        print('父级方法')

class Child(Father):

    def c_fn(self):
super().fn() # 调用父级方法

c = Child('Kevin', 18)
c.c_fn()

  • super(自身类, 对象/self) -> super 传参写法

    • super() 表示父类
    • 只有 python 3+ 中有写法
    • 注意: super 传参和不传参的效果都是一样的,只是写法不同

    • 在类方法中使用

      • 写法一

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

    def fn(self):
        print('父级方法')


class Child(Father):

    def c_fn(self):
super(Child, self).fn() # 调用父级方法


c = Child('Kevin', 18)
c.c_fn()

      • 写法二

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

    def fn(self):
        print('父级方法')


class Child(Father):

    def c_fn(self):
super(self.__class__, self).fn()  # 调用父级方法, self.__class__ -> 获取该对象所在的类


c = Child('Kevin', 18)
c.c_fn()

    • 在类外部使用

# 外类的外部使用 super() 需要传 类名 和 对象

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

    def fn(self):
        print('父级方法')


class Child(Father):
    def fn(self):
        print('子级方法')


c = Child('Kevin', 18)
super(Child, c).fn()  # 父级方法

3. super()的执行顺序

  • super() 不是直接执行父类方法,而是根据被序列化的类的 __mro__ 类的继承顺序来执行对应的方法

  • super() 是根据__mro__类的继承顺序执行方法

class Father:
    def fn(self):
super().fn() # Child.fn -> 根据 __mro__ 类的继承顺序执行,先去Child类找有没有fn方法,如果没有再去object类里面找,如果还没有就报错
"""
            为什么要直接从Child类开始找?
            因为super方法在Father类中执行,且在 __mro__ 类的继承顺序中,Child类是Father的下一个类,所以要从Child类开始找起
        """
        print('Father.fn')


class Child:
    def fn(self):
        print('Child.fn')


class Foo(Father, Child):
    pass


# 不会报错: 因为在Child类中可以找到fn方法

f = Foo()
f.fn()
print(Foo.__mro__)  # (<class '__main__.Foo'>, <class '__main__.Father'>, <class '__main__.Child'>, <class 'object'>)

# ---------------------------------------------------

# 报错: 因为object类中没有fn方法

f = Father()
f.fn()
print(Father.__mro__)  # (<class '__main__.Father'>, <class 'object'>)

4. 派生属性

  • 父类中没有的属性且子类有的属性

# 定义派生属性且保留从父类所继承下来的属性的方法一 -> 在子类的 __init__() 方法中调用 父类的 __init__() 方法 self 必须传

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

    def fn(self, data):
        print('父级方法', data)

class Child(Father):
    def __init__(self, name, age, sex):
        Father.__init__(self, name, age) # 保留从父类所继承下来的属性,self 必须传
        self.sex = sex  # 派生属性

child = Child('Kevin', 18, '男')

print(child.name)  # 'Kevin'
print(child.sex)  # '男'

# 定义派生属性且保留从父类所继承下来的属性的方法二 -> 在子类的 __init__() 方法中调用 super().__init__() 方法 self 不用传 super 方法已经帮你传入, super 表示的就是父类的意思 -> 推荐使用

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

    def fn(self, data):
        print('父级方法', data)

class Child(Father):
    def __init__(self, name, age, sex):
        super().__init__(name, age)  # 保留从父类所继承下来的属性,super 表示父类的意思,self 不用传 super 方法已经帮你传入
        self.sex = sex  # 派生属性

child = Child('Kevin', 18, '男')

print(child.name)  # 'Kevin'
print(child.sex)  # '男'

5. 派生方法

  • 父类中没有的方法且子类有的方法

  • 如果子类的方法名和父类的方法名重名默认调用父类的方法

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

    def fn(self, data):
        print('父级方法', data)

class Child(Father):
    def __init__(self, name, age, sex):
        super().__init__(name, age)
        self.sex = sex

    def c_fn(self):  # 派生方法
        print('派生方法')

child = Child('Kevin', 18, '男')

child.c_fn()

  • 如果子类的方法名和父类的方法名重名且在调用子类的方法的时候保留父类同名方法的功能

# 方法一: 父类名.方法名(self, 参数一, 参数二)

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

    def fn(self):
        print('父级方法')

class Child(Father):
    def __init__(self, name, age, sex):
        super().__init__(name, age)
        self.sex = sex

    def fn(self):
        Father.fn(self)  # 保留父类同名方法的功能
        print('保留了父类同名方法的功能')

child = Child('Kevin', 18, '男')

child.fn()

# 方法二: super().父级方法名(参数一, 参数二) -> 不用传 self -> 推荐使用

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

    def fn(self):
        print('父级方法')

class Child(Father):
    def __init__(self, name, age, sex):
        super().__init__(name, age)
        self.sex = sex

    def fn(self):
        super().fn()  # 保留父类同名方法的功能
        print('保留了父类同名方法的功能')

child = Child('Kevin', 18, '男')

child.fn()

6. 人狗大战通过继承简化代码

class Public:
    def __init__(self, name, blood, aggr):
'''
        :param name: 姓名
        :param blood: 血量
        :param aggr: 攻击力
        '''
        self.name = name
        self.blood = blood
        self.aggr = aggr

    def eat(self):
        print('吃药了')
        self.blood += 100

class Dog(Public):
    def __init__(self, name, blood, aggr, kind):
 '''
        :param kind: 品种
        '''
        Public.__init__(self, name, blood, aggr)
        self.kind = kind

    def bite(self):
        print('狗咬人')

class Person(Public):
    def __init__(self, name, blood, aggr, sex):
'''
        :param sex: 姓名
        '''
        Public.__init__(self, name, blood, aggr)
        self.sex = sex

    def eat(self):
        super().eat()
        self.blood += 50
        print('人吃药而外补多50血')

    def attack(self):
        print('人打狗')

d = Dog('leopard_brother', 100, 20, 'teddy')
p = Person('Kevin', 200, 998, '男')

print(p.name)
d.bite()
p.attack()
p.eat()
print(p.blood)

多继承


  • 只有 python 中有多继承

  • 在开发的过程中最好不要使用多继承的方式,除非使用了开发模式

  • 钻石继承


python 中如果继承了多个类,那么其寻找方式只有两种,分别是 深度优先广度优先

  • 当类是经典类的时,多继承情况下,会按照深度优先方式查找
  • 当类是新式类的时,多继承情况下,会按照广度优先方式查找


class A(object):
    def test(self):
        print('from A')

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

class C(A):
    def test(self):
        print('from C')

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

f1 = D()
f1.test()
print(D.__mro__)  # 只有新式才有这个属性可以查看线性列表,经典类没有这个属性


#新式类继承顺序: D -> B -> C -> A
#经典类继承顺序: D -> B -> A -> C

#python3中统一都是新式类
#pyhon2中才分新式类与经典类


class A(object):
    def test(self):
        print('from A')

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

class C(A):
    def test(self):
        print('from C')

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

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

class F(D,E):
# def test(self):
    #     print('from F')
    pass

f1=F()
f1.test()
print(F.__mro__)  # 只有新式才有这个属性可以查看线性列表,经典类没有这个属性

#新式类继承顺序: F -> D -> B -> E -> C -> A
#经典类继承顺序: F -> D -> B -> A -> E -> C

#python3中统一都是新式类
#pyhon2中才分新式类与经典类

  • 多继承 super() 执行循序 -> 看回上面的 深度优先 和 广度优先度 就能理解了

class A(object):
    def func(self): print('A')

class B(A):
    def func(self):
        super().func()
        print('B')

class C(A):
    def func(self):
        super().func()
        print('C')

class D(B,C):
    def func(self):
        super().func()
        print('D')

b = D()
b.func() # A C B D

  • .__mro__ 查看继承顺序 -> 只有新式类才能使用

class A(object):
    def test(self):
        print('from A')

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

class C(A):
    def test(self):
        print('from C')

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

print(D.__mro__)  # (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)