协慌网

登录 贡献 社区

__str__和__repr__之间的区别?

Python __str____repr__什么区别?

答案

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

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

  • 默认实现是无用的(很难想到一个不会,但是是的)
  • __repr__目标是明确的
  • __str__目标是可读的
  • Container 的__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” 事情出现的原因:如果你有足够的信息,所以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” 等。目标是以用户而不是程序员想要读取它的方式来表示它。砍掉无用的数字,假装是其他类 - 只要它支持可读性,它就是一种改进。

Container 的__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__将对象的__repr__作为一个字符串, eval可以用它来构建一个相等的对象(它太难了,而且不知道相关模块是如何实际导入的它实际上是不可能的)。

所以,我的建议:专注于使__str__合理地让人类可读,并且__repr__尽可能明确无误,即使这会干扰模糊的无法实现的目标,即使__repr__的返回值可以作为__eval__输入!