浅谈python中__str__和__repr__的区别

时间:2023-12-23 23:20:19

很多时候我们在创建一个类的时候,在终端打印类或者查看的时候一般都不会得到一个太满意的结果

class T:
  def __init__(self):
self.color="red"
self.count = 2
t = T()
t
>>> <T object at 0x0000000003444E10>
print(t)
>>> <T object at 0x0000000003444E10>

类转化为字符串,直接打印结果一般都不是我们想要的东西,仅仅包含了类的名称以及实例的 ID (理解为 Python 对象的内存地址即可),例如<T object at 0x0000000003444E10> ,这样类默认转化为字符串根本不能得到我们想要的关于属性信息等的相关东西,只能通过手动打印才能进一步获取到更多的信息,例如print(t.color)

使用 __str__ 实现类到字符串的转化   

不用自己另外的定义方法,和java的toString的方法类似,可以在类里面实现__str__和__repr__方法,从而自已定义类的字符串描述,下面具体看看这两种方式是怎么工作的。

首先,我们先加一个 __str__ 方法到前面的类中看看情况。

class T:
def __str__(self):
return self.color+"__str__"
def __init__(self):
self.color="red"
self.count = 2
t = T()
t
>>> <T object at 0x0000000003444E10>
print(t)
>>> red__str__

查看 t的时候的输出仍然和之前一样,输出内存地址信息,不过打印 t的时候返回的内容和新加的 __str__ 方法的返回一致。类的 __str__ 方法会在某些 需要将对象转为字符串的时候被调用。

比如下面这些情况,需要将对象转为字符串

print(t)
print(str(t))
print("{}".format(t))

使用 __repr__ 也有类似的效果 

会发现,在使用__str__方法的时候,直接在终端查看t对象的时候,输出的时候仍然是内存地址,这是因为在python3中有两种方式控制对象转字符串,第一种就是__str__,第二种就是__repr__,两种的工作方式大体相同,只是调用的时机不同。下面通过一个简单的例子看看__repr__方法

class T:
def __repr__(self):
return self.color+"__repr__"
def __str__(self):
return self.color+"__str__"
def __init__(self):
self.color="red"
self.count = 2
t = T()
t
>>> red__repr__
print(t)
>>> red__str__
print(str(t))
>>> red__str__
print("{}".format(t))
>>> red__str__
print(str(["d","f",t]))
>>> ['d', 'f', red__repr__]

通过如上的例子可以看出,当直接查看对象的时候调用的是__repr__方法,对象需要转字符串的时候调用的是__str__方法,但是当字典列表等容器的时候调用的还是__repr__方法

如果我们需要显示的指定以何种方式进行类到字符串的转化,我们可以使用内置的 str() 或 repr() 方法,它们会调用类中对应的双下划线方法。除了字典、列表等作为容器的时候print(str(["d","f",t])),虽然调用的是str()方法,可是容器是列表,所以对象t转为字符串时调用的就是__repr__

__str__ 和 __repr__ 的差别 

那么他们的区别具体的在哪里,带着这个问题我们python的标准库, 其中datetime.date 这个类是怎么在使用这两个方法的。

import datetime
today = datetime.datetime.today()
print(str(today))
>>> 2019-10-20 20:59:47.003003
print(repr(today))
>>> datetime.datetime(2019, 10, 20, 20, 59, 47, 3003)

__str__ 的返回结果可读性强。也就是说,__str__ 的意义是得到便于人们阅读的信息,就像上面的 '2019-10-20 20:59:47.003003' 一样。

__repr__ 的返回结果应更准确。怎么说,__repr__ 存在的目的在于调试,便于开发者使用。将 __repr__ 返回的方式直接复制到命令行上,是可以直接执行的。

 为什么每个类都最好有一个 __repr__ 方法

如果你想保证每个类到字符串都有一个有效的自定义转换方式,你需要重写__str__或者__repr__中的一个方法,如果没有写__str__方法,python就会去找__repr__方法,所以至少添加一个

class T:
def __repr__(self):
return (self.color)
def __init__(self,color):
self.color = color
t = T("red")
print(t)
>>> red
t
>>> red
print(str(t))
>>> red

实现了 __repr__ 方法后,当我们查看类的实例或者直接调用 repr() 方法,就能得到一个比较满意的结果了。打印或直接调用str()等类转字符串的操作都会得到相同的结果,因为__str__的默认实现就是调用__repr__方法。

这样就能用比较少的工作量,不用手动的去一一获取信息,就能让两个方法都能功能,而且都具有一定的可读性,所以推荐类中至少添加一个__repr__ 方法。
小结
  • 我们可以使用__str__ 和 __repr__中的任意一个方法定义类到字符串的转化方式,不需要手动的打印某些属性和一些额外的信息
  • 一般来说__str__的可读性更强,而__repr__的返回结果更具有准确性,更加的适合开发者
  • 我们在写类的时候,最好至少添加一个__repr__方法,来保证类到字符串的转换具有自定义的有效性,__str__是可选的,因为在默认情况下,__Str__方法默认实现调用的是__repr__方法,所以在对象转字符串时候,找到底层str方法之后,会调用重写的__repr__方法

也就是说,

  直接查看字符串的时候调用的是__repr__方法;

  对象在转字符串的时候:如果容器是字典或者列表调用的是__repr__方法;如果是其他情况则默认调用的是底层的__Str__方法。__str__如果被重写调用的则是重写之后__Str__,就和__repr__没有关系了,如果没有被重写,则调用的是底层__str__方法,底层方法默认实现方式是调用了__repr__ 方法,之后查看__repr__是否被重写,重写则调用重写之后的,否则调用底层的__repr__,最后执行转为字符串

  如果打印str(t)则默认调用的是__str__,如果直接打印repr(t)则默认调用的是__repr__方法