python之装饰器详解
这几天翻看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之装饰器详解的更多相关文章
- python函数装饰器详解
python装饰器(fuctional decorators)简单来说就是修改其他函数的函数. 这样的函数需要满足两个个条件: 1.不能修改原函数的源代码 2.不能改变原函数的调用方式 需要达到的效果 ...
- python 叠加装饰器详解
def out1(func1): #7.func1=in2的内存地址,就是in2 print('out1') def in1(): #8.调用函数index() 因为函数在in1里,所以首先运行in1 ...
- 【python】装饰器详解推荐
复杂道理浅中来,见过的解释最清楚的一篇文章: https://blog.csdn.net/xiangxianghehe/article/details/77170585
- python设计模式之装饰器详解(三)
python的装饰器使用是python语言一个非常重要的部分,装饰器是程序设计模式中装饰模式的具体化,python提供了特殊的语法糖可以非常方便的实现装饰模式. 系列文章 python设计模式之单例模 ...
- Python装饰器详解
python中的装饰器是一个用得非常多的东西,我们可以把一些特定的方法.通用的方法写成一个个装饰器,这就为调用这些方法提供一个非常大的便利,如此提高我们代码的可读性以及简洁性,以及可扩展性. 在学习p ...
- Python中的各种装饰器详解
Python装饰器,分两部分,一是装饰器本身的定义,一是被装饰器对象的定义. 一.函数式装饰器:装饰器本身是一个函数. 1.装饰函数:被装饰对象是一个函数 [1]装饰器无参数: a.被装饰对象无参数: ...
- python装饰器1:函数装饰器详解
装饰器1:函数装饰器 装饰器2:类装饰器 装饰器3:进阶 先混个眼熟 谁可以作为装饰器(可以将谁编写成装饰器): 函数 方法 实现了__call__的可调用类 装饰器可以去装饰谁(谁可以被装饰): 函 ...
- Python—装饰器详解
装饰器:(语法糖) 本质是函数,它是赋予函数新功能,但是不改变函数的源代码及调用方式 原则: 1.不能修改被装饰函数的源代码 2.不能修改被装饰函数的调用方式 3.函数的返回值也不变 这两点简而言 ...
- Python全栈开发之8、装饰器详解
一文让你彻底明白Python装饰器原理,从此面试工作再也不怕了.转载请注明出处http://www.cnblogs.com/Wxtrkbc/p/5486253.html 一.装饰器 装饰器可以使函数执 ...
随机推荐
- ASP.NET MVC中的Json Binding和Validate
引子:电子商务网站支付功能页面往往会有很多信息,对于这些信息的保存,往往是分步完成的,那么使用Ajax最合适不过了,比如其中的收货人信息模块.这些信息的新建和编辑保存都是用Ajax来完成的.那么有几种 ...
- (四面体)CCPC网络赛 HDU5839 Special Tetrahedron
CCPC网络赛 HDU5839 Special Tetrahedron 题意:n个点,选四个出来组成四面体,要符合四面体至少四条边相等,若四条边相等则剩下两条边不相邻,求个数 思路:枚举四面体上一条线 ...
- Java出现No enclosing instance of type E is accessible. Must qualify the allocation with an enclosing
Java出现No enclosing instance of type E is accessible. Must qualify the allocation with an enclosing ...
- 为Android游戏接入第三方登录功能
1. “游戏客户端”调用“SDK客户端”的登录功能向“SDK服务端”进行身份认证 2. 验证通过后,“游戏客户端”可得到用户信息,根据游戏逻辑可将用户信息传给“游戏服务器”进行验证 3. “游戏服务器 ...
- ES 基础
You Know, for Search 安装es时 , jdk最低版本需要 jdk7 默认端口 : 9200 启动后浏览器访问 : localhost:9200 角色关系对照 elasticsear ...
- 关于MySQL数据类型timestamp的讨论
在项目中用到了timestamp这个类型,该字段本意是用于存储改行记录的创建时间的,实际上这是一个很危险的设置: mysql官方文档上有这么一段话: The TIMESTAMP data type p ...
- 在Windows Server 下安装 Oracle 11G 的一般步骤
- Hibernate的常用关键类以及接口介绍
上一篇初步的对Hibernate进行了认识,并测试了Hibernate的HelloWorld, 这里主要介绍HibernateTest类中的相关类和接口,以及其作用和特性,关于Session中的相关方 ...
- Linux查看物理内存信息
Linux查看物理内存信息 1. 查看内存大小 dmidecode|grep Size 输出 Runtime Size: 64 kB ROM Size: 4608 kB Installed Size: ...
- thymeleaf中的内联[ [ ] ]
一.文本内联 [[…]]之间的表达式在Thymeleaf被认为是内联表达式,在其中您可以使用任何类型的表达式,也会有效th:text属性. <p>Hello, [[${session.us ...