从小例子进入装饰器

统计一个函数执行耗时

  • 原始版本

     import time
    
     # time模块有提供时间相关函数
     def do_something():
         print("do_something")
         time.sleep(0.5)  # 让程序停止0.5秒模拟其它操作耗时
    
     start = time.time()
     do_something()
     print(time.time() - start)
     #result:
     # do_something
     # 0.5000283718109131

    问题:上述代码可以完成这个功能,但之后会发现,如果我们要统计其它函数,就必须在每个函数前后加入相应代码

  • 装饰器版本1(无参数)

     import time
    
     def execute_time(func):
         def inner():
             start = time.time()
             func()
             print(time.time() - start)
    
         return inner
    
     # time模块有提供时间相关函数
     def do_something():
         print("do_something")
         time.sleep(0.5)  # 让程序停止0.5秒模拟其它操作耗时
    
     do_something = execute_time(do_something)
     do_something()
     #result:
     # do_something
     # 0.5000283718109131

    从上述代码可以看到,使用了另一个函数execute_time()给我们要统计耗时的函数进行了包装,这时,这个execute_time()函数就叫做装饰器函数,而我们要统计的那个函数也就是do_something()函数就是被装饰的函数.问题:函数执行的时候实际上是调用的execute_time()函数中的inner()函数,这种方法虽然解决了原始版本的问题,但是当我们要统计的函数拥有返回值的时候,这时候我们获取不到返回值.

  • 装饰器版本2(有固定参数)

    import time
    
    def execute_time(func):
        def inner(do):
            start = time.time()
            result = func(do)
            print(time.time() - start)
            return result
        return inner
    
    # time模块有提供时间相关函数
    def do_something(do):
        print("do_something", do)
        time.sleep(0.5)  # 让程序停止0.5秒模拟其它操作耗时
        return 'do_something over'
    
    do_something = execute_time(do_something)
    print(do_something('say hello'))
    # result:
    # do_something say hello
    # 0.5000283718109131
    # do_something over

    为了解决装饰器版本1的问题,我在inner()函数里面加了个返回值.问题:当被装饰函数的参数个数与inner()参数个数不同时,这个装饰器就不适用了

  • 装饰器版本3(动态参数)

     import time
    
     def execute_time(func):
         def inner(*args, **kwargs):
             start = time.time()
             result = func(*args, **kwargs)
             print(time.time() - start)
             return result
    
         return inner
    
     # time模块有提供时间相关函数
     def do_something(do1,do2):
         print("do_something", do1,do2)
         time.sleep(0.5)  # 让程序停止0.5秒模拟其它操作耗时
         return 'do_something over'
    
     do_something = execute_time(do_something)
     print(do_something('say hello1','say hello2'))
     # result:
     # do_something say hello1 say hello2
     # 0.5000283718109131
     # do_something over

    在第七天内容中有个知识点是动态参数,刚好可以解决这个问题

  • 终极版本(语法糖@)

     import time
    
     def execute_time(func):
         def inner(*args, **kwargs):
             start = time.time()
             result = func(*args, **kwargs)
             print(time.time() - start)
             return result
    
         return inner
    
     @execute_time
     def do_something(do1,do2):
         print("do_something", do1,do2)
         time.sleep(0.5)  # 让程序停止0.5秒模拟其它操作耗时
         return 'do_something over'
    
     # do_something = execute_time(do_something)
     print(do_something('say hello1','say hello2'))
     # result:
     # do_something say hello1 say hello2
     # 0.5000283718109131
     # do_something over

    对于装饰器,python内部给我们提供了语法糖支持.在需要被装饰的函数名上部使用[@装饰器函数名称]即可,简化上述代码18行

装饰器进阶

获取被包装函数原生属性

  • 例1:常规函数取函数名

     def func():
         print('执行中')
         print(func.__name__)
    
     func()
     # result:
     #   执行中
     #   func

    常规函数可以通过函数的__name__属性可拿到当前函数名称

  • 例2:被装饰函数取函数名

     def wrapper(func):
         def inner():
             print('执行前')
             result = func()
             print('执行后')
             return result
         return inner;
     @wrapper
     def func():
         print('执行中')
         print(func.__name__)
    
     func()
     # result:
     #   执行前
     #   执行中
     #   inner
     #   执行后

    问题:通过执行结果会发现,结果中想获取的函数名是func,而实际结果是inner.原因是@wrapper进行包装相当于执行一个操作:func=wrapper(func)=inner

  • 解决

    使用functools模块

     from functools import wraps
    
     def wrapper(func):
         @wraps(func)
         def inner():
             print('执行前')
             result = func()
             print('执行后')
             return result
    
         return inner;
    
     @wrapper
     def func():
         print('执行中')
         print(func.__name__)
    
     func()
     # result:
     #   执行前
     #   执行中
     #   func
     #   执行后

    导入functools模块后通过第4行操作,会发现执行的函数即使被包装但还是能获取到它本身的属性

