PEP 3140 – str(container) 应调用 str(item),而不是 repr(item)
- 作者:
- Oleg Broytman <phd at phdru.name>, Jim J. Jewett <jimjjewett at gmail.com>
- 讨论至:
- Python-3000 列表
- 状态:
- 已拒绝
- 类型:
- 标准跟踪
- 创建日期:
- 2008年5月27日
- 发布历史:
- 2008年5月28日
拒绝
Guido 表示,这将在 Beta 发布前引起过多的干扰。参见 [1]。
摘要
本文档讨论了当前 str(container) 实现的优点和缺点。它还讨论了一种不同方法的优缺点——调用 str(item) 而不是 repr(item)。
动机
当前 str(container) 会对 item 调用 repr。支持它的论点
- 容器不应猜测用户希望在
str(container)中看到什么——周围环境、分隔符等; repr(item)通常会显示类型信息——字符串周围的单引号、类名等。
反对的论点
- 不合逻辑;人们期望
str()调用__str__(如果存在),而不是__repr__; - 没有标准的方法可以打印容器的内容并通过调用 item 的
__str__来实现,这在__str__和__repr__返回不同结果的情况下很不方便; repr(item)有时会做错事(例如,对非 ASCII 字符串进行十六进制转义)
本 PEP 提议改变 str(container) 的工作方式。提议模仿 repr(container) 的工作方式,但有一个细节不同——对 item 调用 str 而不是 repr。这允许用户选择他们想要的结果——来自 item.__repr__ 还是 item.__str__。
当前情况
大多数容器类型(元组、列表、字典、集合等)不实现 __str__ 方法,因此 str(container) 调用 container.__repr__,而 container.__repr__ 一旦被调用,就会忘记它是由 str 调用的,并始终对容器的 item 调用 repr。
这种行为有优点也有缺点。一个优点是大多数 item 都用类型信息表示——字符串用单引号括起来,实例可能同时具有类名和实例数据
>>> print([42, '42'])
[42, '42']
>>> print([Decimal('42'), datetime.now()])
[Decimal("42"), datetime.datetime(2008, 5, 27, 19, 57, 43, 485028)]
缺点是 __repr__ 经常返回技术数据(如‘<object at address>’)或不可读的字符串(如果输入是非 ASCII 字符串,则为十六进制编码的字符串)
>>> print(['тест'])
['\xd4\xc5\xd3\xd4']
PEP 3138 的动机之一是 repr 和 str 都无法合理地打印其键为非 ASCII 文本字符串的字典。现在允许使用 Unicode 标识符,这包括 Python 自身的属性字典。这还包括 JSON 序列化(并导致了 json 库的一些麻烦)。
PEP 3138 提议通过打破“repr 是安全的 ASCII”不变性来解决此问题,并改变 repr(用于持久化)的输出某些对象的方式,可能会导致系统依赖性失败。
改变 str(container) 的工作方式将允许在正常情况下进行简单的调试,并保留机器可读情况下的 ASCII 安全性。唯一的缺点是 str(x) 和 repr(x) 会更频繁地不同——但仅限于当前几乎相同的版本不足以满足需求的情况。
似乎不合逻辑的是,str(container) 会对 item 调用 repr 而不是 str。人们自然会期望以下代码
class Test:
def __str__(self):
return "STR"
def __repr__(self):
return "REPR"
test = Test()
print(test)
print(repr(test))
print([test])
print(str([test]))
打印
STR
REPR
[STR]
[STR]
而实际上打印的是
STR
REPR
[REPR]
[REPR]
特别不合逻辑的是,Python 2 中当对看起来像元组的对象调用 print 时,它会使用 str
>>> print Decimal('42'), datetime.now()
42 2008-05-27 20:16:22.534285
而在实际的元组上,它打印的是
>>> print((Decimal('42'), datetime.now()))
(Decimal("42"), datetime.datetime(2008, 5, 27, 20, 16, 27, 937911))
另一种方法——调用 str(item)
例如,对于数字,人们通常只关心其值。
>>> print Decimal('3')
3
但是将值放在列表中会迫使用户阅读类型信息,就像为了机器的利益调用了 repr 一样
>>> print [Decimal('3')]
[Decimal("3")]
在此更改之后,类型信息将不会弄乱 str 的输出
>>> print "%s".format([Decimal('3')])
[3]
>>> str([Decimal('3')]) # ==
[3]
但如果需要,它仍然可用
>>> print "%r".format([Decimal('3')])
[Decimal('3')]
>>> repr([Decimal('3')]) # ==
[Decimal('3')]
有几种策略可以解决这个问题。最激进的方法是修改 __repr__,使其接受一个新参数(标志)“从 str 调用,因此对 item 调用 str,而不是 repr”。此提案的缺点是每个 __repr__ 实现都必须更改。内省可以有所帮助(在调用 __repr__ 之前检查它是否接受 2 个或 3 个参数),但内省不适用于用 C 编写的类,就像所有内置容器一样。
不太激进的提议是为内置容器类型实现 __str__ 方法。显而易见的缺点是重复劳动——所有这些 __str__ 和 __repr__ 实现仅在一个小细节上有所不同——它们是对 item 调用 str 还是 repr。
最保守的提议是根本不更改 str,而是允许开发人员实现自己特定于应用程序或库的“漂亮打印机”。缺点是再次增加了工作量,并且出现了许多小型、特定的容器遍历算法。
向后兼容性
在类型信息比通常更重要的那些情况下,仍然可以通过显式调用 repr 来获得当前结果。
参考资料
版权
本文档已置于公共领域。
来源: https://github.com/python/peps/blob/main/peps/pep-3140.rst
最后修改: 2025-02-01 08:59:27 GMT