Python魔法方法全解:从入门到运算符重载的完整指南
更新时间:2026年11月10日 09:27:03 作者:小庄-Python办公
本文全面解析Python魔法方法(双下划线方法),涵盖构造销毁、字符串表示、比较与算术运算符重载、容器化对象、可调用对象及上下文管理器等问题,文内并附有常用魔法方法速查表和常见坑提示,助你快速掌握这一核心特性
本篇目标
- 理解什么是魔法方法(双下划线方法)
- 掌握常用魔法方法的写法
- 学会运算符重载,让对象支持
+、==、<等 - 实现容器化对象与上下文管理器
一、什么是魔法方法
形如 __xxx__ 的方法,Python 在特定场景自动调用它们。
class Book:
def __init__(self, title, price):
self.title = title
self.price = price
def __str__(self):
return f"《{self.title}》 ¥{self.price}"
def __len__(self):
return len(self.title)
b = Book("Python入门", 59)
print(b) # 《Python入门》 ¥59 自动调 __str__
print(len(b)) # 11 自动调 __len__
二、构造与销毁
| 方法 | 调用时机 |
|---|---|
__init__(self, ...) | 创建对象后初始化 |
__new__(cls, ...) | 创建对象时(很少用) |
__del__(self) | 对象被垃圾回收时 |
class Resource:
def __init__(self, name):
self.name = name
print(f"{name} 被创建")
def __del__(self):
print(f"{self.name} 被销毁")
r = Resource("连接")
del r # 连接 被销毁
三、字符串表示
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __str__(self):
"""给终端用户看:print()、str()"""
return f"({self.x}, {self.y})"
def __repr__(self):
"""给开发者看:调试、交互式环境、容器里打印"""
return f"Point({self.x}, {self.y})"
def __format__(self, spec):
"""支持 f-string 的自定义格式"""
if spec == "verbose":
return f"横坐标={self.x}, 纵坐标={self.y}"
return str(self)
p = Point(3, 4)
print(f"{p}") # (3, 4)
print(f"{p:verbose}") # 横坐标=3, 纵坐标=4
print([p]) # [Point(3, 4)] 容器里用 __repr__
经验:至少写 __repr__,调试时能省很多事。
四、比较运算符重载
class Money:
def __init__(self, amount):
self.amount = amount
def __eq__(self, other):
return self.amount == other.amount
def __lt__(self, other):
return self.amount < other.amount
def __le__(self, other):
return self.amount <= other.amount
def __gt__(self, other):
return self.amount > other.amount
def __ge__(self, other):
return self.amount >= other.amount
def __ne__(self, other):
return self.amount != other.amount
m1, m2 = Money(100), Money(200)
print(m1 < m2) # True
print(sorted([m2, m1])) # 能排序了
用 functools.total_ordering 偷懒
只写 __eq__ 和 __lt__,其余自动生成:
from functools import total_ordering
@total_ordering
class Money:
def __init__(self, amount):
self.amount = amount
def __eq__(self, other):
return self.amount == other.amount
def __lt__(self, other):
return self.amount < other.amount
def __repr__(self):
return f"Money({self.amount})"
别忘了hash
定义了 __eq__ 后,对象默认不可哈希,不能放进集合或做字典 key:
class Money:
def __init__(self, amount):
self.amount = amount
def __eq__(self, other):
return self.amount == other.amount
def __hash__(self):
return hash(self.amount) # 补上就能哈希了
{Money(1), Money(2)} # ✅
五、算术运算符重载
class Vector:
def __init__(self, x, y):
self.x, self.y = x, y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Vector(self.x - other.x, self.y - other.y)
def __mul__(self, n):
return Vector(self.x * n, self.y * n)
def __rmul__(self, n):
return self.__mul__(n) # 支持 n * vector
def __neg__(self):
return Vector(-self.x, -self.y)
def __abs__(self):
return (self.x ** 2 + self.y ** 2) ** 0.5
def __repr__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # Vector(4, 6)
print(v2 - v1) # Vector(2, 2)
print(v1 * 3) # Vector(3, 6)
print(3 * v1) # Vector(3, 6) ← 靠 __rmul__
print(abs(v2)) # 5.0
完整算术方法对照
| 表达式 | 方法 |
|---|---|
a + b | __add__ |
a - b | __sub__ |
a * b | __mul__ |
a / b | __truediv__ |
a // b | __floordiv__ |
a % b | __mod__ |
a ** b | __pow__ |
-a | __neg__ |
abs(a) | __abs__ |
a += b | __iadd__ |
六、容器化魔法方法
让自己的类像列表一样工作:
class Playlist:
def __init__(self):
self._songs = []
def add(self, song):
self._songs.append(song)
def __len__(self):
return len(self._songs)
def __getitem__(self, index):
return self._songs[index] # 支持下标和切片
def __setitem__(self, index, value):
self._songs[index] = value
def __contains__(self, item):
return item in self._songs # 支持 in
def __iter__(self):
return iter(self._songs) # 支持 for 遍历
def __repr__(self):
return f"Playlist({len(self)} songs)"
pl = Playlist()
pl.add("晴天")
pl.add("稻香")
print(len(pl)) # 2
print(pl[0]) # 晴天
print("稻香" in pl) # True
for song in pl: # 能遍历
print(song)
实现 __getitem__ + __len__ + __iter__,类就"像"个列表了。
七、可调用对象call
class Counter:
def __init__(self):
self.count = 0
def __call__(self):
self.count += 1
return self.count
c = Counter()
print(c()) # 1 像函数一样调用
print(c()) # 2
print(callable(c)) # True
常用于带状态的"函数"(装饰器就会用到)。
八、上下文管理器enter/exit
让自己的类支持 with 语句:
class Timer:
import time
def __enter__(self):
self.start = time.time()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.elapsed = time.time() - self.start
print(f"耗时 {self.elapsed:.3f} 秒")
return False # False = 不吞异常
with Timer() as t:
sum(range(10_000_00))
# 耗时 0.045 秒
实战:数据库连接管理
class DBConnection:
def __init__(self, dsn):
self.dsn = dsn
self.conn = None
def __enter__(self):
print(f"连接数据库 {self.dsn}")
self.conn = {"connected": True}
return self.conn
def __exit__(self, exc_type, exc_val, exc_tb):
print("关闭数据库连接")
self.conn = None
if exc_type:
print(f"发生异常:{exc_val}")
return False
with DBConnection("sqlite:///app.db") as conn:
print(conn)
九、常用魔法方法速查
| 分类 | 方法 |
|---|---|
| 构造销毁 | __init__ __new__ __del__ |
| 字符串 | __str__ __repr__ __format__ |
| 比较 | __eq__ __lt__ __le__ __gt__ __ge__ __ne__ __hash__ |
| 算术 | __add__ __sub__ __mul__ __truediv__ __neg__ __abs__ |
| 容器 | __len__ __getitem__ __setitem__ __contains__ __iter__ |
| 调用 | __call__ |
| 上下文 | __enter__ __exit__ |
| 属性 | __getattr__ __setattr__ __getattribute__ |
| 布尔 | __bool__ |
常见坑
| 坑 | 说明 |
|---|---|
定义 __eq__ 后对象不能哈希 | 补 __hash__ |
__repr__ 里调 str(self) 造成递归 | 直接写字段 |
__exit__ 返回 True 吞异常 | 一般不吞,返回 False 或 None |
__getattr__ 写错导致无限递归 | 记得 raise AttributeError |
| 算术方法返回了 None | 必须 return 新对象 |
本篇小结
- 魔法方法是 Python 自动调用的双下划线方法
- 必写:
__init__、__repr__;常用:__str__、__eq__、__len__ - 运算符重载让对象支持
+、<、in、for __enter__/__exit__实现with支持
练习题
- 给
Money类加__add__、__sub__,支持两笔钱相加减。 - 写一个
Stack类,支持push、pop、len()、下标访问、in判断、for遍历。 - 用
__enter__/__exit__写一个"临时切换工作目录"的上下文管理器。
以上就是Python魔法方法全解:从入门到运算符重载的完整指南的详细内容,更多关于Python魔法方法的资料请关注脚本之家其它相关文章!
相关文章
Python中isinstance和hasattr的实现示例
本文详细介绍了Python中的两个内置函数isinstance和hasattr,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧2024-12-12


最新评论