拒绝死记硬背!用 C++ 的底层逻辑,彻底搞懂 Python 的“魔术方法”
引言:打破“魔法”的迷信
初学者看到 __init__、__str__ 这种双下划线方法就头大,只能死记硬背。但对于C++程序员来说,这些其实就是编译器在特定时刻自动调用的“钩子函数”。
核心类比
- Python 的
obj + obj≈ C++ 的operator+重载。 - Python 的
str(obj)≈ C++ 的operator std::string()或toString()虚函数。 - Python 的
cls≈ C++ 的static类作用域。
变身术:类型转换协议 (str vs int)
__str__ (人类视图)
- C++ 类比:相当于 C++ 中重载
std::ostream& operator<<(std::ostream&, const T&)。 - 触发时机:当你执行
print(obj)或str(obj)时。 - 用途:返回一个人类可读的字符串。
__repr__ (机器视图)
- C++ 类比:相当于调试器中显示对象的逻辑。
- 触发时机:当你在交互式终端中直接输入对象名并回车时。
- 用途:返回一个能让
eval()重建对象的字符串。
算术与增量运算协议
基本运算
__add__:对应+运算符。__sub__:对应-运算符。__mul__:对应*运算符。__truediv__:对应/运算符。
反向运算
__radd__:当a + b中a不支持+操作时,会尝试b.__radd__(a)。
增量运算
__iadd__:对应+=运算符,比+更高效。
比较运算协议
__eq__:对应==运算符。__lt__:对应<运算符。__gt__:对应>运算符。__le__:对应<=运算符。__ge__:对应>=运算符。__ne__:对应!=运算符。
技巧
Python 有个 @total_ordering 装饰器,只要你定义了 __eq__ 和一个比较符(如 __lt__),它会自动补全其他的(类似 C++ 的 std::rel_ops)。
容器与布尔协议:Python 特有的“真值测试”
__len__
- C++ 类比:相当于类的
.size()或.length()成员函数。 - 触发时机:当你调用
len(obj)时。
__bool__
- C++ 类比:相当于 C++ 的
explicit operator bool()。 - 触发时机:当你执行
if obj:时。 - 注意:如果没有定义这个,Python 会退而求其次看
__len__(如果长度为 0 则为 False)。
容器协议
__getitem__:对应obj[key]读取操作。__setitem__:对应obj[key] = value赋值操作。__iter__:让对象变成可迭代对象。__next__:实现迭代器协议。
代码实战:Vector 类
1 | from functools import total_ordering |
总结:从“语法糖”到“协议”
C++ 的操作符重载是静态绑定的(编译期决定),而 Python 的魔术方法是动态派发的(运行时查找)。
不要死记硬背这些方法名,而是记住场景:
- 想打印?找
__str__。 - 想相加?找
__add__。 - 想比大小?找
__lt__/__gt__/__eq__。 - 想变类型?找
__int__/__bool__。
一句话心法:“你想让你的对象在什么场景下做什么事,就实现对应的魔术方法。”
All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.

