Python 3.12 MagicMethods - 75 - __floor__
Python 3.12 Magic Method -__floor__(self)__floor__是 Python 中用于定义向下取整向负无穷取整的魔术方法。当对一个对象使用math.floor()时Python 会尝试调用该对象的__floor__方法返回不大于该对象的最大整数。正确实现__floor__可以让自定义数值类型支持math.floor()操作并与其他取整方法如__ceil__、__trunc__形成完整的取整协议。本文将详细解析其定义、底层机制、设计原则并通过多个示例逐行演示如何正确实现。1. 定义与签名def__floor__(self)-int:...参数self当前对象。返回值必须返回一个int类型的整数代表⌊x⌋向下取整的结果。调用时机显式调用math.floor(obj)。在某些需要向下取整的上下文中但通常不会自动调用。2. 用途与典型场景自定义数值类型如分数、有理数、自定义浮点数等需要支持向下取整。数学运算实现与math.floor的集成用于精确取整。与__ceil__、__trunc__协同提供完整的取整协议。金融/工程计算向下取整在计算整数部分、分页、分组等场景中常见。__floor__的语义是“向负无穷取整”即返回小于或等于self的最大整数。例如floor(3.7) 3floor(-3.7) -4因为 -4 ≤ -3.7且 -3 大于 -3.73. 底层实现机制在 Python/C API 层面__floor__对应tp_as_number.nb_floor槽位。这是一个函数指针接受一个对象返回一个 Python 整数PyLongObject。当调用math.floor(obj)时解释器会获取obj的类型对象的tp_as_number结构。如果存在nb_floor则调用它返回结果。如果不存在则尝试调用nb_int__int__或nb_index__index__作为后备但math.floor并不保证这种行为通常推荐显式实现__floor__。在 Python 的math模块中floor函数的 C 实现位于Modules/mathmodule.c大致如下简化staticPyObject*math_floor(PyObject*self,PyObject*x){PyObject*resultPyNumber_Floor(x);if(resultNULL)returnNULL;returnresult;}而PyNumber_Floor会查找nb_floor槽位。对于用户自定义的类如果没有实现__floor__但实现了__int__math.floor不会自动使用__int__它只会使用__floor__若存在或抛出TypeError。因此为了支持math.floor必须显式实现__floor__。4. 设计原则与最佳实践返回整数必须返回int类型的值。向下取整对于正数向下取整与向零截断相同对于负数向下取整是向负无穷方向因此结果比向零截断小 1。不应修改原对象取整操作应是无副作用的返回新整数。与__ceil__、__trunc__的语义区分应确保各方法返回正确的值。与__int__的关系如果类同时实现了__int__通常__int__向零截断__floor__向下取整两者可能不同。5. 示例与逐行解析示例 1自定义分数类实现向下取整importmathclassFraction:def__init__(self,numerator,denominator1):ifdenominator0:raiseZeroDivisionError(denominator cannot be zero)self.numeratornumerator self.denominatordenominatordef__floor__(self):返回向下取整的整数部分# Python 的 // 对于负数会向下取整即 floor这正是我们需要的returnself.numerator//self.denominatordef__trunc__(self):向零截断ifself.numerator0:returnself.numerator//self.denominatorelse:# 负数需要调整-3//2 -2但向零截断应为 -1所以用 -((-numerator)//denominator)return-((-self.numerator)//self.denominator)def__int__(self):returnself.__trunc__()# 通常 int 向零截断def__repr__(self):returnfFraction({self.numerator},{self.denominator})逐行解析行代码解释1-5__init__初始化分数检查分母不为零。6-8__floor__直接使用 Python 的//运算符它对于正负数都是向下取整floor正好满足需求。9-15__trunc__向零截断需要单独处理负数因为//对于负数是 floor不是 trunc。16-18__int__通常int()使用向零截断所以调用__trunc__。19-20__repr__便于显示。为什么这样写利用//的 floor 语义无需额外计算。区分__floor__和__trunc__提供正确的取整方式。验证f1Fraction(7,3)# 2.333...print(math.floor(f1))# 2f2Fraction(-7,3)# -2.333...print(math.floor(f2))# -3运行结果2 -3示例 2自定义浮点数包装类委托给内置 floatimportmathclassMyFloat:def__init__(self,value):self.valuefloat(value)def__floor__(self):向下取整返回整数returnmath.floor(self.value)def__ceil__(self):returnmath.ceil(self.value)def__trunc__(self):returnmath.trunc(self.value)def__repr__(self):returnfMyFloat({self.value})解析直接委托给内置float的对应方法简洁可靠。验证xMyFloat(3.7)print(math.floor(x))# 3yMyFloat(-3.7)print(math.floor(y))# -4运行结果3 -4示例 3复数类返回实部的 floorimportmathclassMyComplex:def__init__(self,real,imag):self.realreal self.imagimagdef__floor__(self):返回实部的向下取整returnmath.floor(self.real)def__ceil__(self):returnmath.ceil(self.real)def__trunc__(self):returnmath.trunc(self.real)def__repr__(self):returnfMyComplex({self.real},{self.imag})解析复数没有自然顺序但可以定义实部的 floor 作为其 floor。验证cMyComplex(3.7,2.5)print(math.floor(c))# 3c2MyComplex(-3.7,2.5)print(math.floor(c2))# -4运行结果3 -4示例 4与__ceil__、__trunc__对比importmathclassDemo:def__init__(self,value):self.valuevaluedef__floor__(self):returnmath.floor(self.value)def__ceil__(self):returnmath.ceil(self.value)def__trunc__(self):returnmath.trunc(self.value)解析演示了三个方法的区别。验证dDemo(-3.7)print(math.floor(d))# -4print(math.ceil(d))# -3print(math.trunc(d))# -3运行结果-4 -3 -36. 与其他取整方法的关系方法作用调用方式返回值数学定义__floor__(self)向下取整向负无穷math.floor(obj)整数⌊x⌋__ceil__(self)向上取整向正无穷math.ceil(obj)整数⌈x⌉__trunc__(self)向零截断math.trunc(obj)整数sign(x) * ⌊__int__(self)转换为整数通常向零int(obj)整数通常向零对于正数floor、trunc、int结果相同。对于负数floor≤trunc≤ceil且trunc与int相同。7. 注意事项与陷阱负数处理务必确保负数向下取整正确。Python 的//已经实现了 floor可直接使用。与__int__的区分不要混淆两者。如果类同时需要支持int()和math.floor()应分别实现或让__int__调用__trunc__。返回类型必须返回int。性能__floor__可能被频繁调用应保持高效。实现完整性如果类需要支持多种取整操作建议同时实现__floor__、__ceil__、__trunc__以提供完整的数学接口。8. 总结特性说明角色定义向下取整支持math.floor()签名__floor__(self) - int返回值整数int调用时机math.floor(obj)底层C 层的nb_floor槽位与__ceil__、__trunc__的关系共同构成取整协议最佳实践返回正确的向下取整整数处理负数不修改原对象掌握__floor__可以让自定义数值类型支持向下取整操作与 Python 的数学函数无缝集成。通过正确实现它你的类可以像内置数值一样自然地使用math.floor()。如果在学习过程中遇到问题欢迎在评论区留言讨论!