ChatGPT解决这个技术问题 Extra ChatGPT

__str__ 和 __repr__ 有什么区别?

Python 中的 __str____repr__ 有什么区别?


F
Francisco

Alex 总结得很好,但令人惊讶的是,它过于简洁。

首先,让我重申 Alex’s post 中的要点:

默认实现是无用的(很难想到一个不会,但是是的)

__repr__ 目标是明确的

__str__ 目标是可读的

容器的 __str__ 使用包含对象的 __repr__

默认实现没用

这主要是一个惊喜,因为 Python 的默认值往往相当有用。但是,在这种情况下,具有 __repr__ 的默认值,其行为如下:

return "%s(%r)" % (self.__class__, self.__dict__)

会太危险(例如,如果对象相互引用,太容易陷入无限递归)。所以 Python 出局了。请注意,有一个默认值为 true:如果定义了 __repr__ 而未定义 __str__,则对象的行为就像 __str__=__repr__

这意味着,简单来说:您实现的几乎每个对象都应该有一个可用于理解该对象的功能 __repr__。实现 __str__ 是可选的:如果您需要“漂亮打印”功能(例如,由报告生成器使用),请执行此操作。

__repr__ 的目标是明确的

让我直接说出来——我不相信调试器。我真的不知道如何使用任何调试器,也从未认真使用过。此外,我认为调试器的最大故障在于它们的基本性质——我调试的大多数故障都发生在很久很久以前,在一个遥远的星系中。这意味着我确实怀着宗教热情相信伐木。日志记录是任何体面的即发即弃服务器系统的命脉。 Python 使记录变得容易:可能使用一些特定于项目的包装器,您所需要的只是一个

log(INFO, "I am in the weird function and a is", a, "and b is", b, "but I got a null C — using default", default_c)

但是你必须做最后一步——确保你实现的每个对象都有一个有用的 repr,这样这样的代码才能正常工作。这就是为什么会出现“评估”的原因:如果您有足够的信息 eval(repr(c))==c,这意味着您了解有关 c 的所有信息。如果这很容易,至少以一种模糊的方式,那就去做吧。如果没有,请确保您有足够的关于 c 的信息。我通常使用类似 eval 的格式:"MyClass(this=%r,that=%r)" % (self.this,self.that)。这并不意味着您可以实际构造 MyClass,或者这些是正确的构造函数参数 - 但它是一种有用的形式来表达“这是您需要了解的有关此实例的所有信息”。

注意:我在上面使用了 %r,而不是 %s。您总是希望在 __repr__ 实现中使用 repr() [或 %r 格式字符,等价地],否则您将违背 repr 的目标。您希望能够区分 MyClass(3)MyClass("3")

__str__ 的目标是可读

具体而言,它并非旨在明确 — 请注意 str(3)==str("3")。同样,如果你实现一个 IP 抽象,让它的 str 看起来像 192.168.1.1 就可以了。在实现日期/时间抽象时,str 可以是“2010/4/12 15:35:22”等。目标是以用户而不是程序员想要阅读的方式表示它。砍掉无用的数字,假装是其他类——只要它支持可读性,它就是一种改进。

容器的 __str__ 使用包含对象的 __repr__

这似乎令人惊讶,不是吗?有点,但是如果使用他们的__str__,它的可读性如何?

