dir()
基本上是一个方便的方法,它不应该返回所有内容,其基本功能是递归地返回类及其基类字典中找到的所有内容。
PyPy对dir()
的实现相当易于理解:
def dir(*args):
...
elif isinstance(obj, (types.TypeType, types.ClassType)):
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:
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'>,)
因此,由于
A
是
type
的一个实例,这些方法并不属于它自己的字典,而是属于它类型的字典。
>> class A(object):
... spam = 'eggs'
...
>>> a = A()
>>> a.foo = 100
>>> a.bar = 200
>>> a.__dict__
>>> A.__dict__
dict_proxy()
由于type
是object
的子类,所以对type
使用dir()
调用将包含一些来自object
的项:
>>> set(dir(type)) - set(type.__dict__)
set(['__reduce_ex__', '__str__', '__format__', '__reduce__', '__class__', '__subclasshook__', '__sizeof__'])