有没有一个内置的函数可以打印对象的所有当前属性和值?

1463
所以我在这里寻找的是类似于PHP的print_r函数。
这样我就可以通过查看对象的状态来调试我的脚本。

2
你是在询问属性(attributes)吗?这个问题有些误导,因为在Python中,_property_具有与_attribute_不同的特定含义。如果我没理解错的话,也许你需要重新表达你的问题? - Jonathan Scholbach
@WinEunuuchs2Unix 接受的答案更好。它表明 pprint 不是必需的,只是一个很好的附加功能,并且展示了所有的方式,而不仅仅是一种。 - questionto42
32个回答

1344

您希望将vars()pprint()混合使用:

from pprint import pprint
pprint(vars(your_object))

45
vars() 简单地返回其参数的 __dict__,并且在没有 __dir__ 方法的情况下,它也是 dir() 的后备。因此,首先使用 dir(),就像我之前说的一样。 - user3850
38
dir()函数会返回所有内置的东西,像__str____new__这些你可能不太关心的。而vars()函数则不会包含它们。 - Timmmm
33
对于没有__dict__属性的集合和其他对象,此方法会失败。 - anatoly techtonik
1
这是绝对正确的答案,再加上:from inspect import getmembers。 - joe-khoa
12
@hop,“vars()”提供字段的值,而“dir()”则将它们保留为一个谜。 - cowlinator
显示剩余3条评论

802

你把两件不同的事情混在一起了。

使用 dir()vars()inspect 模块来获取你感兴趣的内容(我以 __builtins__ 为例;你可以使用任何对象)。

>>> l = dir(__builtins__)
>>> d = __builtins__.__dict__

以任何你喜欢的方式打印那个字典:

>>> print l
['ArithmeticError', 'AssertionError', 'AttributeError',...
或者
>>> from pprint import pprint
>>> pprint(l)
['ArithmeticError',
 'AssertionError',
 'AttributeError',
 'BaseException',
 'DeprecationWarning',
...

>>> pprint(d, indent=2)
{ 'ArithmeticError': <type 'exceptions.ArithmeticError'>,
  'AssertionError': <type 'exceptions.AssertionError'>,
  'AttributeError': <type 'exceptions.AttributeError'>,
...
  '_': [ 'ArithmeticError',
         'AssertionError',
         'AttributeError',
         'BaseException',
         'DeprecationWarning',
...

在交互式调试器中,也可以使用命令进行漂亮的打印:

(Pdb) pp vars()
{'__builtins__': {'ArithmeticError': <type 'exceptions.ArithmeticError'>,
                  'AssertionError': <type 'exceptions.AssertionError'>,
                  'AttributeError': <type 'exceptions.AttributeError'>,
                  'BaseException': <type 'exceptions.BaseException'>,
                  'BufferError': <type 'exceptions.BufferError'>,
                  ...
                  'zip': <built-in function zip>},
 '__file__': 'pass.py',
 '__name__': '__main__'}

36
令人惊讶的是,并非所有的对象都有__dict__成员(例如re.MatchObject),但内置的dir()函数适用于所有对象。 - hobs
1
print re.compile(r'slots').search('No slots here either.').__slots__ - hobs
3
我从未听说过这个。谢了。点号触发了我的大脑模块路径解析器,甚至从未考虑过拉丁语中的“模块”一词。 - hobs
7
为什么你的回答不多谈一下 inspect 模块呢?我认为它是最接近 print_r 或 var_dump 的模块。 - Hai Phaikawl
1
那么,如何访问dir()列出的属性背后的值呢?dir()仅返回名称列表,并非所有这些名称都存在于vars()__dict__属性中。 - HelloGoodbye
显示剩余5条评论

304
def dump(obj):
  for attr in dir(obj):
    print("obj.%s = %r" % (attr, getattr(obj, attr)))

市面上有许多第三方函数可添加诸如异常处理、特殊字符打印、递归嵌套对象等功能,这些功能按照其作者的偏好实现。但实际上,它们都可以归结为以下基本内容。


20
什么?当然,你可以使用标准的 inspect 模块中的 getmembers() 函数,但我认为这个方法更有用,因为它演示了如何进行反射。 - Dan Lenski
27
完全不是。dir(obj) 显示的属性在 __dict__ 中找不到(例如 __doc____module__)。此外,对于使用 __slots__ 声明的对象,__dict__ 根本不起作用。一般来说,__dict__ 显示实际上存储在内部字典中的用户级属性。而 dir() 显示更多信息。 - Dan Lenski
9
有些类/对象不包含任何__dict__属性/成员。我知道这很疯狂,但它是真实的。像intstrre.MatchObject这样的内置对象是常见例子。尝试一下 'hello'.__dict__,然后再尝试一下 dir('hello') - hobs
36
我不在乎这是否“不符合Python语法”或其他任何事情。它能完成工作,在调试中这是唯一重要的事情。 - hidefromkgb
8
目前为止最好的答案。与“不符合Python规范”的方法说拜拜吧:))) - Basil Musa
显示剩余8条评论

109

dir已经提到过了,但它只会给出属性的名称。如果您还想获取它们的值,请尝试使用__dict__

class O:
   def __init__ (self):
      self.value = 3

o = O()

以下是输出结果:

>>> o.__dict__

{'value': 3}

12
set 这样的对象没有 __dict__ 属性,所以对它们使用 __dict__ 会抛出 AttributeError: 'set' object has no attribute '__dict__' 错误。 - anatoly techtonik

75

有没有内置的函数可以打印出一个对象的所有当前属性和值?

没有。最受欢迎的答案排除了某些类型的属性,而被接受的答案则显示如何获取所有属性,包括方法和非公共API的部分。但是没有很好的完整的内置函数可以做到这一点。

所以简短的推论是你可以自己编写一个函数,但它会计算属性和其他计算的数据描述符,这些都是公共API的一部分,而你可能不希望这样做:

from pprint import pprint
from inspect import getmembers
from types import FunctionType

def attributes(obj):
    disallowed_names = {
      name for name, value in getmembers(type(obj)) 
        if isinstance(value, FunctionType)}
    return {
      name: getattr(obj, name) for name in dir(obj) 
        if name[0] != '_' and name not in disallowed_names and hasattr(obj, name)}

def print_attributes(obj):
    pprint(attributes(obj))

其他答案存在的问题

观察当前得票最高的回答在一个包含许多不同类型数据成员的类上的应用:

from pprint import pprint

class Obj:
    __slots__ = 'foo', 'bar', '__dict__'
    def __init__(self, baz):
        self.foo = ''
        self.bar = 0
        self.baz = baz
    @property
    def quux(self):
        return self.foo * self.bar

obj = Obj('baz')
pprint(vars(obj))

仅打印:

{'baz': 'baz'}

由于 vars 仅返回对象的 __dict__ ,并且它不是副本,因此如果您修改由vars返回的字典,则还会修改对象本身的 __dict__ 。

<code><code><code>vars(obj)['quux'] = 'WHAT?!'
vars(obj)
</code></code></code>

返回:

{'baz': 'baz', 'quux': 'WHAT?!'}

--这很糟糕,因为quux是一个我们不应该设置且不应该在命名空间中的属性... 应用当前被接受的答案(和其他答案)的建议并不好。
>>> dir(obj)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'bar', 'baz', 'foo', 'quux']

