Python中__str__和__repr_之间有什么区别?


当前回答

具有toString方法语言经验的程序员倾向于实现__str__而不是__repr_。如果只在Python中实现这些特殊方法之一,请选择__repr_。

来自Luciano Ramalho的《流利的Python》一书。

其他回答

来自effbot的(非官方)Python参考Wiki(存档副本):

__str__“计算对象的“非正式”字符串表示。这与__repr_不同,因为它不必是有效的Python表达式:可以使用更方便或更简洁的表示。”

我的经验法则是:__repr_面向开发者,__str__面向客户。

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

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

默认的实现是无用的(很难想到一个不会是,但是的)__代表的目标是明确无误__str__目标是可读容器的__str__使用包含对象的__repr__

默认实现是无用的

这主要是一个惊喜,因为Python的默认值往往非常有用。然而,在这种情况下,__repr_的默认值如下:

return "%s(%r)" % (self.__class__, self.__dict__)

如果对象相互引用,就太危险了(例如,太容易陷入无限递归)。所以Python就退出了。请注意,有一个默认值是真的:如果__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”等。目标是以用户(而不是程序员)想要阅读的方式表示它。去掉无用的数字,假装是其他类-只要它支持可读性,这就是一种改进。

容器的__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_在任何地方都使用,但print和str方法除外(当定义了__str__时!)

__str__可以通过调用str(obj)在对象上调用,并且应该返回一个人类可读的字符串。

__repr_可以通过调用repr(obj)在对象上调用,并且应该返回内部对象(对象字段/属性)

此示例可能有助于:

class C1:pass

class C2:        
    def __str__(self):
        return str(f"{self.__class__.__name__} class str ")

class C3:        
    def __repr__(self):        
         return str(f"{self.__class__.__name__} class repr")

class C4:        
    def __str__(self):
        return str(f"{self.__class__.__name__} class str ")
    def __repr__(self):        
         return str(f"{self.__class__.__name__} class repr")


ci1 = C1()    
ci2 = C2()  
ci3 = C3()  
ci4 = C4()

print(ci1)       #<__main__.C1 object at 0x0000024C44A80C18>
print(str(ci1))  #<__main__.C1 object at 0x0000024C44A80C18>
print(repr(ci1)) #<__main__.C1 object at 0x0000024C44A80C18>
print(ci2)       #C2 class str
print(str(ci2))  #C2 class str
print(repr(ci2)) #<__main__.C2 object at 0x0000024C44AE12E8>
print(ci3)       #C3 class repr
print(str(ci3))  #C3 class repr
print(repr(ci3)) #C3 class repr
print(ci4)       #C4 class str 
print(str(ci4))  #C4 class str 
print(repr(ci4)) #C4 class repr