Python继承让一个类(子类)自动获得另一个类(父类)已有的属性和方法,并可以在子类里追加字段,或重写同名方法以改变行为。公共逻辑放在父类,差异放在子类,能减少重复代码。

先看一次继承

括号里写上父类名,子类就挂到这条继承链上。下例中 Dog 重写了 speak,但仍然是 Animal 的一种,isinstance 会得到 True

class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        print(f"{self.name} 发出声音")

class Dog(Animal):
    def speak(self):
        print(f"{self.name} 汪汪!")

d = Dog("旺财")
d.speak()
print(isinstance(d, Animal))

基本写法

语法是 class 子类(父类)。子类即使几乎是空的,也能使用父类里定义好的方法与在父类 __init__ 里设置的属性:

Animal 父类 Dog 子类:重写 speak Cat 子类:直接继承
Dog、Cat 继承 Animal 后自动拥有父类的属性与方法(如 eat),子类可以重写方法改变行为。
class Animal:
    def __init__(self, name):
        self.name = name

    def eat(self):
        print(f"{self.name} 在吃东西")

class Cat(Animal):
    pass

c = Cat("咪咪")
c.eat()
print(c.name)

super() 调用父类

子类若自己写了 __init__,默认不会自动执行父类的 __init__。需要父类那部分初始化时,用 super().__init__(...) 显式调用,再写子类自己的字段:

class Animal:
    def __init__(self, name):
        self.name = name

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)
        self.breed = breed

d = Dog("旺财", "金毛")
print(d.name, d.breed)

方法重写

子类定义与父类同名的方法,就会覆盖父类实现。对一组不同子类对象调用同一方法名,各自走出自己的版本,这是多态常见的落地方式:

class Animal:
    def speak(self):
        print("...")

class Dog(Animal):
    def speak(self):
        print("汪汪!")

class Cat(Animal):
    def speak(self):
        print("喵喵!")

for a in [Dog(), Cat()]:
    a.speak()

多层继承

子类还可以再被继承,形成一条链。最底层的实例能沿链向上找到祖先类的方法:

class A:
    def fa(self):
        print("A")

class B(A):
    def fb(self):
        print("B")

class C(B):
    def fc(self):
        print("C")

c = C()
c.fa()
c.fb()
c.fc()

多重继承

一个类也可以同时列出多个父类,从而混入多组能力。同名方法冲突时,由方法解析顺序(MRO)决定用哪一个,括号里靠前的父类通常更优先。能力组合变复杂时,要谨慎使用:

class Swim:
    def swim(self):
        print("游泳")

class Run:
    def run(self):
        print("跑步")

class Duck(Swim, Run):
    pass

d = Duck()
d.swim()
d.run()

isinstance 与 issubclass

isinstance对象, 类型) 判断对象是否为该类型或其子类的实例。issubclass(子, 父) 判断两个类之间是否存在继承关系:

print(isinstance(d, Duck))
print(isinstance(d, Swim))
print(issubclass(Duck, Run))
print(issubclass(Duck, int))

常见错误

  • 子类写了 __init__ 却忘记 super().__init__,父类里本该设置的属性就不存在。
  • 继承层数堆得过深时,阅读成本上升,有时用组合(对象里持有另一个对象)更清楚。
  • 多重继承下同名方法依赖 MRO,结果可能和直觉不一致,尽量保持父类职责干净。