正如我们所看到的,dir 只返回与对象相关联的所有名称(实际上只是大部分)。

在评论中提到的 inspect.getmembers 同样存在缺陷 —— 它会返回所有名称和值。

来自类

在教学时,我让我的学生创建一个函数,提供对象的语义公共 API:

def api(obj):
    return [name for name in dir(obj) if name[0] != '_']

我们可以扩展这个方法,提供一个对象的语义命名空间的拷贝,但是我们需要排除未分配的__slots__,如果我们认真考虑“当前属性”的请求,我们需要排除计算属性(因为它们可能会变得很昂贵,而且可能被解释为不是“当前的”):
from types import FunctionType
from inspect import getmembers

def attrs(obj):
    disallowed_properties = {
        name for name, value in getmembers(type(obj)) 
        if isinstance(value, (property, FunctionType))
    }
    return {
        name: getattr(obj, name) for name in api(obj) 
        if name not in disallowed_properties and hasattr(obj, name)
    }

现在我们不计算或显示属性quux:

>>> attrs(obj)
{'bar': 0, 'baz': 'baz', 'foo': ''}

注意事项

也许我们确实知道我们的属性不贵。我们可能想要更改逻辑以包含它们。或者,我们想排除其他自定义数据描述符。

那么我们需要进一步自定义此函数。因此,具有魔力,完全知道我们想要的并提供它的内置函数是没有意义的。这是我们需要自己创建的功能。

结论

没有现成的函数可以做到这一点,您应该根据您的情况选择最语义化的方法。


https://pypi.org/project/beeprint(或https://github.com/panyanyany/beeprint)可以漂亮地打印“everything”,并且支持递归。 - NZD
如何从函数中获取定制化的内容所需的参数。 - Smart Manoj
@NZD 不适用于 from collections import * ; obj=Counter([3,4]) - Smart Manoj
1
这是最全面的答案,应该得到更多点赞。 - ejkitchen

36
您可以使用“dir()”函数来执行此操作。
>>> import sys
>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__name__', '__stderr__', '__stdin__', '__stdo
t__', '_current_frames', '_getframe', 'api_version', 'argv', 'builtin_module_names', 'byteorder
, 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dllhandle', 'exc_clear', 'exc_info'
 'exc_type', 'excepthook', 'exec_prefix', 'executable', 'exit', 'getcheckinterval', 'getdefault
