亚历克斯总结得很好,但令人惊讶的是,他太简洁了。
首先,让我重申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)
但是你必须做最后一步 - 确保你实现的每个对象都有一个有用的repr,这样这样的代码就可以工作了。这就是为什么会出现“eval”的原因:如果您有足够的信息,那就意味着您知道所有需要了解的信息。如果这足够简单,至少以模糊的方式,那就去做吧。如果没有,请确保您有足够的信息。我通常使用类似 eval 的格式:.这并不意味着您实际上可以构造MyClass,或者这些是正确的构造函数参数 - 但它是表达“这是您需要了解的有关此实例的所有信息”的有用形式。eval(repr(c))==c
c
c
"MyClass(this=%r,that=%r)" % (self.this,self.that)
注意:我在上面用过,不是.你总是想在实现中使用[或格式化字符,等效地],或者你正在击败repr的目标。您希望能够区分 和 。%r
%s
repr()
%r
__repr__
MyClass(3)
MyClass("3")
__str__
的目标是可读
具体来说,它并不是要明确无误 — 请注意。同样,如果您实现IP抽象,则使其str看起来像192.168.1.1就可以了。实现日期/时间抽象时,str 可以是“2010/4/12 15:35:22”等。目标是以用户(而不是程序员)想要阅读的方式表示它。砍掉无用的数字,假装是其他类 - 只要它支持可读性,它就是一种改进。str(3)==str("3")
容器的__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__