在容器中打印对象时使用__str__表示法

11 投票
3 回答
17928 浏览
提问于 2025-04-15 21:34

我注意到,当一个有自定义 __str__ 方法的实例被作为参数传递给 print 函数时,它会按照预期输出。但是,当把一个包含这些实例的容器(比如列表或元组)传给 print 时,它却使用了 __repr__ 方法。也就是说,print(x) 会正确显示 x 的字符串表示,而 print(x, y) 也能正常工作,但 print([x])print((x, y)) 却会输出 __repr__ 的表示形式。

首先,为什么会出现这种情况?其次,有没有办法在这种情况下修正 print 的行为?

3 个回答

1

因为当你打印一个列表的时候,通常是从程序员的角度来看,或者是在调试程序。如果你是想要展示这个列表,你会以一种有意义的方式来处理它的内容,所以会用到repr这个东西。

如果你希望你的对象在放进其他容器里时也能被打印出来,就需要定义repr。

class MyObject:
    def __str__(self): return ""

    __repr__ = __str__

当然,repr应该返回一个字符串,这个字符串可以用作代码来重新创建你的对象,不过你可以根据自己的需要来做。

5

我不太明白为什么列表的 __str__ 方法会返回里面对象的 __repr__,所以我查了一下资料:[Python-3000] PEP: str(container) 应该调用 str(item),而不是 repr(item)

支持这种做法的理由有:

-- 容器不想猜测用户想在 str(container) 中看到什么,比如周围的符号、分隔符等等;

-- repr(item) 通常会显示类型信息,比如字符串周围的引号、类名等。

这样可以更清楚地知道列表里到底有什么(因为对象的字符串表示可能会有逗号等)。这种行为不会改变,正如 Guido "BDFL" van Rossum 所说:

让我直接说,我反对这个改变,我认为这会造成太多的混乱,不能在接近测试版的时候接受。


现在,有两种方法可以解决你代码中的这个问题。

第一种是创建一个新的类,继承自 list,并实现你自己的 __str__ 方法。

class StrList(list):
    def __str__(self):
        string = "["
        for index, item in enumerate(self):
            string += str(item)
            if index != len(self)-1:
                string += ", "
        return string + "]"

class myClass(object):
    def __str__(self):
        return "myClass"

    def __repr__(self):
        return object.__repr__(self)

然后测试一下:

>>> objects = [myClass() for _ in xrange(10)]
>>> print objects
[<__main__.myClass object at 0x02880DB0>, #...
>>> objects = StrList(objects)
>>> print objects
[myClass, myClass, myClass #...
>>> import random
>>> sample = random.sample(objects, 4)
>>> print sample
[<__main__.myClass object at 0x02880F10>, ...

我个人觉得这是个糟糕的主意。有些函数,比如 random.sample,实际上返回的是 list 对象,即使你已经创建了子类。所以如果你选择这种方法,可能会有很多 result = strList(function(mylist)) 的调用,这样效率可能不高。而且,这样做也不好,因为你可能会有一半的代码在使用普通的 list 对象(因为你不打印它们),而另一半在使用 strList 对象,这样会让你的代码变得更加混乱和难以理解。不过,这个选项是存在的,这是让 print 函数(或者在 2.x 中的语句)按照你想要的方式工作的唯一方法。

另一种解决方案是自己写一个函数 strList(),返回你想要的字符串格式:

def strList(theList):
    string = "["
    for index, item in enumerate(theList):
        string += str(item)
        if index != len(theList)-1:
            string += ", "
    return string + "]"

>>> mylist = [myClass() for _ in xrange(10)]
>>> print strList(mylist)
[myClass, myClass, myClass #...

不幸的是,这两种解决方案都需要你重构现有的代码,但 str(container) 的行为是不会改变的。

13

使用对象的 __str__ 方法时,容器会遇到一个问题,那就是完全模糊不清。比如说,如果 print L 显示的是 [1, 2],这到底是什么意思呢?L 可能是 ['1, 2'](一个只有一个元素的列表,这个元素是一个包含逗号的字符串),也可能是四种不同的包含两个元素的列表(因为每个元素可以是字符串或整数)。这种类型的模糊性在使用 print 时是很常见的,但对于元素数量的完全模糊(因为每个逗号 可能 是分隔元素 或者 是字符串的一部分)才是最关键的问题。

撰写回答