python -- 装饰器入门

时间:2021-09-27 08:14:23

用例: 统计函数执行需要的时间

假设我们执行的一段代码的运行时间比我们预想的时间要久,而这段代码块有多个函数调用组成,我们有理由相信至少是其中的一个函数调用导致整个代码块产生了瓶颈。我们如何去发现导致瓶颈产生的原因呢?其中一个方法就是统计函数执行需要花费的具体时间。

让我们以一段简单的代码举例。有一个函数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 -- 装饰器入门的更多相关文章

  1. Python 装饰器入门&lpar;上&rpar;

    翻译前想说的话: 这是一篇介绍python装饰器的文章,对比之前看到的类似介绍装饰器的文章,个人认为无人可出其右,文章由浅到深,由函数介绍到装饰器的高级应用,每个介绍必有例子说明.文章太长,看完原文后 ...

  2. Python 装饰器入门&lpar;下&rpar;

    继续上次的进度:https://www.cnblogs.com/flashBoxer/p/9847521.html 正文: 装饰类 在类中有两种不通的方式使用装饰器,第一个和我们之前做过的函数非常相似 ...

  3. python装饰器入门

    按别人的教程弄的. 要清楚基于类和基于函数的实现的不同之处. #!/usr/bin/env python # -*- coding: utf-8 -*- ''' class entryExit(obj ...

  4. Python 装饰器学习

    Python装饰器学习(九步入门)   这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 1 2 3 4 5 6 7 8 # -*- c ...

  5. Python装饰器与面向切面编程

    今天来讨论一下装饰器.装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数 ...

  6. python装饰器总结

    一.装饰器是什么 python的装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.简单的说装饰器就是一个用来返回函数的函数 ...

  7. Python装饰器学习

    Python装饰器学习(九步入门)   这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 ? 1 2 3 4 5 6 7 8 # -*- ...

  8. 【转】Python装饰器与面向切面编程

    原文请参考: http://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html 今天来讨论一下装饰器.装饰器是一个很著名的设计模式,经常被用于有切 ...

  9. Python装饰器探险

    关于python装饰器的理解和用法,推荐廖雪峰老师和这一篇博客以及知乎 以下代码均已手动敲过,看完本篇内容,包你装饰器小成! 装饰器实际上就是为了给某程序增添功能,但该程序已经上线或已经被使用,那么就 ...

随机推荐

  1. MonjaDB —— 基于 Eclipse 的 MongoDB GUI 客户端工具&lpar;转载&rpar;

    原文链接http://www.oschina.net/question/12_59707 MonjaDB 是一个 MongoDB 的 GUI 客户端工具,提供直观的 MongoDB 数据管理的功能,支 ...

  2. 每日Scrum(5)

    进入冲刺第五天,软件的界面设计成为主打,收集学校的很多美图是我们组的任务: 问题在于软件已很难有很大的改进,大方向也都是变不了的

  3. 【开源】封装HTML5的localstorage

    项目名:web-storage-cache 项目地址:https://github.com/WQTeam/web-storage-cache API说明:https://github.com/WQTe ...

  4. CSS动画&colon;Transform中使用频繁的scale&comma;rotate&comma;translate动画

    动画中,skew只是transform中的一种形式的动画,我们还可以学习scale,rotate,translate.这是目前使用比较频繁的属性动作. 1.scale动画的定义:(单位数值) scal ...

  5. Notepad&plus;&plus;插件之FingerText

    FingerText是一个标签触发片段插件记事本.支持多个热点同时编辑,嵌套的热点,动态热点(很多不仅仅是纯文本的,可以通过命令,或触发另一个片段中的片段),热点的文本提示(而不是仅仅是$或#号)和热 ...

  6. 分享12款经典时尚的HTML5应用

    分享伟大,呵呵.今天给大家分享一下收集的12个HTML5小特效. 我整理一下源码,给大家打包一下,我博客园上传文件大小有限,传不了了. 需要的请留下邮箱就行了,觉得好的话,不要忘了点赞哦~ 1.CSS ...

  7. maven入门(1-1)maven是什么?

    Maven是一个项目管理工具,它包含了 一个项目对象模型 (Project Object Model), 一组标准集合, 一个项目生命周期(Project Lifecycle), 一个依赖管理系统(D ...

  8. Python&lpar;五&rpar; 字典

  9. 【算法】二叉查找树(BST)实现字典API

    参考资料 <算法(java)>                           — — Robert Sedgewick, Kevin Wayne <数据结构>       ...

  10. python --- 14 递归 二分法查找

    一.递归 1.函数自己调用自己 2.官方说明最大深度1000,但跑不到1000,要看解释器, 实测998 3.使⽤递归来遍历各种树形结构 二.    二分法查找 掐头结尾取中间 ,  必须是有序序列 ...