zoukankan      html  css  js  c++  java
  • 装饰器的入门到精通

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

    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

  • 相关阅读:
    Failed to start mysqld.service: Unit not found
    Nginx转发前后端分离跨域引发的问题-转发请求header头中含有下划线,无法转发取值
    云上Centos7新硬盘挂载流程
    马哥教育第二阶段考试
    Linux集群准备-同步
    Lucene查询语法
    权限系统设计
    docker compose thinkphp5.1 lnmp环境搭建加项目部署全过程
    docker compose 的使用
    [转载]PHP-FPM
  • 原文地址:https://www.cnblogs.com/yaraning/p/11376533.html
Copyright © 2011-2022 走看看