为什么cls.__name__不出现在dir()中?

18 投票
4 回答
1786 浏览
提问于 2025-04-16 23:48

假设我有一个简单的类:

class Foobar(object):
    pass

如果我使用 dir(Foobar),我会得到以下输出:

['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

虽然在 dir() 的输出中没有显示,但我仍然可以访问 __name__

Foobar.__name__

并且可以得到 Foobar

为什么Python会这样呢?

4 个回答

3

根据Python的文档,关于dir()方法,你可以在你的对象上实现一个叫做__dir__()的方法,这个方法会返回你希望在调用dir时看到的项目列表。

__name__这个成员是Python对象的一些特殊属性的一部分,文档中提到:

有些属性是dir()这个内置函数不会报告的。

6

最近关于这个问题的重复提问让我想更详细地解释一下这个答案。首先,大家接受的答案是对的。dir 这个函数其实是调用了 __dir__ 这个特殊的方法,而默认的 __dir__ 方法通常只是返回对象的 __dict__ 中的键。

其次,__name__ 并不在 object.__dict__ 中,也不会出现在子类的 __dict__ 里。如果它不在 __dict__ 里,那它在哪里呢?又是怎么被查找的呢?

根据我对 源代码 的理解,有一些描述符是在类创建时由 type 设置的。其中一个描述符就是 __name____name__ 的获取方法会调用 type_name,而设置方法会调用 type_set_name。这些获取和设置方法实际上是在类型对象实例(也就是类)的 tp_name 这个位置上获取或设置名称。由于这是类型对象的一个特殊位置,所以它并不在类的 __dict__ 中,因此通过 varsdir 是无法看到的。

需要注意的是,类型对象实例(也就是类)的 dir 方法特别不包括来自 __class__ 属性的成员,因为 “属于 metaclass 的方法可能会让人更困惑,而不是更有帮助”

那么,让我们把这些内容整理一下。

  • object 没有 __name__ 属性,但 type 有。
  • type__name__ 属性实际上是一个描述符。
  • 由于 object 的 metaclass 是 type,当请求 object.__name__ 时,会调用 type__name__ 描述符。
  • name 描述符会在 type->tp_name 这个位置查找名称。这个位置是由 type.__new__ 在类创建时填充的。
  • object.__dir__ 是特别设计的,不报告类的 metaclass 上的属性,因为 Python 开发者认为这样做会弊大于利。
11

dir 这个命令并不能保证会返回所有可能的属性。根据官方文档的说明:

因为 dir() 主要是为了方便在交互式提示符下使用,所以它更倾向于提供一组有趣的名称,而不是严格或一致地定义一组名称。而且它的具体表现可能会随着版本的更新而变化。例如,当你传入一个类作为参数时,元类的属性不会出现在结果列表中。

撰写回答