17.Python中的继承完全指南

Python 中的继承完全指南

本文档面向零基础新手,目标是让你不只”会写继承语法”,而是真正理解:

  • 继承是什么,为什么要用它
  • 子类和父类的关系
  • 子类如何继承、扩展、覆盖父类
  • super() 到底在做什么
  • isinstance() 和 issubclass() 的用法
  • 多重继承与 MRO(方法解析顺序)
  • 常见陷阱与注意事项

配有大量示例,全部可以直接运行。


第一部分:为什么要用继承?

1.1 没有继承时,重复代码很多

假设你要写”狗”和”猫”两个类,它们都有名字、年龄,都会呼吸、都会介绍自己:

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

    def breathe(self):
        print(f"{self.name} 在呼吸")

    def info(self):
        return f"名字:{self.name},年龄:{self.age}"

    def bark(self):
        print(f"{self.name}:汪汪!")

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

    def breathe(self):
        print(f"{self.name} 在呼吸")   # 和 Dog 一模一样!

    def info(self):
        return f"名字:{self.name},年龄:{self.age}"  # 也一样!

    def meow(self):
        print(f"{self.name}:喵~")

__init__breatheinfo 这三段代码在两个类里几乎一模一样。 问题:

  • 维护困难:改一个地方,另一个地方也要改
  • 容易漏改、改错

1.2 用继承:公共部分只写一次

把”狗和猫都有的”提取到一个父类 Animal 里;”狗特有的””猫特有的”分别写在子类里:

class Animal:               # 父类:放公共属性和方法
    def __init__(self, name, age):
        self.name = name
        self.age  = age

    def breathe(self):
        print(f"{self.name} 在呼吸")

    def info(self):
        return f"名字:{self.name},年龄:{self.age}"

class Dog(Animal):          # 子类:继承 Animal,只写自己特有的
    def bark(self):
        print(f"{self.name}:汪汪!")

class Cat(Animal):          # 子类:继承 Animal,只写自己特有的
    def meow(self):
        print(f"{self.name}:喵~")

dog = Dog("旺财", 3)
cat = Cat("咪咪", 2)

dog.breathe()       # 旺财 在呼吸(继承自父类)
cat.breathe()       # 咪咪 在呼吸(继承自父类)
print(dog.info())   # 名字:旺财,年龄:3
dog.bark()          # 旺财:汪汪!
cat.meow()          # 咪咪:喵~

这就是继承的核心价值:写一次,多处复用,改一处,全部生效。


第二部分:继承的基本语法

2.1 定义子类的语法

class 父类名:
    ...

class 子类名(父类名):   # 括号里写父类名
    ...
  • 子类会自动拥有父类的所有属性和方法(包括 __init__、普通方法等)
  • 子类可以新增自己特有的属性和方法
  • 子类可以覆盖(重写)父类的方法,让同名方法表现出不同的行为

2.2 最简单的继承:子类什么都不写

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

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

class Dog(Animal):
    pass   # 什么都不写,但 Dog 自动拥有 Animal 的一切

dog = Dog("旺财")      # 使用父类的 __init__
print(dog.name)        # 旺财(继承的属性)
dog.speak()            # 旺财 发出声音(继承的方法)

第三部分:子类的 init 与 super()

3.1 子类没有 init:自动用父类的

如果子类没有定义 __init__,Python 会自动使用父类的 __init__

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

class Dog(Animal):
    pass   # 没有 __init__

dog = Dog("旺财", 3)   # 用父类的 __init__
print(dog.name)        # 旺财
print(dog.age)         # 3

3.2 子类有 init:需要自己处理父类属性

当子类需要新增属性时,要自己定义 __init__。这时必须主动调用父类的 __init__,否则父类里的属性不会被初始化。

错误示例(忘记调用父类 init):

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

class Dog(Animal):
    def __init__(self, name, age, breed):   # 子类加了 breed
        # 忘记调用父类的 __init__!
        self.breed = breed

dog = Dog("旺财", 3, "金毛")
print(dog.breed)   # 金毛(breed 有)
print(dog.name)    # AttributeError:name 没有被初始化!

正确写法:用 super() 调用父类的 init

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

class Dog(Animal):
    def __init__(self, name, age, breed):
        super().__init__(name, age)   # 先让父类初始化 name 和 age
        self.breed = breed            # 再初始化子类自己的属性

dog = Dog("旺财", 3, "金毛")
print(dog.name)    # 旺财
print(dog.age)     # 3
print(dog.breed)   # 金毛

3.3 super() 是什么?

super() 的作用是:返回”当前类的父类(在继承链上找下一个)”,让你能调用父类的方法。

最常见用法:在子类的 __init__ 里调用 super().__init__(...) 初始化父类属性。

除了 __init__super() 也可以调用父类的其他方法:

class Animal:
    def info(self):
        return f"名字:{self.name},年龄:{self.age}"

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

    def info(self):
        base = super().info()            # 拿到父类的 info 结果
        return base + f",品种:{self.breed}"   # 在此基础上追加

dog = Dog("旺财", 3, "金毛")
print(dog.info())   # 名字:旺财,年龄:3,品种:金毛

第四部分:方法覆盖(Override)

4.1 什么是方法覆盖?

当子类定义了一个和父类同名的方法,调用时会执行子类的版本,而不是父类的版本。这就是方法覆盖(Override)

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

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

    def speak(self):            # 覆盖父类的 speak
        print(f"{self.name}:汪汪!")

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

    def speak(self):            # 覆盖父类的 speak
        print(f"{self.name}:喵~")

class Fish(Animal):
    def __init__(self, name):
        self.name = name
    # 没有覆盖 speak,会用父类的

dog  = Dog("旺财")
cat  = Cat("咪咪")
fish = Fish("小金")

dog.speak()    # 旺财:汪汪!(用子类的)
cat.speak()    # 咪咪:喵~(用子类的)
fish.speak()   # 小金 发出声音(用父类的,因为没覆盖)

4.2 覆盖时用 super() 扩展父类行为

有时你不想完全替换父类方法,而是在父类的基础上追加新内容

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

    def info(self):
        return f"[动物] 名字:{self.name},年龄:{self.age}"

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

    def info(self):
        parent_info = super().info()            # 调用父类的 info
        return f"{parent_info},品种:{self.breed}"  # 追加子类内容

dog = Dog("旺财", 3, "金毛")
print(dog.info())
# [动物] 名字:旺财,年龄:3,品种:金毛

第五部分:isinstance() 与 issubclass()

5.1 isinstance():判断一个对象是否是某个类(或其子类)的实例

class Animal:
    pass

class Dog(Animal):
    pass

dog = Dog()

print(isinstance(dog, Dog))     # True(dog 是 Dog 的实例)
print(isinstance(dog, Animal))  # True(dog 也是 Animal 的实例,因为 Dog 继承 Animal)
print(isinstance(dog, str))     # False

注意:isinstance(dog, Animal) 返回 True,即使 dog 是子类 Dog 的实例。

5.2 issubclass():判断一个类是否是另一个类的子类

class Animal:
    pass

class Dog(Animal):
    pass

class Cat(Animal):
    pass

print(issubclass(Dog, Animal))   # True
print(issubclass(Cat, Animal))   # True
print(issubclass(Dog, Cat))      # False(Dog 不是 Cat 的子类)
print(issubclass(Animal, Animal))# True(一个类是自身的子类)

5.3 isinstance() 的实际用途

判断传入对象是否是期望的类型,再决定如何处理:

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

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

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

def make_sound(animal):
    if isinstance(animal, Dog):
        animal.bark()
    elif isinstance(animal, Cat):
        animal.meow()
    else:
        print(f"{animal.name} 发出声音")

make_sound(Dog("旺财"))   # 旺财:汪汪!
make_sound(Cat("咪咪"))   # 咪咪:喵~

第六部分:多层继承(继承链)

6.1 子类还可以有子类

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

    def breathe(self):
        print(f"{self.name} 在呼吸")

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

class GoldenRetriever(Dog):   # 金毛继承 Dog,Dog 继承 Animal
    def fetch(self):
        print(f"{self.name} 衔回了飞盘!")

g = GoldenRetriever("小金")
g.breathe()   # 小金 在呼吸(Animal 的方法)
g.bark()      # 小金:汪汪!(Dog 的方法)
g.fetch()     # 小金 衔回了飞盘!(GoldenRetriever 自己的)

6.2 方法解析顺序(MRO):Python 怎么找方法

当你调用 g.bark(),Python 按下面顺序找:

  1. 先在 GoldenRetriever 里找 bark
  2. 找不到,去 Dog 里找 bark ✓ 找到了,执行它
  3. 若还找不到,去 Animal 里找,再往上直到 object

这个顺序叫 MRO(Method Resolution Order),可以用 类名.__mro__ 查看:

print(GoldenRetriever.__mro__)
# (<class 'GoldenRetriever'>, <class 'Dog'>, <class 'Animal'>, <class 'object'>)

或者:

print(GoldenRetriever.mro())
# [<class 'GoldenRetriever'>, <class 'Dog'>, <class 'Animal'>, <class 'object'>]

第七部分:多重继承(一个子类同时继承多个父类)

7.1 多重继承的语法

class 子类(父类A, 父类B, 父类C):
    pass
class Flyable:
    def fly(self):
        print(f"{self.name} 在飞翔")

class Swimmable:
    def swim(self):
        print(f"{self.name} 在游泳")

class Duck(Flyable, Swimmable):
    def __init__(self, name):
        self.name = name

    def quack(self):
        print(f"{self.name}:嘎嘎!")