[moshe is, 3, hello
world, this is a list, oh I don't know, containing just 4 elements]

不是特别的。具体来说,容器中的字符串会很容易干扰其字符串表示。面对歧义,请记住,Python 抵制猜测的诱惑。如果您在打印列表时想要上述行为,只需

print("[" + ", ".join(l) + "]")

(您可能还可以弄清楚如何处理字典。

概括

为您实现的任何类实现 __repr__。这应该是第二天性。如果您认为有一个在可读性方面出错的字符串版本会很有用,请实施 __str__


绝对不同意你的观点,即调试不是要走的路。对于开发使用调试器(和/或日志记录),对于生产使用日志记录。使用调试器,您可以查看问题发生时出现的所有问题。你可以看到完整的图片。除非您记录所有内容,否则您将无法做到这一点。另外,如果您正在记录所有内容,您将不得不通过大量数据来获取您想要的内容。
很好的答案(除了关于不使用调试器的一点)。我只想添加一个指向此 other Q&A about str vs unicode in Python 3 的链接,该链接可能与已进行转换的人的讨论相关。
我听说变量 l(小写字母“L”)在 python 4.0 中会出现语法错误;)
关于调试器与没有调试器:不要得到这样根深蒂固的意见。在某些应用程序中,调试是不现实的,通常是在涉及实时时,或者当您的代码仅在几乎没有访问权限或没有控制台的平台上远程执行时。在大多数其他情况下,停止在异常处进行调查或设置断点会更快,因为您不必经历数千行日志记录(这会使您的磁盘混乱并减慢应用程序的速度)。最后,并非总是可以登录,例如在嵌入式设备上,调试器也是您的朋友。
关于调试与日志记录,它们都很有用。如果一个错误是可重现的,那么调试就更简单了。如果错误是随机的,那么日志记录是必不可少的。
N
Ned Batchelder

我的经验法则:__repr__ 适用于开发人员,__str__ 适用于客户。


这是真的,因为对于 obj = uuid.uuid1(),obj.__str__() 是“2d7fc7f0-7706-11e9-94ae-0242ac110002”,而 obj.__repr__() 是“UUID('2d7fc7f0-7706-11e9-94ae-0242ac110002” ')”。开发人员需要(价值+原产地),而客户需要价值,他们并不关心他们是如何获得的!
这里customer 不一定是指最终用户。它是对象的客户或用户。因此,如果它是一个 SDK,那么 SDK 开发人员将使用 __str__,以便普通开发人员拥有可读的对象。另一方面,__repr__ 是针对 SDK 开发者自己的。
@NarenYellavula 如果您将 UUID 暴露给客户,您可能做错了什么。
@MarkRansom 为什么会这样?
@AbdessabourMtk 它们过于复杂,并且无法防止输入错误。也许在某些情况下,例如作为 QR 码的一部分,它们会没问题。
A
Adrian W

除非您特别采取行动以确保其他情况,否则大多数课程对以下任何一项都没有有用的结果:

>>> class Sic(object): pass
... 
>>> print(str(Sic()))
<__main__.Sic object at 0x8b7d0>
>>> print(repr(Sic()))
<__main__.Sic object at 0x8b7d0>
>>> 

如您所见——没有区别,除了类和对象的 id 之外没有任何信息。如果您只覆盖两者之一......:

>>> class Sic(object): 
...   def __repr__(self): return 'foo'
... 
>>> print(str(Sic()))
foo
>>> print(repr(Sic()))
foo
>>> class Sic(object):
...   def __str__(self): return 'foo'
... 
>>> print(str(Sic()))
foo
>>> print(repr(Sic()))
<__main__.Sic object at 0x2617f0>
>>> 

如您所见,如果您覆盖 __repr__,则它也用于 __str__,但反之则不然。

要了解的其他重要花絮:内置容器上的 __str__ 使用 __repr__ 而不是 __str__ 来表示它包含的项目。而且,尽管在典型文档中找到了有关该主题的文字,但几乎没有人会费心将对象的 __repr__ 设为 eval 可以用来构建相等对象的字符串(这太难了,而且不知道相关模块如何实际上是进口的,这实际上是不可能的)。

因此,我的建议是:专注于使 __str__ 合理地可读,并使 __repr__ 尽可能明确,即使这会干扰使 __repr__ 的返回值可以作为 { 的输入而无法实现的模糊目标4}!


在我的单元测试中,我总是检查 eval(repr(foo)) 的计算结果是否等于 foo。你说得对,因为我不知道模块是如何导入的,所以它不会在我的测试用例之外工作,但这至少可以确保它在 一些 可预测的上下文中工作。我认为这是评估 __repr__ 的结果是否足够明确的好方法。在单元测试中执行此操作还有助于确保 __repr__ 遵循对类的更改。
我总是尝试确保 eval(repr(spam)) == spam(至少在正确的上下文中)或 eval(repr(spam)) 引发 SyntaxError。这样可以避免混淆。 (对于内置函数和大多数标准库来说,几乎都是如此,除了递归列表,其中 a=[]; a.append(a); print(eval(repr(a))) 给你 [[Ellipses]]...)当然我实际上并没有这样做使用 eval(repr(spam)),除了作为单元测试中的健全性检查...但我确实有时将 repr(spam) 复制并粘贴到交互式会话中。
为什么容器(列表、元组)不对每个元素使用 __str__ 而不是 __repr__?对我来说似乎完全错误,因为我在我的对象中实现了一个可读的 __str__,当它是列表的一部分时,我看到的是更丑的 __repr__
刚刚遇到了一个令人讨厌的错误,该错误与 eval(repr(x)) 即使对于内置类型也失败的事实有关:class A(str, Enum): X = 'x' 将在 eval(repr(A.X)) 上引发 SyntaxError。这很可悲,但可以理解。顺便说一句,eval(str(A.X)) 确实有效,但当然只有当 class A 在范围内时——所以它可能不是很有用。
@abarnert:对于自定义 class Spameval(repr(spam)) == spam 也需要实现 Spam.__eq__,对吧?默认情况下 object.__eq__ 使用 is (docs)。
A
Andrew Clark

__repr__:python 对象的表示,通常 eval 会将其转换回该对象

__str__:是您认为的任何文本形式的对象

例如

>>> s="""w'o"w"""
>>> repr(s)
'\'w\\\'o"w\''
>>> str(s)
'w\'o"w'
>>> eval(str(s))==s
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    w'o"w
       ^
SyntaxError: EOL while scanning single-quoted string
>>> eval(repr(s))==s
True

__repr__() :用于在字符串中创建“类似构造函数的表达式”,以便 eval() 可以从该字符串表示中重新构造一个对象 __str__() :用于创建包含对象的可打印表示的字符串
d
deadly

简而言之,__repr__ 的目标是明确的,而 __str__ 是可读的。

这是一个很好的例子:

>>> import datetime
>>> today = datetime.datetime.now()
>>> str(today)
'2012-03-14 09:21:58.130922'
>>> repr(today)
'datetime.datetime(2012, 3, 14, 9, 21, 58, 130922)'

阅读此文档以获取 repr:

repr(object) 返回一个包含对象的可打印表示的字符串。这与转换(反向引号)产生的值相同。有时能够将此操作作为普通函数访问是很有用的。对于许多类型,此函数尝试返回一个字符串,该字符串在传递给 eval() 时会产生具有相同值的对象,否则表示是一个包含在尖括号中的字符串,其中包含对象类型的名称附加信息通常包括对象的名称和地址。类可以通过定义 __repr__() 方法来控制此函数为其实例返回的内容。

这是 str 的文档:

str(object='') 返回一个字符串,该字符串包含一个对象的可打印表示。对于字符串,这将返回字符串本身。与 repr(object) 的区别在于 str(object) 并不总是试图返回一个 eval() 可接受的字符串;它的目标是返回一个可打印的字符串。如果没有给出参数,则返回空字符串 ''。


这里的可打印字符串是什么意思?你能解释一下吗?
通过“bitoffdev”和@deadly 构建上面的示例,我们可以看到 str 对最终用户的作用,因为它只为我们提供了一个可读的字符串,而 repr 是针对开发人员的,因为它为我们提供了值和类型。如果您正在寻找面试答案,那将是完美的。
M
Maggyero

Python 中的 __str__ 和 __repr__ 有什么区别?

__str__(读作“dunder(双下划线)字符串”)和 __repr__(读作“dunder-repper”(表示“表示”))都是基于对象状态返回字符串的特殊方法。

如果缺少 __str____repr__ 会提供备份行为。

因此,首先应该编写一个 __repr__,它允许您从它返回的字符串中重新实例化一个等效对象,例如使用 eval 或在 Python shell 中逐个字符地键入它。

在以后的任何时候,当人们认为有必要时,可以为实例的用户可读字符串表示形式编写 __str__

__str__

如果您打印一个对象,或将其传递给 formatstr.formatstr,则如果定义了 __str__ 方法,则将调用该方法,否则将使用 __repr__

__repr__

__repr__ 方法由内置函数 repr 调用,当它计算返回对象的表达式时,它会在您的 python shell 上回显。

由于它为__str__提供了备份,如果您只能写一个,请从__repr__开始

这是 repr 的内置帮助:

repr(...)
    repr(object) -> string
    
    Return the canonical string representation of the object.
    For most object types, eval(repr(object)) == object.

也就是说,对于大多数对象,如果您输入 repr 打印的内容,您应该能够创建一个等效的对象。 但这不是默认实现。

__repr__ 的默认实现

默认对象 __repr__ 是 (C Python source) 类似于:

def __repr__(self):
    return '<{0}.{1} object at {2}>'.format(
      type(self).__module__, type(self).__qualname__, hex(id(self)))

这意味着默认情况下,您将打印对象来自的模块、类名以及其在内存中位置的十六进制表示 - 例如:

<__main__.Foo object at 0x7f80665abdd0>

这些信息不是很有用,但没有办法推导出如何准确地创建任何给定实例的规范表示,总比没有好,至少告诉我们如何在内存中唯一地识别它。

__repr__ 如何有用?

让我们看看它有多么有用,使用 Python shell 和 datetime 对象。首先我们需要导入 datetime 模块:

import datetime

如果我们在 shell 中调用 datetime.now,我们将看到重新创建等效日期时间对象所需的一切。这是由日期时间 __repr__ 创建的:

>>> datetime.datetime.now()
datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)

