python笔记 带参装饰器上

56 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情

0 环境

  • 编辑器:idea或vscode
  • 系统版本:windows10
  • python版本:3.9.6

1 装饰器的副作用

import datetime
import time

def logger1(fn):
    def wrap(*args, **kwargs):
        print("sleep.__name__ -->", sleep.__name__)
        start = datetime.datetime.now()
        ret = fn(*args, **kwargs)
        end = datetime.datetime.now()
        print("{} called took {}".format(fn.__name__, end - start))
        return ret
    return wrap

@logger1
def sleep(val):
    time.sleep(val)

if __name__ == '__main__':
    sleep(1)

image.png

当我用把装饰器装到sleep函数上后,使用sleep函数后,它的__name__的值,竟然是wrap,而且无论我放在装饰器中,还是sleep函数中都是这个结果。怎样修改呢,如下代码:添加了一个async_properties函数,并且为其重新赋值。这样我们的原函数都能知道它的名字和doc了。

def logger1(fn):
    def wrap(*args, **kwargs):
        print("sleep.__name__ -->", sleep.__name__)
        start = datetime.datetime.now()
        ret = fn(*args, **kwargs)
        end = datetime.datetime.now()
        print("{} called took {}".format(fn.__name__, end - start))
        return ret

    async_properties(fn, wrap)

    return wrap

def async_properties(source, target):
    target.__name__ = source.__name__
    target.__doc__ = source.__doc__

image.png

2 柯里化

柯里化就是这样子的,是不是看着很眼熟,一股浓浓的装饰器的味道,确实返回的是一个装饰器。

def async_properties1(source):
    def _assign(target):
        target.__name__ = source.__name__
        target.__doc__ = source.__doc__
        return target
    return _assign

改造我们的代码,如下代码:

def logger1(fn):
    @async_properties1(fn)
    def wrap(*args, **kwargs):
        print("sleep.__name__ -->", sleep.__name__)
        start = datetime.datetime.now()
        ret = fn(*args, **kwargs)
        end = datetime.datetime.now()
        print("{} called took {}".format(fn.__name__, end - start))
        return ret

    # async_properties(fn, wrap)
    return wrap

def async_properties1(source):
    def _assign(target):
        target.__name__ = source.__name__
        target.__doc__ = source.__doc__
        return target
    return _assign

image.png 看到最终的结果是不是就能猜到是说的带参数的装饰器了。

2 总结

肯定会疑问柯里化(带参数的装饰器)和之前安装在要使用装饰器的函数的区别是吧,为啥之前那种方式sleep上面@xxxx不需要传参,而现在需要传参,仅个人理解哈,就是之前的那种不需要传参的方式,logger1最外层的参数就是sleep,直接帮我们把sleep传过去了,而本来是在wrap函数下调用async_properties1(fn)(wrap),现在用柯里化的方式@async_properties1(fn),反而缺少了wrap函数的代入,盲猜它把wrap自动代入进入了。当然这里的wrap,要放在最后,有点关键字的味道了,不然打印结果的是控制台无任何输出。