python -- 装饰器入门
用例: 统计函数执行需要的时间
假设我们执行的一段代码的运行时间比我们预想的时间要久,而这段代码块有多个函数调用组成,我们有理由相信至少是其中的一个函数调用导致整个代码块产生了瓶颈。我们如何去发现导致瓶颈产生的原因呢?其中一个方法就是统计函数执行需要花费的具体时间。
让我们以一段简单的代码举例。有一个函数func_a(),代码如下:
def func_a(stuff):
a()
b()
c()
时间统计的一个方法就是在每次调用func_a的时候,加上时间统计的代码。比如:
start_a = datetime.datetime.now()
func_a(current_staff)
end_a = datetime.datetime.now()
print("Elapsed Time = {0}".format(end_a - start_a))
这样的确可以完成我们的目标。但是,如果我们有很多地方都调用func_a函数呢?难道每次调用都要修改一下func_a附近的代码么?能不能只是修改一处代码就可以呢?回答是肯定的:可以!
只要将时间统计的代码放到func_a函数内部就可以了。所以,我们修改代码如下:
def func_a(stuff):
start_a = datetime.datetime.now()
a()
b()
c()
end_a = datetime.datetime.now()
print("Elapsed Time = {0}".format(end_a - start_a))
这么改写的好处如下:
1.不需要每次调用func_a都修改一次代码。
2.我们将代码写到了一处。如果需要继续修改代码,只需在一个地方修改代码,而不需要每个地方都修改一次。
至此,我们的目的达到了。但是现实情况是我们可能需要统计多处不同代码的执行时间。有些需求出现第一次,就可能出现第二次、第三次。所以我们可能需要不停地编程类似的代码:
def func_a(stuff):
start_a = datetime.datetime.now()
a()
b()
c()
end_a = datetime.datetime.now()
print("Elapsed Time = {0}".format(end_a - start_a)) def func_b(stuff):
start_b = datetime.datetime.now()
e()
f()
g()
end_b = datetime.datetime.now()
print("Elapsed Time = {0}".format(end_b - start_b)) def func_c(stuff):
start_c = datetime.datetime.now()
h()
m()
n()
end_c = datetime.datetime.now()
print("Elapsed Time = {0}".format(end_c - start_c))
这样是不是很不友好?我们需要做的是找到某种方法,避免对func_a、func_b、func_c每次都做相同的修改。
python是门特别的语言,一旦一个函数被定义了,就可以被传递给其它函数、赋值给变量、甚至作为函数的返回值。
这些特性为装饰器的出现奠定了基础。来看下面的代码,看大家是否知道标签A,B,C,D处都会有什么行为:
def get_func():
print("inside get_func")
def returned_func():
print ("inside returned_function")
return 1
print ("outside returned_func")
return returned_func returned_func() # A
x = get_func() # B
x # C
x() # D
以下是执行结果:
>>> def get_func():
print("inside get_func")
def returned_func():
print ("inside returned_function")
return 1
print ("outside returned_func")
return returned_func >>> returned_func()
Traceback (most recent call last):
File "<pyshell#3>", line 1, in <module>
returned_func()
NameError: name 'returned_func' is not defined
>>> x=get_func()
inside get_func
outside returned_func
>>> x
<function get_func.<locals>.returned_func at 0x00000000032DB8C8>
>>> x()
inside returned_function
1
>>>
结果解释:
A
返回一个NameError,并显示returned_func不存在。有人可能会说,我们在上面不是已经定义了么?是的,上面的确是定义了,但是是在get_func内部定义的。
B
执行外层函数的定义,但是没有执行内层函数的定义returned_func。
C
返回函数get_func()的返回值,也是一个函数
D
既然x是函数,就可以被调用。调用x就是调用returned_func。
回到开头问题的本身!!!
回到开头的问题,该如何解决呢。一个建议就是创建一个函数time_this,这个函数将其他函数作为自己的参数,并在内部将该参数进行一定的封装。比如:
def time_this(original_func):
def new_func(*args,**kwargs):
start = datetime.datetime.now()
x = original_func(*args,**kwargs)
end = datetime.datetime.now()
print("Elapsed Time = {0}'.format(end - start))
return x
return new_func
接下来,测试一下我们的时间统计功能:
def func_a(stuff):
a()
b()
c()
func_a = time_this(func_a) def func_b(stuff):
e()
f()
g()
func_b = time_this(func_b) def func_c(stuff):
h()
m()
n()
func_c = time_this(func_c)
我们看func_a,当我们执行func_a = time_this(func_a)的时候,我们用time_this的返回值替换了func_a,也就是我们用添加了时间统计代码的函数替换了func_a。
装饰器
上面的过程完成了我们的需求,但是看起来很丑陋,也便于阅读。所以,python的作者给我们提供了装饰器。
@time_this
def func_a(stuff):
a()
b()
c()
上面个的定义就等价于:
def func_a(stuff):
a()
b()
c()
func_a = time_this(func_a)
上面就是装饰器的语法糖。@没有什么神奇之处,就是约定的规则而已。
结论!
装饰器就是返回函数的函数。
如果你继续钻研,还可以看到装饰器的类:
@add_class_funcionality
class MyClass:
...
带有参数的装饰器
@require_permission(name="edit"):
def save_changes(stuff):
...
python -- 装饰器入门的更多相关文章
- Python 装饰器入门(上)
		
翻译前想说的话: 这是一篇介绍python装饰器的文章,对比之前看到的类似介绍装饰器的文章,个人认为无人可出其右,文章由浅到深,由函数介绍到装饰器的高级应用,每个介绍必有例子说明.文章太长,看完原文后 ...
 - Python 装饰器入门(下)
		
继续上次的进度:https://www.cnblogs.com/flashBoxer/p/9847521.html 正文: 装饰类 在类中有两种不通的方式使用装饰器,第一个和我们之前做过的函数非常相似 ...
 - python装饰器入门
		
按别人的教程弄的. 要清楚基于类和基于函数的实现的不同之处. #!/usr/bin/env python # -*- coding: utf-8 -*- ''' class entryExit(obj ...
 - Python 装饰器学习
		
Python装饰器学习(九步入门) 这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 1 2 3 4 5 6 7 8 # -*- c ...
 - Python装饰器与面向切面编程
		
今天来讨论一下装饰器.装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数 ...
 - python装饰器总结
		
一.装饰器是什么 python的装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.简单的说装饰器就是一个用来返回函数的函数 ...
 - Python装饰器学习
		
Python装饰器学习(九步入门) 这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 ? 1 2 3 4 5 6 7 8 # -*- ...
 - 【转】Python装饰器与面向切面编程
		
原文请参考: http://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html 今天来讨论一下装饰器.装饰器是一个很著名的设计模式,经常被用于有切 ...
 - Python装饰器探险
		
关于python装饰器的理解和用法,推荐廖雪峰老师和这一篇博客以及知乎 以下代码均已手动敲过,看完本篇内容,包你装饰器小成! 装饰器实际上就是为了给某程序增添功能,但该程序已经上线或已经被使用,那么就 ...
 
随机推荐
- IIS 之 IIS 7及以上多域名或端口绑定同一物理目录并设置不同默认文档
			
今天在 IIS 7 多端口或域名绑定同一物理目录,设置不同的默认文档遇到问题:同一物理目录的多个站点修改任意一个站点默认文档都会一起更改. 原因:在同一个物理目录下只有一个 web.config,并且 ...
 - 共享权限ACL列表出现SID现象
			
http://www.minasi.com/forum/topic.asp?TOPIC_ID=16842 Basically here's what happens, and why it doesn ...
 - 机器学习-分类器-级联分类器训练(Train CascadeClassifier )
			
一.简介: adaboost分类器由级联分类器构成,"级联"是指最终的分类器是由几个简单分类器级联组成.在图像检测中,被检窗口依次通过每一级分类器,这样在前面几层的检测中大部分的候 ...
 - Nginx IP 白名单设置
			
1:ip.config 192.168.3.15 1;192.168.3.10 1;192.168.0.8 1; 2:nginx.conf #geoIP的白名单 geo $remote_addr $i ...
 - 2016 博客导读总结 & 个人感悟
			
此文着笔之时.2017已经在眼前了.预计等我写完,2017已经到了. 二次编辑于2017年1月1日早11点. 关于2016的感悟.十二月初就想写,当时认为是有点太早了,只是却思绪如泉涌. 且那时候才刚 ...
 - loadrunner -27492超时
 - C语言中连接器介绍
			
在C语言中.一个重要的思想就是分别编译.即若干个源程序能够在不同的时候单独进行编译.然后在恰当的时候整合到一起.可是连接器通常是与C编译器分离的,连接器怎样做到把若干个C源程序合并成一个总体呢? 典型 ...
 - Python接收邮件并保存至MySQL
			
转自:http://www.360doc.com/content/14/0103/13/11789990_342303735.shtml 参考了一些网络上的资料,做了个简单程序,使用python接收邮 ...
 - GDAL添加ECW格式支持
			
目录 GDAL添加ECW格式支持 ECW 下载ECW JPEG SDK 在Unix平台构建支持ECW的GDAL 二进制ECW SDK和GCC >= 5.1 在Linux上构建的教程 在Windo ...
 - 使用mapreduce来分析网站的log日志
			
近日,有人和我说分析log日志. 之前,就写过,但是忘了总结了,找了半天也没有找到,看了以后要将东西整理了. 无奈,在网上收拾,看到这个人写的,索性,就搬过来,待我找到我写的,在一块补充一下! 所有网 ...