python基础(8)-装饰器函数&进阶
从小例子进入装饰器
统计一个函数执行耗时
原始版本
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)-装饰器函数&进阶的更多相关文章
- 十. Python基础(10)--装饰器
十. Python基础(10)--装饰器 1 ● 装饰器 A decorator is a function that take a function as an argument and retur ...
- python基础之 装饰器,内置函数
1.闭包回顾 在学习装饰器之前,可以先复习一下什么是闭包? 在嵌套函数内部的函数可以使用外部变量(非全局变量)叫做闭包! def wrapper(): money =10 def inner(num) ...
- Day11 Python基础之装饰器(高级函数)(九)
在python中,装饰器.生成器和迭代器是特别重要的高级函数 https://www.cnblogs.com/yuanchenqi/articles/5830025.html 装饰器 1.如果说装 ...
- python基础--定义装饰器(内置装饰器)
装饰器的定义: 装饰器本质上就是一个python函数,它可以让其它函数在不需要做任何代码改动的前提下增加额外的功能,装饰器的返回值也是一个函数对象.它经常用于有切面需求的场景中,比如-- >插入 ...
- [python基础]关于装饰器
在面试的时候,被问到装饰器,在用的最多的时候就@classmethod ,@staticmethod,开口胡乱回答想这和C#的static public 关键字是不是一样的,等面试回来一看,哇,原来是 ...
- 1.16 Python基础知识 - 装饰器初识
Python中的装饰器就是函数,作用就是包装其他函数,为他们起到修饰作用.在不修改源代码的情况下,为这些函数额外添加一些功能,像日志记录,性能测试等.一个函数可以使用多个装饰器,产生的结果与装饰器的位 ...
- python基础之装饰器(实例)
1.必备 #### 第一波 #### def foo(): print 'foo' foo #表示是函数 foo() #表示执行foo函数 #### 第二波 #### def foo(): print ...
- 【Python基础】装饰器的解释和用法
装饰器的用法比较简单,但是理解装饰器的原理还是比较复杂的,考虑到接下来的爬虫框架中很多用到装饰器的地方,我们先来讲解一下. 函数 我们定义了一个函数,没有什么具体操作,只是返回一个固定值 请注意一下缩 ...
- python之路——装饰器函数
阅读目录 楔子 装饰器的形成过程 开放封闭原则 谈装饰器主要功能和装饰器固定结构 带参数的装饰器 多个装饰器装饰一个函数 返回顶部 楔子 作为一个会写函数的python开发,我们从今天开始要去公司上班 ...
随机推荐
- macOS 下 PHPStorm + Xdebug 调试 Docker 环境中的代码
0x00 描述 宿主机是 mac mini,构建的项目在 docker 中,所以需要在 PHPStorm 上配置 Xdebug 进行远程代码调试. 0x01 环境 宿主机:macOS High Sie ...
- Istio究竟是干嘛的?
要聊ServiceMesh,就不得不提Istio,它是ServiceMesh目前最流行的实践, 当微服务架构体系越来越复杂的时候,需要将“业务服务”和“基础设施”解耦,将一个微服务进程一分为二: 一个 ...
- 【原创 Hadoop&Spark 动手实践 13】Spark综合案例:简易电影推荐系统
[原创 Hadoop&Spark 动手实践 13]Spark综合案例:简易电影推荐系统
- postMessage使用方法
1.子页面向父页面发送消息 var parentData = {type: 'passDataBack', data: passData}; window.parent.postMessage(par ...
- idea maven 集成多模块 module
首先第一步创建 顶级项目 也就是父项目 在创面那部中 不管你勾不勾 create from 那个选项 都无所谓,最终创建的项目要全删的 ,只保留pom.xml 父项目结构 接下来 创建子项目 也是 ...
- 快速入门 WePY 小程序【转】
一.WePY介绍 WePY 是 腾讯 参考了Vue 等框架对原生小程序进行再次封装的框架,更贴近于 MVVM 架构模式, 并支持ES6/7的一些新特性. 二.WePY 使用 1.WePY的安装或更新都 ...
- win10下安装基于caffe的 Faster-Rcnn
安装教程 本篇博客将会教会你在Windows下配置py-faster-rcnn,请细心仔细阅读.说白了,Windows下配置这些东西就是一个坑. 安装配置Anaconda 由于py-faste ...
- Oracle分析函数-统计(sum、avg、max、min)
很多需求中都涉及到统计:均值.累计.范围均值.相邻记录比较等.这些操作会统计多次,或有明确的统计范围,或返回的记录统计的数据集不同... 根据场景不同可分为如下几类: 1. 全统计 2. 滚动统计 3 ...
- What is a TensorFlow Session?
Sep 26, 2016 I've seen a lot of confusion over the rules of tf.Graph and tf.Session in TensorFlow. I ...
- 库存秒杀问题-redis解决方案- 接口限流
<?php/** * Created by PhpStorm. * redis 销量超卖秒杀解决方案 * redis 文档:http://doc.redisfans.com/ * ab -n 1 ...