__repr__用于调试,应返回可重建对象的ascii表达式;__str__用于展示,可读性强。未实现__repr__时调试信息无用,错误实现会导致print和repr行为混乱。

直接说结论:用 __str__ 给人看,用 __repr__ 给开发者/调试器看;不实现或写错其中一个,print() 和 repr() 行为会混乱,日志和交互式调试时反而更难定位问题。
什么时候该重写 __repr__?
当你在 REPL、logging.debug()、pprint 或异常 traceback 中看到类似 <myclass object at></myclass> 这种信息时,说明 __repr__ 没提供有效线索。
- 理想状态:返回能「重建对象」的字符串(如
Point(x=1, y=2)),至少包含类名和关键属性 - 不要返回空字符串、纯类型名或含内存地址的默认值
- 避免调用耗时操作(如数据库查询、文件读取)——
__repr__可能在任意时刻被触发,包括断点暂停时 - 如果对象不可重建(比如含闭包或临时句柄),优先保证「可读 + 唯一性」,例如
Connection(state='closed', id=42)
__str__ 和 __repr__ 的行为差异在哪?
它们触发场景不同,不是“一个简版一个详版”那么简单:
-
print(obj)、str(obj)、f"{obj}"调用__str__;没定义时自动回退到__repr__ -
repr(obj)、交互式环境直接输入变量名、logging.info(repr(obj))强制走__repr__;它从不回退 -
__str__可以返回中文、换行、emoji 等易读内容;__repr__应只含 ASCII 字符,且尽量是合法 Python 表达式 - 列表、字典等容器调用
__repr__显示元素,但内部每个元素都用自己的__repr__—— 所以子对象也得写好,否则嵌套打印全是地址
常见错误写法与修复示例
以下代码看似合理,实则埋雷:
class User:
def __init__(self, name, email):
self.name = name
self.email = email
<pre class="brush:python;toolbar:false;">def __repr__(self):
return f"User({self.name}, {self.email})" # ❌ 缺少引号,eval 会报错
def __str__(self):
return self.name # ✅ 简洁,但可能丢失上下文
修正后:
class User:
def __init__(self, name, email):
self.name = name
self.email = email
<pre class="brush:python;toolbar:false;">def __repr__(self):
return f"User(name={self.name!r}, email={self.email!r})" # ✅ !r 自动加引号
def __str__(self):
return f"{self.name} <{self.email}>" # ✅ 更实用的显示格式
-
!r是关键:它调用repr()对字段做转义,避免name="O'Connor"导致语法错误 - 别在
__repr__里用json.dumps()或pprint.pformat()—— 它们本身依赖__repr__,可能引发递归 - 如果类有大量属性,用
vars(self)动态生成__repr__是可行的,但要过滤掉私有属性和方法:{k: v for k, v in vars(self).items() if not k.startswith('_')}
最常被忽略的一点:第三方库(如 dataclasses、pydantic)默认生成的 __repr__ 很友好,但一旦你手动写了 __str__ 却漏了 __repr__,调试时就只剩地址了——因为 __str__ 不会影响 repr() 的行为。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











