__dir__ 不影响 IDE 补全,因其依赖静态分析而非运行时;它仅在 IPython/Jupyter 的 Tab 补全、dir() 和 help() 中生效,正确实现需调用 super().__dir__()、确保返回字符串列表且与 __getattr__ 配合。

为什么 __dir__ 返回的列表没出现在 IDE 补全里
IDE(如 PyCharm、VS Code 的 Pylance)的补全主要依赖静态分析或类型提示,不是运行时调用 __dir__。即使你重写了 __dir__,它只影响 dir(obj) 和 IPython/Jupyter 的 Tab 补全,对大多数 IDE 无效。
真正起作用的场景是交互式环境(比如 IPython)、help()、以及某些支持运行时反射的工具。如果你期望在 VS Code 里按 Tab 就弹出自定义字段,得靠 __annotations__、__getattr__ 配合类型注解,或者生成 stub 文件。
-
__dir__是运行时机制,不改变 IDE 的静态符号表 - IPython 中
obj.会调用obj.__dir__(),所以这里最管用 - 返回值必须是
list或可迭代对象,且每个元素都得是str
如何正确实现 __dir__ 并避免常见错误
直接覆盖 __dir__ 时,容易忽略父类已有属性,导致内置方法(如 __str__、__repr__)从补全中消失;也可能返回非字符串项,引发 TypeError。
- 务必调用
super().__dir__()获取默认列表,再增删 - 所有新增项必须是
str,不能是int或未str()转换的符号 - 避免返回重复项,否则 IPython 可能去重但行为不可控
- 不要在
__dir__里做耗时操作(如网络请求、文件读取),它可能被高频调用
示例:
class Config:
def __init__(self):
self.host = "localhost"
self.port = 8000
<pre class="brush:php;toolbar:false;">def __dir__(self):
# 先拿到默认列表
result = set(super().__dir__())
# 手动添加动态字段(确保是 str)
result.update(["timeout", "retries"])
# 过滤掉私有/特殊方法,可选
result = {x for x in result if not x.startswith("_") or x in ("__init__", "__dir__")}
return sorted(result)
__dir__ 和 __getattr__ 配合使用才完整
仅靠 __dir__ 添加名称,不代表这些名称真能访问——如果没对应逻辑,访问时仍会抛 AttributeError。要让补全“可用”,必须和 __getattr__(或 __getattribute__)配合。
-
__dir__控制“显示什么”,__getattr__控制“点出来后怎么响应” - 两者返回的名称最好一致,否则用户看到补全项却访问失败,体验割裂
- 注意:若用了
__getattribute__,它会在每次属性访问时触发,比__getattr__更重,慎用
延续上例:
def __getattr__(self, name):
if name == "timeout":
return 30
elif name == "retries":
return 3
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
在 Jupyter/IPython 中验证是否生效
这是唯一能直观看到 __dir__ 效果的主流环境。别在 .py 文件里写完就跑单元测试——先打开 notebook 或 IPython,实例化后按 Tab 看。
- 启动 IPython 后,输入
cfg = Config(),再输cfg.+Tab,应出现timeout和retries - 执行
dir(cfg)应包含这两个字符串,且不含你不想要的私有名 - 如果 Tab 没反应,检查是否忘了
return,或返回了tuple却含非str项(会静默失败或报错)
复杂点在于:某些动态字段可能依赖实例状态(如根据 self.mode 决定暴露哪些键),这时 __dir__ 里需要读取当前状态,但要注意避免触发意外的属性访问链(比如 self.x 触发了另一个 __getattr__)。这种情况下,建议缓存或加保护逻辑。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











