亚历克斯总结得很好,但令人惊讶的是,它太简洁了。
首先,让我重申亚历克斯(Alex)帖子中的要点:
__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)
但是,您必须执行最后一步 - 确保实现的每个对象都有一个有用的代表,以便这样的代码可以正常工作。这就是为什么出现 “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(l) + "]"
(您可能还可以弄清楚如何处理字典。
摘要
为您实现的任何类实现__repr__
。这应该是第二天性。如果您认为有一个字符串版本会影响可读性会很有用,请实施__str__
。
我的经验法则: __repr__
适用于开发人员, __str__
适用于客户。
除非您特别采取行动以确保其他情况,否则大多数类在以下两个方面均不会产生有用的结果:
>>> 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__(object): return 'foo'
...
>>> print str(Sic())
foo
>>> print repr(Sic())
foo
>>> class Sic(object):
... def __str__(object): return 'foo'
...
>>> print str(Sic())
foo
>>> print repr(Sic())
<__main__.Sic object at 0x2617f0>
>>>
如您所见,如果您覆盖__repr__
,那也将用于__str__
,反之亦然。
要知道的其他关键__str__
:内置容器上的__repr__
使用__repr__
而不是__str__
来包含其中的项目。而且,尽管在典型文档中找到了有关该主题的字眼,但几乎没有__repr__
使对象的__repr__
成为eval
可以用来构建相等对象的字符串而__repr__
(这太难了,并且不知道相关模块的实际导入方式使得实际上完全不可能)。
因此,我的建议是:着重于使__str__
合理地为人类所__str__
,并__repr__
使__str__
,即使这干扰了使__repr__
的返回值可以接受作为__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
简而言之,
__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(object)
返回包含对象的可打印表示形式的字符串。这与转化产生的值相同(反引号)。能够以常规功能访问此操作有时很有用。对于许多类型,此函数会尝试返回一个字符串,该字符串在传递给
eval()
时将产生具有相同值的对象,否则表示形式是一个用尖括号括起来的字符串,其中包含对象的类型名称以及通常包括对象名称和地址的其他信息。一个类可以通过定义__repr__()
方法来控制此函数为其实例返回的__repr__()
。
这是 str 的文档:
str(object='')
返回一个字符串,其中包含对象的可很好打印的表示形式。对于字符串,这将返回字符串本身。与
repr(object)
的区别在于str(object)
并不总是尝试返回eval()
可接受的字符串;它的目标是返回可打印的字符串。如果未提供任何参数,则返回空字符串''
。
Python 中
__str__
和__repr__
什么区别?
__str__
(读作 “dunder(双下划线)字符串”)和__repr__
(读作 “dunder-repper”(对于 “表示形式”))都是根据对象状态返回字符串的特殊方法。
如果缺少__str__
则__repr__
提供备份行为。
因此,应该首先编写__repr__
,它允许您从它返回的字符串中重新实例化一个等效的对象,例如使用eval
或通过在 Python shell 中按字符输入字符。
在以后的任何时候,只要有人认为有必要,就可以为该实例的用户可读字符串表示形式编写__str__
。
__str__
如果打印对象,或将其传递给format
, str.format
或str
,则如果定义了__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 源代码 )类似:
def __repr__(self):
return '<{0}.{1} object at {2}>'.format(
self.__module__, type(self).__name__, hex(id(self)))
这意味着默认情况下,您将打印对象所属的模块,类名以及其在内存中位置的十六进制表示形式,例如:
<__main__.Foo object at 0x7f80665abdd0>
这些信息不是很有用,但是无法得出如何准确地创建任何给定实例的规范表示的方法,它总比没有好,至少告诉我们如何在内存中唯一标识它。
__repr__
怎么有用? 让我们看看使用 Python shell 和datetime
对象有多么有用。首先,我们需要导入datetime
模块:
import datetime
如果在外壳程序中调用datetime.now
,我们将看到重新创建等效的 datetime 对象所需的一切。这是由 datetime __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 源 )的方式。由于复制此类对象所需的所有属性,它相当复杂:
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__
表示形式,则可以下一步实现__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__
时才需要__str__
。
为您编写的对象定义__repr__
,以便您和其他开发人员在开发过程中使用它时都可以重现一个示例。当您需要__str__
的人类可读的字符串表示__str__
时,请定义它。
在 Hans Petter Langtangen 撰写的《用于计算科学的Python 脚本 》一书的第 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'
除了给出的所有答案外,我想补充几点:
1)当您在交互式 python 控制台上简单地写对象名称并按 Enter 时,将调用__repr__()
。
2)当您将对象与 print 语句一起使用时,将调用__str__()
。
3)如果缺少__str__
,则打印并使用str()
任何函数调用对象的__repr__()
。
4)容器的__str__()
,在调用时将对其包含的元素执行__repr__()
方法。
5)在没有基本情况的情况下,在__str__()
str()
调用的str()
可能会递归,并且最大递归深度会出错。
6) __repr__()
repr()
__repr__()
可以调用repr()
,它将尝试自动避免无限递归,用...
替换已经表示的对象。
eval(repr(obj))
从未使用过eval(repr(obj))
。如果发现自己正在使用它,则应该停止操作,因为eval
很危险,而字符串是序列化对象的一种非常低效的方法(请改用pickle
)。
因此,我建议设置__repr__ = __str__
。原因是str(list)
对元素调用repr
(我认为这是 Python 3 未能解决的最大设计缺陷之一)。实际的repr
可能不会作为print [your, objects]
的输出很有用。
为了证明这一点,以我的经验, repr
函数最有用的用例是将一个字符串放入另一个字符串中(使用字符串格式)。这样,您不必担心转义引号或其他内容。但是请注意,这里没有eval
发生。
来自 effbot 的(非官方)Python 参考 Wiki(归档副本) :
__str__
” 计算对象的 “非正式” 字符串表示形式。与__repr__
不同之处在于,它不必是有效的 Python 表达式:可以使用更方便或更简洁的表示形式。