当前位置: 首页 > news >正文

Python 3.12 MagicMethods - 43 - __rdivmod__

Python 3.12 Magic Method -__rdivmod__(self, other)


__rdivmod__是 Python 中用于实现反向divmod()操作的魔术方法。当调用divmod(x, y)且左操作数x不支持该运算时,Python 会尝试调用右操作数y__rdivmod__(x)方法,从而允许自定义类处理divmod(other, self)的情形。本文将从定义、底层机制、设计原则到具体示例,全面解析这一方法。


1. 定义与签名

def__rdivmod__(self,other)->tuple:...
  • 参数
    • self:当前对象(右操作数,因为它是被调用的对象)。
    • other:另一个操作数(左操作数),可以是任意类型。
  • 返回值:必须返回一个二元元组(quotient, remainder),其中quotientother // self的结果,remainderother % self的结果。如果运算未定义,应返回单例NotImplemented
  • 调用时机
    • 调用divmod(x, y)时,首先尝试x.__divmod__(y)
    • 如果x.__divmod__(y)返回NotImplemented,则尝试y.__rdivmod__(x)
    • 如果两者都返回NotImplemented,最终抛出TypeError

2. 为什么需要__rdivmod__

divmod是一个内置函数,通常用于整数除法。当你的自定义类出现在divmod的右操作数位置时,例如divmod(10, MyClass(3)),左操作数10(内置int)不知道如何处理MyClass,它会返回NotImplemented。此时,Python 会调用MyClass.__rdivmod__(10),让你的类有机会实现10 // MyClass10 % MyClass的语义。这正是__rdivmod__的作用——支持左操作数为其他类型的divmod运算

由于divmod本质上依赖于地板除和取模,而这两个运算都不满足交换律,因此__rdivmod__必须独立实现,不能简单地委托给__divmod__


3. 底层实现机制

在 CPython 中,divmod函数的 C 实现(PyNumber_Divmod)处理流程如下(简化伪代码):

PyObject*PyNumber_Divmod(PyObject*v,PyObject*w){PyObject*result=binary_op1(v,w,NB_SLOT(nb_divmod));if(result==Py_NotImplemented){Py_DECREF(result);result=binary_op1(w,v,NB_SLOT(nb_divmod));}returnresult;}
  • binary_op1会调用对象的tp_as_number.nb_divmod槽位。
  • 第一次调用时,参数为(v, w),对应v.__divmod__(w)
  • 如果返回NotImplemented,则交换参数再次调用,此时对应w.__rdivmod__(v)

因此,__rdivmod__在 Python 层对应的是nb_divmod槽位的反向调用,无需独立的 C 槽位。只要你的类定义了__rdivmod__,它就会被包装并用于反向运算。


