如何使用print()打印类的实例?

798

当我尝试打印一个类的实例时,我会得到这样的输出:

>>> class Test():
...     def __init__(self):
...         self.a = 'foo'
...
>>> print(Test())
<__main__.Test object at 0x7fc9a9e36d60>

如何使得print显示自定义内容(例如包括a属性值的内容)?也就是说,如何定义类的实例在打印时的外观(它们的字符串表示形式)?

如果您想为类本身定义行为(在这种情况下,使print(Test)显示自定义内容,而不是<class __main__.Test>或类似内容),请参见如何选择类本身的自定义字符串表示形式?。 (实际上,该技术基本相同,但更难应用。)

12个回答

903
>>> class Test:
...     def __repr__(self):
...         return "Test()"
...     def __str__(self):
...         return "member of Test"
... 
>>> t = Test()
>>> t
Test()
>>> print(t)
member of Test

__str__方法是在打印时调用的内容,而__repr__方法是在使用repr()函数(或在交互式提示中查看)时发生的。

如果未提供__str__方法,则Python将打印__repr__的结果。如果定义了__str__但未定义__repr__,则Python仍将使用上述内容作为__repr__,但仍将使用__str__进行打印。


14
还有一个__unicode__方法,你可以用它来替代__str__;请注意它应该返回一个Unicode对象,而不是字符串(但如果你返回一个字符串,转换为Unicode也会被执行...)。 - kender
1
@kender - 我之前不知道这个问题,但回想起来,考虑到 Python 2.x 的破碎的 Unicode 处理,这是完全合理的。 - Chris Lutz
14
我认为这个答案如果没有指向另一个答案的链接就无法完成! - tnotstar
救了我!但是,重新实现 repr(self) 方法后,打印将会误导用户。你知道围绕这个问题的最佳做法吗? - Viet
16
对于Java程序员来说,str(self)就像Python世界的toString()方法。 - Janac Meena
显示剩余2条评论

183

正如Chris Lutz所解释的,这是由你的类中的__repr__方法定义的。

repr()的文档中得知:

对于许多类型,此函数会尝试返回一个字符串,该字符串在传递给eval()时将生成具有相同值的对象,否则表示形式是一个用尖括号括起来的字符串,其中包含对象类型的名称以及常常包含对象的名称和地址的其他信息。 通过定义__repr__()方法,类可以控制其实例为其返回什么。

给定以下Test类:

class Test:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def __repr__(self):
        return f"<Test a:{self.a} b:{self.b}>"

    def __str__(self):
        return f"From str method of Test: a is {self.a}, b is {self.b}"

在Python shell中,它会按照以下方式运行:

>>> t = Test(123, 456)
>>> t
<Test a:123 b:456>
>>> print(repr(t))
<Test a:123 b:456>
>>> print(t)
From str method of Test: a is 123, b is 456
>>> print(str(t))
From str method of Test: a is 123, b is 456

如果没有定义__str__方法,那么print(t)(或者print(str(t)))将使用__repr__的结果。如果没有定义__repr__方法,则会使用默认值,大致相当于:这个链接中的内容
def __repr__(self):
    cls = self.__class__
    return f"<{cls.__module_}.{cls.__qualname__} object at {id(self)}>"

+1,但是你的类代码中的__str__与你提供的交互式shell结果不同。 :P - Chris Lutz
1
哎呀,手动修改REPL输出从来都不是个好主意。我应该对我的帖子进行doctest测试:P - dbr
1
"% " 字符串格式化并未被弃用,根据 http://docs.python.org/whatsnew/2.6.html 的说法,“%”运算符被一个更加强大的字符串格式化方法“format()”所补充。 - dbr
4
确实如此。请注意,“Python 3.0中的新特性”文档还指出:“format()方法[...]计划最终将其作为唯一的字符串格式化API,并开始在Python 3.1中弃用%运算符。” - Ashwin Nanjappa
1
真可惜,%一直非常方便。 - Janusz Lenar
显示剩余2条评论

109

如果你像@Keith一样处于这种情况,你可以尝试:

print(a.__dict__)

这种做法不符合良好的编码风格,但如果你只是想进行调试,那么它应该能够满足你的需求。


你知道如何判断字典键是否有对象在其值中吗? - pranaygoyal02
1
@HadoopEvangelist 你是在问如何递归打印这些对象,还是只是确定是否存在对象? - John
4
在快速调试方面,这是最好的答案之一。感谢@John。 - Mohseen Mulla
基本上与 https://dev59.com/CXI_5IYBdhLWcg3wEe5u#32635523 相同。 - sancho.s ReinstateMonicaCellio
1
print(self.__dict__)同样有效。在类的作者未定义__repr____str__时,这对于调试代码非常有用。 - Sachin Dangol

82

一种通用的方法可以应用于任何类别而不需要特定格式,如下所示:

class Element:
    def __init__(self, name, symbol, number):
        self.name = name
        self.symbol = symbol
        self.number = number

    def __str__(self):
        return str(self.__class__) + ": " + str(self.__dict__)

然后,

elem = Element('my_name', 'some_symbol', 3)
print(elem)

生产

__main__.Element: {'symbol': 'some_symbol', 'name': 'my_name', 'number': 3}

21

@user394430的回复的更美观版本。

class Element:
    def __init__(self, name, symbol, number):
        self.name = name
        self.symbol = symbol
        self.number = number

    def __str__(self):
        return  str(self.__class__) + '\n'+ '\n'.join(('{} = {}'.format(item, self.__dict__[item]) for item in self.__dict__))

elem = Element('my_name', 'some_symbol', 3)
print(elem)

生成视觉效果良好的名称和值列表。

<class '__main__.Element'>
name = my_name
symbol = some_symbol
number = 3

更加花哨的版本(感谢Ruud)对项目进行排序:

def __str__(self):
    return  str(self.__class__) + '\n' + '\n'.join((str(item) + ' = ' + str(self.__dict__[item]) for item in sorted(self.__dict__)))

返回 ','.join(('{} = {}'.format(item, self.dict[item]) for item in self.dict)),将所有内容放在一行上。我删除了类名,只是为了调试目的打印值。 - SleepyBoBos

19

简单。在打印时,执行以下操作:

print(foobar.__dict__)

只要构造函数存在
__init__

部分有趣。它快速且“内置”,但仅限于基本值类型。内部的新类对象将显示类类型而不是内容。因此,与像_str__和__repr__这样的请求相比,它很有帮助和有趣,但不够详尽。 - NeronLeVelu

16

对于Python 3:

如果特定格式不重要(例如用于调试),只需从下面的Printable类继承即可。无需为每个对象编写代码。

回答的启发

class Printable:
    def __repr__(self):
        from pprint import pformat
        return "<" + type(self).__name__ + "> " + pformat(vars(self), indent=4, width=1)

# Example Usage
class MyClass(Printable):
    pass

my_obj = MyClass()
my_obj.msg = "Hello"
my_obj.number = "46"
print(my_obj)

当值包含空格时,这看起来很奇怪... - kenyee
1
print(vars(obj)) -> 对我来说已经足够了,谢谢 - undefined

14

仅在@dbr的答案中添加两分钱,以下是如何实现他引用的官方文档中的这个句子的示例:

"[...] 要返回一个字符串,该字符串在传递给eval()时将生成一个具有相同值的对象 [...]"

鉴于这个类定义:

class Test(object):
    def __init__(self, a, b):
        self._a = a
        self._b = b

    def __str__(self):
        return "An instance of class Test with state: a=%s b=%s" % (self._a, self._b)

    def __repr__(self):
        return 'Test("%s","%s")' % (self._a, self._b)

现在,序列化Test类的实例变得很容易:

x = Test('hello', 'world')
print 'Human readable: ', str(x)
print 'Object representation: ', repr(x)
print

y = eval(repr(x))
print 'Human readable: ', str(y)
print 'Object representation: ', repr(y)
print

因此,运行最后一段代码,我们将得到:

Human readable:  An instance of class Test with state: a=hello b=world
Object representation:  Test("hello","world")

Human readable:  An instance of class Test with state: a=hello b=world
Object representation:  Test("hello","world")

但是,正如我在上一条评论中所说的:更多信息就在这里


12

你需要使用__repr__。这是一个像__init__一样的标准函数。 例如:

class Foobar():
    """This will create Foobar type object."""

    def __init__(self):
        print "Foobar object is created."

    def __repr__(self):
        return "Type what do you want to see here."

a = Foobar()

print a

2
__repr__和__str__具有不同的语义:__repr__应该是能够(重新)创建相同对象的Python源代码 - 这是它在代码中的representation;而__str__应该是对象的漂亮的用户界面字符串表示。 - Eric Towers

9

__repr____str__已经在许多答案中提到了。我只想补充一点,如果您太懒了,不想将这些神奇的函数添加到您的类中,那么您可以使用objprint。一个简单的装饰器@add_objprint将帮助您向您的类中添加__str__方法,然后您就可以使用print来打印实例了。当然,如果你喜欢,你也可以使用该库中的objprint函数以人类可读格式打印任意对象。

from objprint import add_objprint

class Position:
    def __init__(self, x, y):
        self.x = x
        self.y = y

@add_objprint
class Player:
    def __init__(self):
        self.name = "Alice"
        self.age = 18
        self.items = ["axe", "armor"]
        self.coins = {"gold": 1, "silver": 33, "bronze": 57}
        self.position = Position(3, 5)

print(Player())

输出结果如下:
<Player
  .name = 'Alice',
  .age = 18,
  .items = ['axe', 'armor'],
  .coins = {'gold': 1, 'silver': 33, 'bronze': 57},
  .position = <Position
    .x = 3,
    .y = 5
  >
>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接