关于python:如果实现了自定义目录,如何获取对象的实际名称列表?

How to get actual list of names of object if custom __dir__ implemented?

官方文件说:

If the object has a method named __dir__(), this method will be called
and must return the list of attributes. This allows objects that
implement a custom __getattr__() or __getattribute__() function to
customize the way dir() reports their attributes.

如果实现自定义__dir__,则由另一个函数inspect.getmembers()返回的结果也会受到影响。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
class С(object):
    __slots__ = ['atr']
    def __dir__(self):
        return ['nothing']
    def method(self):
        pass
    def __init__(self):
        self.atr = 'string'

c = C()
print dir(f) #If we try this - well get ['nothing'] returned by custom __dir__()
print inspect.getmembers(f) #Here we get []
print f.__dict__ #And here - exception will be raised because of __slots__

在这种情况下,如何获取对象名称列表?


原始问题的答案——inspect.getmembers()是否像dir()一样使用__dir__()

这是inspect.getmembers()的源代码,因此我们可以看到它真正在做什么:

1
2
3
4
5
6
7
8
9
10
11
12
13
def getmembers(object, predicate=None):
   """Return all members of an object as (name, value) pairs sorted by name.                                                                                                                                    
    Optionally, only return members that satisfy a given predicate."""

    results = []
    for key in dir(object):
        try:
            value = getattr(object, key)
        except AttributeError:
            continue
        if not predicate or predicate(value):
            results.append((key, value))
    results.sort()
    return results

从中我们看到它正在使用dir(),只是对结果进行了一点过滤。

如何使用重写的__dir__()获取属性?

根据这个答案,不可能总是得到一个完整的属性列表,但是在某些情况下,我们仍然可以肯定地得到它们/得到足够的有用的。

来自文档:

If the object does not provide __dir__(), the function tries its best
to gather information from the object’s __dict__ attribute, if
defined, and from its type object. The resulting list is not
necessarily complete, and may be inaccurate when the object has a
custom __getattr__().

因此,如果您不使用__slots__,您可以查看对象的__dict__(它是object类型),以获得与dir()通常提供给您的信息基本相同的信息。所以,就像使用dir()一样,您必须使用更严格的方法来获取元类方法。

如果您使用的是__slots__,那么获取类属性在某种程度上更简单。是的,没有dict,但是有__slots__本身,它包含所有属性的名称。例如,在示例代码中添加print c.__slots__将生成['atr']。(同样,需要更严格的方法来获取超类的属性。)

如何获取方法

您可能需要一个不同的解决方案,具体取决于用例,但是如果您只是想轻松地找到方法,您可以简单地使用内置的help()

改性Pypy-dir()

这里有一个以上方法的替代方法:要得到一个忽略用户定义的__dir__方法的dir()版本,只需使用pypy实现dir()并删除引用__dir__方法的部分。


马修在另一个答案中指出,getmembers显然返回了dir结果的子集,这些结果是实际属性。

1
2
3
4
5
6
7
8
9
10
11
12
>>> class C:
>>>   def foo(self):
>>>     pass
>>>   def __dir__(self):
>>>     return ['test']
>>>
>>> import inspect
>>> c = C()
>>> dir(c)
['test']
>>> inspect.getmembers(c)
[]