python的带参装饰器

又是从做铺垫开始: 

*文档字符串(Documentation Strings)

def add(x, y):
     """This is a function for add"""  # 函数语块第一句;一般用三引号(因为习惯是多行的文本);可以使用.__doc__访问;惯例是首写是大写字母;
     return x+y

print("name={}\ndoc={}".format(add.__name__, add.__doc__))  
# 打印出:
add(x, y)
doc=This is a function of addition

  

在装饰器调用过程中,我们打印出这些函数属性信息会发现问题:

def logger(fn):
     def wrapper(*args, **kwargs):
          """I am wrapper"""
          print('begin')
          x = fn(*args, **kwargs)
          print('end')
          return x
     return wrapper

@logger  # add = logger(add)
def add(x, y):
     """This is a function for add"""
     return x+y

print("name={}\ndoc={}".format(add.__name__, add.__doc__))
# 打印出:
name=wrapper
doc=I am wrapper

我们打印出的add()函数的属性并不是add()函数定义时的属性信息,而是wrapper()函数里的属性信息。(因为@logger里将add重新赋值为wrapper)

 原函数对象的属性都被替换了,而使用装饰器,我们的需求是查看被封装函数的属性。怎么解决这个矛盾? ----> 提供一个函数,将被装饰函数的属性拷贝到装饰函数的属性内。
# 将原函数的__name__,__doc__属性赋值到新函数的属性值内
def copy_properties(src, dst):
    dst.__name__ = src.__name__
    dst.__doc__ = src.__doc__
  
def logger(fn):
     def wrapper(*args, **kwargs):
          """I am wrapper"""
          print('begin')
          x = fn(*args, **kwargs)
          print('end')
          return x
    copy_properties(fn, wrapper)
     return wrapper

@logger  # add = logger(add)
def add(x, y):
     """This is a function for add"""
     return x+y

print("name={}\ndoc={}".format(add.__name__, add.__doc__))
# 打印出:
name=add
doc=This is a function for add

通过copy_properties()函数,将被装饰函数的属性赋值给装饰函数。在刚了解完装饰器是对原函数起着增加其额外的功能的作用之后,我们是不是会想到这个copy_properties()函数也可以改造成一个装饰

器?

copy_properties()函数 --柯里化--> 加上语法糖形成装饰器

柯里化:

# copy_properties柯里化
def copy_properties(src):
    def _copy(dst):
        dst.__name__ = src.__name__
        dst.__doc = src.__doc__
        return dst
    return _copy

# 被装饰的logger函数
def logger(fn):
     def wrapper(*args, **kwargs):
          """I am wrapper"""
          print('begin')
          x = fn(*args, **kwargs)
          print('end')
          return x
     copy_propertites(fn)(wrapper)  # 柯里化后copy_propertites的调用
     return wrapper

# 调用
@logger  # add = logger(add)
def add(x, y):
     """This is a function for add"""
     return x+y

print("name={}\ndoc={}".format(add.__name__, add.__doc__))
# 打印出
name=add
doc=This is a function for add

 可以看见这里的柯里化会有些许不同的地方,之前是logger(fn)(*args, **kwargs),现在是copy_properties(fn)(wrapper)。之前是传入函数和函数的参数,现在是分别传入两个函数。

于是就形成了特殊的带参装饰器

# copy_properties柯里化
def copy_properties(src):
    def _copy(dst):
        dst.__name__ = src.__name__
        dst.__doc = src.__doc__
        return dst
    return _copy

# 被装饰的logger函数
def logger(fn):
    @copy_properties(fn) => copy_propertites(fn)(wrapper)  # 带参装饰器
     def wrapper(*args, **kwargs):
          """I am wrapper"""
          print('begin')
          x = fn(*args, **kwargs)
          print('end')
          return x
     return wrapper

# 调用
@logger  # add = logger(add)
def add(x, y):
     """This is a function for add"""
     return x+y

print("name={}\ndoc={}".format(add.__name__, add.__doc__))
# 打印出
name=add
doc=This is a function for add

 根据以上归纳总结带参装饰器的特点

它是一个函数(copy_properties),

函数作为它的形参(fn),

返回值是一个不带参的装饰器函数(wrapper),

使用@function_name(参数列表)语法糖方式调用(@copy_properties(fn))。

可以看作在装饰器外层又加了一层函数。

上述的利用带参装饰器保持被装饰函数的属性不变,在python中functools模块就是这样处理的。有兴趣的可以详细了解一下functools模块。

猜你喜欢

转载自www.cnblogs.com/hongdanni/p/10469712.html