有什么区别 __str__
和 __repr__
在 Python
?
有什么区别 __str__
和 __repr__
在 Python
?
亚历克斯 总结得很好,但令人惊讶的是,它太简洁了。
首先,我要重申其中的要点 亚历克斯的帖子:
__repr__
目标是明确的__str__
目标是可读的__str__
使用包含的对象' __repr__
默认实现没用
这主要是一个惊喜,因为Python的默认值往往非常有用。但是,在这种情况下,具有默认值 __repr__
这将像:
return "%s(%r)" % (self.__class__, self.__dict__)
本来就太危险了(例如,如果对象相互引用,太容易进入无限递归)。所以Python警察出来了。请注意,有一个默认值为true:if __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”事情出现的原因:如果你有足够的信息 eval(repr(c))==c
,这意味着你知道有关的一切 c
。如果这很容易,至少以模糊的方式,做到这一点。如果没有,请确保您有足够的信息 c
无论如何。我通常使用类似eval的格式: "MyClass(this=%r,that=%r)" % (self.this,self.that)
。这并不意味着你可以实际构造MyClass,或者那些是正确的构造函数参数 - 但它是一种有用的形式来表达“这是你需要了解的关于这个实例的一切”。
注意:我用过 %r
以上,不是 %s
。你总想用 repr()
[要么 %r
格式化字符,等效地在里面 __repr__
实施,或者你正在击败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__
这似乎令人惊讶,不是吗?它有点,但可读性如何
[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__
目标是可读的__str__
使用包含的对象' __repr__
默认实现没用
这主要是一个惊喜,因为Python的默认值往往非常有用。但是,在这种情况下,具有默认值 __repr__
这将像:
return "%s(%r)" % (self.__class__, self.__dict__)
本来就太危险了(例如,如果对象相互引用,太容易进入无限递归)。所以Python警察出来了。请注意,有一个默认值为true:if __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”事情出现的原因:如果你有足够的信息 eval(repr(c))==c
,这意味着你知道有关的一切 c
。如果这很容易,至少以模糊的方式,做到这一点。如果没有,请确保您有足够的信息 c
无论如何。我通常使用类似eval的格式: "MyClass(this=%r,that=%r)" % (self.this,self.that)
。这并不意味着你可以实际构造MyClass,或者那些是正确的构造函数参数 - 但它是一种有用的形式来表达“这是你需要了解的关于这个实例的一切”。
注意:我用过 %r
以上,不是 %s
。你总想用 repr()
[要么 %r
格式化字符,等效地在里面 __repr__
实施,或者你正在击败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__
这似乎令人惊讶,不是吗?它有点,但可读性如何
[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__
不是 __str__
,对于它包含的项目。而且,尽管在典型的文档中找到了关于该主题的文字,但几乎没有人会烦恼 __repr__
对象是一个字符串 eval
可能会用来构建一个相同的对象(它太难了,而且不知道相关模块是如何实际导入的,这使得它实际上变得不可能)。
所以,我的建议是:专注于制作 __str__
合理的人类可读,和 __repr__
尽可能明确无误,即使这会干扰模糊不可实现的目标 __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:
repr(object)
返回包含对象的可打印表示的字符串。这与转换产生的值相同(反向 引号)。能够以此方式访问此操作有时很有用 一个普通的功能。对于许多类型,此功能尝试 返回一个字符串,该字符串将产生具有相同值的对象 传递给
eval()
,否则表示是一个包含在其中的字符串 包含对象类型名称的尖括号 以及通常包括名称和名称的其他信息 对象的地址。类可以控制此函数返回的内容 通过定义一个__repr__()
方法。
这是str的文档:
str(object='')
返回包含可打印的字符串 对象的表示。对于字符串,这将返回字符串 本身。与...的区别
repr(object)
就是它str(object)
才不是 总是尝试返回一个可接受的字符串eval()
;它的 目标是返回可打印的字符串。如果没有给出参数,则返回 空字符串,''
。
有什么区别
__str__
和__repr__
在Python?
__str__
(读作“dunder(双下划线)字符串”)和 __repr__
(读作“dunder-repper”(用于“表示”))都是基于对象状态返回字符串的特殊方法。
__repr__
如果提供备份行为 __str__
不见了。
所以首先应该写一个 __repr__
允许您从它返回的字符串重新实例化等效对象,例如运用 eval
或者在Python shell中输入character-for-character。
在以后的任何时候,人们都可以写一个 __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
在shell中,我们将看到重新创建等效日期时间对象所需的一切。这是由日期时间创建的 __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 = ", ".join(map(str, L))
s = "%s(%s)" % ('datetime.' + self.__class__.__name__, s)
if self._tzinfo is not None:
assert s[-1:] == ")"
s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
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__
和a __repr__
,为开发人员在调试中使用而编写,应该在编写之前编写 __str__
。
你需要一个 __str__
仅当您需要对象的文本表示时。
确定 __repr__
对于您编写的对象,您和其他开发人员在开发时使用它时可以获得可重现的示例。确定 __str__
当你需要一个人类可读的字符串表示。
除了给出的所有答案外,我想补充几点:
1) __repr__()
只需在交互式python控制台上编写对象名称并按Enter即可调用。
2) __str__()
使用带有print语句的对象时调用。
3)万一,如果 __str__
缺少,然后打印和使用任何功能 str()
所调用 __repr__()
对象。
4) __str__()
容器,当被调用时将执行 __repr__()
其所含元素的方法。
5) str()
内在呼唤 __str__()
没有基本情况可能会递归,并且最大递归深度会出错。
6) __repr__()
可以打电话 repr()
这将尝试自动避免无限递归,用已替换已经表示的对象 ...
。
实话说, eval(repr(obj))
从未使用过。如果你发现自己使用它,你应该停下来,因为 eval
很危险,字符串是一种非常低效的序列化对象的方法(使用 pickle
代替)。
因此,我建议设置 __repr__ = __str__
。原因是 str(list)
电话 repr
关于元素(我认为这是Python最大的设计缺陷之一,Python 3没有解决)。一个实际的 repr
作为输出可能不会非常有用 print [your, objects]
。
根据我的经验,这是最有用的用例 repr
function是将一个字符串放在另一个字符串中(使用字符串格式化)。这样,您不必担心转义引号或任何内容。但请注意,没有 eval
发生在这里。