带参数的装饰器

  • 例1

     def wrapper(func):
         def inner():
             print('执行前')
             result = func()
             print('执行后')
             return result
    
         return inner;
    
     @wrapper
     def func_1():
         print('执行中')
    
     @wrapper
     def func_2():
         print('执行中')
    
     @wrapper
     def func_3():
         print('执行中')
     ...
     @wrapper
     def func_n():
         print('执行中')

    问题:通过上述代码会发现,有很多函数都用了同一个装饰器,如果有一天要取消这些函数上的装饰,就必须对每一个函数进行修改

  • 解决

    定义一个全局变量flag,并给原本装饰器外部再加一层函数用来接收参数,inner()函数内部通过外部参数flag判断被装饰函数的执行与否,修改flag即可控制装饰器的执行结果,如下:

     from functools import wraps
     flag = True
    
     def wrapper_out(flag):
         def wrapper(func):
             @wraps(func)
             def inner():
                 if (flag):
                     print('{}执行前'.format(func.__name__))
                     result = func()
                     print('{}执行后'.format(func.__name__))
                 else:
                     result = func()
                 return result
    
             return inner
    
         return wrapper
    
     @wrapper_out(flag)
     def func_1():
         print('{}执行中'.format(func_1.__name__))
    
     @wrapper_out(flag)
     def func_2():
         print('{}执行中'.format(func_2.__name__))
    
     @wrapper_out(flag)
     def func_3():
         print('{}执行中'.format(func_3.__name__))
    
     ...
    
     @wrapper_out(flag)
     def func_n():
         print('{}执行中'.format(func_n.__name__))
    
     func_1()
     func_2()
     func_3()
     func_n()
    
     #result:
         # func_1执行前
         # func_1执行中
         # func_1执行后
         # func_2执行前
         # func_2执行中
         # func_2执行后
         # func_3执行前
         # func_3执行中
         # func_3执行后
         # func_n执行前
         # func_n执行中
         # func_n执行后
    from functools import wraps
    flag = False
    
    def wrapper_out(flag):
        def wrapper(func):
            @wraps(func)
            def inner():
                if (flag):
                    print('{}执行前'.format(func.__name__))
                    result = func()
                    print('{}执行后'.format(func.__name__))
                else:
                    result = func()
                return result
    
            return inner
    
        return wrapper
    
    @wrapper_out(flag)
    def func_1():
        print('{}执行中'.format(func_1.__name__))
    
    @wrapper_out(flag)
    def func_2():
        print('{}执行中'.format(func_2.__name__))
    
    @wrapper_out(flag)
    def func_3():
        print('{}执行中'.format(func_3.__name__))
    
    ...
    
    @wrapper_out(flag)
    def func_n():
        print('{}执行中'.format(func_n.__name__))
    
    func_1()
    func_2()
    func_3()
    func_n()
    
    #result:
        # func_1执行中
        # func_2执行中
        # func_3执行中
        # func_n执行中

多个装饰器装饰同一个函数

  • 代码

     def wrapper1(func):
         def inner():
             print('wrapper1 ,before func')
             func()
             print('wrapper1 ,after func')
    
         return inner
    
     def wrapper2(func):
         def inner():
             print('wrapper2 ,before func')
             func()
             print('wrapper2 ,after func')
    
         return inner
    
     @wrapper1
     @wrapper2
     def f():
         print('in f')
    
     f()
     # result:
     # wrapper1 ,before func
     # wrapper2 ,before func
     # in f
     # wrapper2 ,after func
     # wrapper1 ,after func
  • 图解

    从上图可以看到,从1-9步是装饰器的装载过程,10-18步是执行过程.结论:多个装饰器装饰同一个函数时,装载顺序是从下到上,但执行顺序却是从上到下,可以理解为创建了一个装饰器栈(先进后出)

