__str__是给人看的用户友好字符串,__repr__是给开发者看的可调试、可重建对象的字符串;未重写时均返回内存地址;print、str()、f-string等触发__str__,容器显示和%r强制用__repr__;二者返回值必须为str类型。

直接说结论:__str__ 是给人看的,__repr__ 是给开发者看的;不重写时两者都输出 <__main__.yourclass object at></__main__.yourclass>,完全没用。
什么时候触发 __str__?哪些场景会忽略它?
只要涉及“展示给用户”或“字符串化输出”,基本都走 __str__:
-
print(obj)—— 最常见,但前提是类里定义了__str__ -
str(obj)、f"{obj}"、"{}".format(obj) - 日志中用
logging.info("user: %s", user),%s触发__str__
容易踩的坑:
- 如果只写了
__repr__没写__str__,print()会退回到__repr__输出 —— 看起来像“有用了”,其实只是兜底,不是你想要的用户友好格式 - 在
logging.debug("obj=%r", obj)里用了%r,它强制走__repr__,和__str__无关
为什么 __repr__ 要尽量满足 eval(repr(obj)) == obj?
这不是考试要求,而是调试时的真实需求。比如你在 IPython 里输入变量名回车,或者打印一个列表 [u1, u2],Python 默认调用每个元素的 __repr__。此时你希望一眼看出:
- 对象类型(是
User还是Admin?) - 关键字段值(
name='Alice'还是name="Alice"?引号得对) - 是否能复制粘贴后直接运行(
User(name='Alice', uid=1001)是合法代码)
所以写 __repr__ 时必须用 !r 格式化字符串: f"User(name={self.name!r}, uid={self.uid!r})",否则中文、换行符、单双引号混用会导致 eval 失败。
容器打印(list/dict/set)为什么总显示 __repr__?
这是 Python 的统一设计:容器本身不负责“美化”,只负责“准确呈现内容”。所以 print([User("张三", 1001)]) 输出的是 [User(name='张三', uid=1001)],而不是 [用户[张三],ID:1001]。
这意味着:
- 别指望列表里自动变
__str__—— 它不会 - 如果你真需要用户友好的容器输出(比如 API 返回 JSON 前预览),得手动映射:
[str(u) for u in users] - 调试时看到
[User(...), User(...)]是正常的,说明__repr__写对了;如果看到一堆内存地址,说明__repr__没实现
最常被忽略的一点:两个方法的返回值**必须是字符串**,不能是 f-string 以外的表达式,也不能返回 None 或数字 —— 否则会抛 TypeError: __str__ returned non-string。写完务必测试 str(obj) 和 repr(obj) 是否真返回 str 类型。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











