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__、breathe、info 这三段代码在两个类里几乎一模一样。
问题:
- 维护困难:改一个地方,另一个地方也要改
- 容易漏改、改错
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 按下面顺序找:
- 先在
GoldenRetriever里找bark - 找不到,去
Dog里找bark✓ 找到了,执行它 - 若还找不到,去
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 关系用组合 |