Python 魔术方法(Magic Method / 双下划线方法__xxx__)
特征:前后双下划线
__名字__,又叫特殊方法。
你不要手动直接调用它,Python解释器会在特定语法、内置操作触发时自动调用。
举个直观例子:
obj+obj2# 自动调用 obj.__add__(obj2)print(obj)# 自动调用 obj.__str__()len(obj)# 自动调用 obj.__len__()withobj:# 自动调用 __enter__ / __exit__forxinobj:# 自动调用 __iter__ / __next__对比 Swift:相当于 Swift 的
customDebugDescription、+运算符重载、Sequence协议;Python靠魔术方法,Swift靠协议扩展。
一、基础常用魔术方法
1. 实例生命周期
__new__(cls, *args, **kwargs)
创建对象实例(分配内存),类方法,先执行。几乎很少重写。__init__(self, ...)
初始化实例,大家最熟。
obj = A()→ 先__new__得到空对象,再调用__init__给属性赋值。
❗注意:__init__不创建对象,只是初始化。
__del__(self)
对象被垃圾回收销毁时触发。不要用来做资源释放!不可预测什么时候执行,资源释放优先用上下文管理器__enter__/__exit__,不要靠__del__。
classDemo:def__init__(self,name):self.name=nameprint(f"__init__ 初始化{self.name}")d=Demo("aaa")2. 字符串打印
__str__(self):str(obj)/print(obj),面向人阅读的字符串。__repr__(self):repr(obj),调试、控制台输出,面向开发者,理想状态可以eval复原对象。
classUser:def__init__(self,name):self.name=namedef__str__(self):returnf"用户:{self.name}"def__repr__(self):returnf"User(name='{self.name}')"u=User("张三")print(u)# __str__ → 用户:张三print(repr(u))# __repr__ → User(name='张三')3. 长度、下标访问(容器类)
__len__(self):len(obj)触发,必须返回int__getitem__(self, key):obj[key]获取__setitem__(self, key, val):obj[key] = val赋值__delitem__(self, key):del obj[key]删除
classMyList:def__init__(self):self.data=[10,20,30]def__len__(self):returnlen(self.data)def__getitem__(self,idx):returnself.data[idx]ml=MyList()print(len(ml))# 调用 __len__print(ml[1])# 调用 __getitem__4. 上下文管理器(就是刚才 with)
__enter__(self):进入with块执行,返回值赋值给as 变量__exit__(self, exc_type, exc_val, exc_tb):退出with块,无论是否异常一定执行。
classCtx:def__enter__(self):print("进入")returnselfdef__exit__(self,et,ev,tb):print("退出,不管有没有报错都跑")withCtx()asc:passasync协程上下文管理器:
__aenter__、__aexit__,配合async with。
5. 迭代器相关 for in
__iter__(self):for x in obj,返回迭代器对象__next__(self):迭代取下一个,无数据抛StopIteration
classCounter:def__iter__(self):self.n=0returnselfdef__next__(self):ifself.n>=3:raiseStopIteration v=self.n self.n+=1returnvforiinCounter():print(i)6. 运算符重载
__add__(self,other)# +__sub__(self,other)# -__mul__(self,other)# *__eq__(self,other)# ==__lt__(self,other)# <示例:
classPoint:def__init__(self,x,y):self.x=x self.y=ydef__add__(self,other):returnPoint(self.x+other.x,self.y+other.y)def__eq__(self,other):returnself.x==other.xandself.y==other.y p1=Point(1,2)p2=Point(3,4)p3=p1+p2# 触发 __add__print(p3.x,p3.y)print(p1==p2)#触发 __eq__7. 可调用对象__call__
让实例本身可以像函数一样加括号调用obj()。
classCalc:def__call__(self,a,b):returna+b c=Calc()res=c(2,3)# 调用 __call__print(res)#5二、容易混淆的点
不要手动调用魔术方法!
❌ 不要写obj.__str__();✅写str(obj)。解释器帮你调用。魔术方法是定义类行为的协议:
只要你实现对应的双下划线方法,Python语法、内置函数就知道该怎么处理你的自定义对象。
类比Swift:实现协议,就获得对应语法能力。
Swift: 遵守
CustomStringConvertible→ 获得print能力
Python: 实现__str__魔术方法 → 获得print能力
- 不是所有双下划线都是魔术方法:
实例变量self.__abc,这是名称改写(name mangling)私有变量,不是魔术方法!
classA:def__init__(self):self.__secret=100# 私有属性,不是魔术方法!魔术方法是类里面定义的函数:
def __xxx__(self):。实例属性前后双下划线不算。
三、高频速查表
| 魔术方法 | 触发时机 |
|---|---|
__init__ | obj = Cls()实例初始化 |
__str__ | print(obj) / str(obj) |
__repr__ | 调试repr(obj) |
__len__ | len(obj) |
__getitem__ | obj[key] |
__setitem__ | obj[key]=val |
__enter__ / __exit__ | with obj:上下文管理器 |
__aenter__ / __aexit__ | async with obj:异步上下文 |
__iter__ / __next__ | for x in obj循环迭代 |
__call__ | obj()对象当做函数调用 |
__add__/__eq__ | +、==运算符 |
四、举个完整小例子,上下文管理器类(复习刚才with)
classMyLock:def__enter__(self):print("加锁")returnselfdef__exit__(self,exc_type,exc_val,exc_tb):print("释放锁,异常也会执行")# 返回False异常向外抛出returnFalsewithMyLock():print("临界区执行")
threading.Lock之所以支持with,就是它内部已经实现了__enter__、__exit__魔术方法。
五、和Swift简单对照理解
| Python魔术方法 | Swift等价机制 |
|---|---|
__str__ | CustomStringConvertible |
__iter__/__next__ | Sequence、IteratorProtocol |
__add__ | 运算符重载static func + |
with(__enter__/__exit__) | 没有语法;用defer手动实现释放逻辑 |
__call__ | callAsFunction |