python笔记 带参装饰器下

70 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 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)

image.png

假如我不想时间值和匿名函数放在一起,我继续拆分,在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)

image.png

但是我想让上面那个代码跑起来,该怎么改呢,只需要给sleep3上@logger4t,需要注意的是,先要将logger4(2)赋值一个变量,然后@变量()才可以使用,这种方式就是继续柯里化了。

logger4t = logger4(2)
@logger4t()
def sleep3(val):
    time.sleep(val)

image.png

3 总结

带参数的装饰器,老实讲七绕八绕的,为了不被绕晕了,我们只需要定睛在装饰器的定义上,紧跟定义来,它的定义呢,参数是函数,返回值是函数,高阶函数的两大特点,它都具备了,然后一种是调用赋值给变量的方式,用到时在赋值,还有一种是在要用到的函数上加@,算是提前安装好,拿来即用。回忆之前我们学到最简单的那个例子,按照执行过程,先是最外层的参数s,然后是fn函数,最后是*args, **kwargs,拼在一起是不是就是logger(2)(sleep)(2),这样一拆分就一目了然了。