python:__str__和__repr__有什么不同?

__str____repr__Python中的区别是什么?


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

首先,让我重申一下Alex文章的主要观点:

默认实现是无用的(很难想象有哪个实现不是无用的,但是是的)__repr__目标是明确的__str__目标是可读性强容器的__str__使用包含对象的__repr__

默认实现是无用的

这主要是一个意外,因为Python的默认值往往非常有用。但是,在这种情况下,为__repr__设置一个默认值,其作用如下:

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

这样做太危险了(例如,如果对象相互引用,就太容易陷入无限递归)。所以Python退出了。注意,有一个默认值是true:如果定义了__repr__,而没有定义__str__,那么对象的行为就像__str__=__repr__一样。

简单地说,这意味着:几乎您实现的每个对象都应该有一个函数__repr__,可用来理解对象。实现__str__是可选的:如果需要"漂亮的打印"功能(例如,由报表生成器使用),可以这样做。

__repr__的目标是明确的

让我直接说出来——我不相信调试器。我真的不知道如何使用调试器,而且从来没有认真地使用过。此外,我认为调试器的最大错误是它们的基本特性——我调试的大多数故障都发生在很久很久以前,在一个遥远的星系中。这意味着,我以宗教的热情相信伐木业。日志记录是任何一个体面的服务器系统的命脉。Python使日志记录变得很容易:使用一些特定于项目的包装器,您只需要一个

1
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:如果您有足够的信息so 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__

这似乎令人惊讶,不是吗?有一点,但是可读性如何

1
2
[moshe is, 3, hello
world, this is a list, oh I don't know, containing just 4 elements]

是什么?不是很。具体地说,容器中的字符串会发现很容易干扰它的字符串表示。记住,面对模糊性,Python会抵制猜测的诱惑。如果您在打印列表时需要上述行为,只需

1
print"[" +",".join(l) +"]"

(你也许还能想出字典的用处。

总结

为您实现的任何类实现__repr__。这应该是第二天性。实现__str__如果您认为在可读性方面有一个错误的字符串版本是有用的。


我的经验法则是:__repr__适用于开发人员,__str__适用于客户。


除非你特别采取行动,以确保否则,大多数类没有任何有益的结果:

1
2
3
4
5
6
7
>>> class Sic(object): pass
...
>>> print str(Sic())
<__main__.Sic object at 0x8b7d0>
>>> print repr(Sic())
<__main__.Sic object at 0x8b7d0>
>>>

如您所见——没有区别,除了类和对象的id之外没有其他信息。如果你只覆盖其中一个…:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> class Sic(object):
...   def __repr__(object): return 'foo'
...
>>> print str(Sic())
foo
>>> print repr(Sic())
foo
>>> class Sic(object):
...   def __str__(object): 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__的输入可接受这一模糊的不可实现的目标相冲突!


__repr__: python对象的表示形式通常eval会将其转换回该对象

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

如。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> 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


In short, the goal of __repr__ is to be unambiguous and __str__ is to be
readable.

这里有一个很好的例子:

1
2
3
4
5
6
>>> 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(object)

Return a string containing a printable representation of an object. This is the same value yielded by conversions (reverse
quotes). It is sometimes useful to be able to access this operation as
an ordinary function. For many types, this function makes an attempt
to return a string that would yield an object with the same value when
passed to eval(), otherwise the representation is a string enclosed in
angle brackets that contains the name of the type of the object
together with additional information often including the name and
address of the object. A class can control what this function returns
for its instances by defining a __repr__() method.

以下是str的文档:

str(object='')

Return a string containing a nicely printable
representation of an object. For strings, this returns the string
itself. The difference with repr(object) is that str(object) does not
always attempt to return a string that is acceptable to eval(); its
goal is to return a printable string. If no argument is given, returns
the empty string, ''.


What is the difference between __str__ and __repr__ in Python?

__str__(读作"dunder(双下划线)string")和__repr__(读作"dunder-repper"(表示"表示"))都是基于对象状态返回字符串的特殊方法。

如果缺少__str__,则提供备份行为。

因此,首先应该编写一个__repr__,允许您从它返回的字符串中重新插入一个等价的对象,例如使用eval或在Python shell中按字符对字符地输入它。

在以后的任何时候,如果认为有必要,可以为用户可读的实例字符串表示形式编写__str__

__str__

如果您打印一个对象,或者将其传递给formatstr.formatstr,那么如果定义了一个__str__方法,那么将调用该方法,否则将使用__repr__

__repr__

__repr__方法由内置函数repr调用,当它对返回对象的表达式求值时,在python shell中回显该方法。

因为它为__str__提供了一个备份,如果您只能编写一个备份,那么从__repr__开始

这是关于repr的内置帮助:

1
2
3
4
5
repr(...)
    repr(object) -> string

    Return the canonical string representation of the object.
    For most object types, eval(repr(object)) == object.

也就是说,对于大多数对象,如果您输入由repr打印的内容,您应该能够创建一个等效的对象。但这不是默认实现。

默认实现__repr__

默认对象__repr__是(C Python源代码)类似于:

1
2
3
def __repr__(self):
    return '<{0}.{1} object at {2}>'.format(
      self.__module__, type(self).__name__, hex(id(self)))

这意味着在默认情况下,您将打印对象所在的模块、类名以及它在内存中的位置的十六进制表示——例如:

1
<__main__.Foo object at 0x7f80665abdd0>

这些信息不是很有用,但是没有办法精确地创建任何给定实例的规范表示,这总比什么都没有好,至少告诉我们如何在内存中惟一地标识它。

__repr__如何有用?

让我们看看使用Python shell和datetime对象,它有多么有用。首先我们需要导入datetime模块:

1
import datetime

如果在shell中调用datetime.now,我们将看到重新创建一个等价的datetime对象所需的所有内容。这是由datetime __repr__创建的:

1
2
>>> datetime.datetime.now()
datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)

如果我们打印一个datetime对象,我们会看到一个很好的人类可读(实际上是ISO)格式。这是由datetime的__str__实现的:

1
2
>>> print(datetime.datetime.now())
2015-01-24 20:05:44.977951

重新创建我们丢失的对象很简单,因为我们没有通过复制和粘贴__repr__输出将其赋值给变量,然后将其打印出来,我们得到了与另一个对象相同的人类可读输出:

1
2
3
>>> the_past = datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)
>>> print(the_past)
2015-01-24 20:05:36.491180

