repr 和 str 的核心区别
| 特性 | repr | str |
|---|---|---|
| 目的 | 开发者友好的精确表示 | 终端用户友好的简洁表示 |
| 调用场景 | 交互式控制台、调试器、repr()函数 | print()函数、str()函数 |
| 要求 | 应无歧义,最好能重建对象 | 可读性强,简洁明了 |
| 输出示例 | Vector(3, 4) | (3, 4) |
字符串格式化中的表示形式处理
下面三种格式化方式都会对表达式求值结果调用 repr() 函数:
① 经典格式化(% 运算符)
# %r 会调用对象的 __repr__ 方法
print("Vector: %r" % Vector(3, 4))
# 输出:Vector: Vector(3, 4)
② str.format() 方法
# {!r} 会调用对象的 __repr__ 方法
print("Vector: {!r}".format(Vector(3, 4)))
# 输出:Vector: Vector(3, 4)
③ f-strings(格式化字符串字面值)
# {obj!r} 会调用对象的 __repr__ 方法
v = Vector(3, 4)
print(f"Vector: {v!r}")
# 输出:Vector: Vector(3, 4)
为什么需要 !r 和 %r?
关键原因:精确表示 vs 友好显示
考虑这个例子:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Vector({self.x!r}, {self.y!r})"
def __str__(self):
return f"({self.x}, {self.y})"
# 创建两个不同的 Vector 对象
v1 = Vector(1, 2) # 数字参数
v2 = Vector('1', '2') # 字符串参数
不使用 !r 的问题:
# 不正确的 __repr__ 实现(没有使用 !r)
def __repr__(self):
return f"Vector({self.x}, {self.y})"
print(repr(v1)) # 输出:Vector(1, 2)
print(repr(v2)) # 输出:Vector(1, 2) ❌ 无法区分数字和字符串!
使用 !r 的正确实现:
# 正确的 __repr__ 实现(使用 !r)
def __repr__(self):
return f"Vector({self.x!r}, {self.y!r})"
print(repr(v1)) # 输出:Vector(1, 2) → 数字没有引号
print(repr(v2)) # 输出:Vector('1', '2') → 字符串有引号 ✅
实际应用场景分析
- 调试和开发阶段(使用 repr)
# 在交互式控制台
>>> v = Vector(3, 4)
>>> v
Vector(3, 4) # 调用 __repr__
# 调试器显示
# 变量查看窗口显示:Vector(3, 4)
- 最终用户展示(使用 str)
# 打印给用户看
print(v) # 输出:(3, 4) → 简洁友好
# 生成报告
report = f"当前位置: {v}"
print(report) # 输出:当前位置: (3, 4)
- 对象重建(repr 的高级用法)
# 从 __repr__ 输出重建对象
original = Vector(3.5, 4.2)
repr_str = repr(original) # "Vector(3.5, 4.2)"
# 重建对象(实际应用中需安全验证)
reconstructed = eval(repr_str)
print(original == reconstructed) # 需要实现 __eq__ 方法
最佳实践总结
总是实现 repr:
使用 {attr!r} 格式确保类型信息不丢失
返回的字符串应能重建对象
按需实现 str:
当 print(obj) 需要特殊格式时实现
默认回退到 repr
在格式化中的选择:
# 需要精确表示(调试/日志):
log.debug(f"Received vector: {v!r}")
# 用户友好显示:
print(f"当前位置: {v}")
在自定义类中的实现模板:
class MyClass:
def __init__(self, *args):
self.data = args
def __repr__(self):
# 使用 !r 保持类型信息
args = ', '.join(repr(arg) for arg in self.data)
return f"{self.__class__.__name__}({args})"
def __str__(self):
# 简洁的用户友好表示
return f"MyObject with {len(self.data)} elements"
理解 repr 和 str 的区别以及 !r/%r 的作用,是写出专业级 Python 代码的关键。这确保了在开发过程中能看到对象的精确表示,同时又能为用户提供友好的输出。
296

被折叠的 条评论
为什么被折叠?



