当我使用 dir([user-defined object]) 时,为什么它也 return 对象父 class 的名称空间中的名称?

When I use dir([user-defined object]), why does it also return the names in the namespace of the object's parent class?

dir() 函数,如果没有给定参数,returns 当前本地范围内的名称列表。但是,如果我们给参数,它 returns 给定对象参数的属性名称空间内的名称(即对象的属性)。知道这一点,考虑以下 class:

>>> class Foo():

    classVar=23

    def __init__(self,x):
        print("dir in init:",dir())
        self.x=x

    def getx(self):
        return self.x

当我调用 dir(Foo) 时,它 returns 在 class 对象 Foo 中定义的名称列表(a.k.a Foo 的属性列表)如下:

>>> dir(Foo)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'classVar', 'getx']

如果我执行以下操作:

>>> newFoo=Foo(17)
dir in init: ['self', 'x'] #list of names in the local variable namespace from the __init__ method call.
>>> dir(newFoo) #called from the global scope
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'classVar', 'getx', 'x']

我们可以看到 newFoo 的属性列表(在 newFoo 的属性命名空间中定义的名称)还包括在其父 class 中定义的名称,例如 getx,以及 classVar。我认为 dir() 可能不仅返回本地命名空间,还返回封闭的命名空间。我使用一个函数对此进行了测试,但它只返回本地命名空间内的名称,而不是封闭的命名空间。我想知道为什么用户定义 classes/objects 会出现这种情况。当我无法使用 dir() 访问函数调用中的封闭命名空间时,为什么我还能在父 class 的命名空间中看到名称?

class 的名称空间不包含 其实例的名称空间。事实上,对象的属性访问 obj.attr 经历了不同的阶段:

  1. 检查对象类型的 data descriptor named attr exists in the method resolution order 是否为:type(obj).mro().
  2. 检查obj.__dict__中是否存在"attr"
  3. 检查MRO中是否存在attr
  4. 最后,如果上面的none成功并且定义了__getattr__,它将以"attr"作为参数被调用。

以上步骤由 object.__getattribute__ 处理,可以在自定义 classes 中覆盖。

另请注意,类型可以通过定义 __dir__.

自由自定义 dir(obj) 返回的内容

同样重要的是要注意没有 class 范围 包含例如class 定义中的函数定义(参见 Python Scopes and Namespaces)。从以下示例可以看出这是一个常见的陷阱:

>>> class Test:
...     x = 1
...     y = [i*x for i in range(5)]
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in Test
  File "<stdin>", line 3, in <listcomp>
NameError: name 'x' is not defined

这里的列表理解定义了它自己的范围,并尝试根据 LEGB rule(其中“E”代表“封闭”)解析名称 x。但是,由于 class 定义不是正确的范围,因此在名称解析期间无法搜索它。