如果我们打印一个日期时间对象,我们会看到一种很好的人类可读(实际上是 ISO)格式。这是由 datetime 的 __str__ 实现的:

>>> print(datetime.datetime.now())
2015-01-24 20:05:44.977951

重新创建我们丢失的对象是一件简单的事情,因为我们没有通过从 __repr__ 输出复制和粘贴来将它分配给变量,然后打印它,我们在与另一个相同的人类可读输出中得到它目的:

>>> the_past = datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)
>>> print(the_past)
2015-01-24 20:05:36.491180

#如何实现它们?

在开发过程中,如果可能,您将希望能够以相同的状态重现对象。例如,这就是 datetime 对象定义 __repr__ (Python source) 的方式。它相当复杂,因为复制这样一个对象所需的所有属性:

def __repr__(self):
    """Convert to formal string, for repr()."""
    L = [self._year, self._month, self._day,  # These are never zero
         self._hour, self._minute, self._second, self._microsecond]
    if L[-1] == 0:
        del L[-1]
    if L[-1] == 0:
        del L[-1]
    s = "%s.%s(%s)" % (self.__class__.__module__,
                       self.__class__.__qualname__,
                       ", ".join(map(str, L)))
    if self._tzinfo is not None:
        assert s[-1:] == ")"
        s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
    if self._fold:
        assert s[-1:] == ")"
        s = s[:-1] + ", fold=1)"
    return s

