对象的字符串表示形式之repr、__repr__、str、__str__

当我们在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 functions format() and print() 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__以及对应的strrepr,但是并未看出二者的明显差异。下面通过一个例子来说二者的差异:

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'是其无歧义表现形式,且可用作表达式。

五、参考资料

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值