首页 / Python3 入门教程 / 继承与多态

Python3 入门教程

继承与多态

本教程共 70 篇 · 第 33 篇 · 更新于 2026-07-22 · 约 6 分钟阅读

PythonPython3 入门教程继承多态super()MRO

33. 继承与多态

本节目标:学会用 class Child(Parent) 继承父类,理解方法重写和 super() 的用法,感受多态带来的代码灵活性。

写代码时经常遇到这样的场景:两个类长得很像,只是细节不同。比如 DogCat,都会叫、都会跑,但叫声不一样。

复制粘贴一份代码改改也能用,但维护时要在两处同步修改,很痛苦。继承(Inheritance)就是来解决这个问题的。

继承的基本语法

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

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

class Dog(Animal):
    pass

d = Dog('旺财')
print(d.name)   # 旺财

Dog(Animal) 表示 Dog 继承自 AnimalDog 没有写 __init__,但创建实例时自动调用了父类的方法,name 属性就有了。

Tip

所有类默认继承自 object。写 class Animal:class Animal(object): 效果一样。在 Python 3 里,推荐省略 (object)

方法重写(Override)

子类对父类的方法不满意,可以重新写一个同名的。这叫「重写」。

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

class Cat(Animal):
    def speak(self):
        print(f"{self.name}: 喵喵喵~")

Dog('旺财').speak()  # 旺财: 汪汪汪!
Cat('咪咪').speak()  # 咪咪: 喵喵喵~

调用的是哪个 speak,取决于实例的真实类型,不是变量名。

super():调用父类的方法

重写时,子类经常想在父类逻辑的基础上加点东西,而不是完全替换。super() 帮你调用父类:

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)  # 调用父类的 __init__
        self.breed = breed      # 再加自己的逻辑

    def speak(self):
        super().speak()         # 先执行父类的 speak
        print(f"({self.name} 摇着尾巴)")

d = Dog('旺财', '金毛')
d.speak()
# 输出:
# 旺财 发出声音
# (旺财 摇着尾巴)

super().__init__(name) 等价于 Animal.__init__(self, name),但更好:如果父类改了名字,子类代码不用动。

Note

Python 3 里 super() 可以不带参数。Python 2 必须写 super(Dog, self),忘掉它吧。

多态(Polymorphism)

多态的字面意思是「多种形态」。在 Python 里,它体现为:同一个函数,传入不同类型的对象,表现不同。

def animal_concert(animal):
    animal.speak()

animal_concert(Dog('旺财'))
animal_concert(Cat('咪咪'))

animal_concert 不在乎传入的是 Dog 还是 Cat,只要对象有 speak() 方法就行。这就是「鸭子类型(duck typing)」:看起来像鸭子、叫起来像鸭子,那它就是鸭子。

再举个例子,内置函数 len() 也是多态的:

print(len('hello'))      # 字符串,长度 5
print(len([1, 2, 3]))    # 列表,长度 3
print(len({'a': 1}))     # 字典,长度 1

len() 不关心对象的具体类型,只关心对象有没有 __len__ 方法。

判断类型:type vs isinstance

有时候你需要判断一个对象是不是某个类的实例。用 isinstance

d = Dog('旺财')
print(isinstance(d, Dog))      # True
print(isinstance(d, Animal))   # True(Dog 是 Animal 的子类)
print(type(d) == Dog)          # True
print(type(d) == Animal)       # False

isinstance 会考虑继承链,type 只返回最精确的类型。判断类型时,优先用 isinstance

Warning

过度使用 isinstance 会破坏多态的灵活性。如果你写了很多 if isinstance(x, Dog): ... elif isinstance(x, Cat): ...,往往说明设计可以优化。

继承的坑:菱形继承

如果继承结构复杂,可能出现「菱形继承」:

    A
   / \
  B   C
   \ /
    D

D 同时继承 BC,而 BC 都继承 A。这时 D 里调用 super() 会走哪条路?Python 用 C3 线性化算法决定了方法解析顺序(MRO)。

class A:
    def hello(self):
        print('A')

class B(A):
    def hello(self):
        print('B')

class C(A):
    def hello(self):
        print('C')

class D(B, C):
    pass

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

D().hello()  # B

__mro__ 属性显示了方法查找顺序。D 先找自己,再找 B,然后 C,然后 AB 排在 C 前面,因为 class D(B, C)B 写在左边。

Note

多重继承后面会专门讲(第 36 章)。这里只需要知道:Python 有明确的 MRO 规则,不会乱。

实战:设计一个简单的员工系统

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary

    def get_bonus(self):
        return self.salary * 0.1

    def info(self):
        print(f"{self.name}: 工资 {self.salary}, 奖金 {self.get_bonus():.0f}")

class Developer(Employee):
    def get_bonus(self):
        return self.salary * 0.15

class Manager(Employee):
    def get_bonus(self):
        base = super().get_bonus()
        return base + 5000

staff = [Employee('小张', 8000), Developer('小李', 10000), Manager('老王', 15000)]
for s in staff:
    s.info()

Employee 定义了通用逻辑。Developer 重写奖金比例,Manager 在父类奖金基础上再加固定金额。info() 里调用 get_bonus() 时,自动走到各自子类的实现。这就是多态的威力。

小结

  • 继承让子类复用父类的代码,减少重复。
  • 方法重写让子类定制自己的行为。
  • super() 安全地调用父类方法,方便扩展。
  • 多态让代码更灵活,同一套逻辑适配不同对象。

继承不是「为了用而用」。如果两个类确实符合「is-a」关系(狗是一种动物),才适合继承。如果只是有些功能相同,组合(composition)往往是更好的选择。


来源:参考了 runoob「Python3 面向对象」、liaoxuefeng「继承和多态」、pythondoc「9. 类」等,改写后所得。