该用__format__当需支持format()或f-string的格式说明符(如f"{obj:.2f}"),而非替代__str__;它专用于解析format_spec实现多格式变体,与__str__、__repr__职责分离且互不影响。

什么时候该用 __format__ 而不是 __str__ 或 __repr__
__format__ 是为 format() 函数和 f-string 中的格式说明符服务的,不是用来替代 __str__ 的通用显示逻辑。比如你写 f"{obj:.2f}" 或 format(obj, ".2f") 时,Python 才会调用 __format__;而 print(obj) 或 str(obj) 仍走 __str__。如果你的类需要支持多种格式变体(如货币、百分比、科学计数),__format__ 就是唯一正解。
常见错误是把所有格式逻辑塞进 __str__,结果发现 f"{x:%}" 报 ValueError: Unknown format code '%' for object of type 'str' —— 因为 __str__ 返回字符串后,格式化就结束了,Python 不会再解析里面的说明符。
__format__ 的参数 format_spec 怎么解析
format_spec 是传入的格式字符串,比如 f"{obj:#x}" 里就是 "#x",它不带花括号,也不含变量名。你需要自己按规则解析它,Python 不提供内置解析器。
- 空字符串
""表示默认格式,常对应__str__行为,但也可自定义(如返回简写) - 常见前缀如
"!"(转换标志)、":"(分隔符)不会出现在format_spec中 —— 它们是 f-string 语法的一部分,到__format__时只剩冒号后的部分 - 建议用
str.partition(":")或正则提取关键字段,别硬写if format_spec == "hex"这种死匹配,实际中用户可能写"0x"、"#x"、"x"
示例:一个表示角度的类支持 "d"(度)、"rad"(弧度)、"deg"(同 d):
def __format__(self, format_spec):
if format_spec in ("", "d", "deg"):
return f"{self.degrees:.1f}°"
elif format_spec == "rad":
return f"{self.radians:.3f} rad"
else:
raise ValueError(f"Unknown format spec {format_spec!r}")
和内置类型格式说明符对齐的坑
用户习惯写 f"{x:.2f}",如果你的类也想支持小数位控制,就得手动解析 format_spec 里的精度部分。Python 不会帮你拆 ".2f" —— 你得用 string.Formatter.parse 或正则(推荐 re.match(r"^\.(\d+)([eEfFgG])?$", format_spec))。
- 忽略精度会导致
f"{obj:.2f}"输出全精度数字,违背直觉 - 误把
"s"当作字符串格式(像str.format那样)—— 实际上"s"是给字符串类型的,你的类若非str子类,__format__不会自动处理它;必须显式支持,否则抛ValueError - 如果返回值不是字符串,会直接报
TypeError: __format__ must return a string
性能提示:频繁调用 __format__(如日志循环)时,避免在函数内重复编译正则或构造 Formatter 实例,应提前缓存。
和 __str__ / __repr__ 的协作关系
__format__ 不继承 __str__,也不影响 __repr__。三者完全独立。典型协作模式是:
-
__str__返回最常用、可读性最强的默认文本(如"Point(3.5, -2.1)") -
__repr__返回无歧义、可调试的表示(如"Point(x=3.5, y=-2.1)") -
__format__专注响应外部格式请求(如"{:.1f}"输出坐标值,"csv"输出逗号分隔)
容易被忽略的一点:当 format_spec 为空时,很多开发者直接返回 str(self),但这会丢失语义 —— 比如你希望空格式输出 ID 而不是完整对象描述,那就该单独处理 if not format_spec: 分支,而不是无脑委托。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











