首页 新闻 会员 周边 捐助

__getattr__(self, name):作用

0
[已关闭问题] 关闭于 2025-08-25 14:55
def decorator(cls):
    class wrapper():
        def __init__(self, *args, **kwargs):
            self.wrapped = cls(*args, **kwargs)

        def __getattr__(self, name):
            return getattr(self.wrapped, name)
    return wrapper

@decorator
class cls():
    def __init__(self, x, y):
        self.attrx = x
        self.attry = y
    def method(self):
        return self.attrx, self.attry

c = cls(3, 4)
print(c.attrx)
print(c.attry)
print(c.method())
中的这段代码是什么意思,在上面代码中起到什么作用
        def __getattr__(self, name):
            return getattr(self.wrapped, name)
_java_python的主页 _java_python | 小虾三级 | 园豆:984
提问于:2025-08-25 14:42
< >
分享
所有回答(1)
0
这段代码中的 __getattr__ 方法是实现装饰器功能的关键部分,它起到了属性委托的作用。让我详细解释它的功能和意义:

代码功能概述
这是一个类装饰器,它包装了原始类,创建了一个代理类(wrapper),将所有属性和方法访问委托给被包装的类的实例。
def __getattr__(self, name):
    return getattr(self.wrapped, name)
这行代码的作用是:

属性访问拦截:当访问 wrapper 实例上不存在的属性时,Python 会自动调用 __getattr__ 方法
属性委托:将属性访问请求转发给被包装的实例 (self.wrapped)
透明代理:使得 wrapper 实例表现得就像原始类的实例一样
当你执行 c.attrx 或 c.method() 时:

Python 首先在 wrapper 实例中查找 attrx 或 method

由于 wrapper 实例没有这些属性(只有 wrapped 属性),Python 调用 __getattr__ 方法

__getattr__ 使用 getattr(self.wrapped, name) 从被包装的实例中获取属性

返回被包装实例的属性值或方法
  

想给类添加日志功能:
def log_decorator(cls):
    class Wrapper:
        def __init__(self, *args, **kwargs):
            self.wrapped = cls(*args, **kwargs)
            print(f"Created instance of {cls.__name__}")
            
        def __getattr__(self, name):
            attr = getattr(self.wrapped, name)
            if callable(attr):
                # 如果是方法,返回一个带日志的包装版本
                def logged_method(*args, **kwargs):
                    print(f"Calling {name} with args: {args}, kwargs: {kwargs}")
                    return attr(*args, **kwargs)
                return logged_method
            return attr
            
    return Wrapper

@log_decorator
class MyClass:
    def method(self, x):
        return x * 2

obj = MyClass()
result = obj.method(5)  # 输出: Calling method with args: (5,), kwargs: {}

getattr 只在访问不存在的属性时调用

如果需要拦截所有属性访问(包括存在的属性),可以使用 getattribute

这种方法不会包装特殊方法(如 str, len 等),因为它们不通过 getattr 查找
这种模式有几个用途:

透明包装:可以在不修改原始类的情况下添加额外功能

访问控制:可以控制对原始类属性和方法的访问

延迟初始化:可以延迟创建被包装的实例直到真正需要时

适配器模式:可以使一个接口适配另一个接口

在你的代码中:

python
def __getattr__(self, name):
    return getattr(self.wrapped, name)
这段代码的工作流程是:

属性访问触发:当你访问 c.attrx 或 c.method() 时

查找过程:

Python 首先在 wrapper 实例的 __dict__ 中查找 attrx 或 method

由于 wrapper 实例只有 wrapped 属性,找不到请求的属性

调用 __getattr__:由于属性不存在,Python 调用 __getattr__ 方法

委托实现:__getattr__ 方法使用 getattr(self.wrapped, name):

self.wrapped 是被包装的原始类的实例

getattr() 函数在 self.wrapped 上查找请求的属性

返回找到的属性或方法

这种委托模式有效的原因是:

Python 的动态特性:Python 在运行时解析属性和方法

查找链机制:Python 的属性查找机制提供了 getattr 作为后备

getattr 函数:内置的 getattr() 函数可以动态获取对象的属性

方法绑定:Python 自动处理方法绑定,确保方法调用时的 self 参数正确

getattr() 函数的基本用法
getattr() 是 Python 的内置函数,其基本语法是:

python
getattr(object, name[, default])
object: 要从中获取属性的对象
name: 字符串形式的属性名
default (可选): 如果属性不存在时返回的默认值

在您的代码中:

python
def __getattr__(self, name):
    return getattr(self.wrapped, name)
这里的 name 是一个变量,它包含了被请求的属性名称(字符串形式)。

执行流程示例
当您执行 c.attrx 时:

Python 调用 c.__getattr__('attrx')

name 参数的值是字符串 'attrx'

getattr(self.wrapped, 'attrx') 从 self.wrapped 中获取 attrx 属性

当您执行 c.method() 时:

Python 首先调用 c.__getattr__('method') 获取方法

name 参数的值是字符串 'method'

getattr(self.wrapped, 'method') 从 self.wrapped 中获取 method 方法

然后您调用返回的方法:c.method()

name 参数的本质
name 不是一个固定的值如 'attrx',而是一个变量参数,它的值取决于您尝试访问的属性名称:

访问 c.attrx → name = 'attrx'

访问 c.attry → name = 'attry'

访问 c.method → name = 'method'

访问 c.any_other_attribute → name = 'any_other_attribute'

这种设计模式有效的原因是:

动态属性访问:getattr() 允许通过字符串名称动态访问属性

委托模式:包装类将所有未知的属性访问委托给被包装的对象

透明代理:使用者不需要知道他们正在与包装对象交互,可以像直接使用原始对象一样操作

因为使用者 完全感觉不到代理的存在。
对调用方来说,就像直接在操作 原对象;
实际上请求被 wrapper 无缝转发 到 self.wrapped,“透明” 指这层转发 不可见、不干扰。

实际应用场景
这种技术常用于:

装饰器/包装器:在不修改原始类的情况下添加功能

适配器模式:使一个接口适配另一个接口

延迟加载:只有在实际需要时才创建或加载对象

访问控制:控制对对象属性和方法的访问权限

总结来说,getattr(self.wrapped, name) 中的 name 是一个变量参数,它的值取决于您尝试访问的属性名称,这使得 __getattr__ 方法能够动态地将任何属性访问请求转发给被包装的对象。
name 参数可以完全替换为任何其他有效的变量名。它只是一个形式参数(形参),用于接收传递给 __getattr__ 方法的属性名称。
虽然技术上可以使用任何名称,但选择有意义的参数名是良好的编程实践:

描述性名称:如 attribute_name, prop_name, attr 等,使代码更易读

约定俗成:在 Python 社区中,name 是常见的参数名,用于表示属性或方法的名称

一致性:保持与 Python 标准库和其他代码的一致性
_java_python | 园豆:984 (小虾三级) | 2025-08-25 14:55
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册