d = Duck("唐老鸭")
d.fly()    # 唐老鸭 在飞翔
d.swim()   # 唐老鸭 在游泳
d.quack()  # 唐老鸭:嘎嘎!

7.2 多重继承的 MRO(C3 线性化)

当多个父类里有同名方法,Python 按 C3 线性化算法决定用哪个。新手只需记住:从左到右,深度优先,不重复

class A:
    def hello(self):
        print("A 的 hello")

class B(A):
    def hello(self):
        print("B 的 hello")

class C(A):
    def hello(self):
        print("C 的 hello")

class D(B, C):   # D 继承 B 和 C
    pass

d = D()
d.hello()   # B 的 hello(MRO:D → B → C → A,先找到 B 就用 B 的)

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

7.3 多重继承中 super() 的完整传递(协作式调用)

在多重继承里,super() 按 MRO 顺序传递,确保每个父类的 __init__ 都能被调用。 建议新手先搞清楚单继承,多重继承中的 super() 链是进阶内容。

class A:
    def __init__(self):
        print("A.__init__")
        super().__init__()

class B(A):
    def __init__(self):
        print("B.__init__")
        super().__init__()

class C(A):
    def __init__(self):
        print("C.__init__")
        super().__init__()

class D(B, C):
    def __init__(self):
        print("D.__init__")
        super().__init__()

D()
# D.__init__
# B.__init__
# C.__init__
# A.__init__

每一级都调了 super().__init__(),于是 MRO 链上的每个 __init__ 都被调到了。


第八部分:抽象基类(abstract class)

8.1 什么是抽象基类?

有时你定义一个”基类”,但这个基类本身不应该被直接创建实例,只用来当”模板”:

  • 规定子类必须实现哪些方法
  • 如果子类没有实现,就不能创建它的实例

Python 用 abc 模块实现这一功能。

8.2 用 abc.ABC 和 @abstractmethod

from abc import ABC, abstractmethod

class Shape(ABC):              # 继承 ABC 就成了抽象基类
    @abstractmethod
    def area(self):            # 标记为抽象方法:子类必须实现
        pass

    @abstractmethod
    def perimeter(self):       # 子类必须实现
        pass

    def describe(self):        # 普通方法:子类继承即可(不强制重写)
        return f"面积:{self.area():.2f},周长:{self.perimeter():.2f}"

直接创建 Shape 实例会报错:

s = Shape()   # TypeError: Can't instantiate abstract class Shape ...

8.3 子类实现抽象方法后才能创建实例

from abc import ABC, abstractmethod
import math

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

    @abstractmethod
    def perimeter(self):
        pass

    def describe(self):
        return f"面积:{self.area():.2f},周长:{self.perimeter():.2f}"

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return math.pi * self.radius ** 2

    def perimeter(self):
        return 2 * math.pi * self.radius

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width  = width
        self.height = height

    def area(self):
        return self.width * self.height

    def perimeter(self):
        return 2 * (self.width + self.height)

c = Circle(5)
r = Rectangle(4, 6)

print(c.describe())   # 面积:78.54,周长:31.42
print(r.describe())   # 面积:24.00,周长:20.00

8.4 如果子类没有实现所有抽象方法?

class Triangle(Shape):
    def area(self):           # 只实现了 area,没实现 perimeter
        return 0

t = Triangle()   # TypeError: Can't instantiate abstract class Triangle ...

只有当子类实现了全部抽象方法,才能创建实例。


第九部分:继承与组合的选择

9.1 继承(is-a 关系)

用继承的场景:子类“是一种”父类。

  • 是一种动物 → class Dog(Animal)
  • 储蓄账户是一种银行账户 → class SavingsAccount(BankAccount)

9.2 组合(has-a 关系)

如果是“有一个”关系,用组合(把另一个对象作为属性)更合适。

  • 汽车有一个引擎 → 不要 class Car(Engine),而是 self.engine = Engine(...)
class Engine:
    def __init__(self, horsepower):
        self.horsepower = horsepower

    def start(self):
        print(f"{self.horsepower}马力引擎启动!")

class Car:
    def __init__(self, brand, horsepower):
        self.brand  = brand
        self.engine = Engine(horsepower)   # 组合:Car "有一个" Engine

    def drive(self):
        self.engine.start()
        print(f"{self.brand} 出发了!")

car = Car("宝马", 300)
car.drive()
# 300马力引擎启动!
# 宝马 出发了!

判断口诀:

  • 是”is-a”关系 → 用继承
  • 是”has-a”关系 → 用组合

第十部分:综合示例

10.1 动物系统(多层继承 + 覆盖 + super())

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

    def breathe(self):
        print(f"{self.name} 在呼吸")

    def info(self):
        return f"名字:{self.name},年龄:{self.age}"

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

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

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

    def info(self):
        return super().info() + f",品种:{self.breed}"

