Python继承int调换父类顺序时MRO解析__str__/__repr__异常问题
问题原因
这个现象没有违反MRO规则,核心原因是C实现的内置类型int没有在Python类层面定义__str__方法:
- int的字符串转换逻辑是直接写在C层的
tp_str类型槽位中的,没有映射为Python类属性字典里可被MRO检索到的__str__方法。 - 而
__repr__不同,int的__repr__是明确暴露在Python类层面的方法,会被MRO正常检索。
当定义class MyValue(int, Repr)时,MRO顺序为MyValue -> int -> Repr -> object,方法查找逻辑如下:
- 调用
__repr__时:MRO检索到int自带的__repr__方法,直接调用,不会走到Repr的实现,符合预期。 - 调用
__str__时:先查MyValue自身,无对应方法;再查int的类属性,找不到__str__;继续检索下一个父类Repr,发现Repr实现了__str__,就直接调用该版本,因此输出了自定义格式的字符串。
补充说明:单继承int时
str()能正常返回数字字符串,是因为整条MRO链都找不到Python层面的__str__时,Python会回退调用C层的tp_str槽位逻辑;但只要MRO链上任意一个类定义了__str__,就会优先调用这个Python方法,不会触发C层回退。
修复方案
根据使用场景,可以选以下任意一种方案:
方案1:给int补全Python层面的__str__方法(最便捷)
int的__str__返回结果和__repr__完全一致,只需要在代码初始化阶段给int补上类属性,就能让MRO正常检索到int的字符串方法,行为和__repr__完全对齐:
# 代码最开头执行一次即可,无侵入性副作用 int.__str__ = int.__repr__
补全之后,不管父类顺序怎么写,MRO都会严格按照从左到右的优先级匹配方法,和最初的预期完全一致。
方案2:显式指定子类的方法绑定(无内置类型修改)
如果不想修改内置类型,可以在定义子类时显式声明__str__的绑定关系,不依赖MRO的隐式查找:
class MyValue(int, Repr): # 显式指定用int的字符串逻辑,和__repr__行为一致 __str__ = int.__repr__
缺点是每个需要int优先级的子类都要单独写这一行。
方案3:修改Repr类兼容内置类型的特殊查找逻辑
如果既不想修改内置类型,也不想每个子类重复声明,可以修改Repr类的__str__逻辑,主动判断MRO链上排在自身前面的类,优先使用前置父类的字符串逻辑:
import numbers class Repr: def __str__(self): # 找到Repr在MRO中的位置,遍历所有前置父类 mro = self.__class__.__mro__ repr_idx = mro.index(Repr) for cls in mro[:repr_idx]: # 如果前置父类是整数类型,直接返回原始数值的字符串表示 if issubclass(cls, numbers.Integral): return cls.__repr__(self) # 前置父类无数值类型时,走自定义格式化逻辑 # 这里替换成你实际的千万亿/亿亿量级格式化逻辑 return '123.456qa' def __repr__(self): # 用super()替代手动遍历MRO,更符合Python继承规范 return f'{self.__class__.__name__}({super().__repr__()})'
内容的提问来源于stack exchange,提问作者bm13kk
相关产品推荐
相关产品推荐

