获取所有可能的类属性完整列表。

5

有没有一种方法可以输出一个简单类的所有可能属性?标准属性如 __class__ __doc__ ,以及特殊只读属性 __mro__ __bases__ 等等。一般来说,所有现有属性?

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

class myClass:
    pass

dir()vars()inspect.getmembers()都会排除某些builtin属性。最完整的列表可以通过使用myClass.__dir__(MyClass)来获取,它会添加内置属性,但会排除MyClass的用户定义属性,例如:

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'}

根据其中一个类似问题的回答,似乎这是不可能的。如果现在仍然不可能,那么"隐藏"某些属性(如从标准调用到dir()、vars()和inspect而不是像__name__)的理由是什么呢?

类似的问题:


3
可能是如何获取对象的所有方法和属性的完整列表?的重复问题。 - ArtOfWarfare
你连我的问题都没看吧? - Dimitris Fasarakis Hilliard
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - ArtOfWarfare
这就是为什么问题被标记为“Python 3.x”而不是“Python 2.x”的原因。 - Dimitris Fasarakis Hilliard
1
那个问题通常是关于Python的。它适用于2.x和3.x版本。这个问题是那个问题的子问题。答案已经在那里了。 - ArtOfWarfare
1个回答

4

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

PyPy对dir()的实现相当易于理解:

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 字典的一部分:

>>> class A(object):
    pass
...
>>> set(dir(A)) == set(list(object.__dict__) + list(A.__dict__))
True

那么,__bases__和其他缺失的项目呢?

首先,object本身是某个实例的一个实例,实际上有点混乱

>>> 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的一部分:
>>> 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__ 时,实际上是在查找名称为 type 的类型的描述符:(有关描述符的详细信息)。请注意保留 HTML 标记。
>>> A.__bases__
(<type 'object'>,)
>>> type(A).__dict__['__bases__'].__get__(A, type)
(<type 'object'>,)

因此,由于Atype的一个实例,这些方法并不属于它自己的字典,而是属于它类型的字典。
>> class A(object):
...     spam = 'eggs'
...
>>> a = A()
>>> a.foo = 100
>>> a.bar = 200
>>> a.__dict__
{'foo': 100, 'bar': 200}
>>> A.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None, 'spam': 'eggs'})

由于typeobject的子类,所以对type使用dir()调用将包含一些来自object的项:

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

我明白了,所以我最初的假设是它们被隐藏了,这是相当错误的。像__bases__这样的属性只是从type继承而来,因此在子类上调用dir()这样的函数就不会返回继承的属性。另外,关于存在一个获取所有内容的函数,我猜短答案是,如果我想要这样的函数,最好自己实现,因为它不存在,对吗? - Dimitris Fasarakis Hilliard
@DimitrisJim 差不多正确,但是 "dir() on a subclass of type" 应该是 "dir() on an instance of type",例如如果你创建了一个具有某些属性的元类,那么它的实例(也就是类)对我们来说是不可见的,但是属性访问仍然可以工作。不,至少在标准库中没有这样的函数可用。 - Ashwini Chaudhary
好的,我明白了。唯一我还不理解的是(也许我应该提出另一个问题),假设再次给出一个空类定义,为什么从type继承的某些属性,如__ge____dict__和/或__dir__中可见,而其他属性,如__bases__则不可见。 - Dimitris Fasarakis Hilliard
1
@DimitrisJim 因为它们是对象的 __dict__ 的一部分,而其他属性如 __bases__ 不是:set(dir(A)) == set(list(object.__dict__) + list(A.__dict__))Python 2: http://ideone.com/TR0YPh ; Python 3: http://ideone.com/tmlHfW - Ashwini Chaudhary
谢谢,Ashwini。我想我明白了。如果我再迷失了,我会问一个相关的问题。祝你好运! - Dimitris Fasarakis Hilliard

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