python基础(8)-装饰器函数&进阶的更多相关文章

  1. 十. Python基础(10)--装饰器

    十. Python基础(10)--装饰器 1 ● 装饰器 A decorator is a function that take a function as an argument and retur ...

  2. python基础之 装饰器,内置函数

    1.闭包回顾 在学习装饰器之前,可以先复习一下什么是闭包? 在嵌套函数内部的函数可以使用外部变量(非全局变量)叫做闭包! def wrapper(): money =10 def inner(num) ...

  3. Day11 Python基础之装饰器(高级函数)(九)

    在python中,装饰器.生成器和迭代器是特别重要的高级函数   https://www.cnblogs.com/yuanchenqi/articles/5830025.html 装饰器 1.如果说装 ...

  4. python基础--定义装饰器(内置装饰器)

    装饰器的定义: 装饰器本质上就是一个python函数,它可以让其它函数在不需要做任何代码改动的前提下增加额外的功能,装饰器的返回值也是一个函数对象.它经常用于有切面需求的场景中,比如-- >插入 ...

  5. [python基础]关于装饰器

    在面试的时候,被问到装饰器,在用的最多的时候就@classmethod ,@staticmethod,开口胡乱回答想这和C#的static public 关键字是不是一样的,等面试回来一看,哇,原来是 ...

  6. 1.16 Python基础知识 - 装饰器初识

    Python中的装饰器就是函数,作用就是包装其他函数,为他们起到修饰作用.在不修改源代码的情况下,为这些函数额外添加一些功能,像日志记录,性能测试等.一个函数可以使用多个装饰器,产生的结果与装饰器的位 ...

  7. python基础之装饰器(实例)

    1.必备 #### 第一波 #### def foo(): print 'foo' foo #表示是函数 foo() #表示执行foo函数 #### 第二波 #### def foo(): print ...

  8. 【Python基础】装饰器的解释和用法

    装饰器的用法比较简单,但是理解装饰器的原理还是比较复杂的,考虑到接下来的爬虫框架中很多用到装饰器的地方,我们先来讲解一下. 函数 我们定义了一个函数,没有什么具体操作,只是返回一个固定值 请注意一下缩 ...

  9. python之路——装饰器函数

    阅读目录 楔子 装饰器的形成过程 开放封闭原则 谈装饰器主要功能和装饰器固定结构 带参数的装饰器 多个装饰器装饰一个函数 返回顶部 楔子 作为一个会写函数的python开发,我们从今天开始要去公司上班 ...

随机推荐

  1. NO.1 hadoop简介

    第一次接触这个时候在网上查了很多讲解,以下很多只是来自网络. 1.Hadoop (1)Hadoop简介    Hadoop是一个分布式系统基础架构,由Apache基金会开发.用户可以在不了解分布式底层 ...

  2. am335x ubi Read-only mode

    是因为kernel里面有一个错误,要注释一下就好.

  3. Netty 学习笔记(1)通信原理

    前言 本文主要从 select 和 epoll 系统调用入手,来打开 Netty 的大门,从认识 Netty 的基础原理 —— I/O 多路复用模型开始.   Netty 的通信原理 Netty 底层 ...

  4. SpringBoot2.X + SpringCache + redis解决乱码问题

    环境:SpringBoot2.X + SpringCache + Redis Spring boot默认使用的是SimpleCacheConfiguration,使用ConcurrentMapCach ...

  5. tensorflow, TypeError:'Tensor' object is not callable

    解决办法:两个tensor相乘,需要加“*”.

  6. 认识 SSH 密钥对

    SSH 密钥对是阿里云为您提供的新的远程登录 ECS 实例的认证方式. 相较于传统的用户名和密码认证方式,SSH 密钥对有以下特点: 仅适用于 Linux 实例: SSH 密钥对登录认证更为安全可靠: ...

  7. u3d内嵌H5游戏 设置cookie

    Intent intent1 = getIntent(); Log.d("SS", "onCreate: "); String data = intent1.g ...

  8. python中导入一个需要传参的模块

    最近跑实验,遇到了一个问题:由于实验数据集比较多,每次跑完一个数据集就需要手动更改文件路径,再将文件传到服务器,再运行实验,这样的话效率很低,必须要专门看着这个实验,啥时候跑完就手动修改运行下一个实验 ...

  9. WEB服务器与应用服务器的区别

    一.简述 WEB服务器与应用服务器的区别: 1.WEB服务器: 理解WEB服务器,首先要理解什么是WEB?WEB可以简单理解为我们所看到的HTML页面就是WEB的数据元素,处理这些数据元素的应用软件就 ...

  10. CF3A Shortest path of the king

    The king is left alone on the chessboard. In spite of this loneliness, he doesn't lose heart, becaus ...