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)
这段代码中的 __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 标准库和其他代码的一致性