Alex个总结得很好,但令人惊讶的是,过于简洁.
首先,让我重申Alex’s post条中的要点:
__repr__
的目标是毫不含糊__str__
的目标是可读性__str__
个使用包含对象的__repr__
个Default implementation is useless
这在很大程度上是令人惊讶的,因为Python的缺省值往往相当有用.但是,在本例中,如果默认值为__repr__
,则如下所示:
return "%s(%r)" % (self.__class__, self.__dict__)
太危险了(例如,如果对象相互引用,就很容易陷入无限递归).所以巨 Python 逃走了.请注意,有一个默认值为true:如果定义了__repr__
,而没有定义__str__
,则对象的行为将与__str__=__repr__
类似.
这意味着,简单地说:几乎你实现的每个对象都应该有一个功能__repr__
,可以用来理解对象.实现__str__
是可选的:如果需要"漂亮的打印"功能(例如,由报表生成器使用),可以这样做.
The goal of 100 is to be unambiguous
让我直截了当地说吧-我不相信调试器.我真的不知道如何使用任何调试器,而且从来没有认真使用过.此外,我认为调试器的最大缺点是它们的基本性质--我调试的大多数故障都发生在很久很久以前,在遥远的银河系.这意味着我确实怀着宗教的热情相信伐木.日志(log)记录是任何像样的"即火即忘"服务器系统的命脉.Python使日志(log)记录变得很容易:可能有一些特定于项目的包装器,您所需要的只是一个
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(repr(c))==c
,这意味着你知道关于c
的一切.如果这足够简单,至少是以一种模糊的方式,那就go 做吧.如果没有,无论如何要确保你有足够的关于c
的信息.我通常使用类似计算的格式:"MyClass(this=%r,that=%r)" % (self.this,self.that)
.这并不意味着您可以实际构造MyClass,或者这些都是正确的构造函数参数-但它是一种有用的形式来表达"这就是您需要了解的关于此实例的一切".
注意:我用的是上面的%r
,不是%s
.您总是希望在__repr__
实现中使用repr()
[或%r
个格式化字符,相当于],否则您就会与repr的目标背道而驰.您希望能够区分MyClass(3)
和MyClass("3")
.
The goal of 100 is to be readable
具体来说,它并不打算明确无误——请注意,str(3)==str("3")
.同样,如果你实现一个IP抽象,让它的str看起来像192.168.1.1就可以了.在实现日期/时间抽象时,str可以是"2010/4/12 15:35:22",等等.目标是以用户而不是程序员希望阅读的方式表示它.切掉无用的数字,假装是其他类——只要它支持可读性,这就是一种改进.
Container’s 100 uses contained objects’ 101
这似乎令人惊讶,不是吗?虽然有一点,但如果使用他们的__str__
,它的可读性会如何呢?
[moshe is, 3, hello
world, this is a list, oh I don't know, containing just 4 elements]
不是很好.具体地说,容器中的字符串会发现太容易干扰其字符串表示.请记住,面对模棱两可的情况,Python抵制住了猜测的诱惑.如果在打印列表时需要上述行为,只需
print("[" + ", ".join(l) + "]")
(你可能还可以想出如何处理字典.
Summary
对于你实现的任何类,实现__repr__
.这应该是第二天性.如果您认为字符串版本有助于提高可读性,请实现__str__
.