day17-函数装饰器
一、什么是装饰器
装饰器可以让其他函数在不需要做任何代码改变的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。
在 Python 中,函数是第一类对象,也就是说,函数可以做为参数传递给另外一个函数,一个函数可以将另一函数作为返回值,这就是装饰器实现的基础。
装饰器本质上是一个函数,它接受一个函数作为参数。
装饰器的应用场景:插入日志、性能测试、事务处理、缓存等场景。
二、装饰器的形成过程
2.1、不使用装饰器(使用闭包函数)
如果想要测试一个函数的执行时间,在不改变这个函数的前提下可以这样做
import time def func1():
print('aaa') def timer(func):
def inner():
start_time = time.time()
func()
end_time = time.time()
print('程序用时%s'%(end_time - start_time))
return inner func1 = timer(func1)
func1()
结果:
aaa
程序用时0.0
2.2、不带参数的装饰器
如果有多个函数都需要测试运行时间,就需要分别定义多个func2、func3等等,再调用这样函数,非常麻烦
所以python提供了语法糖,也叫装饰器。
import time def timer(func):
def inner():
start_time = time.time()
func()
end_time = time.time()
print('程序用时%s'%(end_time - start_time))
return inner @timer #这里相当于执行了func1 = timer(func1)
def func1():
print('aaa') func1()
结果:
aaa
程序用时0.0
func1其实就是执行了timer(func1),返回值其实就是inner,
那么执行func1()其实就是执行了inner(),而inner()函数其实又是执行了传入的函数func1()并打印了一句话
2.3、带参数的装饰器
如果函数需要传入一个参数,那么上面的程序会报错,因为在执行timer()函数时,内部其实是调用了func函数,但是这里并没有给func()函数传入参数,所以会报错。那么尝试给timer()函数也传入一个参数。
import time def timer(func):
def inner(a): #inner()需要传入参数给里面的子函数func()
start_time = time.time()
func(a) #调用传入的带参数的函数func1()
end_time = time.time()
print('程序用时%s'%(end_time - start_time))
return inner @timer #这里相当于执行了func1 = timer(func1)
def func1(n):
print(n) func1(100)
结果:
100
程序用时0.0
流程:
1)func1其实就是在执行timer(func1),也就是得到了返回值inner
2)func1(100)其实是在执行inner(100)
3)inner(100)其实是在执行func(100)和print语句
2.4、可以接收所有类型的参数的装饰器
上面的例子中只是传入了一个参数,如果传入2个或多个参数,会报错如下:
TypeError: inner() takes 1 positional argument but 2 were given
那么装饰器也需要随着传入多个参数,这显然是不方便的。
那么就需要在装饰器的形参中使用万能参数(*args, **kwargs)来接收所有类型的实参。
import time def timer(func):
def inner(*args, **kwargs): #这里要求使用万能参数来接收所有类型的多个参数
start_time = time.time()
func(*args, **kwargs) #这里也是要使用万能参数
end_time = time.time()
print('程序用时%s'%(end_time - start_time))
return inner @timer #这里相当于执行了func1 = timer(func1)
def func1(x,y):
print(x,y) func1(100,200)
结果:
100 200
程序用时0.0
2.5、有返回值的参数器
如果被装饰函数有return返回值,那么装饰器内部也需要将被装饰函数的值返回
def timer(func):
def inner(*args, **kwargs):
执行代码前要做的
ret = func(*args, **kwargs)
执行代码后要做的
return ret
return inner
例子:
import time
def timer(func):
def inner(*args, **kwargs): #这里要求使用万能参数来接收所有类型的多个参数
start_time = time.time()
ret = func(*args, **kwargs) #这里也是要使用万能参数
end_time = time.time()
print('程序用时%s'%(end_time - start_time))
return ret
return inner @timer
def func1(x,y):
return x+y
print(func1(100,200))
结果:
程序用时0.0
300
2.6、装饰器查看函数信息
如果被装饰的函数中存在函数说明文档时
def func1(x,y):
'''
这是func1的说明文档
'''
print(x,y) #func1(100,200)
print(func1.__doc__) #查看函数注释的方法
print(func1.__name__) #查看函数名的方法
结果:
这是func1的说明文档
func1
但是当函数加上装饰器后则不能正常显示被装饰函数的注释和函数名,而是显示了装饰器的注释和方法
None
inner
解决方法:使用warps
import time
from functools import wraps def timer(func):
@wraps(func) #加在最内层函数的正上方
def inner(*args, **kwargs):
start_time = time.time()
ret = func(*args, **kwargs)
end_time = time.time()
print('程序用时%s'%(end_time - start_time))
return ret
return inner @timer #这里相当于执行了func1 = timer(func1)
def func1(x,y):
'''
这是func1的说明文档
'''
print(x,y) #func1(100,200)
print(func1.__doc__)
print(func1.__name__)
结果:
这是func1的说明文档
func1
三、 开放封闭原则
对扩展是开放的
对于任何一个程序,在设计之初是可能将所有的功能都一次性设计好,后续一般都会有功能的添加、修改等操作,所以就必须要求代码是可
扩展的。
对修改是封闭的
在一个函数被开发出来之后很有可能会被其他程序引用,这个如果修改了这个函数的代码,就很有可能会影响到其他函数或程序。
而装饰器完美的遵循了这个开放封闭原则
四、装饰器的功能和固定结构
4.1、不支持函数说明文档的
def timer(func):
def inner(*args, **kwargs):
执行代码前要做的
ret = func(*args, **kwargs)
执行代码后要做的
return ret
return inner
4.2、支持函数说明文档的
from functools import wraps
def timer(func):
@wraps(func) #加在最内层函数的正上方
def inner(*args, **kwargs):
执行代码前要做的
ret = func(*args, **kwargs)
执行代码后要做的
return ret #return要放在最后,因为return后函数也就退出了
return inner
五、带参数的装饰器
如果有大量的函数使用了同一个装饰器,这时需要将这些函数同时取消掉这个装饰器,那么一个一个取消就太麻烦了。
这时可以在设计装饰器的时候就让装饰器带一个参数,当需要生效或者取消时可以改变被装饰函数上面的装饰器参数。
import time
from functools import wraps #修改装饰器代码,在原装饰器外面再包裹一层函数用来接收flag状态
def outer(flag):
def timer(func):
@wraps(func)
def inner(*args, **kwargs):
if flag: #这里判断flag的bool值,如果为True则执行下面代码,如果为False则不执行,从而使装饰器失效
start_time = time.time()
ret = func(*args, **kwargs)
end_time = time.time()
print('程序用时%s'%(end_time - start_time))
return ret
return inner
return timer #这里加上一句,返回原装饰器函数 @outer(False) #这里装饰器改为新的装饰器并传入参数True或False,来控制装饰器的启用状态
def func1(x,y):
print(x,y) func1(100,200)
结果:当新的装饰器outer()参数为False时则装饰器失效,如果想再次启用该装饰器,只需要批量将outer()参数改为True即可
六、多个装饰器装饰一个函数
当某个函数需要被多个装饰器装饰时,只需要在该函数上面添加多个装饰器即可
def wrapper1(func):
def inner(*args, **kwargs):
执行代码前要做的
ret = func(*args, **kwargs)
执行代码后要做的
return ret
return inner def wrapper2(func):
def inner(*args, **kwargs):
执行代码前要做的
ret = func(*args, **kwargs)
执行代码后要做的
return ret
return inner @wrapper2
@wrapper1
def func():
print('')
func()
这里func()函数的状态是先被wrapper1装饰,再被wrapper2装饰。
例子:
def wrapper1(func):
def inner(*args, **kwargs):
print('wrapper1,before func')
ret = func(*args ,**kwargs)
print('wrapper1,after func')
return ret
return inner def wrapper2(func):
def inner(*args, **kwargs):
print('wrapper2,before func')
ret = func(*args, **kwargs)
print('wrapper2,after func')
return ret
return inner
@wrapper2 #f = wrapper2(f),里面的f是inner1,外面的f是inner2
@wrapper1 #f = wrapper2(f),里面的f是函数名f,外面的f是inner1
def f():
print('haha')
结果:
wrapper2,before func
wrapper1,before func
haha
wrapper1,after func
wrapper2,after func

