一开始没打算写这篇文章的,今天整理笔记看到了偏函数的帖子在拿装饰器举例对比。然后就想到既然装饰器本身也是函数,为啥不可以给装饰器也设置上参数,来进一步提高装饰器的复用性呢?于是把思路整理了一下,就有了本文。
在我的另一篇关于装饰器的文章中有跟大家提到过,装饰器实际上就是一个闭包函数,在闭包函数的内部函数中添加装饰的内容并调用被装饰的函数。以达到 fun = decorator(fun) 的效果:
def decorator(fun):
def inner(name):
print("Hi")
fun(name)
return inner
@decorator
def get_last_name(name):
print(f"你就是小{name[0]}吧")
get_last_name("王富贵")
从上面的例子中我们可以简单的总结出装饰器不同参数设置的规律:装饰器外部的函数传的参数是被装饰函数的函数名,内部函数的参数是被装饰函数的参数。使用装饰器时,被装饰函数的参数name是先传递给装饰器的内部函数,内部函数调用被装饰函数时将参数name传递给被装饰函数。而外部函数接收的参数时被装饰函数的函数名,注意,外部函数的参数只能有一个也只能是被装饰函数的函数名。
"""错误写法"""
def decorator(fun,*args_outer):
def inner(*args_inner):
pass
一个很简单的道理,装饰器的用法是@+装饰器名+被装饰函数。其工作原理就是fun = decorator(fun) ,即装饰器接收的第一个参数就必须是被装饰函数名,如果设置其他参数的话在第一个参数都缺失的情况下怎么传?不过并不是完全没办法,换一换思路就可以了,下面就是本文要讲的干货了
我们都知道,装饰器就是一个闭包函数,外部接收被装饰函数名,内部装饰再调用被装饰函数。那么我们为啥不在这个闭包函数的外部再嵌套一层函数用来接收自己想要的参数呢?废话不多说,举例子上代码:
"""假设一家烧烤店可以送外卖,外卖比堂食要多出打包盒跟配送费两项费用"""
def decorator_outer(*args_outer): # 接收正常在店内消费之外的其他费用
def decorator_inner(fun):
def inner(*args_inner): # 接收在正常参数的费用
new_args = args_outer + args_inner
return fun(*new_args)
return inner
return decorator_inner
@decorator_outer(3,5) # 打包盒3元,配送费5元
def monetary(*args): # 计算每份菜品价格之和
themoney = 0
for m in args:
themoney += m
return themoney
上一次的文章说到,闭包调用内部函数的方法是decorator()(),那么类似上述代码中多嵌套了一层就自然是decorator_outer()()()。也就是说按照装饰器的工作原理,上述代码的工作原理就是 monetary = decorator_outer(3,5)(monetary)。小小的提醒一下,闭包嵌套层次越多就一定要注意看看被装饰函数的返回值有没有被一层层的返回出来,不然最后返回一个空值又不报错很难排查。