一尘不染

__str__ 和 __repr__ 有什么区别?

python

__str__Python和Python有什么区别__repr__


阅读 183

收藏
2022-01-18

共2个答案

一尘不染

除非您特别采取行动以确保其他情况,否则大多数课程对以下任何一项都没有有用的结果:

>>> 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__

2022-01-18
一尘不染

  • 默认实现是无用的(很难想到一个不会,但是是的)
  • __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(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__

这似乎令人惊讶,不是吗?有点,但是如果它使用它们的可读性如何__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__如果您认为有一个在可读性方面出错的字符串版本会很有用,请实施。

2022-01-18