这几天翻看python语法,看到装饰器这里着实卡了一阵,最初认为也就是个函数指针的用法,但仔细研究后发现,不止这么简单。

首先很多资料将装饰器定义为AOP的范畴,也就是Aspect Oriented Programming面向切面编程的概念,不懂AOP不要紧,只要有函数指针的概念,又有嵌套函数的基础知识,看懂此文一点压力都没有。

先说说为什么要有装饰器这么个东西存在吧,这是一种设计模式,较为经典的有插入日志、性能测试、事务处理等等。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。

关于嵌套函数,C/C++程序员可能会觉得一头雾水,因为谭老爷子的书明确指出函数不可以嵌套定义,在C++ primer也仅仅有嵌套类的讲解,并不存在嵌套函数一说,但是在一些高级语言中,比如Object C,swift都存在嵌套函数主题,感兴趣的同学可以去翻一下,尤其是当下热门swift,嵌套函数和闭包密不可分,可以方便的实现很多逻辑,比如返回一个函数给调用者。

书归正传,所谓装饰器,顾名思义,就是用来装饰的神器,你有一个函数A(),觉得它不够漂亮,但是又不想重写它,咋办呢?装饰器(Decorator)这时候就派上用场了。

为了方便大家的理解,我先写一个简单的嵌套函数:

def funcA():
print("i am funcA()!")
def funcB():
print("i am funcB()")
return funcB
callSth=funcA()//调用funcA得到返回的funcB
callSth()//相当于调用funcB
 

值得说明一点,在python里只要你不怕蛋疼,你可以任意嵌套,哪怕你嵌套个百十层直到栈溢出都没问题。

如下:

def ppp():
print("return vlaue")
def first():
a=10// a被嵌套内的函数捕获!
print("the first layer")
def second():
print("the second layer %d" % a)
def third():
print("the third layer %d " % a)
def forth():
print("the forth layer")
return ppp()
return forth
return third
return second aaa=first()()()//得到forth()
aaa()

关于嵌套函数是这样执行的,先执行最外层的first(),返回second然后发现后面跟着(),那继续返回third,以此类推,聪明如你,肯定明白了。嵌套函数就这样,简单到爆。如果到这里诸君理解了,装饰器基本上也就理解了。

下面正式开始装饰器的概念(开启摘抄模式):

装饰器的定义很是抽象,我们来看一个小例子。

def foo():
print 'in foo()'
foo()

这是一个很无聊的函数没错。但是突然有一个更无聊的人,我们称呼他为B君,说我想看看执行这个函数用了多长时间,好吧,那么我们可以这样做:

import time
def foo():
start = time.clock()
print 'in foo()'
end = time.clock()
print 'used:', end - start foo()

很好,功能看起来无懈可击。可是蛋疼的B君此刻突然不想看这个函数了,他对另一个叫foo2的函数产生了更浓厚的兴趣。

怎么办呢?如果把以上新增加的代码复制到foo2里,这就犯了大忌了~复制什么的难道不是最讨厌了么!而且,如果B君继续看了其他的函数呢?

以不变应万变:还记得吗,函数在Python中是一等公民,那么我们可以考虑重新定义一个函数timeit,将foo的引用传递给他,然后在timeit中调用foo并进行计时,这样,我们就达到了不改动foo定义的目的,而且,不论B君看了多少个函数,我们都不用去修改函数定义了!

import time

def foo():
print 'in foo()' def timeit(func):
start = time.clock()
func()
end =time.clock()
print 'used:', end - start timeit(foo)

看起来逻辑上并没有问题,一切都很美好并且运作正常!……等等,我们似乎修改了调用部分的代码。原本我们是这样调用的:foo(),修改以后变成了:timeit(foo)。这样的话,如果foo在N处都被调用了,你就不得不去修改这N处的代码。或者更极端的,考虑其中某处调用的代码无法修改这个情况,比如:这个函数是你交给别人使用的。

最大限度地少改动!

既然如此,我们就来想想办法不修改调用的代码;如果不修改调用代码,也就意味着调用foo()需要产生调用timeit(foo)的效果。我们可以想到将timeit赋值给foo,但是timeit似乎带有一个参数……想办法把参数统一吧!如果timeit(foo)不是直接产生调用效果,而是返回一个与foo参数列表一致的函数的话……就很好办了,将timeit(foo)的返回值赋值给foo,然后,调用foo()的代码完全不用修改!

一个真正意义上的装饰器诞生了:

#-*- coding: UTF-8 -*-
import time def foo():
print 'in foo()' # 定义一个计时器,传入一个,并返回另一个附加了计时功能的方法
def timeit(func): # 定义一个内嵌的包装函数,给传入的函数加上计时功能的包装
def wrapper():
start = time.clock()
func()
end =time.clock()
print 'used:', end - start # 将包装后的函数返回
return wrapper foo = timeit(foo)
foo()

样,一个简易的计时器就做好了!我们只需要在定义foo以后调用foo之前,加上foo = timeit(foo),就可以达到计时的目的,这也就是装饰器的概念,看起来像是foo被timeit装饰了。在在这个例子中,函数进入和退出时需要计时,这被称为一个横切面(Aspect),这种编程方式被称为面向切面的编程(Aspect-Oriented Programming)。与传统编程习惯的从上往下执行方式相比较而言,像是在函数执行的流程中横向地插入了一段逻辑。在特定的业务领域里,能减少大量重复代码。面向切面编程还有相当多的术语,这里就不多做介绍,感兴趣的话可以去找找相关的资料。

