Python 魔法方法(Magic Methods)完全指南
一、概述
1. 定义
魔法方法(Magic Methods)是 Python 中形如 __xxx__(前后各两个下划线)的特殊方法,由 Python 解释器自动调用(无需手动触发),是 Python 面向对象编程的核心特性之一。
2. 核心作用
- 给自定义类赋予「Python 内置类型行为」(如支持加减运算、切片、打印、迭代等);
- 简化代码逻辑,让自定义类使用起来更符合 Python 语法习惯(Pythonic);
- 实现底层功能封装(如实例创建、资源管理、哈希计算等)。
3. 调用机制
Python 解释器在执行特定操作时,会自动触发对应的魔法方法。例如:
- 执行
a + b时,触发a.__add__(b); - 执行
print(a)时,触发a.__str__(); - 执行
len(a)时,触发a.__len__()。
二、常用魔法方法分类与详解
(一)构造与初始化:控制实例的创建、初始化与销毁
用于管理实例的生命周期,是自定义类最基础的魔法方法。
| 魔法方法 | 触发场景 | 核心作用 | 示例代码 |
|---|---|---|---|
__new__(cls, *args, **kwargs) |
实例创建(在 __init__ 之前调用) |
控制实例创建过程(如单例模式、限制实例数量) | python class Singleton: _instance = None def __new__(cls): if not cls._instance: cls._instance = super().__new__(cls) return cls._instance |
__init__(self, *args, **kwargs) |
实例初始化(创建实例后自动调用) | 给实例绑定属性、初始化参数 | python class Person: def __init__(self, name, age): self.name = name self.age = age # 使用:p = Person("张三", 25) |
__del__(self) |
实例销毁(垃圾回收时调用) | 释放资源(如关闭文件、断开数据库连接) | python class FileHandler: def __init__(self, path): self.file = open(path, "w") def __del__(self): self.file.close() # 垃圾回收时自动关闭文件 |
关键说明:
__new__是「创建实例」的方法,返回实例对象;__init__是「初始化实例」的方法,不返回值,仅给实例赋值;__del__不推荐过度依赖(垃圾回收时机不确定),释放资源建议优先使用上下文管理器(__enter__/__exit__)。
(二)字符串表示:控制实例的打印与转换格式
用于定义实例的字符串输出格式,让打印、转换时的结果更易读(面向用户或开发者)。
| 魔法方法 | 触发场景 | 核心作用 | 示例代码 |
|---|---|---|---|
__str__(self) |
print(self)、str(self) 时触发 |
面向用户的友好字符串表示 | python class Person: def __str__(self): return f"Person(name='{self.name}', age={self.age})" # 使用:print(p) → Person(name='张三', age=25) |
__repr__(self) |
终端直接输入实例、repr(self) 时触发 |
面向开发者的精确字符串表示(可还原实例) | python class Person: def __repr__(self): return f"Person('{self.name}', {self.age})" # 使用:终端输入 p → Person('张三', 25)(eval(repr(p)) 可重建实例) |
__format__(self, fmt_spec) |
format(self, fmt_spec) 时触发 |
自定义格式化字符串输出 | python class Date: def __init__(self, year, month, day): self.year = year self.month = month self.day = day def __format__(self, fmt): if fmt == "ymd": return f"{self.year}-{self.month}-{self.day}" elif fmt == "mdy": return f"{self.month}/{self.day}/{self.year}" return f"{self.year}.{self.month}.{self.day}" # 使用:format(Date(2024, 5, 20), "ymd") → "2024-05-20" |
关键说明:
- 若未实现
__str__,Python 会自动调用__repr__替代; __repr__的设计目标是「让输出可还原实例」,__str__的设计目标是「让输出更易读」。
(三)运算符重载:让自定义类支持数学运算与比较
用于给自定义类赋予运算符行为(如 +、-、==、< 等),避免编写繁琐的普通方法(如 add()、compare())。
| 魔法方法 | 触发场景 | 核心作用 | 示例代码(自定义向量类) |
|---|---|---|---|
__add__(self, other) |
self + other(加法) |
实现两个实例的加法运算 | python class Vector: def __init__(self, x, y): self.x = x self.y = y def __add__(self, other): # 向量加法:(x1+x2, y1+y2) return Vector(self.x + other.x, self.y + other.y) def __str__(self): return f"Vector({self.x}, {self.y})" # 使用:v1 = Vector(1,2); v2 = Vector(3,4); print(v1+v2) → Vector(4, 6) |
__sub__(self, other) |
self - other(减法) |
实现两个实例的减法运算 | python def __sub__(self, other): return Vector(self.x - other.x, self.y - other.y) |
__mul__(self, other) |
self * other(乘法) |
实现实例与标量/实例的乘法运算 | python def __mul__(self, scalar): # 向量标量乘法:(x*s, y*s) return Vector(self.x * scalar, self.y * scalar) |
__truediv__(self, other) |
self / other(除法) |
实现实例与标量/实例的除法运算 | python def __truediv__(self, scalar): return Vector(self.x / scalar, self.y / scalar) |
__eq__(self, other) |
self == other(等于) |
定义实例相等的判断逻辑 | python def __eq__(self, other): return self.x == other.x and self.y == other.y # 使用:v1 == v2 → False |
__lt__(self, other) |
self < other(小于) |
定义实例小于的判断逻辑 | python def __lt__(self, other): # 比较向量模长:sqrt(x²+y²) return (self.x**2 + self.y**2) < (other.x**2 + other.y**2) # 使用:v1 < v2 → True |
__ge__(self, other) |
self >= other(大于等于) |
定义实例大于等于的判断逻辑 | python def __ge__(self, other): return (self.x**2 + self.y**2) >= (other.x**2 + other.y**2) |
关键说明:
- 运算符重载需遵循「语义一致性」(如
__add__仅实现加法,不滥用为其他逻辑); - 比较运算符(
==、<等)若未实现,默认比较实例的内存地址(id)。
(四)容器行为:让自定义类支持列表/字典式操作
用于给自定义类赋予容器特性(如索引、切片、长度、迭代),适合封装「集合型数据」(如自定义列表、缓存池、数据集)。
| 魔法方法 | 触发场景 | 核心作用 | 示例代码(自定义列表类) |
|---|---|---|---|
__len__(self) |
len(self) 时触发 |
返回实例的长度(元素个数) | python class MyList: def __init__(self, data): self.data = list(data) def __len__(self): return len(self.data) # 使用:ml = MyList([1,2,3]); len(ml) → 3 |
__getitem__(self, key) |
self[key](索引/切片)时触发 |
实现实例的索引访问、切片功能 | python def __getitem__(self, key): return self.data[key] # 使用:ml[0] → 1;ml[1:3] → [2,3] |
__setitem__(self, key, value) |
self[key] = value 时触发 |
实现实例的索引赋值功能 | python def __setitem__(self, key, value): self.data[key] = value # 使用:ml[0] = 100 → ml.data 变成 [100,2,3] |
__delitem__(self, key) |
del self[key] 时触发 |
实现实例的索引删除功能 | python def __delitem__(self, key): del self.data[key] # 使用:del ml[0] → ml.data 变成 [2,3] |
__iter__(self) |
for x in self 时触发 |
实现实例的迭代功能(支持 for 循环) | python def __iter__(self): return iter(self.data) # 使用:for x in ml: print(x) → 1、2、3 |
__contains__(self, x) |
x in self 时触发 |
实现实例的包含判断功能 | python def __contains__(self, x): return x in self.data # 使用:2 in ml → True |
示例使用完整流程:
ml = MyList([1, 2, 3, 4])
print(len(ml)) # 触发 __len__ → 4
print(ml[1:3]) # 触发 __getitem__ → [2, 3]
ml[0] = 100 # 触发 __setitem__ → ml.data = [100, 2, 3, 4]
print(2 in ml) # 触发 __contains__ → True
for x in ml: # 触发 __iter__ → 遍历输出 100、2、3、4
print(x)
del ml[1] # 触发 __delitem__ → ml.data = [100, 3, 4]