ZetCode

Python __dir__ 方法

最后修改于 2025 年 4 月 8 日

本综合指南探讨 Python 的 __dir__ 方法,该特殊方法用于自定义属性列表。我们将介绍基本用法、自定义实现、继承行为和实际示例。

基本定义

__dir__ 方法返回对象的有效属性列表。 它由内置的 dir() 函数调用,以获取对象的属性名称。

主要特征:它应该返回一个字符串列表,有助于内省,并且可以被重写以自定义属性可见性。 未定义时,Python 提供默认实现。

基本 __dir__ 实现

这是一个简单的类,实现了 __dir__ 来演示其基本行为。 这展示了它如何与 dir() 函数交互。

basic_dir.py
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __dir__(self):
        return ['name', 'age', 'greet']
    
    def greet(self):
        return f"Hello, I'm {self.name}"

p = Person("Alice", 30)
print(dir(p))  # Shows ['age', 'greet', 'name']

此示例显示了一个自定义 __dir__ 实现,该实现显式列出了可用的属性。 无论可能存在的其他属性如何,输出都与返回的列表匹配。

请注意,默认实现将包括更多属性,例如 __class____dict__。 我们的自定义版本将可见性限制为仅指定的名称。

与默认属性结合

通常,您希望同时包含自定义属性和 Python 的默认属性。 此示例展示了如何将它们结合起来。

combined_dir.py
class Vehicle:
    def __init__(self, make, model):
        self.make = make
        self.model = model
    
    def __dir__(self):
        # Get default attributes
        default = super().__dir__()
        # Add our custom attributes
        custom = ['make', 'model', 'info']
        return sorted(set(default + custom))
    
    def info(self):
        return f"{self.make} {self.model}"

v = Vehicle("Toyota", "Corolla")
print(dir(v))  # Includes both default and custom attributes

此实现首先使用 super().__dir__() 获取默认属性,然后将它们与自定义属性结合。 set() 确保没有重复项,而 sorted() 提供一致的排序。

当您想要保持标准的 Python 行为,同时向列表中添加特定属性时,此模式非常有用。

动态属性列表

__dir__ 可以根据对象状态或其他条件动态生成属性名称。 此示例显示了动态属性生成。

dynamic_dir.py
class Config:
    def __init__(self):
        self._settings = {
            'debug': False,
            'log_level': 'INFO',
            'timeout': 30
        }
    
    def __dir__(self):
        base = super().__dir__()
        settings = [f"get_{k}" for k in self._settings]
        settings += [f"set_{k}" for k in self._settings]
        return sorted(set(base + settings))
    
    def __getattr__(self, name):
        if name.startswith('get_'):
            key = name[4:]
            return lambda: self._settings.get(key)
        elif name.startswith('set_'):
            key = name[4:]
            return lambda v: self._settings.update({key: v})
        raise AttributeError(name)

c = Config()
print(dir(c))  # Shows get_* and set_* methods for each setting

Config__dir__ 列出这些动态方法,以使其可以通过 dir() 和 IDE 自动完成功能发现。

__getattr__ 方法处理实际的属性访问,而 __dir__ 确保这些动态属性出现在列表中。

过滤属性

__dir__ 还可以过滤属性,以从属性列表中隐藏实现细节或敏感数据。

filtered_dir.py
class SecureData:
    def __init__(self, public, secret):
        self.public_data = public
        self._secret_data = secret
    
    def __dir__(self):
        return [attr for attr in super().__dir__() 
                if not attr.startswith('_') or attr == '__dir__']

sd = SecureData("Open info", "Top secret")
print(dir(sd))  # Doesn't show _secret_data

此实现过滤掉大多数以下划线开头的名称(在 Python 中被认为是私有的),除了像 __dir__ 本身这样的特殊方法。

此模式对于创建更清晰的公共接口,同时将内部实现细节隐藏在随意检查之外非常有用。

继承行为

此示例演示了 __dir__ 如何与继承一起工作,以及如何在子类中正确扩展它。

inheritance_dir.py
class Base:
    def __dir__(self):
        return ['base_attr', 'common_method']

class Derived(Base):
    def __init__(self):
        self.derived_attr = "value"
    
    def __dir__(self):
        base_attrs = super().__dir__()
        derived_attrs = ['derived_attr', 'new_method']
        return sorted(set(base_attrs + derived_attrs))
    
    def new_method(self):
        pass

d = Derived()
print(dir(d))  # Shows attributes from both classes

Derived 类将其自身的属性与来自 Base 类的属性结合。 使用 super().__dir__() 确保正确的继承行为。

在创建类层次结构时,此模式很重要,其中每个类都将其自身的属性添加到父类提供的属性中。

最佳实践

资料来源

作者

我叫 Jan Bodnar,是一位热情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直在撰写编程文章。 迄今为止,我撰写了超过 1,400 篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 Python 教程