类装饰器与函数装饰器的一些应用,描述符装饰器

类装饰器包含普通类作为一个装饰器,

同时描述符类作为一个装饰器的2种方式;

类装饰器需要小心使用的一点是当作用于另一个类(被装饰的类)方法时, 被装饰的类的self传递的问题(见下面例子)

结论:如果要编写一个既适用函数也适用类方法的装饰器,要么是用函数装饰器,要么是用描述符装饰器


#类装饰器与函数装饰器的一些错误应用
#以下2个tracer 功能一样
#类装饰器
class tracer:
    def __init__(self,func):    #接受一个函数对象
        self.calls = 0
        self.func = func
    def __call__(self, *args, **kwargs):
        self.calls += 1
        print('类装饰器:call %s to %s'%(self.calls,self.func.__name__),',args:',*args)
        return self.func(*args,**kwargs)        #调用函数
#函数装饰器
def tracer_func(func):
    calls = 0   # 调用次数
    def on_call(*args, **kwargs):
        nonlocal calls
        calls += 1
        print('函数装饰器:call %s to %s' % (calls, func.__name__), ',args:', *args)
        return func(*args,**kwargs)
    return on_call


#类装饰器作用于函数上
@tracer
def testFunc(x,y):          #此时testFunc是一个tracer实例
    print(x,y)
testFunc(1,2)           #ok,调用tracer.__call__()
#echo:
#call 1 to testFunc
#def spam: 1 2

#类装饰器作用于类方法上
class Person:
    def __init__(self,name='person'):
        self.name = name
    @tracer
    def lastName(self):             #lastName 是一个tracer实例
        return self.name.split()[-1]
p = Person()
#print(p.lastName())
#这里将会出错
#lastName是一个tracer实例,lastName(),相当于tracer(),tracer实例本身作为self传递进了__call__ ,而Person实例并未传递进去

#函数装饰器作用于类方法
class Person1:
    def __init__(self,name='person'):
        self.name = name
    @tracer_func
    def lastName(self):            #lastName = tracer_func(lastName),
        return self.name.split()[-1]
p = Person1()
print(p.lastName())                 #调用on_call , p 被传入进*args
#使用描述符作为装饰器
#使用描述符来作装饰器的理由是__get__(self,instance,owner)
#能够接受2个实例对象, 一个是描述符本身,还有一个是被装饰的对象
#描述符对象作装饰器能弥补普通类作装饰器无法很好的作用于类的方法上
class tracer:
    def __init__(self,func):            #描述符接受一个函数对象
        self.calls = 0
        self.func = func
    def __call__(self, *args, **kwargs):       #能用于函数装饰器
        self.calls += 1
        print('tracer call %s to %s'%(self.calls,self.func.__name__), ',args:' , args)
        return self.func(*args,**kwargs)
    def __get__(self, instance, owner):        #__get__ 获取2个实例对象,1个self(描述符对象) , instance(被装饰的对象)
        print('__get__')
        return wrapper(self,instance)         #返回一个临时对象

#包装类,接受2个参数,1个是描述符对象,用于描述符__get__的返回值
class wrapper:
    def __init__(self,desc,instance):
        self.desc = desc
        self.instance = instance
    def __call__(self, *args, **kwargs):      #用于函数调用
        return self.desc(self.instance,*args,**kwargs)  #调用tracer的__call__
class Person:
    def __init__(self, name='person'):
        self.name = name
    @tracer
    def lastName(self):  # lastName 是一个tracer实例
        return self.name.split()[-1]
p = Person()
print(p.lastName())


描述符作为装饰器的另一种方法, 把tracer描述符类修改__get__ 中的一些代码,更好的实现:

class tracer:
    def __init__(self,func):            #描述符接受一个函数对象
        self.calls = 0
        self.func = func
    def __call__(self, *args, **kwargs):       #能用于函数装饰器
        self.calls += 1
        print('tracer call %s to %s'%(self.calls,self.func.__name__), ',args:' , args)
        return self.func(*args,**kwargs)
    def __get__(self, instance, owner):        #__get__ 获取2个实例对象,1个self(描述符对象) , instance(被装饰的对象)
        print('__get__')
        def wrapper_func(*args, **kwargs):      #一个内部函数 . 通过闭包获取参数 
            return self(instance,*args,**kwargs)
        return wrapper_func                     #返回闭包



你可能感兴趣的:(py)