Python中dir()和__dict__有什么最大的区别?

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

输出:

{}

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

为什么在self.__dict__中没有'f'

3个回答

159

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__只是该实例上的'本地'属性集,并不包含实例上可用的每个属性。相反,您需要查看类和类的继承树。


4
如果实现了__dir__,则dir函数也会查看它,并返回其中的字符串。这些字符串可能代表那些并不是用正式属性或者属性值实现的内容,而仅仅是在__getattr__中实现的惰性实现。 - PaulMcG
1
@PaulMcGuire:我不想详细解释dir()的作用,因为答案已经很长了,而且我并不需要包含那些细节来表达我的观点。 :-) - Martijn Pieters
2
@Davos:property 对象是一个描述符对象,它存在于类上而不是实例上。属性 getter、setter 或 deleter 的作用完全取决于实现方式。属性可以做任何它想做的事情,它不必与实例有任何关系。在实例上使用 dir() 仍将列出类中的任何属性以及实例 __dict__ 中的任何属性,因此在 DatasourceItem 实例上使用 dir() 将显示在类上定义的 connections 属性和实例上的 _connections 属性。 - Martijn Pieters
1
@Davos:是的,这完全是预期的。DatasourceItem.id property 对象存在于类上,而不是实例上。instance.id 将触发 getter 方法,然后返回 self._id,其中 self._id 是实例上的属性。 - Martijn Pieters
谢谢解释,这对我非常有帮助。我试图检索现有的数据源对象,将其中大部分属性用于新对象实例,并将其传回服务器以进行覆盖。我想也许使用字典获取属性会更具动态性,但仅使用点符号获取属性值可能更好地“编码接口”,而且只有几个非默认值。我很懒,试图自动化一些不必要的事情。 - Davos
显示剩余2条评论

6

函数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>})

一个对象可以引用其类(以及所有祖先类)的属性,但是所引用的类属性并不成为关联dict 的一部分。因此,虽然访问在类C中定义的函数f 的方式 是合法的,但它并不会出现在c.__dict__ 中作为c的属性。

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

4

类的 dir() 方法和 dict 类型只显示已定义的函数(方法)。实例的 dir() 方法显示数据和方法,而其 dict 属性仅显示数据属性,不包括方法。

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

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