如果您希望您的对象具有更易读的表示,您可以接下来实现 __str__。以下是日期时间对象 (Python source) 实现 __str__ 的方式,它很容易做到,因为它已经有一个以 ISO 格式显示它的函数:

def __str__(self):
    "Convert to string, for str()."
    return self.isoformat(sep=' ')

设置 __repr__ = __str__?

这是对此处建议设置 __repr__ = __str__ 的另一个答案的批评。

设置 __repr__ = __str__ 很愚蠢 - __repr____str__ 的后备,而为开发人员在调试中使用而编写的 __repr__ 应该在编写 __str__ 之前编写。

仅当您需要对象的文本表示时才需要 __str__

结论

为您编写的对象定义 __repr__,以便您和其他开发人员在开发时使用它时有一个可重现的示例。当您需要人类可读的字符串表示形式时定义 __str__


不应该是类似于 type(obj).__qualname__ 的东西吗?
@SolomonUcko 是的,在 Python 3 中,情况似乎就是这样——我一直在寻找实现它的源代码,当我把它放在一起时,我会用这些信息更新我的答案。
这个答案对初学者会更有帮助。很好的解释!!
我已将 self.__module__ 更改为 type(self).__module__(例如 3 没有 __module__)并将 type(self).__name__ 更改为 type(self).__qualname__(例如 class A: class B: passrepr(A.B()) 返回的)。
N
NelsonGon

在 Hans Petter Langtangen 所著的 Python scripting for Computing Science 一书的第 358 页上,它明确指出