我如何实现它们?

在开发过程中,如果可能的话,您希望能够以相同的状态复制对象。例如,datetime对象就是这样定义__repr__ (Python源代码)的。它相当复杂,因为复制这样一个对象需要所有的属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def __repr__(self):
   """Convert to formal string, for repr()."""
    L = [self._year, self._month, self._day, # These are never zero
         self._hour, self._minute, self._second, self._microsecond]
    if L[-1] == 0:
        del L[-1]
    if L[-1] == 0:
        del L[-1]
    s =",".join(map(str, L))
    s ="%s(%s)" % ('datetime.' + self.__class__.__name__, s)
    if self._tzinfo is not None:
        assert s[-1:] ==")"
        s = s[:-1] +", tzinfo=%r" % self._tzinfo +")"
    return s

如果希望对象具有更人性化的可读表示,可以接下来实现__str__。下面是datetime对象(Python源代码)是如何实现__str__的,它很容易实现,因为它已经有了一个函数来以ISO格式显示它:

1
2
3
def __str__(self):
   "Convert to string, for str()."
    return self.isoformat(sep=' ')

__repr__ = __str__ ?

这是对另一个建议设置__repr__ = __str__的答案的批评。

设置__repr__ = __str__是愚蠢的——__repr____str__的一个后备方法,而__repr__是为开发人员在调试中使用而编写的,应该在编写__str__之前编写。

只有在需要对象的文本表示时才需要__str__

结论

为您编写的对象定义__repr__,以便您和其他开发人员在开发时使用它时有一个可重复的示例。定义__str__当您需要它的可读字符串表示形式时。


除了所有的答案,我想补充几点:-