七、例子
例子1、记录函数的调用日志:
from datetime import datetime
def log(func):
def decorator(*args, **kwargs):
print('Function ' + func.__name__ + ' has been called at ' + datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
ret = func(*args, **kwargs)
return ret
return decorator
@log
def add(x, y):
return x + y
print(add(1, 2))
结果:
Function add has been called at 2018-11-01 18:01:34
3
day17-函数装饰器的更多相关文章
- Python高手之路【四】python函数装饰器
def outer(func): def inner(): print('hello') print('hello') print('hello') r = func() print('end') p ...
- Python: 无参数的函数装饰器
写带参数的函数装饰器最纠结的是需要包好多层,最外层是接收参数的函数,它返回一个接收函数的的函数.但这样有个问题是,最终包装出来的装饰器必须加()调用一下,即使没有参数也需要这样做,因为调用这个最外层函 ...
- Python中利用函数装饰器实现备忘功能
Python中利用函数装饰器实现备忘功能 这篇文章主要介绍了Python中利用函数装饰器实现备忘功能,同时还降到了利用装饰器来检查函数的递归.确保参数传递的正确,需要的朋友可以参考下 " ...
- 【Python 函数对象 命名空间与作用域 闭包函数 装饰器 迭代器 内置函数】
一.函数对象 函数(Function)作为程序语言中不可或缺的一部分,但函数作为第一类对象(First-Class Object)却是 Python 函数的一大特性. 那到底什么是第一类对象(Firs ...
- python 函数 装饰器 内置函数
函数 装饰器 内置函数 一.命名空间和作用域 二.装饰器 1.无参数 2.函数有参数 3.函数动态参数 4.装饰器参数 三.内置函数 salaries={ 'egon':3000, 'alex':10 ...
- python基础—函数装饰器
python基础-函数装饰器 1.什么是装饰器 装饰器本质上是一个python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能. 装饰器的返回值是也是一个函数对象. 装饰器经常用于有切 ...
- Python档案袋(函数与函数装饰器 )
特点:代码复用.可扩展.保持一致性 函数简单的实现,返回值的不同: #定义方法 def funx1(): pass def funx2(): return 0 def funx3(): return ...
- C++函数装饰器
今天在网上看到一个python实现的函数装饰器,尝试用C++11实现了一下,最后很粗糙的完成了,代码如下. 函数装饰器:接受一个函数.将此函数进行一些装饰,成为另一个函数.新生产的函数具有原函数的功能 ...
- python 修改的函数装饰器
把好的代码记录下来 方便以后学习 修改的函数参数装饰器 from functools import wraps import time import logging def warn(timeout) ...
- python装饰器1:函数装饰器详解
装饰器1:函数装饰器 装饰器2:类装饰器 装饰器3:进阶 先混个眼熟 谁可以作为装饰器(可以将谁编写成装饰器): 函数 方法 实现了__call__的可调用类 装饰器可以去装饰谁(谁可以被装饰): 函 ...
随机推荐
- 阿里云服务器 ECS Linux操作系统加固
1. 账号和口令 1.1 禁用或删除无用账号 减少系统无用账号,降低安全风险. 操作步骤 使用命令 userdel <用户名> 删除不必要的账号. 使用命令 passwd -l <用 ...
- [转][C#]拆分参数对
本文来自:https://www.jb51.net/article/62932.htm /// <summary> /// 分析 url 字符串中的参数信息 /// </summar ...
- 定位cpu内存问题
定位cpu内存问题 ps -Hp pid 显示进程的线程,P 按CPU内存排序 jstack pid>pid.log 将线程栈 dump 到日志文件中 线程号 十进制转换为 十六进制 在文件中 ...
- RHEL-server-7.0-Linux-centos安装过程
虚拟机centos7的安装过程 win10 ()vmware软件的时候,1.先关闭防火墙(杀毒软件),在安装vmware虚拟机.2.试着全装一下 vmware-14版本. 1. 打开VMware Wo ...
- java打印一下九九乘法表
public class Multiplication { public static void main(String[] args) { printTable(); } // 打印九九乘法表 pu ...
- Jmeter(十)检查点
检查点又名断言,我们在手工测试过程中肉眼以及自己的逻辑思维对实际结果进行判断是否与预期结果一致,但是工具是死的,没有眼睛,没有思维,并不知道需要判断的信息在哪块,或者是来判断什么东西,我们需要让工具更 ...
- linux达人养成计划
一.命令基本格式: ls -1 详细列表 (ll) -h 人性化显示文件大小 -a 显示所有文件,包括隐藏文件 -d 查看目录属性 -i 显示iNode 二.文件处理命令 mkdir -p [目录名] ...
- centos6和centos7的防火墙的操作
1:centos6的两种方式 1.1:service方式 查看防火墙状态: [root@centos6 ~]# service iptables status iptables:未运行防火墙. 开启防 ...
- 有关于Integer的一些小问题
先看一小段源码: Integer a1=; Integer a2=; Integer b1=); Integer b2=); Integer c1=; Integer c2=; System.out. ...
- Wsgi的web框架实例
建立server服务端: from wsgiref.simple_server import make_server import time def f1(request): return [b'&l ...