__repr__() vs __str__() 的区别

2024 年 08 月 29 日 | 阅读 9 分钟

在本教程中,我们将学习 __repr__() 和 __str__() 方法之间的区别以及何时使用这些方法。向用户显示数据是计算机程序的常见任务之一。程序向用户显示信息,以便用户可以解释一些结果。但是,程序还必须向程序员显示信息以进行开发和维护。程序员需要有关对象的一些信息,这些信息可能对最终用户来说不必要或难以理解。这就是 .repr() 和 .str() 方法之间区别变得重要的地方。

一个 Python 对象包含各种提供特定行为的特殊方法。__repr__()__str__() 方法是类似的特殊方法,它们使用字符串表示定义对象。__repr__() 方法返回需要维护和调试代码的程序员的全面描述。另一方面,__str__() 方法向程序用户返回更简单的描述

我们可以在任何 Python 类中定义这些方法,这使我们能够控制程序以不同的输出方式显示公共对象。

对程序员使用 .__repr__() 与对用户使用 .__str__()

Python 类附带了几个特殊方法。这些方法以名称旁边的双下划线表示。由于名称中的双下划线,它们也被称为 dunder 方法。特殊方法 .repr() 和 .str() 提供对象的字符串表示。字符串表示是有关对象的信息。我们还可以出于不同的目的更改信息,例如针对程序用户或同事程序员。

存在两种不同的方法来显示对象是由于它们独特而独立的目的。这两种方法中的每一种都在显示对象方面发挥着特定作用,这就是为什么两者都是必需的。

  • .__repr__() 方法旨在提供对象的正式字符串表示,供程序员使用,而 .__str__() 方法用于生成对象的非正式字符串表示,面向最终用户。

__repr__() 方法面向开发和维护程序的程序员,因为它提供有关项目的明确信息。正式字符串表示的另一个关键特征是它能够允许程序员重构一个与原始对象相同的对象。

.str() 方法的目的是生成专门为程序的最终用户设计的字符串表示,最终用户可能不一定了解 Python 编程语言。因此,此字符串表示旨在方便用户理解对象中包含的数据。通常,此表示以更直接和用户友好的形式呈现。

我们可以使用 Python 的标准 REPL 来表示对象的两种表示。

当一个对象在 REPL 中自行评估时,将显示 .__repr__() 方法返回的字符串表示。另一方面,如果使用内置的 print() 函数输出对象,则会显示由 .__str__() 方法生成的非正式字符串表示。

让我们理解下面的例子。

通过调用 .now() 方法,我们创建一个名为“today”的 datetime.datetime 对象,它表示当前的日期和时间。当变量名“today”自行评估时,REPL 显示由 .repr() 方法返回的对象的正式字符串表示。此表示包括数据类型名称和重新创建对象所需的所有参数。

使用 print() 函数后,REPL 显示由 .str() 方法返回的对象的非正式字符串表示。此字符串表示遵循 ISO 标准格式显示日期和时间,这并非 Python 独有,而是广泛采用的日期和时间表示标准。

通常,对象的正式字符串表示是一个合法的 Python 表达式,可用于实例化具有相同值的新对象。我们可以通过复制 datetime.datetime 对象的正式字符串表示并将其分配给新的变量名来验证这一点。相反,以这种方式尝试处理非正式字符串表示将不会产生所需的结果。

示例 -

当变量“today”在 REPL 中评估时,显示了由 .repr() 方法生成的正式字符串表示,可用于构造一个与原始对象相同的新对象。

相反,由 .str() 方法生成的字符串表示(通过使用 print() 函数获得)不是有效的 Python 表达式。因此,尝试使用它来构造新对象将导致 SyntaxError。

当一个对象或其变量名在 Python REPL 中评估时,将返回由 .repr() 方法指定的官方字符串表示。但是,当使用 print() 函数时,将返回由 .str() 方法生成的非正式字符串表示。官方字符串表示和非正式字符串表示是相同或几乎相同的。这主要是因为这两种表示主要基于用于实例化对象的字面量。

在以下示例中,我们表示列表和字典。

对象的官方和非正式字符串表示清晰明确,能够生成具有相同值的新对象。此外,它们足够简单,可供程序用户理解,因为它们有效地传达了对象的信息,并且不能再简化。因此,此类对象不需要为程序员和用户提供不同的字符串表示。

