Python __getattr__() 语法
__getattr__() 是 Python 的一个特殊方法(又称 “魔法方法” 或 “双下划线方法”),它用于定义当尝试访问对象不存在的属性时,其行为是怎样的。
语法:
object.__getattr__(self, name)
说明:
__getattr__() 方法接收以下 2 个参数。
self(必选):是一个约定俗成的名称,表示实例对象。name(必选):是一个字符串,表示尝试访问的属性的名称。
__getattr__() 应该返回 name 对应的属性值。如果无法处理该属性,它必须抛出 AttributeError 异常。
对于 __getattr__() 方法来说,它常用于以下场景。
- 委托(Delegation):将属性访问转发给内部包含的其他对象。
- 动态生成属性:根据访问的属性名动态计算或生成属性值。
- 错误处理:提供更友好的错误消息,而不是默认的 AttributeError。
当我们通过 obj.attr 语法或 getattr(obj, 'attr') 函数访问对象的属性时,如果该属性在对象的 __dict__ 或其继承链中找不到,则 Python 会自动调用 obj.__getattr__('attr') 方法。
注意:__getattr__() 只有在正常属性查找失败时才会被调用。也就是说,如果一个属性已经存在于对象的 __dict__ 或其类的继承链中,__getattr__() 不会被触发。
Python __getattr__() 摘要
| 属于 | Python 魔法方法 |
|---|---|
| 使用频率 | 低 |
| 官方文档 | 查看 |
| 相关方法 | __setattr__()、__delattr__() |
Python __getattr__() 示例
接下来,我们通过几个简单的例子来讲解一下 Python __getattr__() 方法是如何使用的。
示例 1:__getattr__() 基本用法
class User:
def __init__(self, name):
self.name = name
def __getattr__(self, name):
if name == 'age':
return '未知年龄'
else:
# 如果不能处理,则抛出 AttributeError
raise AttributeError(f"'{self.__class__.__name__}' 对象没有属性 '{name}'")
user = User('Jack')
print(user.name)
print(user.age)
# 访问不存在的属性 “city”
try:
print(user.city)
except AttributeError as e:
print(e)
运行结果如下。
Jack
未知年龄
'User' 对象没有属性 'city'
分析:
在这个例子中,我们定义了一个 User 类,并重写了 __getattr__() 方法,以便在访问不存在的属性时返回一个默认值或执行特定操作。
- 当访问 user.name 时,name 属性存在,因此 __getattr__() 不会被调用。
- 当访问 user.age 时,age 属性不存在,因此 __getattr__() 被调用,并返回 '未知年龄'。
- 当访问 user.city 时,city 属性不存在,因此 __getattr__() 被调用。但由于我们没有为其定义特殊处理,所以它会抛出 AttributeError 异常。
示例 2:使用 __getattr__() 实现动态属性访问
class User:
def __init__(self, data):
self._data = data
def __getattr__(self, name):
# 动态获取属性 “name”
if name in self._data:
return self._data[name]
else:
raise AttributeError(f"'{self.__class__.__name__}' 对象没有不存在 '{name}' 属性")
data = {'name': 'Jack', 'age': 20, 'city': 'Guangzhou'}
user = User(data)
print(user.name)
print(user.age)
# 访问不存在的属性
try:
print(user.country)
except AttributeError as e:
print(e)
运行结果如下。
Jack
20
'User' 对象没有不存在 'country'属性
分析:
__getattr__() 可以用于实现动态生成属性,例如从字典中获取数据。这种方式可以实现类似 JavaScript 对象 那样的动态属性访问。
在这个例子中,User 类的实例并没有直接定义 name、age 等属性。当尝试访问这些属性时,__getattr__() 会被调用,它会尝试从内部的 _data 字典中查找并返回对应的值。
示例 3:__getattr__() 实现惰性加载
class LazyLoader:
def __init__(self, resource_name):
self._resource_name = resource_name
self._loaded_data = None
def _load_data(self):
# 模拟耗时操作,例如从文件或网络加载数据
import time
time.sleep(1)
return f"这是来自 '{self._resource_name}' 的加载数据"
def __getattr__(self, name):
if name == 'data':
if self._loaded_data is None:
self._loaded_data = self._load_data()
return self._loaded_data
else:
raise AttributeError(f"'{self.__class__.__name__}' 对象不存在 '{name}' 属性")
loader = LazyLoader('large_report.csv')
print(loader.data) # 第 1 次访问,会触发加载
print(loader.data) # 第 2 次访问,直接返回已加载的数据,不再触发加载
运行结果如下。
这是来自 'large_report.csv' 的加载数据
这是来自 'large_report.csv' 的加载数据
分析:
在某些场景下,属性的值可能比较耗费资源,我们希望只在第一次访问时才计算或加载它。__getattr__() 可以帮助实现这种惰性加载。
在这个例子中,LazyLoader 对象的 data 属性是惰性加载的。当我们第 1 次访问 loader.data 时,__getattr__() 会调用 _load_data() 方法来获取数据,并将其存储在 _loaded_data 中。然后后续对 data 属性的访问将直接返回已加载的数据,而不再重新加载,从而提高了效率。
