Python中的__str__
和__repr__
有什么区别?
Alex总结得很好,但令人惊讶的是,它太简洁了。
首先,让我重申Alex’s post的要点:
__repr__
的目标是明确无误的__str__
的目标是可读的__str__
使用包含对象'__repr__
默认实现是无用的
这主要是一个惊喜,因为Python的默认值往往非常有用。但是,在这种情况下,具有__repr__
的默认值,其行为如下:
return "%s(%r)" % (self.__class__, self.__dict__)
本来就太危险了(例如,如果对象相互引用,太容易进入无限递归)。所以Python警察出来了。请注意,有一个默认值为true:如果定义了__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()
实现中使用%r
[或__repr__
格式化字符,或者你正在击败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__
这似乎令人惊讶,不是吗?它有点,但可读性如何
[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__
用于显示您的对象的字符串表示形式,以供其他人轻松阅读。
__repr__
用于显示对象的字符串表示。
假设我想创建一个Fraction
类,其中分数的字符串表示为'(1/2)',对象(分数类)将表示为'分数(1,2)'
所以我们可以创建一个简单的Fraction类:
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)
来自qbxswpoi的effbot:
an (An Unofficial) Python Reference Wiki (archive copy)“计算”对象的非正式“字符串表示。这与__str__
的不同之处在于它不必是有效的Python表达式:可以使用更方便或简洁的表示。”
__repr__
- 从给定对象创建一个新的字符串对象。
str
- 返回对象的规范字符串表示形式。
差异:
STR()
再版():
优秀的答案已经涵盖了repr
和__str__
之间的区别,对我来说,归结为前者甚至可以被最终用户读取,而后者对开发人员尽可能有用。鉴于此,我发现__repr__
的默认实现通常无法实现此目标,因为它省略了对开发人员有用的信息。
出于这个原因,如果我有一个简单的__repr__
,我通常只是试图通过以下方式获得两个世界中最好的:
__str__
从Fluent Python一书:
Python对象的基本要求是提供自身的可用字符串表示,一个用于调试和记录,另一个用于向最终用户呈现。这就是为什么 特殊方法
def __repr__(self): return '{0} ({1})'.format(object.__repr__(self), str(self))
和__repr__
存在于数据模型中。
要记住的一件重要事情是容器的
__str__
使用包含对象的__str__
。
__repr__
Python倾向于明确可读性,>>> 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))"
调用__str__
称为包含对象的tuple
,即对象的“形式”表示。虽然正式表示比非正式表达更难阅读,但它对于错误是明确的和更强大的。
简而言之:
__repr__
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'
当>>> print(decimal.Decimal(23) / decimal.Decimal("1.05"))
21.90476190476190476190476190
>>> decimal.Decimal(23) / decimal.Decimal("1.05")
Decimal('21.90476190476190476190476190')
被调用print()
的结果时,原始数字被打印出来;这个输出是字符串形式,可以用decimal.Decimal(23) / decimal.Decimal("1.05")
实现。如果我们只是输入表达式,我们得到一个__str__()
输出 - 这个输出是代表性的形式,可以用decimal.Decimal
实现。所有Python对象都有两种输出形式。字符串形式设计为人类可读的。表示形式旨在产生输出,如果输入到Python解释器将(如果可能)再现所表示的对象。
理解__repr__()
和__str__
,直观地和永久地区分它们。
__repr__
返回给定对象的字符串伪装体,以便可读取眼睛
__str__
返回给定对象的真实肉体(返回自身)以确定无歧义。
在一个例子中看到它
__repr__
至于In [30]: str(datetime.datetime.now())
Out[30]: '2017-12-07 15:41:14.002752'
Disguised in string form
__repr__
我们可以方便地对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__
如果在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__
只返回错误。
另一个例子。
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'
希望这有助于您建立具体理由来探索更多答案。
我的经验法则:__repr__
适用于开发人员,__str__
适用于客户。
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
在任何地方都使用,除了__repr__
和print
方法(当str
is定义!)
__str__
必须返回字符串对象,而__str__
可以返回任何python表达式。__repr__
实现,则使用__str__
函数作为后备。如果缺少__repr__
函数实现,则没有回退。__repr__
函数返回对象的String表示,我们可以跳过__repr__
函数的实现。资料来源:__str__
可以通过调用https://www.journaldev.com/22460/python-str-repr-functions在对象上调用__str__
,并返回一个人类可读的字符串。
可以通过调用str(obj)
在对象上调用__repr__
并返回内部对象(对象字段/属性)
此示例可能有所帮助:
repr(obj)
除非您特别采取行动以确保其他方面,否则大多数课程都没有任何有用的结果:
>>> 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__(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__
:你认为文本形式的对象是什么
EG
>>> 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
简而言之,
__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:
repr(object)
返回包含对象的可打印表示的字符串。这与转换(反向引号)产生的值相同。能够以普通函数的形式访问此操作有时很有用。对于许多类型,此函数尝试返回一个字符串,该字符串在传递给
eval()
时会产生具有相同值的对象,否则表示形式是一个用尖括号括起来的字符串,其中包含对象类型的名称以及其他信息通常包括对象的名称和地址。类可以通过定义__repr__()
方法来控制此函数为其实例返回的内容。
这是str的文档:
str(object='')
返回一个包含对象的可打印表示的字符串。对于字符串,这将返回字符串本身。与
repr(object)
的区别在于str(object)
并不总是试图返回eval()
可接受的字符串;它的目标是返回一个可打印的字符串。如果没有给出参数,则返回空字符串''
。
What is the difference between
__str__
and__repr__
in Python?
__str__
(读作“dunder(双下划线)字符串”)和__repr__
(读作“dunder-repper”(用于“表示”))都是基于对象状态返回字符串的特殊方法。
如果缺少__repr__
,__str__
会提供备份行为。
因此,首先应该编写一个__repr__
,它允许您从它返回的字符串中重新实例化等效对象,例如使用eval
或在Python shell中输入character-for-character。
在以后的任何时候,当人们认为有必要时,可以为实例的用户可读字符串表示写一个__str__
。
__str__
如果您打印一个对象,或将其传递给format
,str.format
或str
,那么如果定义了__str__
方法,则将调用该方法,否则将使用__repr__
。
__repr__
__repr__
方法由内置函数repr
调用,并且当它评估返回对象的表达式时,在python shell上回显。
既然它为__str__
提供了备份,如果你只能写一个,那就从__repr__
开始
这是repr
的内置帮助:
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 source)类似于:
def __repr__(self):
return '<{0}.{1} object at {2}>'.format(
self.__module__, type(self).__name__, hex(id(self)))
这意味着默认情况下,您将打印对象所在的模块,类名以及其在内存中的位置的十六进制表示形式 - 例如:
<__main__.Foo object at 0x7f80665abdd0>
这些信息不是很有用,但是没有办法得出如何准确地创建任何给定实例的规范表示,并且它总比没有好,至少告诉我们如何在内存中唯一地识别它。
__repr__
be useful?让我们看看它有多么有用,使用Python shell和datetime
对象。首先,我们需要导入datetime
模块:
import datetime
如果我们在shell中调用datetime.now
,我们将看到重新创建等效的datetime对象所需的一切。这是由日期时间__repr__
创建的:
>>> datetime.datetime.now()
datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)
如果我们打印一个日期时间对象,我们会看到一个很好的人类可读(实际上是ISO)格式。这是由datetime的__str__
实现的:
>>> print(datetime.datetime.now())
2015-01-24 20:05:44.977951
重新创建我们丢失的对象是一件简单的事情,因为我们没有通过从__repr__
输出复制和粘贴,然后打印它来将它分配给变量,我们将它与其他对象在同一个人类可读输出中:
>>> the_past = datetime.datetime(2015, 1, 24, 20, 5, 36, 491180)
>>> print(the_past)
2015-01-24 20:05:36.491180
在开发过程中,如果可能的话,您将希望能够以相同的状态再现对象。例如,这是datetime对象如何定义__repr__
(Python source)。它相当复杂,因为重现这样一个对象所需的所有属性:
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 = "%s.%s(%s)" % (self.__class__.__module__,
self.__class__.__qualname__,
", ".join(map(str, L)))
if self._tzinfo is not None:
assert s[-1:] == ")"
s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
if self._fold:
assert s[-1:] == ")"
s = s[:-1] + ", fold=1)"
return s
如果您希望对象具有更易读的表示形式,则可以接下来实现__str__
。以下是datetime对象(Python source)如何实现__str__
,它很容易做到,因为它已经有一个以ISO格式显示它的函数:
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__()
。
2)当您使用带有print语句的对象时,将调用__str__()
。
3)如果__str__
缺失,则打印和使用str()
的任何函数调用对象的__repr__()
。
4)容器的__str__()
,当被调用时将执行其包含元素的__repr__()
方法。
5)在str()
中调用的__str__()
可能会在没有基本情况的情况下递归,并且在最大递归深度上出错。
6)__repr__()
可以调用repr()
,它将尝试自动避免无限递归,用...
替换已经表示的对象。
在Hans Petter Langtangen的“Python脚本编程”一书的第358页上,它清楚地表明了这一点
__repr__
旨在对象的完整字符串表示;__str__
将返回一个很好的字符串进行打印。所以,我更喜欢将它们理解为
从用户的角度来看虽然这是我在学习python时所犯的误解。
一个小但很好的例子也在同一页面上给出如下:
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'
老实说,eval(repr(obj))
从未使用过。如果你发现自己使用它,你应该停下来,因为eval
是危险的,并且字符串是一种非常低效的序列化对象的方法(使用pickle
代替)。
因此,我建议设置__repr__ = __str__
。原因是str(list)
在元素上调用repr
(我认为这是Python 3中没有解决的Python最大的设计缺陷之一)。作为repr
的输出,实际的print [your, objects]
可能不会非常有用。
根据我的经验,repr
函数最有用的用例是将一个字符串放在另一个字符串中(使用字符串格式化)。这样,您不必担心转义引号或任何内容。但请注意,这里没有发生eval
。