Alex Martelli总结得很好, 但令人惊讶的是,他太简洁了。
首先,让我重申Alex帖子中的主要观点:
__repr__
的目标是无歧义的__str__
的目标是可读性强__str__
使用包含对象的__repr__
默认实现是无用的
这主要是因为Python的默认值通常相当有用。然而,在这种情况下,如果__repr__
的默认值像这样运行:
return "%s(%r)" % (self.__class__, self.__dict__)
这样做可能会太危险了(例如,如果对象相互引用,则很容易进入无限递归)。因此,Python选择了一种妥协方式。请注意,有一个默认值为True:如果定义了__repr__
,而没有定义__str__
,则对象将表现得好像__str__=__repr__
。
简单来说,这意味着:几乎每个你实现的对象都应该有一个可用于理解该对象的功能性__repr__
。实现__str__
是可选的:如果需要“漂亮的打印”功能(例如,由报告生成器使用),可以这样做。
__repr__
的目标是不含歧义的
让我直截了当地说吧——我不相信调试器。我真的不知道如何使用任何调试器,也从未认真使用过。此外,我认为调试器的主要问题在于它们的基本性质——我调试的大多数故障都发生在很久很久以前,在一个遥远的星系中。这意味着,我确实像宗教狂热者一样相信日志记录。日志记录是任何体面的“fire-and-forget”服务器系统的命脉。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”会出现的原因:如果您有足够的信息以便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(lst) + "]")
(你可能也能理解如何处理字典)。
概述
对于您实现的任何类,请实现__repr__
。这应该是第二天性的事情。如果您认为具有以可读性为导向的字符串版本会很有用,则实现__str__
。
__repr__
面向开发者,__str__
面向客户。__str__
来使普通开发人员能够读取对象。另一方面,__repr__
是为SDK开发人员自己准备的。 - Shiplu Mokaddimd = datetime.date.today()
str: 2023-05-20
repr: datetime.date(2023, 5, 20)
。 - wjandrea除非你特别采取行动来确保不同,否则大多数类都没有有用的结果:
>>> 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__
的返回值可以作为eval
的输入的模糊不可达目标!
eval(repr(foo))
是否会被评估为等于foo
的对象。你说得对,在我的测试案例之外它可能不起作用,因为我不知道该模块是如何被引入的,但至少这可以确保它在某些可预测的上下文中运行正常。我认为这是一种评估__repr__
结果是否足够明确的好方法。在单元测试中执行此操作还有助于确保__repr__
遵循类的更改。 - Steven T. Snydereval(repr(spam)) == spam
要么成立,要么 eval(repr(spam))
抛出 SyntaxError
以避免混淆。(这个规则对于内置函数和大多数标准库都 几乎 适用,除了例如递归列表,在这种情况下 a=[]; a.append(a); print(eval(repr(a)))
将会返回 [[Ellipses]]
…)当然,我并不打算真正地使用 eval(repr(spam))
,除非是在单元测试时检查代码是否正常……但有时我确实会将 repr(spam)
复制粘贴到交互式会话中。 - abarnert__str__
来代替 __repr__
来处理每个元素?这对我来说似乎是错误的,因为我在我的对象中实现了可读的 __str__
,但当它成为列表的一部分时,我看到的是更丑陋的 __repr__
。 - SuperGeorepr
,因为[1, 2, 3]
!= ["1", "2, 3"]
。 - mtraceurclass Spam
,eval(repr(spam)) == spam
也需要实现Spam.__eq__
,是吗?默认情况下,object.__eq__
使用is
([文档](https://docs.python.org/3/reference/datamodel.html#object.__eq__))。 - djvg简而言之,
__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()
;其目标是返回一个可打印的字符串。如果没有给出参数,则返回空字符串''
。
__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
__str__
(读作“dunder(双下划线)string”)和__repr__
(读作“dunder-repper”(表示“representation”))都是返回基于对象状态的字符串的特殊方法。__str__
不存在,__repr__
提供备用行为。__repr__
,它允许您从返回的字符串重新实例化等效对象,例如使用eval
或在Python shell中逐字符输入。__str__
,以提供用户可读的字符串表示形式。
__str__
format
、str.format
或str
,那么如果定义了__str__
方法,将调用该方法,否则将使用__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>
import datetime
datetime.now
,我们将看到我们需要重新创建一个等效的datetime对象的所有内容。这是由datetime __repr__
创建的。>>> datetime.datetime.now()
datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)
>>> 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
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__
方法。下面是datetime对象(Python源代码)如何实现__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__
这样的东西吗? - Solomon Uckoself.__module__
更改为 type(self).__module__
(因为例如 3
没有 __module__
),并将 type(self).__name__
更改为 type(self).__qualname__
(因为例如对于 class A: class B: pass
,repr(A.B())
返回的就是这个)。 - Géry Ogam在Hans Petter Langtangen的书Python scripting for computational science第358页上,明确指出:
__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'
repr
称为“reproduce”有点误导人,更好的理解应该是它表示了一个对象,而不是复制了一个对象。 - NelsonGoneval
的用法。 - undefined简而言之:
长
当涉及到细枝末节的问题时,这个问题类似于询问str()
和repr()
内置函数之间的区别。我将用自己的话来描述这些区别(这意味着我可能会大量借鉴《核心Python编程》的内容,请原谅我)。
str()和repr()
的基本工作是相同的:它们的目标是返回Python对象的字符串表示。不同之处在于它们返回的字符串表示的类型。
str()
和__str__()
返回一个可打印的字符串表示,即人类可读/供人类消费的字符串。repr()
和__repr__()
返回一个字符串表示,它是一个有效的Python表达式,即一个可以传递给eval()
或在Python shell中输入而不会出错的对象。x
赋一个字符串,给变量 y
赋一个整数,并简单地显示它们的可读字符串版本。>>> x, y = 'foo', 123
>>> str(x), str(y)
('foo', '123')
>>> 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
eval()
。现在,让我们对这两个表达式进行repr()
,看看我们得到什么。更具体地说,将其输出转储到解释器中(我们稍后会解释这一点的原因):>>> repr(x), repr(y)
("'foo'", '123')
>>> 123
123
>>> 'foo'
'foo'
>>> eval('123')
123
>>> eval("'foo'")
'foo'
123
和'foo'
都是有效的Python对象。另一个关键点是,有时候它们返回相同的东西(相同的字符串表示),但并非总是如此。(是的,是的,我可以创建一个变量foo
,使eval()
有效,但这不是重点。)
关于这两对的更多事实
有时候,str()
和repr()
会被隐式地调用,这意味着它们会代表用户被调用:当用户使用print
时,即使他们没有显式地调用str()
,在对象显示之前,也会代表他们进行这样的调用。>>>
提示符下输入一个变量并按下RETURN键,解释器会隐式地调用repr()
来显示该对象的结果。str()
和repr()
与__str__()
和__repr__()
连接起来,需要意识到对内置函数的调用,即str(x)
或repr(y)
会导致调用它们对象的相应特殊方法:x.__str__()
或y.__repr__()
通过为你的Python类实现__str__()
和__repr__()
,你可以重载内置函数(str()
和repr()
),允许你的类的实例被传递给str()
和repr()
。当进行这样的调用时,它们会转而调用类的__str__()
和__repr__()
(参见#3)。repr()
和__repr__()
返回一个对象的字符串表示,该表示是一个有效的Python表达式" — 并非总是如此。是的,这是一个目标,但有些对象无法有意义地表示,比如object
,例如:object()
→ <object object at 0x7f4aa8b38f50>
。我知道其他答案已经涵盖了这一点,你可能也意识到了,但我觉得奇怪的是你在这个长篇解释中没有提到它。 - undefined简单来说:
__str__
用于展示对象的字符串表示,以便其他人可以轻松阅读。
__repr__
用于展示对象的字符串表示。
假设我想创建一个 Fraction
类,其中分数的字符串表示为“(1/2)”,而对象(Fraction 类)的表示形式为“Fraction(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)
__repr__
,只需添加!r
即可:您可以将"MyClass(this=%r,that=%r)" % (self.this,self.that)
替换为f"MyClass(this={self.this!r},that={self.that!r})"
。否则,感谢这篇优秀的文章! - joanis