在容器中打印对象时使用__str__表示法
我注意到,当一个有自定义 __str__
方法的实例被作为参数传递给 print
函数时,它会按照预期输出。但是,当把一个包含这些实例的容器(比如列表或元组)传给 print
时,它却使用了 __repr__
方法。也就是说,print(x)
会正确显示 x
的字符串表示,而 print(x, y)
也能正常工作,但 print([x])
或 print((x, y))
却会输出 __repr__
的表示形式。
首先,为什么会出现这种情况?其次,有没有办法在这种情况下修正 print
的行为?
3 个回答
因为当你打印一个列表的时候,通常是从程序员的角度来看,或者是在调试程序。如果你是想要展示这个列表,你会以一种有意义的方式来处理它的内容,所以会用到repr这个东西。
如果你希望你的对象在放进其他容器里时也能被打印出来,就需要定义repr。
class MyObject:
def __str__(self): return ""
__repr__ = __str__
当然,repr应该返回一个字符串,这个字符串可以用作代码来重新创建你的对象,不过你可以根据自己的需要来做。
我不太明白为什么列表的 __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)
的行为是不会改变的。
使用对象的 __str__
方法时,容器会遇到一个问题,那就是完全模糊不清。比如说,如果 print L
显示的是 [1, 2]
,这到底是什么意思呢?L
可能是 ['1, 2']
(一个只有一个元素的列表,这个元素是一个包含逗号的字符串),也可能是四种不同的包含两个元素的列表(因为每个元素可以是字符串或整数)。这种类型的模糊性在使用 print
时是很常见的,但对于元素数量的完全模糊(因为每个逗号 可能 是分隔元素 或者 是字符串的一部分)才是最关键的问题。