[转] Python中的装饰器(decorator)
想理解Python的decorator首先要知道在Python中函数也是一个对象,所以你可以
- 将函数复制给变量
 - 将函数当做参数
 - 返回一个函数
 
函数在Python中和变量的用法一样也是一等公民,也就是高阶函数(High Order Function)。所有的魔法都是由此而来。
1,起源
我们想在函数login中输出调试信息,我们可以这样做
def login():
print('in login') def printdebug(func):
print('enter the login')
func()
print('exit the login') printdebug(login)
这个方法讨厌的是每次调用login是,都通过printdebug来调用,但毕竟这是可行的。
2,让代码变得优美一点
既然函数可以作为返回值,可以赋值给变量,我们可以让代码优美一点。
def login():
print('in login') def printdebug(func):
def __decorator():
print('enter the login')
func()
print('exit the login')
return __decorator #function as return value debug_login = printdebug(login) #function assign to variable debug_login() #execute the returned function
这样我们每次只要调用debug_login就可以了,这个名字更符合直觉。我们将原先的两个函数printdebug和login绑定到一起,成为debug_login。这种耦合叫内聚:-)。
3,让代码再优美一点
printdebug和login是通过debug_login = printdebug(login)这一句来结合的,这一句似乎也是多余的,能不能在定义login是加个标注,从而将printdebug和login结合起来?
上面的代码从语句组织的角度来讲很难再优美了,Python的解决方案是提供一个语法糖(Syntax Sugar),用一个@符号来结合它们。
def printdebug(func):
def __decorator():
print('enter the login')
func()
print('exit the login')
return __decorator @printdebug #combine the printdebug and login
def login():
print('in login') login() #make the calling point more intuitive
可以看出decorator就是一个:使用函数作参数并且返回函数的函数。通过改进我们可以得到:
- 更简短的代码,将结合点放在函数定义时
 - 不改变原函数的函数名
 
在Python解释器发现login调用时,他会将login转换为printdebug(login)()。也就是说真正执行的是__decorator这个函数。
4,加上参数
1,login函数带参数
login函数可能有参数,比如login的时候传人user的信息。也就是说,我们要这样调用login:
login(user)
Python会将login的参数直接传给__decorator这个函数。我们可以直接在__decorator中使用user变量:
def printdebug(func):
def __decorator(user): #add parameter receive the user information
print('enter the login')
func(user) #pass user to login
print('exit the login')
return __decorator @printdebug
def login(user):
print('in login:' + user) login('jatsz') #arguments:jatsz
我们来解释一下login(‘jatsz’)的调用过程:
[decorated] login(‘jatsz’) => printdebug(login)(‘jatsz’) => __decorator(‘jatsz’) => [real] login(‘jatsz’)
2,装饰器本身有参数
我们在定义decorator时,也可以带入参数,比如我们这样使用decorator,我们传入一个参数来指定debug level。
@printdebug(level=5)
def login
pass
为了接收decorator传来的参数,我们在原本的decorator上再包装一个函数来接收参数:
def printdebug_level(level): #add wrapper to recevie decorator's parameter
def printdebug(func):
def __decorator(user):
print('enter the login, and debug level is: ' + str(level)) #print debug level
func(user)
print('exit the login')
return __decorator
return printdebug #return original decorator @printdebug_level(level=5) #decorator's parameter, debug level set to 5
def login(user):
print('in login:' + user) login('jatsz')
我们再来解释一下login(‘jatsz’)整个调用过程:
[decorated]login(‘jatsz’) => printdebug_level(5) => printdebug[with closure value 5](login)(‘jatsz’) => __decorator(‘jatsz’)[use value 5] => [real]login(‘jatsz’)
5,装饰有返回值的函数
有时候login会有返回值,比如返回message来表明login是否成功。
| 
 1 
 | 
login_result = login(‘jatsz’) | 
我们需要将返回值在decorator和调用函数间传递:
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
 | 