1)当您在交互式python控制台简单地编写对象的名称并按enter键时,将调用__repr__()

当您使用object with print语句时调用__str__()

3)如果缺少__str__,则打印,任何使用str()的函数都会调用对象的__repr__()

4) __str__()的容器,调用时将执行其所包含元素的__repr__()方法。

5) str()内调用的__str__()可以在没有基本情况下进行递归,并且在最大递归深度上存在错误。

6) __repr__()可以调用repr(),这将试图自动避免无限递归,用...替换一个已经表示的对象。


在Hans Petter Langtangen所著的《计算科学用Python脚本编写》一书的358页,它清楚地说明了这一点

__repr__的目标是对象的完整字符串表示;__str__将返回一个良好的字符串用于打印。

所以,我更愿意把它们理解为

repr =复制str =字符串(表示形式)

从用户的角度来看虽然这是我在学习python时产生的一个误解。

在同一页上还有一个虽小但很好的例子:

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
In [38]: str('s')
Out[38]: 's'

In [39]: repr('s')
Out[39]:"'s'"

In [40]: eval(str('s'))
Traceback (most recent call last):

  File"<ipython-input-40-abd46c0c43e7>", line 1, in <module>
    eval(str('s'))

  File"<string>", line 1, in <module>

NameError: name 's' is not defined


In [41]: eval(repr('s'))
Out[41]: 's'


简单地说:

__str__用于显示对象的字符串表示形式,以便其他人易于读取。

__repr__用于显示对象的字符串表示形式。

假设我想创建一个Fraction类,其中分数的字符串表示为'(1/2)',对象(分数类)表示为' fraction (1,2)'

所以我们可以创建一个简单的分数类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Fraction:
    def __init__(self, num, den):
        self.__num = num
        self.__den = den

    def __str__(self):
        return '(' + str(self.__num) + '/' + str(self.__den) + ')'

    def __repr__(self):
        return 'Fraction (' + str(self.__num) + ',' + str(self.__den) + ')'



f = Fraction(1,2)
print('I want to represent the Fraction STRING as ' + str(f)) # (1/2)
print('I want to represent the Fraction OBJECT as ', repr(f)) # Fraction (1,2)


老实说,eval(repr(obj))从未被使用过。如果您发现自己在使用它,您应该停止,因为eval是危险的,字符串是序列化对象的一种非常低效的方法(使用pickle代替)。

因此,我建议设置__repr__ = __str__。原因是str(list)对元素调用repr(我认为这是Python最大的设计缺陷之一,Python 3没有解决这个问题)。

根据我的经验,要限定这一点,repr函数最有用的用例是将一个字符串放在另一个字符串中(使用字符串格式)。这样,您就不必担心转义引号之类的问题。但是请注意这里没有发生eval


来自effbot的http://pyref.infogami.com/_str__:

计算对象的"非正式"字符串表示形式。这与__repr__的不同之处在于,它不必是一个有效的Python表达式:可以使用更方便或更简洁的表示。"


其他答案中缺少的一个方面。这是真的,一般的模式是:

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

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


str -从给定对象创建一个新的字符串对象。

repr -返回对象的规范字符串表示形式。

的区别:

str ():

使对象可读为最终用户生成输出

repr ():

需要生成对象的代码为开发人员生成输出


优秀的答案已经涵盖了__str____repr__之间的差异,对我来说,这可以归结为前者即使对最终用户也是可读的,而后者对开发人员尽可能有用。考虑到这一点,我发现__repr__的默认实现常常不能实现这个目标,因为它忽略了对开发人员有用的信息。

由于这个原因,如果我有一个足够简单的__str__,我通常只需要像这样:

1
2
def __repr__(self):
    return '{0} ({1})'.format(object.__repr__(self), str(self))


1
2
3
4
"A basic requirement for a Python object is to provide usable
 string   representations of itself, one used for debugging and
 logging, another for presentation to end users. That is why the  
 special methods __repr__ and __str__ exist in the data model."

书中:Fluent Python


