What's the biggest difference between dir() and __dict__ in Python

class C(object):
def f(self):
print self.__dict__
print dir(self)
c = C()
c.f()

output:

{}


['__class__', '__delattr__','f',....]

why there is not a 'f' in self.__dict__

33372 次浏览

dir()不仅仅是查找 __dict__

首先,dir()是一个 API 方法,它知道如何使用像 __dict__这样的属性来查找对象的属性。

但是,并非所有对象都具有 __dict__属性。例如,如果要向自定义类中添加 __slots__属性,该类的实例将不具有 __dict__属性,但是 dir()仍然可以列出这些实例上的可用属性:

>>> class Foo(object):
...     __slots__ = ('bar',)
...     bar = 'spam'
...
>>> Foo().__dict__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__dict__'
>>> dir(Foo())
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'bar']

这同样适用于许多内置类型; list没有 __dict__属性,但是您仍然可以使用 dir()列出所有属性:

>>> [].__dict__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__dict__'
>>> dir([])
['__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']

dir()如何处理实例

Python 实例有它们自己的 __dict__,但是它们的类也有:

>>> class Foo(object):
...     bar = 'spam'
...
>>> Foo().__dict__
{}
>>> Foo.__dict__.items()
[('__dict__', <attribute '__dict__' of 'Foo' objects>), ('__weakref__', <attribute '__weakref__' of 'Foo' objects>), ('__module__', '__main__'), ('bar', 'spam'), ('__doc__', None)]

dir()方法使用 都有这些 __dict__属性,还有这个 object上的属性来创建一个关于实例、类和类的所有祖先的可用属性的完整列表。

当您在类上设置属性时,实例也会看到这些属性:

>>> f = Foo()
>>> f.ham
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'ham'
>>> Foo.ham = 'eggs'
>>> f.ham
'eggs'

因为属性被添加到类 __dict__:

>>> Foo.__dict__['ham']
'eggs'
>>> f.__dict__
{}

注意实例 __dict__是如何保持为空的。Python 对象的属性查找遵循从实例到类型到父类的对象层次结构来搜索属性。

只有在实例上直接设置属性时,才会看到反映在实例的 __dict__中的属性,而类 __dict__保持不变:

>>> f.stack = 'overflow'
>>> f.__dict__
{'stack': 'overflow'}
>>> 'stack' in Foo.__dict__
False

或摘要

dir()不仅仅查找对象的 __dict__(有时甚至不存在) ,它还会使用对象的传统(它的类或类型,以及该类或类型的任何超类或父类)来给你所有可用属性的完整图片。

实例 __dict__只是该实例上的“本地”属性集,并不包含该实例上可用的所有属性。相反,您还需要查看类和类的继承树。

函数 f属于类 C的字典。 c.__dict__产生特定于实例 c的属性。

>>> class C(object):
def f(self):
print self.__dict__




>>> c = C()
>>> c.__dict__
{}
>>> c.a = 1
>>> c.__dict__
{'a': 1}

C.__dict__将产生类 C的属性,包括函数 f

>>> C.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'C' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'C' objects>, '__doc__': None, 'f': <function f at 0x0313C1F0>})

虽然一个对象可以引用它的类的一个属性(实际上是所有的祖先类) ,但是这样引用的类属性不会成为相关的 迪克特本身的一部分。因此,虽然它可以合法地访问在类 C中定义为 c.f()的函数 f,但它在 c.__dict__中并不显示为 c的属性。

>>> c.a = 1
>>> c.__dict__
{'a': 1}

类 dir ()和 迪克特只显示已定义的函数(方法)。 实例 dir ()显示 data + 方法,而它的 迪克特只显示 数据属性,而不是方法。

class C2:
...     def __init__(self, id):
...         self.id = id
...     def set(self, len):
...         self.len = len
...
c2 = C2("stick")
dir(C2)
['__class__',.., '__init__', 'set']
dir(c2)
['__class__',.., '__init__', 'id', 'set']
C2.__dict__
mappingproxy({'__init__': <function C2.__init__ at 0x000001899C61E9D0>, 'set': <function C2.set at 0x000001899C61ED30>,...})
c2.__dict__
{'id': 'stick'}