如何访问对象的字符串表示

到目前为止,我们已经学习了如何在标准 Python REPL 中显示两种字符串表示。我们还可以使用内置函数 repr() 和 str() 访问官方和非正式字符串表示。我们可以将一个对象作为参数传递给 repr() 和 str() 方法,这些方法会调用对象的 __repr__() 或 __str__()。让我们理解以下示例。

示例 -

如果将变量“today”传递给 Python 的内置 repr() 函数,程序将调用对象的 repr() 特殊方法。虽然可以使用 today.repr() 直接调用 repr() 方法,但通常建议使用内置的 repr() 函数。这是因为不鼓励直接访问特殊方法,并且这些方法主要旨在为对象提供附加功能,而不是直接调用。

在自定义类中定义 __repr__() 和 __str__()

创建类时,我们可以定义多个特殊方法来增强类的功能。让我们看以下示例。

示例 -

我们使用 __init__() 方法定义 Book 类,其中我们传递两个必需的参数 name 和 roll_no。

我们创建此类的实例,包含学生的姓名和学号,并将 stu_obj 传递给 print() 函数。我们使用 print() 是因为当我们在脚本中评估仅包含变量名的行时,不会打印任何输出。运行此脚本时,您将获得以下输出

我们使用 __init__() 方法定义 Book 类,其中我们传递两个必需的参数 names 和 roll_no。

输出

<__main__.Student object at 0x2025c8ed0>

此输出是对象从对象类继承的默认字符串表示。对象类是所有 Python 类的基类。它显示

  • __main__.Student: 类的名称及其定义位置
  • 0x2025c8ed0 - 对象的内存地址

默认情况下,Python 为对象提供一个字符串表示,它以十六进制格式显示对象的类名和内存地址。在 CPython 中,内存地址等同于对象的身份。如果需要检索对象的身份,可以使用内置函数 id()。与字符串表示不同,id() 返回一个整数值而不是十六进制值。

在大多数情况下,对象默认表示提供的内存地址可能更有用。此表示不提供有关对象的有价值信息,这些信息将使用户或程序员受益。

让我们理解以下示例 -

示例 -

输出

<__main__.Student object at 0x000001CDBE6B5310>
<__main__.Student object at 0x000001CDBE6B5310>

我们可以为类定义 __repr__() 特殊方法。

输出

Student(name='John', roll_no='101')
Student(name='John', roll_no='101')

.repr() 方法除了 self 之外不接受任何其他参数,并且必须始终返回一个字符串。如代码片段所示,输出证实 repr(str_obj) 和 str(str_obj) 都生成了先前定义的字符串表示。

当我们对一个对象调用内置函数 repr() 时,它会调用对象的 .repr() 方法。另一方面,如果一个类没有定义 .str() 方法,那么 str() 方法将默认使用 .repr() 方法。

现在,我们将定义 __str__() 方法。

示例 -

输出

Student(name='John', roll_no='101')
John

要定义特殊方法 .str(),它应该只包含参数“self”,并且应该返回一个字符串。我们选择返回的字符串表示可以对程序的最终用户最有用。

默认情况下,由 .str() 方法返回的非正式字符串表示只显示学生的姓名,不包括类名引用。但是,可以根据我们的具体要求修改非正式字符串表示。

示例 -

输出

Student(name='John', roll_no='101')
John's roll_no=101

对象的官方字符串表示包含程序员可能需要的所有必要信息,它使他们能够更详细地重新创建和探索对象。这有两个显著的优点

  • 它提高了程序的可维护性。
  • 它简化了调试过程。

创建类时,建议定义 .repr() 方法以提供对象的官方字符串表示。通过实现此方法,您可以避免通常无用的默认表示。此外,当同一表示可用于两种用例时,此方法可用作非正式字符串表示的备用选项。它在确保对象表示的一致性和效率方面特别有益。

结论

在本教程中,我们已经了解了 Python 对象的官方和非正式字符串表示之间的区别。.repr() 方法提供官方字符串表示,主要供程序员在程序的开发和维护期间使用。另一方面,.str() 方法返回非正式字符串表示,这是一种更用户友好的格式,面向程序的最终用户。


下一主题Anytree Python