如何在 Python 中枚举对象的属性?

我们通过反射来实现,在 Javascript 中很简单:

for(var propertyName in objectName)
var currentPropertyValue = objectName[propertyName];

如何在 Python 中做到这一点?

216461 次浏览
for property, value in vars(theObject).items():
print(property, ":", value)

请注意,在某些罕见的情况下,有一个 __slots__属性,这样的类通常没有 __dict__

dir() 是一种简单的方法,参见这里:

Python 自省指南

对象的 __dict__属性是其所有其他定义属性的字典。注意,Python 类可以覆盖 < a href = “ http://docs.Python.org/reference/datamodel.html # object._ _ getattr _ _”rel = “ noReferrer”> < strong > getattr 并使事物看起来像属性,但不是在 __dict__。还有内置的功能 vars()dir(),这是不同的在微妙的方式。在一些特殊的类中,__slots__可以取代 __dict__

Python 中的对象很复杂。__dict__是开始反射样式编程的正确位置。如果您在交互式 shell 中进行黑客操作,那么可以从 dir()开始。

inspect.getmembers(object[, predicate])

返回按名称排序的(名称、值)对列表中对象的所有成员。如果提供了可选谓词参数,则只包含谓词为其返回真值的成员。

>>> [name for name,thing in inspect.getmembers([])]
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__',
'__delslice__',    '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
'__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__',
'__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__','__reduce_ex__',
'__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__',
'__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index',
'insert', 'pop', 'remove', 'reverse', 'sort']
>>>

一句俏皮话:

print vars(theObject)

如果你正在寻找所有属性的反射,上面的答案是伟大的。

如果您只是希望获取 dictionary 的键(这与 Python 中的“ object”不同) ,请使用

my_dict.keys()

my_dict = {'abc': {}, 'def': 12, 'ghi': 'string' }
my_dict.keys()
> ['abc', 'def', 'ghi']

其他答案已经完全覆盖了这个问题,但我会说得更清楚一些。 对象可以具有类属性以及静态和动态实例属性。

class foo:
classy = 1
@property
def dyno(self):
return 1
def __init__(self):
self.stasis = 2


def fx(self):
return 3

stasis是静态的,dyno是动态的(参见属性修饰符) ,classy是类属性。如果我们只做 __dict__vars,我们将只得到静态的一个。

o = foo()
print(o.__dict__) #{'stasis': 2}
print(vars(o)) #{'stasis': 2}

因此,如果我们想其他 __dict__将得到一切(和更多)。 这包括神奇的方法和属性以及普通的绑定方法。因此,让我们避免这些:

d = {k: getattr(o, k, '') for k in o.__dir__() if k[:2] != '__' and type(getattr(o, k, '')).__name__ != 'method'}
print(d) #{'stasis': 2, 'classy': 1, 'dyno': 1}

使用属性修饰方法(动态属性)调用的 type将提供返回值的类型,而不是 method。为了证明这一点,让我们把它变成:

import json
print(json.dumps(d)) #{"stasis": 2, "classy": 1, "dyno": 1}

如果这是一种方法,它就会崩溃。

DR 试着为所有三个调用 extravar = lambda o: {k: getattr(o, k, '') for k in o.__dir__() if k[:2] != '__' and type(getattr(o, k, '')).__name__ != 'method'},但不要调用方法或魔法。

我认为有必要说明上面提到的各种选项之间的区别——通常一张图片胜过千言万语。

>>> from pprint import pprint
>>> import inspect
>>>
>>> class a():
x = 1               # static class member
def __init__(self):
self.y = 2      # static instance member
@property
def dyn_prop(self): # dynamic property
print('DYNPROP WAS HERE')
return 3
def test(self):     # function member
pass
@classmethod
def myclassmethod(cls): # class method; static methods behave the same
pass


>>> i = a()
>>> pprint(i.__dict__)
{'y': 2}
>>> pprint(vars(i))
{'y': 2}
>>> pprint(dir(i))
['__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__',
'__str__',
'__subclasshook__',
'__weakref__',
'dyn_prop',
'myclassmethod',
'test',
'x',
'y']
>>> pprint(inspect.getmembers(i))
DYNPROP WAS HERE
[('__class__', <class '__main__.a'>),
('__delattr__',
<method-wrapper '__delattr__' of a object at 0x000001CB891BC7F0>),
('__dict__', {'y': 2}),
('__dir__', <built-in method __dir__ of a object at 0x000001CB891BC7F0>),
('__doc__', None),
('__eq__', <method-wrapper '__eq__' of a object at 0x000001CB891BC7F0>),
('__format__', <built-in method __format__ of a object at 0x000001CB891BC7F0>),
('__ge__', <method-wrapper '__ge__' of a object at 0x000001CB891BC7F0>),
('__getattribute__',
<method-wrapper '__getattribute__' of a object at 0x000001CB891BC7F0>),
('__gt__', <method-wrapper '__gt__' of a object at 0x000001CB891BC7F0>),
('__hash__', <method-wrapper '__hash__' of a object at 0x000001CB891BC7F0>),
('__init__',
<bound method a.__init__ of <__main__.a object at 0x000001CB891BC7F0>>),
('__init_subclass__',
<built-in method __init_subclass__ of type object at 0x000001CB87CA6A70>),
('__le__', <method-wrapper '__le__' of a object at 0x000001CB891BC7F0>),
('__lt__', <method-wrapper '__lt__' of a object at 0x000001CB891BC7F0>),
('__module__', '__main__'),
('__ne__', <method-wrapper '__ne__' of a object at 0x000001CB891BC7F0>),
('__new__', <built-in method __new__ of type object at 0x00007FFCA630AB50>),
('__reduce__', <built-in method __reduce__ of a object at 0x000001CB891BC7F0>),
('__reduce_ex__',
<built-in method __reduce_ex__ of a object at 0x000001CB891BC7F0>),
('__repr__', <method-wrapper '__repr__' of a object at 0x000001CB891BC7F0>),
('__setattr__',
<method-wrapper '__setattr__' of a object at 0x000001CB891BC7F0>),
('__sizeof__', <built-in method __sizeof__ of a object at 0x000001CB891BC7F0>),
('__str__', <method-wrapper '__str__' of a object at 0x000001CB891BC7F0>),
('__subclasshook__',
<built-in method __subclasshook__ of type object at 0x000001CB87CA6A70>),
('__weakref__', None),
('dyn_prop', 3),
('myclassmethod', <bound method a.myclassmethod of <class '__main__.a'>>),
('test', <bound method a.test of <__main__.a object at 0x000001CB891BC7F0>>),
('x', 1),
('y', 2)]

总结一下:

  • vars()__dict__只返回 instance-local 属性;
  • dir()返回所有内容,但仅作为字符串成员名称列表; 不调用动态属性;
  • inspect.getmembers()以元组 (name, value)的形式返回所有内容; 它实际上运行动态属性,并接受一个可选的 predicate参数,该参数可以过滤掉成员 按价值计算

因此,我的常识方法通常是在命令行上使用 dir(),在程序中使用 getmembers(),除非有特定的性能考虑。

请注意,为了保持清洁,我没有包括 __slots__-如果存在,它是 明确地放在那里查询,应该直接使用。我也没有介绍元类,这可能会有点麻烦(大多数人永远不会使用它们)。