1
2
3
4
>>> print(decimal.Decimal(23) / decimal.Decimal("1.05"))
21.90476190476190476190476190
>>> decimal.Decimal(23) / decimal.Decimal("1.05")
Decimal('21.90476190476190476190476190')

当对decimal.Decimal(23) / deci- mal.Decimal("1.05")的结果调用print()时,将打印原始数字;这个输出是字符串形式的,可以用_str __()实现。如果我们简单地输入表达式,就会得到一个小数。十进制输出—这个输出是用……repr __()实现的表示形式。所有Python对象都有两种输出形式。字符串形式被设计成人类可读的。具象形式的设计目的是生成输出,如果将输出提供给Python解释器,则会(在可能的情况下)重新生成所表示的对象


简而言之:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Demo:
  def __repr__(self):
    return 'repr'
  def __str__(self):
    return 'str'

demo = Demo()
print(demo) # use __str__, output 'str' to stdout

s = str(demo) # __str__ is used, return 'str'
r = repr(demo) # __repr__ is used, return 'repr'

import logging
logger = logging.getLogger(logging.INFO)
logger.info(demo) # use __str__, output 'str' to stdout

from pprint import pprint, pformat
pprint(demo) # use __repr__, output 'repr' to stdout
result = pformat(demo) # use __repr__, result is string which value is 'str'

所以更清晰从博客

str类似于toString。创建,以便您可以打印数据repr类似于序列化或pickle。如果我需要使用eval()来重新创建这个对象,我该如何做呢?

1
2
3
4
5
6
7
>>> import datetime
>>> now = datetime.datetime.now()
>>> str(now)
'2015-04-04 20:51:31.766862'
>>> repr(now)
'datetime.datetime(2015, 4, 4, 20, 51, 31, 766862)'
>>mydate = eval(repr(now))

One important thing to keep in mind is that container's __str__ uses contained objects' __repr__.

1
2
3
4
5
6
>>> from datetime import datetime
>>> from decimal import Decimal
>>> print (Decimal('52'), datetime.now())
(Decimal('52'), datetime.datetime(2015, 11, 16, 10, 51, 26, 185000))
>>> str((Decimal('52'), datetime.now()))
"(Decimal('52'), datetime.datetime(2015, 11, 16, 10, 52, 22, 176000))"

Python倾向于不歧义而不是可读性,tuple__str__调用所包含对象的__repr__,即对象的"正式"表示。虽然正式的表示形式比非正式的表示形式更难阅读,但是它是明确的,并且对bug更加健壮。


直观地理解__str____repr__并永久地区分它们。

__str__返回一个给定对象的字符串伪装体,以供眼睛阅读__repr__返回给定对象的真实肉体(返回本身),以便明确标识。

看一个例子

1
2
3
In [30]: str(datetime.datetime.now())
Out[30]: '2017-12-07 15:41:14.002752'
Disguised in string form

至于__repr__

1
2
3
In [32]: datetime.datetime.now()
Out[32]: datetime.datetime(2017, 12, 7, 15, 43, 27, 297769)
Presence in real body which allows to be manipulated directly.

我们可以方便地对__repr__结果进行算术运算。

1
2
3
4
5
In [33]: datetime.datetime.now()
Out[33]: datetime.datetime(2017, 12, 7, 15, 47, 9, 741521)
In [34]: datetime.datetime(2017, 12, 7, 15, 47, 9, 741521) - datetime.datetime(2
    ...: 017, 12, 7, 15, 43, 27, 297769)
Out[34]: datetime.timedelta(0, 222, 443752)

如果在__str__上应用该操作

1
2
In [35]: '2017-12-07 15:43:14.002752' - '2017-12-07 15:41:14.002752'
TypeError: unsupported operand type(s) for -: 'str' and 'str'

只返回错误。

另一个例子。

1
2
3
4
5
In [36]: str('string_body')
Out[36]: 'string_body' # in string form

In [37]: repr('real_body')
Out[37]:"'real_body'" #its real body hide inside

希望这能帮助你建立具体的基础来探索更多的答案。


除了printstr在定义__str__时使用__repr__外,其他地方都使用__repr__