【问题标题】:What's the biggest difference between dir() and __dict__ in PythonPython 中 dir() 和 __dict__ 的最大区别是什么
【发布时间】:2012-12-30 22:33:33
【问题描述】:
class C(object):
    def f(self):
        print self.__dict__
        print dir(self)
c = C()
c.f()

输出:

{}

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

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

【问题讨论】:

    标签: python introspection


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      dir() 不仅仅是查找 __dict__

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

      但并非所有对象都有__dict__ 属性。例如,如果您要向自定义类添加__slots__ attribute,该类的实例将没有__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']
      

      这同样适用于许多内置类型; lists 没有__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() 方法使用 both 这些 __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
      

      TLDR;或摘要

      dir() 不只是查找对象的__dict__(有时甚至不存在),它会使用对象的继承(它的类或类型,以及该类的任何超类或父类或type) 为您提供所有可用属性的完整图片。

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

      【讨论】:

      • dir 也将查看__dir__(如果已实现),它可以为甚至没有作为正式属性或属性实现但在__getattr__ 中简单地延迟实现的事物返回字符串,例如。
      • @PaulMcGuire:我不想详细说明dir() 的作用;这是一个很长的答案,我不需要包含那个细节来理解这一点。 :-)
      • 查看what's the difference between dir(self) and self.__dict__以获得更简洁的答案。
      • @Davos:property 对象是一个描述符对象,它存在于类上,而不是实例上。属性 getter、setter 或 deleter 所做的是完全依赖于实现。属性可以做任何它喜欢的事情,它根本不需要对实例做任何事情。实例上的dir() 仍会列出类 实例__dict__ 中的任何属性,因此DatasourceItem 实例上的dir() 将同时显示connections 定义的属性类上,以及实例上的_connections 属性。
      • @Davos:是的,这完全在意料之中。 DatasourceItem.id property 对象存在于类上,而不是实例上。 instance.id 将触发 getter 方法,然后返回 self._id,其中 self._id 是实例上的一个属性。
      【解决方案3】:

      函数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 中定义为c.f() 的函数f 的合法访问,但它不会作为c 的属性出现在c.__dict__ 中。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-07-31
        • 2015-01-05
        • 1970-01-01
        • 1970-01-01
        • 2020-05-01
        • 2016-08-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多