关于python:获取所有可能类属性的完整列表

Get complete list of all possible Class Attributes

对于一个简单的类,有没有一种方法可以为它输出所有可能的属性?标准属性如__class____doc__以及特殊只读属性如__mro____bases__等。一般来说,所有现有的属性?

考虑到一个类最简单的情况:

1
2
class myClass:
    pass

dir()vars()inspect.getmembers()都不包括某些builtin属性。最完整的列表是通过使用myClass.__dir__(MyClass)提供的,它在添加内置属性时不包括MyClass的用户定义属性,例如:

1
2
3
4
5
6
7
8
In [3]: set(MyClass.__dir__(MyClass)) - set(dir(MyClass))
Out[3]:
{'__abstractmethods__', '__base__', '__bases__',
 '__basicsize__', '__call__', '__dictoffset__',
 '__flags__', '__instancecheck__', '__itemsize__',
 '__mro__', '__name__', '__prepare__', '__qualname__',
 '__subclasscheck__', '__subclasses__', '__text_signature__',
 '__weakrefoffset__', 'mro'}

根据添加的一个类似问题,这是不可能的。如果目前仍然不可能,那么"隐藏"某些属性(如__bases__(从对dir(), vars() & inspect的标准调用而非__name__的标准调用)背后的理由是什么?

类似问题:

  • 如何获得对象方法和属性的完整列表?


    This is the most likely to be labeled as a duplicate, but, it is old and mostly regarding Python 2.x. The accepted answer is that there isn't a way but it was provided in 08'. The most recent answer in 12' suggests dir() for new style classes.

  • 打印python类的所有属性


    Similar title, different content.

  • 获取类的属性


    Offers dir() and inspect solutions.

  • 获取python中的所有对象属性?


    Again, proposing dir().


dir()基本上是一种方便的方法,它不应该返回任何东西,它的基本功能是递归地返回一个类及其基的字典中找到的所有东西。

Pypy对dir()的实施很容易理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def dir(*args):
    ...
    elif isinstance(obj, (types.TypeType, types.ClassType)):
        # Don't look at __class__, as metaclass methods would be confusing.
        return sorted(_classdir(obj))
    ...

def _classdir(klass):
   """Return a set of the accessible attributes of class/type klass.

    This includes all attributes of klass and all of the base classes
    recursively.
   """

    names = set()
    ns = getattr(klass, '__dict__', None)
    if ns is not None:
        names.update(ns)
    bases = getattr(klass, '__bases__', None)
    if bases is not None:
        # Note that since we are only interested in the keys, the order
        # we merge classes is unimportant
        for base in bases:
            names.update(_classdir(base))
    return names

由于每个类基本上都继承自object,您将看到其中包含了一些dunder方法,因为它们实际上是object字典的一部分:

1
2
3
4
5
>>> class A(object):
    pass
...
>>> set(dir(A)) == set(list(object.__dict__) + list(A.__dict__))
True

Now what about __bases__ and other missing items?

首先,object本身就是一个例子,实际上有点混乱:

1
2
3
4
5
6
7
8
9
10
11
12
>>> isinstance(type, object)
True
>>> isinstance(object, type)
True
>>> issubclass(type, object)
True
>>> issubclass(object, type)
False
>>> type.mro(object)
[<type 'object'>]
>>> type.mro(type)
[<type 'type'>, <type 'object'>]

因此,__bases____ge__等属性实际上是type的一部分:

1
2
>>> list(type.__dict__)
['__module__', '__abstractmethods__', '__getattribute__', '__weakrefoffset__', '__dict__', '__lt__', '__init__', '__setattr__', '__subclasses__', '__new__', '__base__', '__mro__', 'mro', '__dictoffset__', '__call__', '__itemsize__', '__ne__', '__instancecheck__', '__subclasscheck__', '__gt__', '__name__', '__eq__', '__basicsize__', '__bases__', '__flags__', '__doc__', '__delattr__', '__le__', '__repr__', '__hash__', '__ge__']

因此,当我们进行A.__bases__时,实际上是在查找A类型的描述符,即type

1
2
3
4
>>> A.__bases__
(<type 'object'>,)
>>> type(A).__dict__['__bases__'].__get__(A, type)
(<type 'object'>,)

因此,由于Atype的一个实例,这些方法不是它自己字典的一部分,而是它的类型字典。

1
2
3
4
5
6
7
8
9
10
>> class A(object):
...     spam = 'eggs'
...
>>> a = A()
>>> a.foo = 100
>>> a.bar = 200
>>> a.__dict__
{'foo': 100, 'bar': 200}
>>> A.__dict__
dict_proxy({'__dict__': , '__module__': '__main__', '__weakref__': , '__doc__': None, 'spam': 'eggs'})

由于typeobject的一个子类,因此dir()type的调用将包含来自object的一些项目:

1
2
>>> set(dir(type)) - set(type.__dict__)
set(['__reduce_ex__', '__str__', '__format__', '__reduce__', '__class__', '__subclasshook__', '__sizeof__'])