题 __str__和__repr__之间的区别?


有什么区别 __str__ 和 __repr__ 在 Python


2112
2017-09-17 04:27


起源




答案:


亚历克斯 总结得很好,但令人惊讶的是,它太简洁了。

首先,我要重申其中的要点 亚历克斯的帖子

  • 默认实现是无用的(很难想到一个不会,但是是的)
  • __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__ 如果你认为让一个字符串版本更容易出错而更容易出现更多歧义,那将会很有用。


2174
2018-04-13 00:56



绝对不同意您的意见,调试不是要走的路。对于开发,使用调试器(和/或日志记录),用于生产使用日志记录。使用调试器,您可以查看问题发生时出错的所有内容。你可以看到完整的图片。除非你记录一切,否则你无法得到它。此外,如果您要记录所有内容,那么您需要通过大量数据来获取所需内容。 - Samuel
很好的答案(除了关于不使用调试器的一点)。我只想添加一个链接 其他问答 海峡 VS 统一 在Python 3中 这可能与进行转换的人的讨论有关。 - ThatAintWorking
", ".join(l)  - 使用使用小写字母“L”作为变量名称的不良做法。让我们不要在权威来源中教授编程新手这样的例子。 - Nikolay Prokopyev
调试器的plus1是无用的,并且不值一毛钱。相反,请提高您的日志吞吐量。是的,这是一篇写得很好的帖子。结果 __repr__ 是调试所需要的。感谢您的帮助。 - personal_cloud


我的经验法则: __repr__ 适合开发者, __str__ 是给客户的。


370
2017-09-17 11:35





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

>>> 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__


319
2017-09-17 04:49



在我的单元测试中,我总是检查一下 eval(repr(foo)) 求值为等于的对象 foo。你是对的,它不会在我的测试用例之外工作,因为我不知道如何导入模块,但这至少可以确保它在 一些 可预测的背景。我认为这是评估结果的好方法 __repr__ 很明确。在单元测试中执行此操作也有助于确保这一点 __repr__ 跟随课程的变化。 - Steven T. Snyder
我总是尽力确保 eval(repr(spam)) == spam (至少在正确的背景下),或 eval(repr(spam)) 提出一个 SyntaxError。这样你就可以避免混淆。 (就是这样 几乎 对于内置函数和大多数stdlib都是如此,除了例如递归列表之外 a=[]; a.append(a); print(eval(repr(a))) 给你 [[Ellipses]]...)当然,我实际上并没有这样做 使用  eval(repr(spam)),除了作为单位测试中的健全检查......但是我 做 有时复制和粘贴 repr(spam) 进入互动环节。 - abarnert
加1解释 __str__ 是以实施的方式实施的 __repr__。似乎是其他帖子遗漏的东西。 - personal_cloud
为什么容器(列表,元组)不会使用 __str__ 对于每个元素而不是 __repr__?对我来说似乎是错误的,因为我实现了可读性 __str__ 在我的对象中,当它是列表的一部分时,我看到了丑陋的东西 __repr__ 代替。 - SuperGeo


__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

143
2017-09-17 04:35





总之,目标 __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();它的   目标是返回可打印的字符串。如果没有给出参数,则返回   空字符串, ''


115
2017-10-25 18:38





有什么区别 __str__ 和 __repr__ 在Python?

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

__repr__ 如果提供备份行为 __str__ 不见了。

所以首先应该写一个 __repr__ 允许您从它返回的字符串重新实例化等效对象,例如运用 eval 或者在Python shell中输入character-for-character。

在以后的任何时候,人们都可以写一个 __str__ 对于用户可读的字符串表示形式,当一个人认为有必要时。

__str__

如果您打印对象,或将其传递给 formatstr.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__ 当你需要一个人类可读的字符串表示。


85
2018-01-25 02:01





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

1) __repr__() 只需在交互式python控制台上编写对象名称并按Enter即可调用。

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

3)万一,如果 __str__ 缺少,然后打印和使用任何功能 str() 所调用 __repr__() 对象。

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

5) str() 内在呼唤 __str__() 没有基本情况可能会递归,并且最大递归深度会出错。

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


12
2017-09-08 03:27





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

因此,我建议设置 __repr__ = __str__。原因是 str(list) 电话 repr 关于元素(我认为这是Python最大的设计缺陷之一,Python 3没有解决)。一个实际的 repr 作为输出可能不会非常有用 print [your, objects]

根据我的经验,这是最有用的用例 repr function是将一个字符串放在另一个字符串中(使用字符串格式化)。这样,您不必担心转义引号或任何内容。但请注意,没有 eval 发生在这里。


11
2017-11-15 10:39



我认为这忽略了这一点。指某东西的用途 eval(repr(obj)) 是一个健全测试和经验法则 - 如果这正确地重新创建原始对象,那么你有一个体面的 __repr__ 实现。这并不是指您以这种方式实际序列化对象。 - jwg
eval 本质上并不危险。并不比危险更危险 unlink, open或写入文件。我们是否应该停止写入文件,因为恶意攻击可能会使用任意文件路径将内容放入其中?如果愚蠢的人愚蠢地使用,一切都是危险的。白痴很危险。 Dunning-Kruger效果很危险。 eval 只是一个功能。 - Luis Masuelli