一、魔术方法的概念与定位
在前文的基础上,我们今天来学一下python的特殊方法;
定义:以双下划线开头和结尾的特殊方法(如__init__、__add__),在 Python 中具有预定义的语义。
特性:无需显式调用,由 Python 解释器在特定操作(如对象实例化、下标访问、算术运算)发生时自动触发。
这些方法本质上是一种“钩子”机制——开发者定义其行为,Python 在对应的语言事件中予以执行。掌握这些方法,即掌握了自定义对象行为与语言内置类型行为对齐的核心途径。
二、核心魔术方法逐一解析
1.__init__(self[, ...])—— 实例初始化器
该方法在对象实例化后自动执行,用于设置实例的初始状态。它并非真正意义上的构造器(实际构造由__new__负责),而是初始化阶段。
classEx:def__init__(self,arg1,arg2):print(f"__init__被调用, arg1:{arg1}, arg2:{arg2}")e=Ex("a","b")# 输出:__init__被调用, arg1:a, arg2:b2.__call__(self[, ...])—— 使实例可调用
定义该方法后,实例对象可以像函数一样使用括号语法调用。
classEx:def__call__(self,arg1,arg2):print(f"__call__被调用, arg1:{arg1}, arg2:{arg2}")e=Ex()e("a","b")# 输出:__call__被调用, arg1:a, arg2:b此特性在实现有状态函数、装饰器、工厂类等场景中具有实用价值。
3.__getitem__(self, key)—— 支持下标访问(obj[key])
实现该方法后,对象可使用方括号语法进行索引或键值访问,行为类似于列表或字典。
classEx:def__getitem__(self,key):print(f"__getitem__被调用, key:{key}")return["a","b","c"][key]ifisinstance(key,int)else{0:"零",1:"壹"}[key]e=Ex()print(e[2])# 先打印调用信息,再返回 "c"注:该方法亦能支持切片操作(当传入
slice对象时),属进阶用法。
4.__repr__与__str__—— 对象字符串表示
__str__:面向终端用户,在print(obj)或str(obj)时调用,强调可读性。__repr__:面向开发者,在repr(obj)或交互式环境直接输入对象名时调用,要求返回的字符串应尽可能明确、准确,通常可用于重建对象。
优先级关系:若未定义__str__,则回退至__repr__;反之不成立。
classEx:def__repr__(self):return"repr__被调用"def__str__(self):return"str__被调用"e=Ex()print(str(e))# str__被调用print(f"{e}")# str__被调用(f-string默认采用__str__)print(e)# 同样输出 str__(print优先使用__str__)建议:至少实现
__repr__,以提升调试效率和信息可读性。
5. 算术运算符对应方法(加减乘除)
(1)正向运算:__add__/__sub__/__mul__/__truediv__
当实例对象位于运算符左侧时,对应方法被调用。
classNumber:def__init__(self,num):self.num=numdef__add__(self,other):returnself.num+otherdef__sub__(self,other):returnself.num-otherdef__mul__(self,other):returnself.num*otherdef__truediv__(self,other):returnself.num/other n=Number(6)print(n+7)# 13print(n-4)# 2print(n*4)# 24print(n/3)# 2.0(2)反向运算:__radd__/__rsub__/__rmul__/__rtruediv__
当实例对象位于运算符右侧,且左侧操作数不是该类的实例时,触发反向方法。
classNumber:def__init__(self,num):self.num=numdef__rsub__(self,other):returnother-self.numdef__rmul__(self,other):returnother*self.numdef__rtruediv__(self,other):returnother/self.num n=Number(6)print(4-n)# -2 (4 - 6 = -2)print(4*n)# 24print(3/n)# 0.5要点:反向方法使得运算符两侧操作数类型不对等时,仍能正确完成运算,从而实现对称性。
三、综合案例:分数类(Fraction)的实现
以下示例综合运用上述魔术方法,构建一个支持四则运算、自动约分、并能与整数、浮点数及布尔值混合运算的分数类。
辅助函数
# 求最大公约数(用于约分),需处理负数defget_gcd(a,b):foriinrange(min(abs(a),abs(b)),0,-1):ifnota%iandnotb%i:return-iifa<0andb<0elseireturnb# 保底# 类型转换适配器:将 int/float 统一转为 Fraction 对象defget_frac(obj):ifisinstance(obj,Fraction):returnobjelifisinstance(obj,int):returnFraction(obj,1)elifisinstance(obj,float):decimal_str=str(obj).split('.')[-1]b=10**len(decimal_str)returnFraction(int(obj*b),b)raiseTypeError("类型错误,仅支持 int、float、Fraction")分数类主体
classFraction:def__init__(self,a,b):gcd=get_gcd(a,b)self.a=a//gcd self.b=b//gcddef__repr__(self):# 规范化输出格式,处理分母为 ±1 或负数的情形ifself.b==1:returnstr(self.a)ifself.b==-1:returnstr(-self.a)ifself.b<0:returnf'{self.a}/{self.b}'returnf"{self.a}/{self.b}"# ---- 正向算术运算 ----def__add__(self,other):other=get_frac(other)returnFraction(self.a*other.b+other.a*self.b,self.b*other.b)def__sub__(self,other):other=get_frac(other)returnFraction(self.a*other.b-other.a*self.b,self.b*other.b)def__mul__(self,other):other=get_frac(other)returnFraction(self.a*other.a,self.b*other.b)def__truediv__(self,other):other=get_frac(other)returnFraction(self.a*other.b,self.b*other.a)# ---- 反向算术运算(支持左侧为 int/float/bool) ----def__radd__(self,other):other=get_frac(other)returnFraction(other.a*self.b+other.b*self.a,other.b*self.b)def__rsub__(self,other):other=get_frac(other)returnFraction(other.a*self.b-other.b*self.a,other.b*self.b)def__rmul__(self,other):other=get_frac(other)returnFraction(other.a*self.a,other.b*self.b)def__rtruediv__(self,other):other=get_frac(other)returnFraction(other.a*self.b,other.b*self.a)测试验证
f1=Fraction(3,4)f2=Fraction(2,3)print(f1+f2)# 17/12print(f1-f2)# 1/12print(f1*f2)# 1/2print(f1/f2)# 9/8print(f1+3)# 15/4print(f1-3.2)# -49/20 (0.75 - 3.2 = -2.45 = -49/20)print(f1*False)# 0print(f1/True)# 3/4# 反向运算print(3+f1)# 15/4print(3.2-f1)# 49/20print(True*f1)# 3/4print(False/f1)# 0该示例系统性地运用了正向和反向算术方法,并通过get_frac实现了对整数和浮点数的无缝适配,具有较好的扩展性。
四、常用魔术方法速查表
| 方法 | 触发时机 | 说明 |
|---|---|---|
__init__ | 实例化(obj = Class(...)) | 初始化实例属性 |
__call__ | 实例对象作为函数调用(obj()) | 使对象可调用 |
__getitem__ | 下标访问(obj[key]) | 支持[]操作 |
__repr__ | repr(obj)、交互式环境直接输出 | 面向开发者,应信息详尽 |
__str__ | str(obj)、print(obj)、f-string | 面向用户,宜简洁可读 |
__add__ | 加法(实例在左) | 正向加法 |
__radd__ | 加法(实例在右,左侧非本类) | 反向加法 |
__sub__/__rsub__ | 减法 | 正向/反向 |
__mul__/__rmul__ | 乘法 | 正向/反向 |
__truediv__/__rtruediv__ | 真除法(/) | 正向/反向(Python 3 中/即为真除) |
此外,还有
__floordiv__(//)、__mod__(%)、__pow__(**)等,其机制与上述方法同构,可触类旁通。
五、总结与实践建议
魔术方法是 Python 面向对象编程中自定义行为的重要工具。合理运用它们,可以使自定义类具备与内置类型一致的操作语法,从而提升代码的表达力和可维护性。
学习路径建议:
- 初级阶段:重点掌握
__init__、__str__、__add__等最常用方法。 - 进阶阶段:按需查阅文档,补充实现反向运算、比较运算、迭代协议、上下文管理等。
- 实践方式:自行实现如二维向量(
Vector2D)、复数、矩阵等数学对象,在具体场景中体会魔术方法的协作机制。
P.S.:魔术方法并非不可捉摸的“黑魔法”,而是 Python 语言规范中预留的扩展接口。规范使用它们,是编写 Pythonic 代码的重要一环。
如需进一步探讨其他方法(如比较运算符、迭代器协议、上下文管理器等),欢迎继续交流。祝编码顺利。
下图为本文知识点的思维导图(示意):