Python Every Class Needs a __repr__
一、思考
当我们在Python中定义一个类的时候,如果我们通过print打印这个类的实例化对象,或者我们直接输入这个类实例化对象会返回怎么样的结果,如下代码:
>>> class People(object): ... def __init__(self, name, age): ... self.name = name ... self.age = age ... >>> tom = People("Tom", 23) >>> print(tom) <__main__.People object at 0x00000000027A7160> >>> tom <__main__.People object at 0x00000000027A7160> >>>
默认情况下,你得到的是一个字符串,其中包含类名和对象实例的id(这是CPython中对象的内存地址),其实有更加Pythonic的方式去控制不同情况下将对象进行转换为字符串,也就是控制其显示的结果内容。 我们把上面的代码进行更改的内容如下:
>>> class People(object): ... def __init__(self, name, age): ... self.name = name ... self.age = age ... def __str__(self): ... return f"people name is {self.name}" ... >>> tom = People("Tom", 23) >>> print(tom) people name is Tom >>> tom <__main__.People object at 0x00000000021B7208> >>>
str 是python的内置方法,并且当你在尝试去吧一个对象转换为一个字符串的时候怎么调用这个str方法,如我们进行如下操作时:
>>> class People(object): ... def __init__(self, name, age): ... self.name = name ... self.age = age ... def __str__(self): ... return f"people name is {self.name}" ... >>> tom = People("Tom", 23) >>> print(tom) people name is Tom >>> str(tom) 'people name is Tom' >>> '{}'.format(tom) 'people name is Tom' >>> tom <__main__.People object at 0x00000000021E7208> >>>
二、__str__ vs __repr__
在上面的代码中我们添加了str_方法之后,当我们将对象转换为字符串的时候都会调用str方法,并得到我们自己定义的内容,但是并没有影响到我们在python交互模式下直接输入对象的返回内容。其实
repr 和 str 其实是非常类似的,只不过用的场景不同,将上面的代码进行调整:
>>> class People(object): ... def __init__(self, name, age): ... self.name = name ... self.age = age ... def __str__(self): ... return f"people name is {self.name}" ... def __repr__(self): ... return f"__repr__: people name is {self.name}" ... >>> tom = People("Tom", 23) >>> print(tom) people name is Tom >>> str(tom) 'people name is Tom' >>> '{}'.format(tom) 'people name is Tom' >>> tom __repr__: people name is Tom >>>
其实这里也就验证了,在Python的交互模式下,检查一个对象,其实就是在调用对象的repr方法,还有一个你可能没有发现的地方就是当你在list,dict等容器中存储对象的时候,我们打印看到的都是repr的内容,我们把上面的tom存到列表里, 然后打印查看如下:
>>> print([tom]) [__repr__: people name is Tom] >>>
为了验证我们到底应该怎么用这两个方法,毕竟这两个方法的作用还是非常类似的,我们可以通过Python标准库来验证一下,跟着标准库走总不会有错
>>> import datetime >>> today = datetime.date.today() >>> today datetime.date(2019, 3, 5) >>> str(today) '2019-03-05' >>> repr(today) 'datetime.date(2019, 3, 5)' >>>
从这个Python标准库的用法,我们也能非常好的理解str方法其实就是为了返回一个人们容易理解的字符串类型的结果,而repr方法更偏向于程序员方便去调试,能从结果中看到更加有用的信息,它甚至包括完整的模块和类
三、Why Every Class Needs a repr
我们先看一下我们将我们上面代码的str方法去掉之后的结果:
>>> class People(object): ... def __init__(self, name, age): ... self.name = name ... self.age = age ... # def __str__(self): ... # return f"people name is {self.name}" ... def __repr__(self): ... return f"__repr__: people name is {self.name}" ... >>> tom = People("Tom", 23) >>> print(tom) __repr__: people name is Tom >>> str(tom) '__repr__: people name is Tom' >>> '{}'.format(tom) '__repr__: people name is Tom' >>> tom __repr__: people name is Tom >>>
从这里我们发现当当你把对象进行字符串转换的时候,就会先去找str方法,如果没有则再去找repr方法执行
所以还是建议在自己定义的类中都至少有一个repr方法,这样不管在上面情况下,你都能有一个对你来说有用的字符串返回结果,而不再是一个干巴巴的内存地址,所以在最后我们规范一下我们写一个Python类时的代码:
class People(object): def __init__(self, name, age): self.name = name self.age = age def __str__(self): return f'people name is {self.name}' def __repr__(self): return (f'{self.__class__.__name__}(' f'{self.name!r}, {self.age!r})')
在最后的repr的返回中我们用了!r 这个意味着我们要的repr(self.name) repr(self.age)而不是要str(self.name) str(self.age)的返回结果