Python装饰器(Decorator)简介
Python有许多出色的语言特性,装饰器(Decorator)便是其中一朵奇葩。先来看看一段代码:
def deco1(f):
print 'decorate 1'
return f def deco2(f):
print 'decorate 2'
return f
@deco1
@deco2
def foo():
return 'hello'
保存并执行上面的代码,你会看到如下输出:
decorate 2
decorate 1
函数foo没有被调用,但是deco1,deco2被按照一个顺序被调用了。deco1和deco2就是装饰器。从上面的输出可以看出两点:
1 装饰器是在代码装载时被调用的
2 调用的顺是从下到上
现在来说说装配器到底做了什么。看代码:
def deco1(f):
print 'decorate 1',f
return f def deco2(f):
print 'decorate 2',f
return f @deco1
@deco2
def foo():
return 'hello'
这段代码的执行结果为:
decorate 2 <function foo at 0x00000000021F79E8>
decorate 1 <function foo at 0x00000000021F79E8>
装饰器函数的参数‘f’被打印出来,而且就是我们的函数‘foo’,可见装饰器的参数就是被装饰的函数。真的是这样吗?再来看代码:
def wraper(n,f):
def foo1():
return '%s(%s)'%(n,f())return foo1 def deco1(f):
print 'decorate 1',f
return wraper('decorate 1',f) def deco2(f):
print 'decorate 2',f
return wraper('decorate 2',f) @deco1
@deco2
def foo():
return 'hello'
这段代码的执行结果为:
decorate 2 <function foo at 0x0000000002147A58>
decorate 1 <function foo1 at 0x0000000002147AC8>
我觉得我现在可以总结一下了:
1 装饰器在代码装载时被调用;
2 调用顺序是从下到上的;
3 被装饰函数‘foo’作为参数传递给第一个装饰器‘deco2’,返回值将作为参数传递给第二个装饰器‘deco1’,然后依次向上直到最顶端的装饰器;
4 最顶端的装饰器的返回值就是被装饰以后的函数,我们暂时称之为,也就是我们将来要执行的那个‘foo’。
下面,来我们说说装饰后的函数,被调用会有什么结果,看代码:
def wraper(n,f):
def foo1():
return '%s(%s)'%(n,f())
return foo1 def deco1(f):
print 'decorate 1',f
return wraper('decorate 1',f) def deco2(f):
print 'decorate 2',f
return wraper('decorate 2',f) @deco1
@deco2
def foo():
return 'hello' print foo()
这段代码的执行结果为:
decorate 2 <function foo at 0x0000000002127A58>
decorate 1 <function foo1 at 0x0000000002127AC8>
decorate 1(decorate 2(hello))
不难看出,调用顺序与装饰顺序刚好相反,最终函数也就是最顶端的装饰器返回的函数最先被调用,然后依次调用到最初那个函数‘foo’。我在这里故意规避了一个基本的事实,就是:其实最后被调用的就是顶端装饰返回的那个函数,你必须手动在这个函数中调用前面的函数,见这里:
return '%s(%s)'%(n,f())
,才会产生调用连的效果。所以装饰器并不会帮你完成所有的事情,他给了你充分的自由。说的这里,你们要问了(如果你有足够的好奇心):‘装饰器能带参数吗’。答案是能,见下面的代码:
def wraper(n,f):
def foo1():
return '%s(%s)'%(n,f())
return foo1 def deco(n):
def deco1(f):
print n,f
return wraper(n,f)
return deco1 @deco('decorate 1')
@deco('decorate 2')
def foo():
return 'hello' print foo()
这段代码的执行结果为:
decorate 2 <function foo at 0x0000000002117AC8>
decorate 1 <function foo1 at 0x0000000002117B38>
decorate 1(decorate 2(hello))
哇,和之前的结果一模一样,代码还被简化了不少。这是怎么回事呢,我来简单解释下,函数‘deco’不是装饰器,他只是一个返回装饰器的函数,当你把它放到装饰符号‘@‘后面时,python的语法起了一个美妙的作用,他会先调用这个函数,然后用返回值值作为装饰器。
大概就是这样啦,语法大师也许会描述的更详细更专业。源码狂人还可能深入到python的C代码里寻找成因。不过,作为一个有理智的好青年我们就点到为止吧。
下次,我们再来掰扯一下,装饰器都可以变出哪些戏法吧。洗洗睡了。
Python装饰器(Decorator)简介的更多相关文章
- python 装饰器(decorator)
装饰器(decorator) 作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 装饰器(decorator)是一种高级Python语 ...
- Python装饰器--decorator
装饰器 装饰器实质是一个函数,其作用就是在不改动其它函数代码的情况下,增加一些功能.如果我们需要打印函数调用前后日志,可以这么做 def log(func): print('%s is running ...
- Python 装饰器Decorator(一)
(一) 装饰器基础知识 什么是Python装饰器?Python里装饰器是一个可调用的对象(函数),其参数是另一个函数(被装饰的函数) 假如有一个名字为somedecorator的装饰器,target是 ...
- Python 装饰器Decorator(二)
对于上一篇“”Python闭包“”随笔中提到的make_averager()函数的如下实现,我们把历史值保存在列表里,每次计算平均值都需要重新求和,当历史值较多时,需要占用比较多的空间并且效率也不高. ...
- python 语法之 装饰器decorator
装饰器 decorator 或者称为包装器,是对函数的一种包装. 它能使函数的功能得到扩充,而同时不用修改函数本身的代码. 它能够增加函数执行前.执行后的行为,而不需对调用函数的代码做任何改变. 下面 ...
- python 装饰器学习(decorator)
最近看到有个装饰器的例子,没看懂, #!/usr/bin/python class decorator(object): def __init__(self,f): print "initi ...
- python语法32[装饰器decorator](转)
一 装饰器decorator decorator设计模式允许动态地对现有的对象或函数包装以至于修改现有的职责和行为,简单地讲用来动态地扩展现有的功能.其实也就是其他语言中的AOP的概念,将对象或函数的 ...
- python函数编程-装饰器decorator
函数是个对象,并且可以赋值给一个变量,通过变量也能调用该函数: >>> def now(): ... print('2017-12-28') ... >>> l = ...
- Python——装饰器(Decorator)
1.什么是装饰器? 装饰器放在一个函数开始定义的地方,它就像一顶帽子一样戴在这个函数的头上.和这个函数绑定在一起.在我们调用这个函数的时候,第一件事并不是执行这个函数,而是将这个函数做为参数传入它头顶 ...
随机推荐
- 试了一把Intel的核显转码的威力
今天小试了一把Intel Cpu的核显转码的威力,确实非常快,使用的工具是MediaCoder,能达到10x的速度,效果非常明显,并且CPU占用非常低.为了比较,实用x264纯转同一个文件的,软编码能 ...
- 关于spring-mvc.xml的mvc:resources元素浅析。
配置如下: <!-- 配置静态资源 --><mvc:resources location="/static/" mapping="/static/**& ...
- js继承——到继承结束
说到继承,其它语言里可能有两种: 接口继承是继承方法签名,而实现继承则继承实际方法.ES函数没有签名,不能实现接口继承,只支持实现继承,而实现继承主要依靠原型链.(这两句话,说来轻松,理解来不易,且行 ...
- redis.conf配置解释
daemonize:如果需要在后台运行,把该项改为yespidfile:配置多个pid的地址,默认在/var/run/redis.pidbind:绑定ip,设置后只接受来自该ip的请求port:监听端 ...
- 微信公众平台—— 获取微信服务器IP地址
微信公众平台—— 获取微信服务器IP地址 const ServerIpUrl = 'https://api.weixin.qq.com/cgi-bin/getcallbackip?&acces ...
- gdb对应vc调试命令
gdb vc调试对照表: 实现功能 vc gdb 修改后编译 f7 ma ...
- MySQL中SYSDATE()和NOW()函数的区别和联系
MySQL中有5个函数需要计算当前时间的值: NOW.返回时间,格式如:2012-09-23 06:48:28 CURDATE,返回时间的日期,格式如:2012-09-23 CURTIME,返回时间, ...
- 推荐一份 Google 面试指南
经常有不少读者在我公众号后台留言,说自己还是应届毕业生,缺乏工作经验与项目经验,不知道如何才能通过面试? 其实,项目经验固然重要,但是企业也知道,对于应届毕业生,是很难有拿得出手的项目经验出来的,毕竟 ...
- css3新单位vw、vh、vmin、vmax的使用介绍
1,vw.vh.vmin.vmax 的含义 (1)vw.vh.vmin.vmax 是一种视窗单位,也是相对单位.它相对的不是父节点或者页面的根节点.而是由视窗(Viewport)大小来决定的,单位 1 ...
- 谷歌浏览器开发调试工具中Sources面板 js调试等 完全介绍
这次分享的是Chrome开发工具中最有用的面板Sources. Sources面板几乎是我最常用到的Chrome功能面板,也是在我看来决解一般问题的主要功能面板.通常只要是开发遇到了js报错或者其他代 ...