携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情
0 环境
- 编辑器:pycharm或vscode
- 系统版本:windows10
- python版本:3.9.6
1 多参数的问题
本来我们带参数的装饰器里只有一个s,作为对比时间的值,可以加入多个参数,可以传入函数吗,可以的。现在呢,假如哪一天,我想对wrap里的打印结果做一些变化,常规方式是直接找到wrap里的print,然后修改,若多来几次呢,直接修改它,会很繁琐,若是再最外层加个自定义的函数,来动态使用它,这样岂不是很省事,如下代码:将之前的打印抽离成匿名函数,然后wrap里的if判断成功后,调用这个匿名函数,其他没有任何的改动,很方便。
def logger3(s, show=lambda name, second: print(f"call {name} took {second} ")):
def _logger(fn):
@functools.wraps(fn)
def wrap(*args, **kwargs):
start = datetime.datetime.now()
ret = fn(*args, **kwargs)
end = datetime.datetime.now()
if (end -start).total_seconds() > s:
show(fn.__name__, end - start)
return ret
return wrap
return _logger
@logger3(2)
def sleep2(val):
time.sleep(val)
if __name__ == '__main__':
sleep2(2)
假如我不想时间值和匿名函数放在一起,我继续拆分,在logger3(最外层)和装饰器中间在加一层,可以再次调吗?三层装饰器的代码如下:pycharm会直接报错不支持,报错信息如下图。
def logger4(s):
def __logger(show=lambda name, second: print(f"call {name} took {second} ")):
def _logger(fn):
@functools.wraps(fn)
def wrap(*args, **kwargs):
start = datetime.datetime.now()
ret = fn(*args, **kwargs)
end = datetime.datetime.now()
if (end - start).total_seconds() > s:
show(fn.__name__, end - start)
return ret
return wrap
return _logger
return __logger
@logger3(2)()
def sleep3(val):
time.sleep(val)
if __name__ == '__main__':
sleep3(2)
但是我想让上面那个代码跑起来,该怎么改呢,只需要给sleep3上@logger4t,需要注意的是,先要将logger4(2)赋值一个变量,然后@变量()才可以使用,这种方式就是继续柯里化了。
logger4t = logger4(2)
@logger4t()
def sleep3(val):
time.sleep(val)
3 总结
带参数的装饰器,老实讲七绕八绕的,为了不被绕晕了,我们只需要定睛在装饰器的定义上,紧跟定义来,它的定义呢,参数是函数,返回值是函数,高阶函数的两大特点,它都具备了,然后一种是调用赋值给变量的方式,用到时在赋值,还有一种是在要用到的函数上加@,算是提前安装好,拿来即用。回忆之前我们学到最简单的那个例子,按照执行过程,先是最外层的参数s,然后是fn函数,最后是*args, **kwargs,拼在一起是不是就是logger(2)(sleep)(2),这样一拆分就一目了然了。