Python类装饰器跟踪委托对象的属性访问

发布时间:2024年01月12日

1 Python类装饰器跟踪委托对象的属性访问

类装饰器通过添加逻辑层,管理实例接口的访问。

通过委托的方式类跟踪对象属性的访问。

通过拦截未定义属性的访问,并且转向委托对象的属性访问,来实现委托。

1.1 非装饰器委托

描述

python通过__getattr__()拦截未定义属性,再使用getattr()访问委托对象的属性,实现委托对象的访问。

(1) 定义一个类,构造函数内将委托对象赋值给实例属性;

(2) 重载__getattr__()方法,拦截未定义属性的点号运算,使用getattr()转到委托对象的属性访问;已定义属性的点号运算不会被拦截;

示例

>>> class MyWrapper:
    def __init__(self,obj):
        # 将委托对象赋值给实例属性
        self.obj=obj
    def __getattr__(self,attr):
        # 拦截未定义属性的点号运算
        print('atrr:',attr)
        # 通过委托对象获取属性
        return getattr(self.obj,attr)

    
>>> mwl=MyWrapper(list('梯阅线条'))
>>> mwl.append(9555)
atrr: append
# 已定义属性不触发 __getattr__() 方法
>>> mwl.obj
['梯', '阅', '线', '条', 9555]
>>> mwd=MyWrapper({'a':1,'b':2})
>>> tuple(mwd.keys())
atrr: keys
('a', 'b')

1.2 类装饰器委托

描述

python可以通过函数编写的类装饰器来实现委托,进行跟踪委托对象的属性访问。

(1) 定义一个函数为类装饰器,参数接收一个类,内部定义一个类,并且返回这个类;

(2) 类装饰器需要返回可调用对象,因为装饰类创建实例对象时,会调用()小括号;

(3) 将装饰类的实例对象赋值给装饰器的实例属性;

(4) 装饰器重载__getattr__()拦截未定义属性的点号运算,即拦截装饰类实例属性;

(5) 将未定义属性通过getattr()转到装饰器类实例的属性获取实现委托;

示例

>>> def DecoWrapper(cls):#类装饰器接收一个类参数
    class MyWrapper:
        def __init__(self,*args,**kargs):
            # 将装饰类的实例对象赋值给装饰器的实例属性
            self.cls=cls(*args,**kargs)
            self.gets=0
        def __getattr__(self,attr):#拦截未定义属性的点号运算
            print('atrr:',attr)
            self.gets+=1
            # 将未定义属性通过装饰类的实例对象进行获取实现委托
            return getattr(self.cls,attr)
    return MyWrapper#类装饰器返回可调用对象

>>> @DecoWrapper
class TestDW:
    def doubles(self):
        print('梯阅线条'*2)

        
>>> @DecoWrapper
class Clerk:
    def __init__(self,name,rate,days):
        self.name=name
        self.rate=rate
        self.days=days
    def pay(self):
        return self.rate*self.days

# 通过类名()的方式创建类的实例对象
# 即()小括号运算创建实例对象,触发__init__()构造方法 
>>> tdw1=TestDW()
>>> tdw1.doubles()
atrr: doubles
梯阅线条梯阅线条
>>> tdw1.gets
1
>>> c1=Clerk('梯阅线条',2000,22)
>>> c2=Clerk(9555,3000,23)
>>> c1.name,c1.pay()
atrr: name
atrr: pay
('梯阅线条', 44000)
>>> c2.name,c2.pay()
atrr: name
atrr: pay
(9555, 69000)
>>> c1.gets,c2.gets
(2, 2)
>>> c1.name
atrr: name
'梯阅线条'
>>> c1.gets,c2.gets
(3, 2)

1.3 类装饰器-类无法保存多个实例

描述

类装饰器用类实现的时候,同一个装饰类无法保存多个装饰类的实例对象。

因为用类实现类装饰器时,重载__call__()方法,返回装饰器实例对象,创建多个相同装饰类的实例对象时,相当于执行同一个装饰器实例对象,并且只保存了最后一个实例的属性。

示例

>>> class DecoWrapper:
        def __init__(self,cls):
            # 将装饰类赋值给装饰器的实例属性
            self.cls=cls
            self.gets=0
        def __call__(self,*args):
            # 装饰类小括号()运算触发 __call__() 方法
            # 装饰类实例对象赋值给装饰器类实例属性
            self.wrapped=self.cls(*args)
            # 需返回装饰器实例对象,
            # 否则后续装饰类实例对象无法进行属性拦截运算
            return self
        def __getattr__(self,attr):#拦截未定义属性的点号运算
            print('atrr:',attr)
            self.gets+=1
            # 将未定义属性通过装饰类的实例对象进行获取实现委托
            return getattr(self.wrapped,attr)

# @DecoWrapper 将 DecoWrapper 类的实例对象赋值给 Clerk
# 触发 DecoWrapper 的 __init__()方法
>>> @DecoWrapper
class Clerk:
    def __init__(self,name,rate,days):
        self.name=name
        self.rate=rate
        self.days=days
    def pay(self):
        return self.rate*self.days


# Clerk()小括号运算触发 __call__()方法 
>>> c1=Clerk('梯阅线条',2000,22)
>>> id(c1)
68722448
# 属性点号运算触发 __getattr__() 方法
>>> c1.name,c1.pay()
atrr: name
atrr: pay
('梯阅线条', 44000)
# Clerk没变化,相当于再次调用 __call__() 方法
# 修改了 self.wrapped 的属性值
>>> c2=Clerk(9555,3000,23)
>>> id(c2)
68722448
# c1 和 c2 指向同一个对象
# 都指向修改后的 self.wrapped 的属性值
>>> id(c1)==id(c2)
True
>>> c2.name,c2.pay()
atrr: name
atrr: pay
(9555, 69000)
# c1的值被c2修改
>>> c1.name,c1.pay()
atrr: name
atrr: pay
(9555, 69000)
文章来源:https://blog.csdn.net/sinat_34735632/article/details/135563041
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。