这个讨论链接,有很精彩的分析:
https://stackoverflow.com/questions/1436703/difference-between-str-and-repr
总结起来,主要有以下四个方面:
- 对象字符串化 。
- 调用方式。
- 请至少实现
__repr__
。 - 使用场景。
1. 对象字符串化
如下自定义对象,当直接打印输出时,默认为内存地址:
class User:
def __init__(self, name, age):
self.name = name
self.age =age
if __name__ == '__main__':
user = User('tom', 5)
print(user)
输出可能是这样的结果:
<__main__.User object at 0x7f60bb9ecbe0>
实现__repr__
或者__str__
,为区分讨论,一个用了(),一个用了[],如下可以实现任意一个:
def __repr__(self):
return 'User(name=%r, age=%d)' % (self.name, self.age)
def __str__(self):
return 'User[name=%r, age=%d]' % (self.name, self.age)
输出:
User(name='tom', age=5)
#或者
User[name='tom', age=5]
2. 调用方式
__repr__
通过 repr 函数调用,或者%r、{!r}格式化调用;__str__
通过 str 函数调用,或者%s、{!s}格式化调用。
上面是通过print(user)
隐式调用对象字符串方法的,解析器会先找str方法,若未实现,再找repr。
如果保留上面两个函数,输出结果为:
User[name='tom', age=5] #默认先找str实现
保留上面两个实现函数,可以强制调用方式(演示交替使用了%和format格式化方式):
if __name__ == '__main__':
user = User('tom', 5)
print(user) # User[name='tom', age=5]
print(str(user)) # User[name='tom', age=5]
print(repr(user)) # User(name='tom', age=5)
print('hello, %s' % user) # hello, User[name='tom', age=5]
print('hello, {!r}'.format(user)) # hello, User(name='tom', age=5)
3. 请至少实现__repr__
上面说了,解析器会先找str实现,再找repr实现,这个顺序是固定的。
如果只注释掉__str__
方法,2.中所有的输出都将是User(name='tom', age=5)
这样的方式;
如果只注释掉__repr__
方法,str
调用得到的是User[name='tom', age=5]
这样的方式,但是repr
调用将得到内存地址。
有一本书叫《Effective Java》,值得每个java程序员翻阅。其中有条建议:总是重载toString方法。
对于Python,这条建议就是:总是重载__repr__
方法。
4. 使用场景
为什么要引入两个对象字符串方法?暂时不得而知,不过社区有一些约定,值得我们考虑:__repr__
针对开发人员;__str__
针对普通用户
比如上面的User对象,假设这个对象在内部开发人员使用,我们可以考虑加上详细的信息,甚至每次调用都记录日志,比如log(repr(user))等,可以重载__repr__
。
如果针对普通用户,比如客户自己查看自己的信息时,直接返回显示客户信息就行了,比如print(user)),这里可以实现__str__
。