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

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

让我们以一段简单的代码举例。有一个函数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 装饰器入门(上)

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

  2. Python 装饰器入门(下)

    继续上次的进度: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. java enum 用法

    /* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2010, Red Hat Inc. or th ...

  2. CAD VC++安装失败 1603错误

    问题描述 想安装一个高版本的CAD来着,可安装报错始终报错1603.具体表现为 DESKTOP-F7K8C37    Installing Microsoft Visual C++ 2008 SP1 ...

  3. javascript 替换 window.onload 的 document.ready

    通常我们想要在页面内容加载完成后运行 JS 时,都会使用 window.onload 来处理,比如: window.onload = function(){      alert('Hello Wor ...

  4. jquery如何获取元素的滚动高度

    获取浏览器显示区域(可视区域)的高度 : $(window).height(); 获取浏览器显示区域(可视区域)的宽度 : $(window).width(); 获取页面的文档高度 $(documen ...

  5. PYQT实现简单的浏览器功能

    主要的类 QMainWindow 提供一个有菜单条.锚接窗口(例如工具条)和一个状态条的主应用程序窗口. http://www.kuqin.com/qtdocument/qmainwindow.htm ...

  6. html增加和删除div

    <!DOCTYPE HHTML"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> ...

  7. MongoDB副本集配置系列二:配置MongoDB副本集

    接上一篇博客: http://www.cnblogs.com/xiaoit/p/4479066.html 1:首先创建3台虚拟机作为配置环境 IP1:192.168.91.128 IP2:192.16 ...

  8. 数组的翻转(非reverse())

    方法一: var arr = [1,2,3,4]; var arr2 = []; while(arr.length) { var num = arr.pop(); //删除数组最后一个元素并返回被删除 ...

  9. Spring MVC的Post请求参数中文乱码的原因&处理

    一.项目配置: Spring 4.4.1-RELEASE Jetty 9.3.5 JDK 1.8 Servlet 3.1.0 web.xml文件中没有配置编解码Filter 二.实际遇到的问题:客户端 ...

  10. Inno Setup入门(十)——操作注册表

    有些程序需要随系统启动,或者需要建立某些文件关联等问题,这些都是通过在安装程序中对注册表进行操作的结果.Inno Setup中通过[registry]段实现对注册表的操作. 本段说明: 参数列表: 参 ...