__repr__ 针对对象的完整字符串表示;

__str__ 是返回一个漂亮的字符串进行打印。

所以,我更愿意将它们理解为

repr = 再现

str = 字符串(表示)

从用户的角度来看,虽然这是我在学习python时犯的一个误解。

同一页面上还给出了一个小而好的示例,如下所示:

例子

In [38]: str('s')
Out[38]: 's'

In [39]: repr('s')
Out[39]: "'s'"

In [40]: eval(str('s'))
Traceback (most recent call last):

  File "<ipython-input-40-abd46c0c43e7>", line 1, in <module>
    eval(str('s'))

  File "<string>", line 1, in <module>

NameError: name 's' is not defined


In [41]: eval(repr('s'))
Out[41]: 's'

它在 pg。 #351
repr 称为复制是一种误导。最好将其视为代表。
D
David Augusto Villa

除了给出的所有答案,我想补充几点:-

1) 当您在交互式 python 控制台上简单地写入对象的名称并按 Enter 键时,会调用 __repr__()

2) __str__() 在您使用带有 print 语句的对象时被调用。

3) 如果缺少 __str__,则 print 和任何使用 str() 的函数调用对象的 __repr__()

4) __str__() 的容器,调用时将执行其包含的元素的 __repr__() 方法。

5) 在 __str__() 中调用的 str() 可能会在没有基本情况的情况下进行递归,并且最大递归深度会出错。

6) __repr__() 可以调用 repr(),它将尝试自动避免无限递归,用 ... 替换已经表示的对象。


w
wescpy

(2020 项)

问:__str__()__repr__() 有什么区别?

https://i.stack.imgur.com/cpqeK.png

这个问题已经存在了很长时间,并且有各种各样的答案,其中大多数是正确的(更不用说来自几个 Python 社区的传说[!])。然而,当涉及到细节时,这个问题类似于询问 str()repr() 内置函数之间的区别。我将用我自己的话来描述这些差异(这意味着我可能从 Core Python Programming 大量“借用”了,所以请原谅我)。

str()repr() 都有相同的基本工作:它们的目标是返回 Python 对象的字符串表示形式。字符串表示的种类是它们的区别。

str() & __str__() 返回一个对象的可打印字符串表示......人类可读/供人类消费的东西

repr() 和 __repr__() 返回一个对象的字符串表示,它是一个有效的 Python 表达式,一个可以传递给 eval() 或键入 Python shell 而不会出错的对象。

例如,让我们将一个字符串分配给 x,将一个 int 分配给 y,并简单地显示每个字符串的人类可读的字符串版本:

>>> x, y = 'foo', 123
>>> str(x), str(y)
('foo', '123')

我们可以把这两种情况下的引号内的内容逐字输入到 Python 解释器中吗?试一试吧:

>>> 123
123
>>> foo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'foo' is not defined

显然,您可以使用 int,但不一定适用于 str。同样,虽然我可以将 '123' 传递给 eval(),但它不适用于 'foo'

>>> eval('123')
123
>>> eval('foo')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
NameError: name 'foo' is not defined

因此,这告诉您 Python shell eval()就是您给它的。知道了?现在,让我们repr() 两个表达式,看看我们得到了什么。更具体地说,取出它的输出并将 那些 转储到解释器中(有一点我们将在之后解决):

>>> repr(x), repr(y)
("'foo'", '123')
>>> 123
123
>>> 'foo'
'foo'

哇,他们工作?这是因为 'foo' 虽然是该字符串的可打印字符串表示,但它不可 可评估,但 "'foo'" 可以。 123 是由 str()repr() 调用的有效 Python int。当我们用这些调用 eval() 时会发生什么?

>>> eval('123')
123
>>> eval("'foo'")
'foo'

它之所以有效,是因为 123'foo' 是有效的 Python 对象。另一个关键要点是,虽然有时两者都返回相同的东西(相同的字符串表示),但情况并非总是如此。 (是的,是的,我可以在 eval() 起作用的地方创建一个变量 foo,但这不是重点。)

关于这两对的更多事实