ncoding', 'getfilesystemencoding', 'getrecursionlimit', 'getrefcount', 'getwindowsversion', 'he
version', 'maxint', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_
ache', 'platform', 'prefix', 'ps1', 'ps2', 'setcheckinterval', 'setprofile', 'setrecursionlimit
, 'settrace', 'stderr', 'stdin', 'stdout', 'subversion', 'version', 'version_info', 'warnoption
', 'winver']
>>>

另一个有用的功能是帮助。

>>> help(sys)
Help on built-in module sys:

NAME
    sys

FILE
    (built-in)

MODULE DOCS
    http://www.python.org/doc/current/lib/module-sys.html

DESCRIPTION
    This module provides access to some objects used or maintained by the
    interpreter and to functions that interact strongly with the interpreter.

    Dynamic objects:

    argv -- command line arguments; argv[0] is the script pathname if known

27

为打印出对象的当前状态,您可以执行以下操作:

>>> obj # in an interpreter
或者
print repr(obj) # in a script
或者
print obj

对于你的类,定义__str____repr__方法。引用自Python官方文档:

__repr__(self)被内置函数repr()和字符串转换(反引号)调用,计算对象的“官方”字符串表示。如果可能的话,这应该看起来像一个有效的Python表达式,可以使用该表达式重新创建具有相同值的对象(在适当的环境中)。如果不可能,请返回像“<...一些有用的描述...>”这样的字符串形式。返回值必须是一个字符串对象。如果一个类定义了repr()但没有定义__str__(),那么__repr__()也会在需要该类实例的“非正式”字符串表示时使用。通常用于调试,因此重要的是表示信息丰富且无歧义。

__str__(self)被内置函数str()和print语句调用,计算对象的“非正式”字符串表示。这与__repr__()不同,因为它不必是有效的Python表达式:可以使用更方便或更简洁的表示方法。返回值必须是一个字符串对象。


这个选项对于打印与对象内容连接的字符串非常有用:print "DEBUG: object value: " + repr(obj) - AlejandroVD

21
我建议使用help(your_object)help(dir)
 If called without an argument, return the names in the current scope.
 Else, return an alphabetized list of names comprising (some of) the attributes
 of the given object, and of attributes reachable from it.
 If the object supplies a method named __dir__, it will be used; otherwise
 the default dir() logic is used and returns:
 for a module object: the module's attributes.
 for a class object:  its attributes, and recursively the attributes
 of its bases.
 for any other object: its attributes, its class's attributes, and
 recursively the attributes of its class's base classes.

help(vars)

Without arguments, equivalent to locals().
With an argument, equivalent to object.__dict__.

20

可能值得一试 --

Python是否有类似于Perl的Data::Dumper的工具?

我建议使用这个 --

https://gist.github.com/1071857

请注意,Perl有一个称为Data::Dumper的模块,可以将对象数据转换回Perl源代码(注意:它并不将代码转换回源代码,并且几乎始终不希望在输出中使用对象方法函数)。这可用于持久性,但常见目的是进行调试。

标准的Python pprint无法实现许多功能,特别是在看到对象实例时会停止下降并给出对象的内部十六进制指针(那个指针实际上并没有什么用途)。因此,简而言之,Python的设计理念都在于这种伟大的面向对象范例,但是你开箱即用的工具却不是专门用于处理对象的。

Perl的Data::Dumper允许您控制深度,还会检测循环链接结构(这真的很重要)。在Perl中,这个过程基本上更容易实现,因为对象除了它们的祝福(一个普遍定义的过程)之外没有特别的魔力。


这应该是一个pip和deb文件,而不仅仅是一个gist! - phobie
2
简而言之,Python是关于这种伟大的面向对象范式的,但是开箱即用的工具旨在处理除对象以外的其他东西... 当你提供的唯一示例是次要模块时,这是一个相当大的说法。 - memeplex
@memeplex 在哪里说Python是完全基于面向对象编程的? - Peter Wood
好的,它只是说这一切都与面向对象编程有关,我的错。 - memeplex
这仅适用于2.7版本。 - Rainb

15

在大多数情况下,使用__dict__dir()可以获取你想要的信息。如果你需要更多细节,标准库包括inspect模块,它允许你获取一些令人印象深刻的详细信息。一些真正有用的信息包括:

  • 函数和方法参数的名称
  • 类层次结构
  • 函数/类对象实现的源代码
  • 帧对象中的局部变量

如果你只是想知道“我的对象有哪些属性值?”,那么dir()__dict__可能已经足够了。如果你真的想深入探索任意对象的当前状态(请记住,在Python中几乎所有东西都是对象),那么考虑使用inspect模块。


使用您关于检查的解释来改进最完整的答案。希望这对您没有问题。 - Fernando César

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