Python __getattr__() 方法

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 属性的访问将直接返回已加载的数据,而不再重新加载,从而提高了效率。

上一篇: __call__()

下一篇: __setattr__()

给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号