4. 设计原则与最佳实践

  • __rfloordiv____rmod__保持一致__rdivmod__返回的元组应等于(other // self, other % self)。如果类实现了反向地板除和反向取模,应确保结果一致。
  • 返回元组:必须返回一个包含两个元素的元组。如果返回其他类型,Python 会尝试将其转换为元组,但可能导致错误。
  • 类型检查:应检查other的类型是否兼容,如果类型不匹配,应返回NotImplemented,而不是抛出异常。这给其他类型机会。
  • 处理除零异常:当self为 0 时,应抛出ZeroDivisionError,与内置divmod一致。
  • 返回新对象:商和余数通常应为新对象,不应修改原操作数。
  • 遵循 Python 的取模规则:结果的符号应与除数(即self)一致,且满足other = self * q + r。这通常可以通过直接使用 Python 的内置整数运算实现。
  • 避免无限递归:在__rdivmod__中不要调用divmod(self, other)或类似形式,否则可能导致递归。

5. 示例与逐行解析

示例 1:自定义整数类支持反向divmod

classMyInt:def__init__(self,value):self.value=valuedef__rdivmod__(self,other):# 处理 other (int) divmod selfifisinstance(other,int):ifself.value==0:raiseZeroDivisionError("divmod by zero")q=other//self.value r=other%self.valuereturn(MyInt(q),MyInt(r))returnNotImplementeddef__repr__(self):returnf"MyInt({self.value})"

逐行解析

代码解释
1-3__init__初始化整数值。
4-11__rdivmod__反向divmod方法。
5-9处理int类型如果other是整数,检查除零,计算商和余数,返回由两个MyInt组成的元组。
10返回NotImplemented如果类型不兼容,返回NotImplemented
11-12__repr__便于显示。

为什么这样写?

  • __rdivmod__直接使用 Python 整数运算,保证结果符合取模规则。
  • 返回的商和余数用MyInt包装,保持类型一致性。
  • 除零检查与内置行为一致。

验证

a=MyInt(3)q,r=divmod(10,a)print(q)# MyInt(3)print(r)# MyInt(1)

运行结果:

MyInt(3) MyInt(1)

示例 2:同时实现__divmod____rdivmod__提供完整支持

一个完整的自定义数值类应同时实现正向和反向divmod,以及//%运算符。

classMyInt:def__init__(self,value):self.value=valuedef__floordiv__(self,other):ifisinstance(other,MyInt):returnMyInt(self.value//other.value)returnNotImplementeddef__mod__(self,other):ifisinstance(other,MyInt):returnMyInt(self.value%other.value)returnNotImplementeddef__divmod__(self,other):ifisinstance(other,MyInt):return(self//other,self%other)returnNotImplementeddef__rfloordiv__(self,other):ifisinstance(other,int):returnMyInt(other//self.value)returnNotImplementeddef__rmod__(self,other):ifisinstance(other,int):returnMyInt(other%self.value)returnNotImplementeddef__rdivmod__(self,other):ifisinstance(other,int):ifself.value==0:raiseZeroDivisionErrorreturn(MyInt(other//self.value),MyInt(other%self.value))returnNotImplementeddef__repr__(self):returnf"MyInt({self.value})"

解析
__rdivmod____rfloordiv____rmod__保持一致,避免逻辑重复,同时保证了divmod的语义与分别调用一致。

验证

a=MyInt(3)print(divmod(10,a))# (MyInt(3), MyInt(1))print(10//a)# MyInt(3) (通过 __rfloordiv__)print(10%a)# MyInt(1) (通过 __rmod__)

运行结果:

(MyInt(3), MyInt(1)) MyInt(3) MyInt(1)

示例 3:处理负数(利用内置运算自动遵循规则)

Python 的整数运算已经正确处理负数取模,因此直接委托即可。

classMyInt:def__init__(self,value):self.value=valuedef__rdivmod__(self,other):ifisinstance(other,int):return(MyInt(other//self.value),MyInt(other%self.value))returnNotImplemented

验证:

a=MyInt(-3)print(divmod(10,a))# 10 // -3 = -4, 10 % -3 = -2 → (MyInt(-4), MyInt(-2))

运行结果:

(MyInt(-4), MyInt(-2))

示例 4:当不支持反向时返回NotImplemented

如果类没有实现__rdivmod__,或者类型不匹配,会正确抛出TypeError

classOnlyForward:def__divmod__(self,other):return("forward",other)of=OnlyForward()# print(divmod(10, of)) # TypeError: unsupported operand type(s) for divmod()

6. 注意事项与陷阱

  • 不要调用divmod(self, other):在__rdivmod__中调用divmod(self, other)会再次触发正向__divmod__,可能导致无限递归(如果正向也返回NotImplemented)。应直接计算。
  • __rfloordiv____rmod__的一致性:如果类实现了这些反向方法,__rdivmod__应返回与它们一致的结果,否则会导致不一致。
  • 处理除零:当self.value为 0 时,必须抛出ZeroDivisionError。不要返回NotImplemented,因为除零是逻辑错误,不是类型不匹配。
  • 返回类型:元组中的元素类型应与__rfloordiv____rmod__返回的类型一致。如果它们返回MyInt,那么__rdivmod__也应返回MyInt对象。
  • 性能考虑:如果计算商和余数可以一次完成(如大整数),直接计算比分别调用//%更高效。
  • __idivmod__:Python 中不存在就地 divmod 运算符,因为 divmod 是函数不是运算符,所以没有对应的就地版本。如果需要类似功能,可以定义普通方法。

7. 总结

特性说明
角色定义反向divmod(),处理divmod(other, self)的场景
签名__rdivmod__(self, other) -> tuple
返回值二元元组(other // self, other % self),或NotImplemented
调用时机左操作数的__divmod__返回NotImplemented
底层C 层的nb_divmod槽位,通过交换参数调用
__rfloordiv__/__rmod__的关系应保持一致,避免重复计算
最佳实践类型检查、返回NotImplemented、处理除零、返回新对象

掌握__rdivmod__可以让你自定义类完美融入 Python 的数值运算生态,尤其是在处理混合类型时。通过理解其底层机制和设计原则,你可以构建出行为可预测、与内置类型无缝协作的类。

如果在学习过程中遇到问题,欢迎在评论区留言讨论!

http://www.jsqmd.com/news/468939/

相关文章:

  • 提升开发效率:用快马一键生成openclaw风格的可复用功能模块
  • 【Linux系统编程】System V 共享内存精讲:Linux 最快 IPC 的原理与实战精髓
  • Ubuntu系统优化Janus-Pro-7B运行环境
  • 红外热成像技术入门:从夜视监控到工业检测的5个实用场景解析
  • 安防相机WDR技术实战:如何解决逆光场景下的监控难题(含海思方案配置)
  • AI技术提升软件项目沟通效率的策略
  • 基于Fish-Speech-1.5的AI配音工作室解决方案
  • 海软25校赛CTF-Reverse逆向:Come_on
  • linux sftp 设置了用户对目录有 0700权限,但在上传时报 Permission denied错误
  • C#进程和线程
  • 实战指南:如何用PyTorch实现DANN对抗迁移学习(附完整代码解析)
  • Allpairs工具与Deepseek联动实战:5分钟搞定正交表测试用例生成
  • STM32 CRYP硬件加密详解:CTR/GCM/CCM模式与中断恢复机制
  • 攻克股票数据接口难题:5个创新方案与底层原理
  • 高效3D模型编辑:NifSkope如何破解游戏开发中的格式兼容与效率难题
  • 华为交换机镜像端口配置进阶:基于ACL和MQC的流镜像详解
  • 网页设计毕业设计选题实战指南:从需求分析到可部署原型的全流程实现
  • MogFace工具完整使用指南:侧边栏上传+双列对比+原始数据查看
  • UE4 Niagara粒子碰撞实战:从参数解析到游戏特效优化(附常见问题解决方案)
  • 深度学习入门全解析:从核心概念到实战基础 | 技术研讨会精华总结
  • 如何用MATLAB高效处理医学影像RAW数据?512x512矩阵实战解析
  • 文墨共鸣效果展示:教育考试命题防重复系统|题干语义相似度阈值预警
  • 实战指南:基于快马平台构建高可用Copaw宠物服务官网
  • 360Controller安全机制全面解析:代码签名与系统扩展加载深度指南
  • 手把手教你部署MT5改写工具:30分钟搞定,文案润色不再难
  • nanobot实战案例:DevOps工程师用nanobot自动解析Jenkins日志报错原因
  • 高效全平台媒体采集工具:一站式无水印资源下载解决方案
  • python中Matplotlib模块介绍
  • StructBERT WebUI效果实测:支持GB2312/UTF-8编码自动识别与转换
  • 从凯撒密码到量子加密:密码学发展史中的5个关键转折点