#Python中的 __str__
和__repr__
之间的区别
原问题地址:http://stackoverflow.com/questions/1436703/difference-between-str-and-repr-in-python
##问题:
Python中的__str__
和__repr__
有什么区别呢?
##回答:
Alex总结得很好,但是简洁得出乎意料。
首先,让我重申Alex的主要观点:
- 默认实现是没有用的(很难想象一个有用的默认实现,但事实的确如此)
__repr__
目标是准确性__str__
目标是可读性- 容器的
__str__
使用容器所包含对象的__repr__
###默认实现是没有用的
多半是惊喜,因为Python的默认值往往是相当有用的。然而,在这种情况下,有一个像这样的针对__repr__
的默认实现
return "%s(%r)" % (self.__class__, self.__dict__)
会有很大的危险(例如,如果对象之间相互引用,太容易陷入无限递归)。所以Python避开了这一点。注意,有一个默认实现:如果__repr__
被定义,并且__str__
没有被定义,对象的工作原理就好像__str__=__repr__
。
简单来说,这意味着:几乎你所实现的每一个对象都应该有一个__repr__
函数来用于理解这个对象。实现__str__
是可选的:如果你需要 “优质打印”功能(例如,用于报表)【译者注:“优质打印”,意思是要得到非常友好的输出效果】,你就要实现__str__
。
坦率地说,我不相信调试器。我真的不知道如何使用,而且从来没有认真地使用过任何调试器。此外,我认为调试器的一个大缺点在于它的本质——很久以前,我在进行调试过程中发现了太多的问题距真正的错误差距很大。这就意味着,我对日志要有着宗教般的热情。日志记录是任何一个像样的容灾备份服务器系统的生命线。用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地址抽象,有个类似于192.168.1.1的str就好。当实现一个日期/时间抽象时,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__
导致了歧义,但较少有错误,并有更好的可读性,则可以使用__str__
。