结论:__str__ 面向用户,返回可读字符串,被 str() 和 print() 调用;__repr__ 面向开发者,返回尽量无歧义、最好能重建对象的表示,被 repr()、交互式解释器回显、以及容器元素展示调用。__str__ 缺失时回退到 __repr__,__repr__ 缺失时回退到默认的 <__main__.X object at 0x...>。
展开:设计准则是官方文档一句话:repr 尽可能做到 eval(repr(x)) == x(如列表的 [1, 2, 3]、datetime 的 datetime.datetime(2024, 1, 1)),做不到就用尖括号明确标注(<Logger: /tmp/app.log>)。容易忽略的点:容器打印元素时用的是元素的 __repr__ 而非 __str__——print([MyObj()]) 的输出走 repr,所以自定义对象至少要把 __repr__ 写好,调试和日志(%r、!r 转换、f"{x!r}")全靠它。两者同时实现是常态:__str__ 给最终用户看的友好文案,__repr__ 给日志/调试用的详细信息。
class Point:
def __init__(self, x, y): self.x, self.y = x, y
def __repr__(self): return f"Point({self.x}, {self.y})" # 可重建
def __str__(self): return f"({self.x}, {self.y})" # 可读
p = Point(1, 2)
print(p) # (1, 2) —— __str__
print([p]) # [Point(1, 2)] —— 容器用 __repr__
易错点:只实现 __str__ 的类在列表里打印仍然是默认地址表示,排障时一头雾水。追问方向:f-string 的 !s/!r/= 转换说明符、__format__ 与前两者的关系。