装饰器的入门到精通

关于装饰器,在面试时,经常会被问到这两个问题:

1、你都用过装饰器实现过什么样的功能?

2、请手写一个可以传参的装饰器?

这篇博客就根据这两个问题,带大家系统的学习装饰器的所有内容.希望对大家有所帮助.

  1. hello,装饰器
  2. 入门: 日志打印器
  3. 入门: 时间计时器
  4. 进阶: 带参数的函数装饰器
  5. 高阶: 不带参数的类装饰器
  6. 高阶: 带参数的类装饰器
  7. 使用偏函数与类实现装饰器
  8. 装饰类的装饰器
  9. wraps装饰器的作用
  10. 内置装饰器: property
  11. 其他装饰器: 装饰器实战

1. hello,装饰器

装饰器的使用方法很简单:

1. 先定义一个装饰器

2. 再定义你的业务函数或者类

3. 最后把装饰器加在这个函数上面

举个小栗子:

def decorator(func):
    def wrapper(*args, **kw):
        return func()
    return wrapper

@decorator
def function():
    print("hello, decorator")

实际上,装饰器并不是编码必须性,意思就是说,你不使用装饰器完全可以,它的出现,应该是使我们的代码

  • 更加优雅,代码结构更加清晰

  • 将实现特定的功能代码封装成装饰器,提高代码复用率,增强代码可读性

2. 入门: 日志打印器

 实现的功能:

1. 在函数执行前,先打印一行日志,通知要执行函数了

2. 函数执行完后,再打印一行日志,宣布函数执行完毕.

def log(func):  #装饰函数,参数 func 是被装饰的函数
    def inner(*args,**kwargs):
        print('start running: {} function'.format(func.__name__))
        res = func(*args,**kwargs)  # 执行主函数
        print('stop running')
        return res
    return inner

@log
def main():
    print('我是主函数')

输出结果:

start running: main function
我是主函数
stop running

3. 入门: 时间计时器

时间计时器,顾名思义,就是实现一个能够计算函数执行时长的功能.

import time

def timer(func):
    def inner(*args,**kwargs):
        start = time.time()
        func(*args,**kwargs)  #函数真正执行的地方
        print('执行了 {}s'.format(time.time()-start))  #计算时长
    return inner

@timer
def main(sleep_time):
    time.sleep(sleep_time)

main(10)

输出结果:

执行了 10.0073800086975098s

4. 进阶: 带参数的函数装饰器

 通过上面两个简单的入门实例,大家应该能体会到装饰器的工作原理了.

不过,装饰器的用法还远不止如此. 回过头去看看上面的例子,装饰器是不能接收参数的。其用法,只能适用于一些简单的场景。不传参的装饰器,只能对被装饰函数,执行固定逻辑。

装饰器本身是一个函数,做为一个函数,如果不能传参,那这个函数的功能就会很受限,只能执行固定的逻辑。这意味着,如果装饰器的逻辑代码的执行需要根据不同场景进行调整,若不能传参的话,我们就要写两个装饰器,这显然是不合理的。

比如说,我们要循环执行某一个函数,循环的次数是随机指定的.

def loop(count):
    def wrap(func):
        def inner(*args,**kwargs):
            for i in range(count):
                func(i)
        return inner
    return wrap

@loop(6)
def main(i):
    print('第 {} 次循环'.format(i+1))

main()

输出结果:

第 1 次循环
第 2 次循环
第 3 次循环
第 4 次循环
第 5 次循环
第 6 次循环

5. 高阶: 不带参数的类装饰器

以上都是基于函数实现的装饰器,在阅读别人代码时,还可以时常发现还有基于类实现的装饰器。

基于类装饰器的实现,必须实现 __call__ 和 __init__两个内置函数。
__init__ :接收被装饰函数
__call__ :实现装饰逻辑。

还是以日志打印这个例子为例.

class Log:
    def __init__(self,func):  #接收被装饰函数
        self.func = func
    def __call__(self, *args, **kwargs):  #实现装饰逻辑
        print('[INFO]: {} 正在执行'.format(self.func.__name__))
        res = self.func(*args,**kwargs)
        print('函数执行完毕')
        return res

@Log
def main():
    print('我是主函数')

main()

执行结果:

[INFO]: main 正在执行
我是主函数
函数执行完毕

6. 高阶: 带参数的类装饰器

 上面不带参数的例子,只能打印 INFO 级别的日志,正常情况下,我们还需要打印 DEBUG, WARNING 等级别的日志.这就需要给类装饰器传入参数,指定日志级别了.

带参数和不带参数的类装饰器有很大不同:

__init__ :不再接收被装饰函数,而是接收传入参数。
__call__ :接收被装饰函数,实现装饰逻辑。

class Logger():
    def __init__(self,level='INFO'):  #接收参数
        self.level = level
    def __call__(self, func):   # 接收被装饰函数,实现装饰逻辑
        def wrap(*args,**kwargs):
            print('[{}]正在执行 {}'.format(self.level,func.__name__))
            return func(*args,**kwargs)
        return wrap


@Logger(level='WARNING')
def main():
    print('我是主函数')

main()

执行结果:

[WARNING]正在执行 main
我是主函数

 

 

*参考: https://mp.weixin.qq.com/s/bOMlJNhRKW3vFkR9vIGhkQ

posted @ 2019-08-19 14:38  yara-ning  阅读(151)  评论(1编辑  收藏  举报