Python中__str__和__repr_之间有什么区别?
当前回答
str-从给定对象创建一个新的字符串对象。
repr-返回对象的规范字符串表示形式。
差异:
str():
使对象可读为最终用户生成输出
repr():
需要复制对象的代码为开发人员生成输出
其他回答
其他答案中缺少的一个方面。的确,一般来说,模式是:
__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__
需要记住的一点是,容器的__str__使用包含的对象的__repr_。
>>> 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比可读性更倾向于明确性,元组的__str__调用调用所包含对象的__repr_,即对象的“形式”表示。虽然正式表示比非正式表示更难理解,但它是明确的,并且对bug更为健壮。
具有toString方法语言经验的程序员倾向于实现__str__而不是__repr_。如果只在Python中实现这些特殊方法之一,请选择__repr_。
来自Luciano Ramalho的《流利的Python》一书。
亚历克斯总结得很好,但令人惊讶的是,过于简洁。
首先,让我重申亚历克斯帖子中的要点:
默认的实现是无用的(很难想到一个不会是,但是的)__代表的目标是明确无误__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__。
推荐文章
- 证书验证失败:无法获得本地颁发者证书
- 当使用pip3安装包时,“Python中的ssl模块不可用”
- 无法切换Python与pyenv
- Python if not == vs if !=
- 如何从scikit-learn决策树中提取决策规则?
- 为什么在Mac OS X v10.9 (Mavericks)的终端中apt-get功能不起作用?
- 将旋转的xtick标签与各自的xtick对齐
- 为什么元组可以包含可变项?
- 如何合并字典的字典?
- 如何创建类属性?
- 不区分大小写的“in”
- 在Python中获取迭代器中的元素个数
- 解析日期字符串并更改格式
- 使用try和。Python中的if
- 如何在Python中获得所有直接子目录