class GoldenRetriever(Dog):
    def __init__(self, name, age):
        super().__init__(name, age, breed="金毛寻回犬")

    def fetch(self):
        print(f"{self.name} 飞快地衔回飞盘!")

class Cat(Animal):
    def __init__(self, name, age, indoor=True):
        super().__init__(name, age)
        self.indoor = indoor

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

    def info(self):
        location = "室内" if self.indoor else "室外"
        return super().info() + f",{location}猫"

# 测试
golden = GoldenRetriever("小金", 2)
cat    = Cat("咪咪", 3, indoor=False)

golden.breathe()       # 小金 在呼吸(Animal 的)
golden.speak()         # 小金:汪汪!(Dog 的)
golden.fetch()         # 小金 飞快地衔回飞盘!(GoldenRetriever 的)
print(golden.info())   # 名字:小金,年龄:2,品种:金毛寻回犬

print()

cat.speak()            # 咪咪:喵~
print(cat.info())      # 名字:咪咪,年龄:3,室外猫

print()

# isinstance 判断
print(isinstance(golden, GoldenRetriever))  # True
print(isinstance(golden, Dog))              # True
print(isinstance(golden, Animal))           # True
print(isinstance(golden, Cat))              # False

10.2 图形系统(抽象基类)

from abc import ABC, abstractmethod
import math

class Shape(ABC):
    def __init__(self, color="白"):
        self.color = color

    @abstractmethod
    def area(self):
        pass

    @abstractmethod
    def perimeter(self):
        pass

    def describe(self):
        return (f"{self.color}色图形 | "
                f"面积:{self.area():.2f} | "
                f"周长:{self.perimeter():.2f}")

class Circle(Shape):
    def __init__(self, radius, color="白"):
        super().__init__(color)
        self.radius = radius

    def area(self):
        return math.pi * self.radius ** 2

    def perimeter(self):
        return 2 * math.pi * self.radius

class Rectangle(Shape):
    def __init__(self, width, height, color="白"):
        super().__init__(color)
        self.width  = width
        self.height = height

    def area(self):
        return self.width * self.height

    def perimeter(self):
        return 2 * (self.width + self.height)

class Square(Rectangle):       # 正方形 is-a 矩形
    def __init__(self, side, color="白"):
        super().__init__(side, side, color)

shapes = [
    Circle(5, "红"),
    Rectangle(4, 6, "蓝"),
    Square(3, "绿"),
]

for s in shapes:
    print(s.describe())
# 红色图形 | 面积:78.54 | 周长:31.42
# 蓝色图形 | 面积:24.00 | 周长:20.00
# 绿色图形 | 面积:9.00  | 周长:12.00

第十一部分:常见陷阱与注意事项

11.1 子类 init 忘记调用 super().init()

后果:父类的属性没有初始化,访问时报 AttributeError

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

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

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

修复: 加上 super().__init__(name)


11.2 覆盖方法时参数签名不匹配

class Animal:
    def speak(self, volume="正常"):
        print(f"发出{volume}声音")

class Dog(Animal):
    def speak(self):            # 参数少了!
        print("汪汪")

d = Dog()
d.speak()              # 汪汪(正常调用)
d.speak("大声")        # TypeError:speak() takes 1 positional argument but 2 were given

覆盖方法时,通常要保证签名兼容(新手先保持参数一致或更宽松)。


11.3 多重继承的菱形问题(Diamond Problem)

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

D().hello()   # B(MRO:D → B → C → A,先找到 B)

Python 用 C3 线性化算法避免了”A 被初始化两次”的问题,新手遇到菱形继承时先查 __mro__ 确认顺序。


11.4 不要过度使用继承

继承层次太深时,代码难以追踪。一般建议:

  • 继承层次不超过 3 层(根类 → 中间类 → 具体类)
  • 如果多个类”共享行为”但不是”is-a”关系,考虑用组合或 Mixin

第十二部分:小结表

概念 说明
继承 class 子类(父类):,子类自动拥有父类的属性和方法
子类新增属性 在子类的 __init__ 里定义,同时用 super().__init__(...) 初始化父类属性
方法覆盖 子类定义同名方法,调用时优先用子类的版本
super() 返回父类,让你调用父类的方法;最常见用途是调用父类的 __init__
isinstance() 判断对象是否是某类(或其子类)的实例
issubclass() 判断一个类是否是另一个类的子类
MRO 方法解析顺序,Python 按 C3 算法确定;用 类.__mro__ 查看
多重继承 class D(A, B):,子类继承多个父类;MRO 从左到右深度优先
抽象基类 abc.ABC + @abstractmethod;子类必须实现抽象方法才能创建实例
继承 vs 组合 is-a 关系用继承;has-a 关系用组合

发表评论