__repr__ 必须始终实现,用于调试、日志和交互式环境,应返回含类名和关键字段的可辨识字符串;__str__ 可选,仅用于面向用户的友好显示,缺失时自动回退到 __repr__。

什么时候该用 __str__,什么时候必须写 __repr__
用户看到对象打印出来是 <__main__.Point object at 0x7f8a1c2b3d90>,第一反应往往是“加个 __str__ 就行了”——但这是错的起点。__repr__ 才是 Python 对象的“身份证”,__str__ 只是给终端用户看的“友好昵称”。
调试时你用 print(obj) 或直接敲变量名回车,背后调用的是不同方法:print() 走 __str__(如果存在),而交互式环境里直接输入变量名、logging.debug()、repr() 函数都强制走 __repr__。
-
__repr__应该返回「可复现」或「有辨识度」的字符串,理想情况下能用eval(repr(obj))重建对象(不强求,但至少含类名+关键字段,比如Point(x=1, y=2)) -
__str__可以更宽松:带单位、换行、中文描述都行,比如"坐标点 (1, 2)",但它一旦缺失,Python 会自动 fallback 到__repr__的结果 - 如果你只写
__str__不写__repr__,日志里全是地址,pdb 里看不出状态,单元测试失败时断言报错也难读
__repr__ 返回空字符串或 None 会怎样
这会导致 repr(obj) 报 TypeError: __repr__ returned non-string (type NoneType),连 print(repr(obj)) 都崩。Python 明确要求 __repr__ 必须返回 str,且不能是空串(空串合法,但毫无意义)。
- 常见错误:写了
print(...)却忘了return,结果隐式返回None - 另一个坑:在
__repr__里调用了可能抛异常的属性(比如未初始化的self._data),导致repr()调用直接中断,连对象都 print 不出来 - 安全写法:用
getattr(self, 'x', '?')替代直接访问,或用try/except包裹关键字段读取
自定义类中 __str__ 和 __repr__ 的典型写法差异
差异不在语法,而在信息密度和用途。拿一个简单二维点类举例:
立即学习“Python免费学习笔记(深入)”;
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
<pre class='brush:python;toolbar:false;'>def __repr__(self):
return f"Point(x={self.x!r}, y={self.y!r})" # !r 确保字符串带引号,数字不变形
def __str__(self):
return f"({self.x}, {self.y})"</pre>注意三点:
-
__repr__用!r格式化,让"hello"变成'hello',避免歧义;__str__直接用{self.x}更干净 -
__repr__包含类名,__str__不需要——用户已经知道这是个点 - 如果字段可能为
None或复杂对象(如嵌套 dict),__repr__里建议用repr(field)而非str(field),保持层级一致性
第三方库和内置类型怎么处理这两个方法
像 datetime、pathlib.Path 这类标准库类型,__repr__ 和 __str__ 差异明显:datetime(2023, 1, 1) vs "2023-01-01";而 numpy.ndarray 的 __str__ 会省略长数组中间部分,__repr__ 则带 dtype 和 shape 信息。
- 第三方库(如
pydantic、dataclasses)通常默认只实现__repr__,靠生成逻辑保证可读性;__str__得你自己补 - 继承内置类型(如
list子类)时,如果不重写__repr__,会沿用父类行为(比如显示所有元素),可能泄露敏感数据或拖慢调试 - 用
dataclass时加repr=True(默认)只控制__repr__,__str__依然不存在——别以为加了@dataclass就万事大吉
最常被忽略的一点:当对象嵌套很深(比如 A 里有 B,B 里有 C),只要其中任意一层没写 __repr__,整条链路的调试输出就退化成内存地址。这不是性能问题,是信息黑洞。


















