亚历克斯总结得很好,但令人惊讶的是,它太简洁了。
首先,让我重申亚历克斯的帖子中的要点:
__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)
但是你必须做最后一步 - 确保你实现的每个对象都有一个有用的 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__
输入!