结论:用__str__返回用户友好的字符串,用__repr__返回可复现、带类型和关键状态的调试字符串——两者不互换,也不该返回相同内容;__repr__应在默认输出为时重写,必须含类名和关键字段,推荐为合法python表达式,避免副作用;__str__与__repr__雷同会导致日志冗余难读,logging中应显式用{obj!r}或{obj!s}控制调用路径。

直接说结论:用 __str__ 返回用户友好的字符串,用 __repr__ 返回可复现、带类型和关键状态的调试字符串——两者不互换,也不该返回相同内容。
什么时候该重写 __repr__?
当你在日志里看到类似 <__main__.user object at></__main__.user> 这种信息时,就该写了。这种默认输出对调试毫无帮助,尤其在 logging.debug() 或 print() 查看变量时。
- 必须包含类名,比如
User,而不是只写字段值 - 推荐返回合法 Python 表达式(能直接
eval()还原对象最好),至少也要含关键字段和类型标识 - 避免在
__repr__中调用复杂方法或访问数据库——它可能被频繁调用(比如在 pdb、日志、容器 repr 中) - 示例:
def __repr__(self): return f"User(id={self.id!r}, name={self.name!r})",注意用!r确保引号和转义正确
__str__ 和 __repr__ 返回一样会出什么问题?
常见错误是偷懒写成 return str(self.__dict__) 或直接返回 __repr__ 结果。这会让 print(obj) 和 logging.info(str(obj)) 输出冗长、带引号、含下划线字段(如 _cache)的字符串,反而干扰业务日志阅读。
-
__str__面向终端用户或日志阅读者,应简洁、无技术细节:比如"Alice (active)" -
__repr__面向开发者,要暴露结构:比如User(id=123, name='Alice', is_active=True) - 如果两者一样,
logging.info(f"Created: {user}")就会混入调试级信息,日志变难扫描 - 某些库(如
dataclasses的repr=True)默认只生成__repr__,你仍需手动加__str__
在 logging 中怎么安全使用它们?
别在日志格式字符串里裸写 {obj} 或 {obj!s}——这会触发 __str__,但如果你没定义它,就会 fallback 到 __repr__,而后者可能含敏感字段(如 password)或引发异常(如字段为 None 时拼接失败)。
- 显式用
{obj!r}触发__repr__,确保行为可控;用{obj!s}显式走__str__ - 对敏感类,在
__repr__中过滤字段:f"User(id={self.id!r}, name={self.name!r}, is_active={self.is_active!r})",不出现self._token - 如果对象可能为
None,日志里先判空再格式化,否则None.__repr__()虽安全,但None.__str__()也安全,不过自定义类里未定义__str__时 fallback 可能意外触发__repr__
真正容易被忽略的是:很多团队把 __repr__ 当成“多打点信息就好”,结果字段越堆越多,甚至包含嵌套对象的完整 repr,导致日志行宽爆炸、性能下降。一个 __repr__ 最好控制在 3–5 个核心字段内,且字段值本身不能触发副作用。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











