Decimal子类自定义__str__在f-string中不生效的问题排查与解决
解决Decimal子类自定义__str__在f-string中不生效的问题
问题重现
你编写了Decimal的子类Money来封装金额,自定义__str__实现带符号的货币格式,但f-string场景下未调用该方法:
from decimal import Decimal class Money(Decimal): CURRENCY = "€" def __new__(cls, number): return super().__new__(cls, Decimal(number).quantize(Decimal("0.01"))) def __str__(self): return f"{self:+}{self.CURRENCY}" m = Money(10) print("Test 1 - str():", str(m)) print("Test 2 - print():", m) print(f"Test 3 - f-string: {m}") print("Test 4 - f-string str():", f"{str(m)}")
运行结果:
Test 1 - str(): +10.00€ Test 2 - print(): +10.00€ Test 3 - f-string: 10.00 # 此处未调用自定义__str__ Test 4 - f-string str(): +10.00€
原因分析
核心差异在于不同场景触发的底层方法不同:
str(m)和print(m)直接调用子类重写的__str__,因此能得到预期格式。- f-string的
{m}等价于format(m, ''),即调用对象的__format__('')方法。而Decimal类自带的__format__并未遵循Python默认的object.__format__逻辑(默认会委托给__str__),而是直接基于Decimal内部数值实现格式化,完全绕过了子类重写的__str__。
解决方案
在Money类中重写__format__方法,让无格式说明符时调用自定义的__str__,有格式需求时仍复用父类的格式化逻辑:
from decimal import Decimal class Money(Decimal): CURRENCY = "€" def __new__(cls, number): return super().__new__(cls, Decimal(number).quantize(Decimal("0.01"))) def __str__(self): return f"{self:+}{self.CURRENCY}" def __format__(self, format_spec): # 无格式说明符时,使用自定义__str__的结果 if not format_spec: return str(self) # 有格式说明符时,调用父类的__format__保持原有逻辑 return super().__format__(format_spec) m = Money(10) print(f"Test 3 - f-string: {m}") # 现在输出 +10.00€
机制详解
__str__vs__format__:__str__用于生成对象的“友好字符串表示”,直接被str()和print()调用。__format__用于格式化字符串,支持自定义格式说明符,是f-string、format()函数的底层调用方法。
- 继承与父类方法行为:
- 普通Python类继承自
object,object.__format__默认会调用__str__,因此子类重写__str__后,f-string会自动生效。 - 但Decimal类重写了
__format__,实现了适配自身数值特性的格式化逻辑,因此子类必须显式重写__format__才能介入格式化流程。
- 普通Python类继承自
内容的提问来源于stack exchange,提问作者Antoine Gallix
相关产品推荐
相关产品推荐

