文章目录
当我们在Python中自定义一个类后尝试打印这个类的实例对象至控制台,以期获得关于这个对象的详细信息时,通常的结果都是不尽如人意的,如下面的例子:
In [1]: class Car:
...: def __init__(self, color, mileage):
...: self.color = color
...: self.mileage = mileage
...:
In [2]: my_car = Car('red', 4000)
In [3]: my_car
Out[3]: <__main__.Car at 0x7f80241b2748>
In [4]: print(my_car)
<__main__.Car object at 0x7f80241b2748>
由上述代码运行结果可知:在Python中,实例对象的字符串表示形式默认情况下仅包含类名、实例对象的id(对象在CPython解释器中的内存地址)。
针对上述问题,你大可以向在Java中一样,在类中自定义一个to_string()方法,但Python设计得很周到,其中的repr
、__repr__
、str
、__str__
、eval
这五个方法主要就是为了让用户可以自定义对象的字符串表现形式。
一、__str__()
方法和str()类
Python官方文档关于__str__()
方法的说明为:
- Called by
str(object)
and the built-in functionsformat()
andprint()
to compute the “informal” or nicely printable string representation of an object.
在执行str(object)
以及调用內置函数format()
、print()
时该魔法方法被调用以获取对象可读性较高的字符串表示形式。- The return value must be a string object.
该魔法方法的返回值必须是一个字符串对象。
下面还是以上面的Car
类为例,在其中增加一个__str__
方法:
class Car:
def __init__(self, color, mileage):
self.color = color
self.mileage = mileage
def __str__(self):
return f"a {self.color} car described by __str__()"
def main():
my_car = Car('red', 4000)
print("str(my_car) = ", str(my_car))
print("my_car.__str__() = ", my_car.__str__())
print(""""{}".format(my_car) = """, "{}".format(my_car))
print("my_car = ", my_car)
if __name__ == '__main__':
main()
上述代码的运行结果为:
str(my_car) = a red car described by __str__()
my_car.__str__() = a red car described by __str__()
“{}”.format(my_car) = a red car described by __str__()
my_car = a red car described by __str__()
由上述代码运行结果可知:
- 直接调用
__str__()
方法将返回方法中的字符串; - 调用內置类
str(object)
时,会默认调用__str__
方法,并返回后者的返回值; - 当调用
format()
方法或直接打印对象时,也会默认调用__str__()
方法。
二、__repr__()
方法和repr()函数
Python官方文档关于__repr__()
方法的说明为:
- Called by the
repr(object)
built-in function to compute the “official” string representation of an object.
当调用內置函数repr(object)
时,该魔法方法被调用以返回对象的字符串表示形式。- If at all possible, this should look like a valid Python expression that could be used to recreate an object with the same value (given an appropriate environment).
如果可能的话,该返回的字符串表示形式应该看起来像一个有效的Python表达式,该表达式可用于重建一个对象。- The return value must be a string object. If a class defines
__repr__()
but not__str__()
, then__repr__()
is also used when an “informal” string representation of instances of that class is required.
该方法的返回值必须是一个字符串对象。如果一个类定义了__repr__()
方法而未定义__str__()
方法,那么在用到__str__()
方法的地方,将默认执行__repr__()
方法。
关于Python官方文档中的上述描述,验证如下:
class Car:
def __init__(self, color, mileage):
self.color = color
self.mileage = mileage
def __repr__(self):
return f"a {self.color} car described by __repr__()"
def main():
my_car = Car("red", 4000)
print("repr(my_car) = ", repr(my_car))
print("my_car.__repr__() = ", my_car.__repr__())
print(""""{}".format(my_car) = """, "{}".format(my_car))
print("my_car = ", my_car)
if __name__ == '__main__':
main()
上述程序的运行结果为:
repr(my_car) = a red car described by __repr__()
my_car.__repr__() = a red car described by __repr__()
“{}”.format(my_car) = a red car described by __repr__()
my_car = a red car described by __repr__()
通常,在实际的代码中,推荐使用str(object)
以及repr(object)
而不是采用object.__str__()
或者object.__repr__()
来获取对象的字符串表示形式,因为前者更加直观。
三、__str__
和__repr__
或str和repr
上述讨论虽然介绍了__str__
、__repr__
以及对应的str
、repr
,但是并未看出二者的明显差异。下面通过一个例子来说二者的差异:
import datetime
def main():
today = datetime.date.today()
print("str(today) = ", str(today))
print("repr(today) = ", repr(today))
print(eval(repr(today)))
if __name__ == '__main__':
main()
上述代码的运行结果为:
str(today) = 2020-05-26
repr(today) = datetime.date(2020, 5, 26)
2020-05-26
由上述运行结果可知:
- 通过
str(object)
或object.__str__()
得到的对象字符串表示形式是直观且有高可读性特点的; - 通过
repr(object)
或object.__repr__()
得到的对象字符串表示形式是无歧义的,且可以根据结果重建该实例对象(这也是为什么前面援引Python官方文档关于repr()
的说明时提到“如果可能的话,该返回的字符串表示形式应该看起来像一个有效的Python表达式,该表达式可用于重建一个对象”),如:直接运行datetime.date(2020, 5, 26)
将可以得到today
对象。
__repr__
()和__str__
()之间还有一个细微的差异在于:像列表、字典等容器总是使用__repr__
()的结果来表示其内部包含的对象,即使你显式调用str(object)
也是如此,如下列代码所示:
class Car:
def __init__(self, color, mileage):
self.color = color
self.mileage = mileage
def __repr__(self):
return f"a {self.color} car described by __repr__()"
def __str__(self):
return f"a {self.color} car described by __str__()"
def main():
my_car = Car("red", 4000)
print("str([my_car]) = ", str([my_car]))
if __name__ == '__main__':
main()
上述代码运行结果为:
str([my_car]) = [a red car described by __repr__()]
四、自定义类__repr__()
和__str__()
的通用实现
还是以上面自定义的Car
类为例,以下是针对该类实现其对象字符串表示形式的一般方式:
class Car:
def __init__(self, color, mileage):
self.color = color
self.mileage = mileage
def __repr__(self):
return (f'{self.__class__.__name__}('
f'{self.color!r}, {self.mileage!r})')
def __str__(self):
return f'a {self.color} car'
def main():
my_car = Car("red", 4000)
print(repr(my_car))
print(str(my_car))
if __name__ == '__main__':
main()
上述代码的运行结果为:
Car(‘red’, 4000)
a red car
针对上述示例代码,结合运行结果,有几点需要说明的是:
- 由于
print(repr(my_car))
的运行结果为Car('red', 4000)
,进一步印证了无歧义的含义,且易知直接复制该结果即可以获得my_car
对象; - 通过
self.__class__.__name__
可以先获取创建该对象的类对象,进而获取该类对象的名称属性,这样的好处在于:如果将类名更改,则无需修改__repr__()
方法中的代码; - 代码中使用的
!r
是为了分别使用repr(self.color)
和repr(self.mileage)
而不是str(self.color)
和str(self.mileage)
,如对字符串red
,"red"
或'red'
是其无歧义表现形式,且可用作表达式。