语法糖

上面这段代码看起来似乎已经不能再精简了,Python于是提供了一个语法糖来降低字符输入量。

import time

def timeit(func):
def wrapper():
start = time.clock()
func()
end =time.clock()
print 'used:', end - start
return wrapper @timeit// syntax sugar 是也,在定义上加上这一行与另外写foo = timeit(foo)完全等价,千万不要以为@有另外的魔力。除了字符输入少了一些,还有一个额外的好处:这样看上去更有装饰器的感觉。
def foo():
print 'in foo()' foo()

给出原文链接:http://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html

相关资料:http://blog.csdn.net/thy38/article/details/4471421

http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html

python之装饰器详解的更多相关文章

  1. python函数装饰器详解

    python装饰器(fuctional decorators)简单来说就是修改其他函数的函数. 这样的函数需要满足两个个条件: 1.不能修改原函数的源代码 2.不能改变原函数的调用方式 需要达到的效果 ...

  2. python 叠加装饰器详解

    def out1(func1): #7.func1=in2的内存地址,就是in2 print('out1') def in1(): #8.调用函数index() 因为函数在in1里,所以首先运行in1 ...

  3. 【python】装饰器详解推荐

    复杂道理浅中来,见过的解释最清楚的一篇文章: https://blog.csdn.net/xiangxianghehe/article/details/77170585

  4. python设计模式之装饰器详解(三)

    python的装饰器使用是python语言一个非常重要的部分,装饰器是程序设计模式中装饰模式的具体化,python提供了特殊的语法糖可以非常方便的实现装饰模式. 系列文章 python设计模式之单例模 ...

  5. Python装饰器详解

    python中的装饰器是一个用得非常多的东西,我们可以把一些特定的方法.通用的方法写成一个个装饰器,这就为调用这些方法提供一个非常大的便利,如此提高我们代码的可读性以及简洁性,以及可扩展性. 在学习p ...

  6. Python中的各种装饰器详解

    Python装饰器,分两部分,一是装饰器本身的定义,一是被装饰器对象的定义. 一.函数式装饰器:装饰器本身是一个函数. 1.装饰函数:被装饰对象是一个函数 [1]装饰器无参数: a.被装饰对象无参数: ...

  7. python装饰器1:函数装饰器详解

    装饰器1:函数装饰器 装饰器2:类装饰器 装饰器3:进阶 先混个眼熟 谁可以作为装饰器(可以将谁编写成装饰器): 函数 方法 实现了__call__的可调用类 装饰器可以去装饰谁(谁可以被装饰): 函 ...

  8. Python—装饰器详解

    装饰器:(语法糖) 本质是函数,它是赋予函数新功能,但是不改变函数的源代码及调用方式   原则: 1.不能修改被装饰函数的源代码 2.不能修改被装饰函数的调用方式 3.函数的返回值也不变 这两点简而言 ...

  9. Python全栈开发之8、装饰器详解

    一文让你彻底明白Python装饰器原理,从此面试工作再也不怕了.转载请注明出处http://www.cnblogs.com/Wxtrkbc/p/5486253.html 一.装饰器 装饰器可以使函数执 ...

随机推荐

  1. ESXI安装

    1.设置从USB启动 按DEL进入BIOS设置 参考http://www.upanok.com/jiaocheng/68.html 先插入USB,在选择ADVANCAED BIOS FEATURES ...

  2. 修改MongoDb的 DB 和 Log 存储路径 (Windows)-摘自网络

    Create a file called mongod.cfg in MongoDB folder if you dont have it. In my case: C:\Users\ivanbtru ...

  3. windows下编译支持https的libcurl

    本文参考http://blog.csdn.net/fragmentalice/article/details/39430293特此感谢.公司项目中用到几个http get请求,用的libcurl开源库 ...

  4. Spring MVC MultiActionController example

    In Spring MVC application, MultiActionController is used to group related actions into a single cont ...

  5. hdu 5363 Key Set

    http://acm.hdu.edu.cn/showproblem.php?pid=5363 Key Set Time Limit: 2000/1000 MS (Java/Others)    Mem ...

  6. bzoj 2190 仪仗队(欧拉函数)

    2190: [SDOI2008]仪仗队 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 2245  Solved: 1413[Submit][Statu ...

  7. sql server 2014内存表

    内存数据库,指的是将数据库的数据放在内存中直接操作.相对于存放在磁盘上,内存的数据读写速度要高出很多,故可以提高应用的性能.微软的SQL Server 2014已于2014年4月1日正式发布,SQL ...

  8. [iOS微博项目 - 2.1] - 获得新浪授权接口

    A.如何获得新浪的授权接口 登陆新浪的开放平台 注册新浪账号 创建应用 获得应用id和请求地址 查阅相关API 关联需要进行测试的账号   1.登陆开放平台 http://open.weibo.com ...

  9. eclipse 安装scons

    http://www.sconsolidator.com/update  Installation To use SConsolidator, you first have to install SC ...

  10. 前端响应式设计中@media等的相关运用

    现在做前端响应式网站特别,响应式成为现在前端设计一个热点,它成为热点的最主要的原因就是,移动端设备屏幕的种类多样,那么如何设置响应式屏幕. /*打印样式*/ @mediaprint{color:red ...