Python __getattr__ 方法
最后修改于 2025 年 4 月 8 日
这份综合指南探讨了 Python 的 __getattr__ 方法,这是处理属性访问的特殊方法。我们将涵盖基本用法、动态属性、回退机制、代理模式和实际示例。
基本定义
当属性查找失败时,会调用 __getattr__ 方法。它充当属性访问的回退机制。该方法将属性名称作为参数,并且应返回一个值或引发 AttributeError。
关键特征:它仅针对缺失的属性调用,可以动态计算值,并且是 Python 属性访问协议的一部分。它与拦截所有属性访问的 __getattribute__ 不同。
基本的 __getattr__ 实现
这是一个简单的实现,展示了 __getattr__ 如何作为缺失属性的回退工作。 它演示了基本语法和行为。
class DynamicAttributes:
def __getattr__(self, name):
if name == 'color':
return 'blue'
raise AttributeError(f"'DynamicAttributes' has no attribute '{name}'")
obj = DynamicAttributes()
print(obj.color) # 'blue'
# print(obj.size) # Raises AttributeError
此示例显示 __getattr__ 处理对未定义属性的请求。 当访问 'color' 时,它返回 'blue'。 其他名称会引发 AttributeError,这是推荐的做法。
该方法接收属性名称作为字符串。 您可以实现任何逻辑来计算或获取该值。 始终为无效名称引发 AttributeError。
动态属性计算
__getattr__ 可以根据模式或命名约定动态计算属性。 这对于具有可预测属性名称的 API 很有用。
class DynamicCalculator:
def __getattr__(self, name):
if name.startswith('calc_'):
operation = name[5:]
def calculator(*args):
return f"Calculated {operation} of {args}"
return calculator
raise AttributeError(f"No such attribute: {name}")
calc = DynamicCalculator()
print(calc.calc_sum(1, 2, 3)) # "Calculated sum of (1, 2, 3)"
print(calc.calc_product(4, 5)) # "Calculated product of (4, 5)"
此类基于以 'calc_' 开头的属性名称动态创建计算方法。 返回的函数在其输出中使用操作名称。
该示例演示了 __getattr__ 如何按需创建并返回函数。 这种模式用于许多 ORM 和 API 包装器中。
延迟属性加载
__getattr__ 可以实现昂贵资源的延迟加载,仅在首次访问时才获取它们,并为将来使用进行缓存。
class LazyLoader:
def __init__(self):
self._cache = {}
def __getattr__(self, name):
if name not in self._cache:
print(f"Loading {name}...")
self._cache[name] = f"Value of {name}"
return self._cache[name]
loader = LazyLoader()
print(loader.data1) # "Loading data1..." then "Value of data1"
print(loader.data1) # "Value of data1" (from cache)
此实现仅在首次访问时才加载属性,并将它们存储在缓存字典中。 后续访问将返回缓存的值。
这种模式对于诸如数据库连接或文件加载之类的昂贵操作很有用。 它通过延迟工作直到需要时来优化性能。
代理模式实现
__getattr__ 可以将属性访问转发到另一个对象,从而实现代理模式。 这对于包装器和装饰器很有用。
class Proxy:
def __init__(self, target):
self._target = target
def __getattr__(self, name):
return getattr(self._target, name)
class Target:
def __init__(self, value):
self.value = value
def method(self):
return f"Target method: {self.value}"
target = Target(42)
proxy = Proxy(target)
print(proxy.value) # 42
print(proxy.method()) # "Target method: 42"
Proxy 类使用 getattr 将所有属性访问转发到其目标对象。 这会在目标周围创建一个透明的包装器。
代理可以在转发调用之前/之后添加行为。 这种模式用于模拟对象、远程处理和访问控制系统中。
类似字典的属性访问
__getattr__ 可以提供对数据的类似字典的访问,同时保持对象语法。 这会创建一个混合的对象-字典接口。
class DictLikeObject:
def __init__(self, data):
self._data = data
def __getattr__(self, name):
if name in self._data:
return self._data[name]
raise AttributeError(f"No attribute or key '{name}'")
data = {'name': 'Alice', 'age': 30, 'city': 'London'}
obj = DictLikeObject(data)
print(obj.name) # 'Alice'
print(obj.age) # 30
# print(obj.job) # Raises AttributeError
此类包装一个字典并将其键公开为属性。 它结合了字典存储和对象访问语法。
该实现会在引发 AttributeError 之前检查字典。 这种模式用于配置系统和 JSON 对象包装器中。
最佳实践
- 为缺失的属性引发 AttributeError: 遵循 Python 的约定
- 保持简单: 复杂的逻辑会使代码难以调试
- 记录动态属性: 帮助用户了解可用的内容
- 考虑性能: __getattr__ 比常规属性慢
- 使用 __dir__: 实现 __dir__ 以帮助进行内省
资料来源
作者
列出所有 Python 教程。