def printdebug(func):    def __decorator(user):            print('enter the login')        result = func(user)  #recevie the native function call result        print('exit the login')        return result        #return to caller    return __decorator  @printdebug def login(user):    print('in login:' + user)    msg = "success" if user == "jatsz" else "fail"    return msg  #login with a return valueresult1 = login('jatsz');print result1  #print login resultresult2 = login('candy');print result2 | 
我们解释一下返回值的传递过程:
...omit for brief…[real][msg from login(‘jatsz’) => [result from]__decorator => [assign to] result1
6,应用多个装饰器
我们可以对一个函数应用多个装饰器,这时我们需要留心的是应用装饰器的顺序对结果会产生。影响比如:
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
 | 
def printdebug(func):    def __decorator():            print('enter the login')        func()         print('exit the login')    return __decorator  def others(func):    #define a other decorator    def __decorator():        print '***other decorator***'        func()    return __decorator@others         #apply two of decorator@printdebugdef login():    print('in login:')@printdebug    #switch decorator order@othersdef logout():    print('in logout:')login()print('---------------------------') logout() | 
我们定义了另一个装饰器others,然后我们对login函数和logout函数分别应用这两个装饰器。应用方式很简单,在函数定义是直接用两个@@就可以了。我们看一下上面代码的输出:
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
 | 
$ python deoc.py***other decorator***enter the loginin login:exit the login---------------------------enter the login***other decorator***in logout:exit the login | 
我们看到两个装饰器都已经成功应用上去了,不过输出却不相同。造成这个输出不同的原因是我们应用装饰器的顺序不同。回头看看我们login的定义,我们是先应用others,然后才是printdebug。而logout函数真好相反,发生了什么?如果你仔细看logout函数的输出结果,可以看到装饰器的递归。从输出可以看出:logout函数先应用printdebug,打印出“enter the login”。printdebug的__decorator调用中间应用了others的__decorator,打印出“***other decorator***”。其实在逻辑上我们可以将logout函数应用装饰器的过程这样看(伪代码):
| 
 1 
2 
3 
4 
5 
6 
7 
8 
 | 
@printdebug    #switch decorator order(    @others    (        def logout():            print('in logout:')    )) | 
我们解释一下整个递归应用decorator的过程:
[printdebug decorated]logout() =>
printdebug.__decorator[call [others decorated]logout() ] =>
printdebug.__decorator.other.__decorator[call real logout]
7,灵活运用
什么情况下装饰器不适用?装饰器不能对函数的一部分应用,只能作用于整个函数。
login函数是一个整体,当我们想对部分函数应用装饰器时,装饰器变的无从下手。比如我们想对下面这行语句应用装饰器:
| 
 1 
 | 
msg = "success" if user == "jatsz" else "fail" | 
怎么办?
一个变通的办法是“提取函数”,我们将这行语句提取成函数,然后对提取出来的函数应用装饰器:
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
 | 
def printdebug(func):    def __decorator(user):            print('enter the login')        result = func(user)         print('exit the login')        return result          return __decorator  def login(user):    print('in login:' + user)    msg = validate(user)  #exact to a method    return msg  @printdebug  #apply the decorator for exacted methoddef validate(user):    msg = "success" if user == "jatsz" else "fail"    return msgresult1 = login('jatsz');print result1 | 
来个更加真实的应用,有时候validate是个耗时的过程。为了提高应用的性能,我们会将validate的结果cache一段时间(30 seconds),借助decorator和上面的方法,我们可以这样实现:
| 
 1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
 | 
import timedictcache = {}def cache(func):    def __decorator(user):            now = time.time()        if (user in dictcache):            result,cache_time = dictcache[user]            if (now - cache_time) > 30:  #cache expired                result = func(user)                dictcache[user] = (result, now)  #cache the result by user            else:                print('cache hits')        else:            result = func(user)            dictcache[user] = (result, now)        return result          return __decorator  def login(user):    print('in login:' + user)    msg = validate(user)      return msg  @cache  #apply the cache for this slow validationdef validate(user):    time.sleep(5)  #simulate 10 second block    msg = "success" if user == "jatsz" else "fail"    return msgresult1 = login('jatsz'); print result1  result2 = login('jatsz'); print result2    #this login will return immediately by hit the cacheresult3 = login('candy'); print result3 | 
Reference:
http://stackoverflow.com/questions/739654/understanding-python-decorators --Understanding Python decorators
http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html --Python装饰器学习(九步入门)
http://www.python.org/dev/peps/pep-0318/ --PEP 318 -- Decorators for Functions and Methods
[转] Python中的装饰器(decorator)的更多相关文章
- python中的装饰器decorator
		
python中的装饰器 装饰器是为了解决以下描述的问题而产生的方法 我们在已有的函数代码的基础上,想要动态的为这个函数增加功能而又不改变原函数的代码 例如有三个函数: def f1(x): retur ...
 - 浅析python中的装饰器decorator
		
最近学习python,其中decorator比较难理解,遂写一篇来总结供后续查阅. 定义一个函数,想在运行时动态的改变函数的功能,又不想改变函数本身的代码,可以使用高阶函数(可以使用函数作为参数) 装 ...
 - 简单说明Python中的装饰器的用法
		
简单说明Python中的装饰器的用法 这篇文章主要简单说明了Python中的装饰器的用法,装饰器在Python的进阶学习中非常重要,示例代码基于Python2.x,需要的朋友可以参考下 装饰器对与 ...
 - 写python中的装饰器
		
python中的装饰器主要用于在已有函数实现功能前附加需要输出的信息,下面将用实例展示我如何写装饰器. 首先分别尝试写装饰器装饰一个无参函数和一个有参函数(被装饰函数仅输出,无返回值情况下) def ...
 - 【Python】python中的装饰器——@
		
对装饰器本来就一知半解的,今天终于弄清楚了,Python中的装饰器是对装饰者模式的很好运用,简化到骨子里了. python中为什么需要装饰器,看这里:http://www.cnblogs.com/hu ...
 - Python 中实现装饰器时使用 @functools.wraps 的理由
		
Python 中使用装饰器对在运行期对函数进行一些外部功能的扩展.但是在使用过程中,由于装饰器的加入导致解释器认为函数本身发生了改变,在某些情况下——比如测试时——会导致一些问题.Python 通过 ...
 - python中@property装饰器的使用
		
目录 python中@property装饰器的使用 1.引出问题 2.初步改善 3.使用@property 4.解析@property 5.总结 python中@property装饰器的使用 1.引出 ...
 - python语法32[装饰器decorator](转)
		
一 装饰器decorator decorator设计模式允许动态地对现有的对象或函数包装以至于修改现有的职责和行为,简单地讲用来动态地扩展现有的功能.其实也就是其他语言中的AOP的概念,将对象或函数的 ...
 - python 语法之 装饰器decorator
		
装饰器 decorator 或者称为包装器,是对函数的一种包装. 它能使函数的功能得到扩充,而同时不用修改函数本身的代码. 它能够增加函数执行前.执行后的行为,而不需对调用函数的代码做任何改变. 下面 ...
 
随机推荐
- Java实验课:命令行参数是什么?
			
命令行参数:在命令行中给定的参数就是命令行参数.(即从输入位置角度理解). 命令行的参数 1. 什么是命令行的参数? 如: java Test1 365 156 "China" ( ...
 - Vue_(组件通讯)组件
			
Vue组件 传送门 组件Component,可扩展HTML元素,封装可重用的代码.通俗的来说,组件将可重用的HTML元素封装成为标签方便复用: 组件的使用: 使用全局方法Vue.extend创建构造器 ...
 - Spring常用的jar+普通构造注入
			
常用工具 jar 说明 提供AOP(面向切面编程)实现:spring -aop spring提供对AspectJ框架的整合:spring-aspects 提供 IoC(控制反转)的基础实现:sprin ...
 - 2018-2019-2 20165215《网络对抗技术》Exp10 Final Windows本地内核提权+Exploit-Exercises Nebula学习与实践
			
目录 PART ONE :Windows本地内核提权 漏洞概述 漏洞原理 漏洞复现 windbg调试本地内核 查看SSDT表和SSDTShadow表 查看窗口站结构体信息 利用Poc验证漏洞 漏洞利用 ...
 - Redis5.0 3台主机三主三从部署Cluster集群
			
1.下载安装 [root@ip101 redis-5.0.4]# pwd /opt/app/redis-5.0.4 [root@ip101 redis-5.0.4]# ls 00-RELEASENOT ...
 - ftp列出具体目录的所有目录,和目录按照文件类型列出
			
package com.haiyisoft.cAssistantWeb.util; import java.io.IOException; import java.io.PrintWriter; im ...
 - 简易的美图秀秀利用别人的so库
			
在实际开发中,有时候时间短,任务量大,可以查看类似的apk,将行apk反编译,通过看源码分析,用里面的JNI代码! 本案例中用了美图秀秀的JNI.java和jni.so链接库 项目中调用别人写的c代码 ...
 - Springboot获取resource的路径
			
1.获取resource目录下的template路径 String path = Thread.currentThread().getContextClassLoader().getResource( ...
 - 2.oracle数据库:[1]oracle简易客户端安装方法
			
准备oracle简易客户端程序,如果没有请到oracle网站下载www.oracle.com,可以下载基本包及其他扩展程序包,例如:如果要使用sqlplus则需要下载sqlplus包,笔者下载了i ...
 - kubernetes系列:(三)、helm的安装和使用
			
一.helm简介 kubernetes : 解决了容器维护的难题,通过yaml编写,比如deployment,job,statefulset.configmap等等,通过控制循环,让容器镜像便于管理, ...