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


当前回答

除非您特别采取行动以确保其他情况,否则大多数类对以下两种情况都没有帮助:

>>> 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__(self): return 'foo'
... 
>>> print(str(Sic()))
foo
>>> print(repr(Sic()))
foo
>>> class Sic(object):
...   def __str__(self): return 'foo'
... 
>>> print(str(Sic()))
foo
>>> print(repr(Sic()))
<__main__.Sic object at 0x2617f0>
>>> 

如您所见,如果重写__repr_,那么它也用于__str__,但反之亦然。

要知道的其他关键提示:内置容器上的__str__使用__repr_而不是__str__来表示它包含的项。而且,尽管在典型的文档中找到了关于这个主题的单词,但几乎没有人会将__repr_对象设置为eval可以用来构建相等对象的字符串(这太难了,而且不知道相关模块是如何实际导入的,这实际上是不可能的)。

因此,我的建议是:专注于使__str__合理地具有可读性,并尽可能地明确__repr___,即使这会干扰模糊的不可实现的目标,即使__repr_的返回值可接受作为__eval___的输入!

其他回答

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

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

默认的实现是无用的(很难想到一个不会是,但是的)__代表的目标是明确无误__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_:python对象的表示通常eval会将其转换回该对象

__str__:是你认为的文本形式的对象

e.g.

>>> s="""w'o"w"""
>>> repr(s)
'\'w\\\'o"w\''
>>> str(s)
'w\'o"w'
>>> eval(str(s))==s
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    w'o"w
       ^
SyntaxError: EOL while scanning single-quoted string
>>> eval(repr(s))==s
True

其他答案中缺少的一个方面。的确,一般来说,模式是:

__str__的目标:人类可读__repr_的目标:明确,可能通过eval机器可读

不幸的是,这种区别是有缺陷的,因为Python REPL和IPython都使用__repr_在REPL控制台中打印对象(请参见Python和IPython的相关问题)。因此,以交互控制台工作为目标的项目(例如,Numpy或Pandas)已经开始忽略上述规则,转而提供一个人类可读的__repr_实现。

每个对象都从所有对象创建的基类继承__repr_。

class Person:
     pass

p=Person()

如果你调用repr(p),你会得到默认值:

 <__main__.Person object at 0x7fb2604f03a0>

但如果调用str(p),则会得到相同的输出。这是因为当__str__不存在时,Python调用__repr__

让我们实现自己的__str__

class Person:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __repr__(self):
        print("__repr__ called")
        return f"Person(name='{self.name}',age={self.age})"

p=Person("ali",20)

print(p)和str(p)将返回

 __repr__ called
     Person(name='ali',age=20)

让我们添加__str__()

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    def __repr__(self):
        print('__repr__ called')
        return f"Person(name='{self.name}, age=self.age')"
    
    def __str__(self):
        print('__str__ called')
        return self.name

p=Person("ali",20)

如果我们调用print(p)和str(p),它将调用__str__(),因此它将返回

__str__ called
ali

repr(p)将返回

repr调用“人(name=‘ali,age=self.age’)”

让我们省略__repr_,只实现__str__。

class Person:
def __init__(self, name, age):
    self.name = name
    self.age = age

def __str__(self):
    print('__str__ called')
    return self.name

p=Person('ali',20)

print(p)将查找__str__并返回:

__str__ called
ali

注意:如果我们定义了__repr_和__str__,则f'name为{p}'将调用__str__

简而言之,__repr_的目标是明确,__str__是可读。

这里有一个很好的例子:

>>> import datetime
>>> today = datetime.datetime.now()
>>> str(today)
'2012-03-14 09:21:58.130922'
>>> repr(today)
'datetime.datetime(2012, 3, 14, 9, 21, 58, 130922)'

阅读本文档以了解代表:

repr(对象)返回包含对象的可打印表示形式的字符串。这与转换产生的值相同(相反引号)。有时,作为一个普通的函数。对于许多类型,此函数会尝试返回一个字符串,当传递给eval(),否则表示形式是一个字符串包含对象类型名称的尖括号以及通常包括姓名和对象的地址。类可以控制此函数返回的内容为其实例定义__repr_()方法。

以下是str的文档:

str(对象=“”)返回一个字符串,该字符串包含可打印的对象的表示。对于字符串,这将返回字符串它本身与repr(object)的区别在于str(object)没有始终尝试返回eval()可接受的字符串;它的目标是返回一个可打印的字符串。如果未给定参数,则返回空字符串“”。