有时,str() 和 repr() 被隐式调用,这意味着它们是代表用户调用的:当用户执行 print (Py1/Py2) 或调用 print() (Py3+) 时,即使用户不调用 str( ) 明确地,在显示对象之前代表他们进行此类调用。在 Python shell(交互式解释器)中,如果在 >>> 提示符下输入变量并按 RETURN,解释器会显示对该对象隐式调用的 repr() 的结果。要将 str() 和 repr() 连接到 __str__() 和 __repr__(),请意识到调用内置函数,即 str(x) 或 repr(y) 会导致调用其对象的相应特殊方法:x。 __str__() 或 y.__repr()__ 通过为 Python 类实现 __str__() 和 __repr__(),可以重载内置函数(str() 和 repr()),从而允许传入类的实例到 str() 和 repr()。当进行此类调用时,它们会转身并调用类的 __str__() 和 __repr__()(根据 #3)。


u
user222758

简而言之:

__str__ 用于显示您的对象的字符串表示便于他人阅读

__repr__ 用于显示 对象的字符串表示形式。

假设我想创建一个 Fraction 类,其中分数的字符串表示形式为“(1/2)”,而对象(分数类)表示为“分数 (1,2)”

所以我们可以创建一个简单的 Fraction 类:

class Fraction:
    def __init__(self, num, den):
        self.__num = num
        self.__den = den

    def __str__(self):
        return '(' + str(self.__num) + '/' + str(self.__den) + ')'

    def __repr__(self):
        return 'Fraction (' + str(self.__num) + ',' + str(self.__den) + ')'



f = Fraction(1,2)
print('I want to represent the Fraction STRING as ' + str(f)) # (1/2)
print('I want to represent the Fraction OBJECT as ', repr(f)) # Fraction (1,2)

G
Georgy

来自 effbot 的 an (An Unofficial) Python Reference Wiki (archive copy)

__str__ "计算对象的“非正式”字符串表示。这与 __repr__ 的不同之处在于它不必是有效的 Python 表达式:可以使用更方便或更简洁的表示。"


__repr__ 绝不需要返回有效的 Python 表达式。
F
Francisco

老实说,从未使用过 eval(repr(obj))。如果你发现自己在使用它,你应该停止,因为 eval 是危险的,而且字符串是一种非常低效的序列化对象的方法(改用 pickle)。

因此,我建议设置 __repr__ = __str__。原因是 str(list) 在元素上调用 repr(我认为这是 Python 3 未解决的 Python 最大的设计缺陷之一)。作为 print([your, objects]) 的输出,实际的 repr 可能不会很有帮助。

为了证明这一点,根据我的经验,repr 函数最有用的用例是将一个字符串放入另一个字符串中(使用字符串格式)。这样,您不必担心转义引号或任何东西。但请注意,这里没有发生 eval


我认为这没有抓住重点。使用 eval(repr(obj)) 是一个健全性测试和经验法则 - 如果它正确地重新创建了原始对象,那么您就有了一个不错的 __repr__ 实现。并不打算以这种方式实际序列化对象。
eval 本身并不危险。不比 unlinkopen 或写入文件更危险。我们是否应该停止写入文件,因为恶意攻击可能会使用任意文件路径将内容放入其中?如果被愚蠢的人愚蠢地使用,一切都是危险的。白痴是危险的。邓宁-克鲁格效应是危险的。 eval 只是一个函数。
T
Taufiq Rahman

str - 从给定对象创建一个新的字符串对象。

repr - 返回对象的规范字符串表示。

区别:

字符串():

使对象可读

为最终用户生成输出

代表():

需要重现对象的代码

为开发人员生成输出


b
bluenote10

其他答案中缺少的一个方面。的确,一般来说,模式是:

__str__ 的目标:人类可读

__repr__ 的目标:明确,可能通过 eval 机器可读

不幸的是,这种区分是有缺陷的,因为 Python REPL 和 IPython 使用 __repr__ 在 REPL 控制台中打印对象(请参阅 PythonIPython 的相关问题)。因此,针对交互式控制台工作的项目(例如,Numpy 或 Pandas)已经开始忽略上述规则,而是提供人类可读的 __repr__ 实现。


G
Georgy

来自《流利的 Python》一书:

Python 对象的一个基本要求是提供其自身的可用字符串表示,一个用于调试和日志记录,另一个用于向最终用户展示。这就是数据模型中存在特殊方法 __repr__ 和 __str__ 的原因。


J
JonnyRobbie

您可以从此代码中获得一些见解:

class Foo():
    def __repr__(self):
        return("repr")
    def __str__(self):
        return("str")

foo = Foo()
foo #repr
print(foo) #str

p
prosti

__str__ 可以通过调用 str(obj) 在对象上调用,并且应该返回人类可读的字符串。

__repr__ 可以通过调用 repr(obj) 在对象上调用,并且应该返回内部对象(对象字段/属性)

这个例子可能会有所帮助:

class C1:pass

class C2:        
    def __str__(self):
        return str(f"{self.__class__.__name__} class str ")

class C3:        
    def __repr__(self):        
         return str(f"{self.__class__.__name__} class repr")

class C4:        
    def __str__(self):
        return str(f"{self.__class__.__name__} class str ")
    def __repr__(self):        
         return str(f"{self.__class__.__name__} class repr")


ci1 = C1()    
ci2 = C2()  
ci3 = C3()  
ci4 = C4()

print(ci1)       #<__main__.C1 object at 0x0000024C44A80C18>
print(str(ci1))  #<__main__.C1 object at 0x0000024C44A80C18>
print(repr(ci1)) #<__main__.C1 object at 0x0000024C44A80C18>
print(ci2)       #C2 class str
print(str(ci2))  #C2 class str
print(repr(ci2)) #<__main__.C2 object at 0x0000024C44AE12E8>
print(ci3)       #C3 class repr
print(str(ci3))  #C3 class repr
print(repr(ci3)) #C3 class repr
print(ci4)       #C4 class str 
print(str(ci4))  #C4 class str 
print(repr(ci4)) #C4 class repr

c
cs95

优秀的答案已经涵盖了 __str____repr__ 之间的区别,对我来说,这归结为前者即使是最终用户也可以阅读,而后者对开发人员来说尽可能有用。鉴于此,我发现 __repr__ 的默认实现通常无法实现此目标,因为它省略了对开发人员有用的信息。

出于这个原因,如果我有一个足够简单的 __str__,我通常只是尝试通过以下方式获得两全其美:

def __repr__(self):
    return '{0} ({1})'.format(object.__repr__(self), str(self))

G
Georgy
>>> print(decimal.Decimal(23) / decimal.Decimal("1.05"))
21.90476190476190476190476190
>>> decimal.Decimal(23) / decimal.Decimal("1.05")
Decimal('21.90476190476190476190476190')

当对 decimal.Decimal(23) / decimal.Decimal("1.05") 的结果调用 print() 时,将打印原始数字;此输出采用 字符串形式,可通过 __str__() 实现。如果我们简单地输入表达式,我们会得到一个 decimal.Decimal 输出——这个输出是 representational form,可以使用 __repr__() 来实现。所有 Python 对象都有两种输出形式。字符串形式被设计成人类可读的。表示形式旨在产生输出,如果将其提供给 Python 解释器将(如果可能)再现表示的对象。


z
zangw

要记住的一件重要事情是容器的 __str__ 使用包含对象的 __repr__。

>>> from datetime import datetime
>>> from decimal import Decimal
>>> print (Decimal('52'), datetime.now())
(Decimal('52'), datetime.datetime(2015, 11, 16, 10, 51, 26, 185000))
>>> str((Decimal('52'), datetime.now()))
"(Decimal('52'), datetime.datetime(2015, 11, 16, 10, 52, 22, 176000))"

Python 更倾向于明确性而不是可读性tuple__str__ 调用调用包含对象的 __repr__,即对象的“正式” 表示。尽管正式表示比非正式表示更难阅读,但它是明确的,并且对错误更健壮。


当它 (__str__ ) 未定义时,它使用 __repr__!所以,你错了。
B
BioGeek

简而言之:

class Demo:
  def __repr__(self):
    return 'repr'
  def __str__(self):
    return 'str'

demo = Demo()
print(demo) # use __str__, output 'str' to stdout

s = str(demo) # __str__ is used, return 'str'
r = repr(demo) # __repr__ is used, return 'repr'

import logging
logger = logging.getLogger(logging.INFO)
logger.info(demo) # use __str__, output 'str' to stdout

from pprint import pprint, pformat
pprint(demo) # use __repr__, output 'repr' to stdout
result = pformat(demo) # use __repr__, result is string which value is 'str'

A
AbstProcDo

直观地理解 __str____repr__ 并永久区分它们。

__str__ 返回给定对象的字符串伪装体,以便眼睛可读
__repr__ 返回给定对象的真实肉体(返回自身),以便明确识别。

在一个例子中看到它

In [30]: str(datetime.datetime.now())
Out[30]: '2017-12-07 15:41:14.002752'
Disguised in string form

至于__repr__

In [32]: datetime.datetime.now()
Out[32]: datetime.datetime(2017, 12, 7, 15, 43, 27, 297769)
Presence in real body which allows to be manipulated directly.

我们可以方便地对 __repr__ 结果进行算术运算。

In [33]: datetime.datetime.now()
Out[33]: datetime.datetime(2017, 12, 7, 15, 47, 9, 741521)
In [34]: datetime.datetime(2017, 12, 7, 15, 47, 9, 741521) - datetime.datetime(2
    ...: 017, 12, 7, 15, 43, 27, 297769)
Out[34]: datetime.timedelta(0, 222, 443752)

如果在 __str__ 上应用该操作

In [35]: '2017-12-07 15:43:14.002752' - '2017-12-07 15:41:14.002752'
TypeError: unsupported operand type(s) for -: 'str' and 'str'

只返回错误。

另一个例子。

In [36]: str('string_body')
Out[36]: 'string_body' # in string form

In [37]: repr('real_body')
Out[37]: "'real_body'" #its real body hide inside

希望这可以帮助您建立具体的基础以探索更多答案。


S
Sampath

__str__ 必须返回字符串对象,而 __repr__ 可以返回任何 python 表达式。如果缺少 __str__ 实现,则使用 __repr__ 函数作为后备。如果缺少 __repr__ 函数实现,则没有后备。如果 __repr__ 函数返回对象的字符串表示形式,我们可以跳过 __str__ 函数的实现。

来源:https://www.journaldev.com/22460/python-str-repr-functions


t
techkuz

__repr__ 在任何地方都可以使用,除了 printstr 方法(定义了 __str__ 时!)


Y
Yilmaz

每个对象都从所有对象创建的基类继承 __repr__

class Person:
     pass

p=Person()

如果您调用 repr(p),您将得到默认值:

 <__main__.Person object at 0x7fb2604f03a0>

但是,如果您调用 str(p),您将获得相同的输出。这是因为当 __str__ 不存在时,Python 调用 __repr__

让我们实现自己的 __str__

class Person:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __repr__(self):
        print("__repr__ called")
        return f"Person(name='{self.name}',age={self.age})"

p=Person("ali",20)

print(p)str(p) 将返回

 __repr__ called
     Person(name='ali',age=20)

让我们添加 __str__()

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    def __repr__(self):
        print('__repr__ called')
        return f"Person(name='{self.name}, age=self.age')"
    
    def __str__(self):
        print('__str__ called')
        return self.name

p=Person("ali",20)

如果我们调用 print(p) 和 str(p),它会调用 __str__() 所以它会返回

__str__ called
ali

repr(p) 将返回

repr 称为“人(姓名='ali,年龄=self.age')”

让我们省略 __repr__,只实现 __str__

class Person:
def __init__(self, name, age):
    self.name = name
    self.age = age

def __str__(self):
    print('__str__ called')
    return self.name

p=Person('ali',20)

print(p) 将查找 __str__ 并返回:

__str__ called
ali

注意=如果我们定义了 __repr____str__f'name is {p}' 将调用 __str__


V
Vlad Bezden

具有 toString 方法的语言经验的程序员倾向于实现 __str__ 而不是 __repr__。如果您只在 Python 中实现这些特殊方法之一,请选择 __repr__。

来自 Fluent Python 本书,作者 Ramalho, Luciano。


b
barii

基本上 __str__str() 用于创建人类可读的输出,必须为最终用户提供。另一方面,repr()__repr__ 主要返回对象的规范字符串表示,用于调试和开发帮助程序员。