Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

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 的动机之一是 reprstr 都无法合理地打印其键为非 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