项目解析1、登录验证用户是否存在 储备知识 Python 之 decorator装饰器
下面是我对 装饰器 这一小节的总结, 以及自己的理解。
注:【本文中的代码参考上述教程】
很多时候我会把Python的很多语法与C++相融合,在C++中,函数的名称即为函数的地址,我们可以通过定义成为"函数指针"的变量,并且将函数名称赋值给该变量,那么我们在调用函数的时候,就可以直接使用该变量调用函数。
例如下面的C++的代码就是一个简单的函数指针的定义以及调用的过程。
- #include <iostream>
- using namespace std;
- int f(int x, int y)
- {
- return x+y;
- }
- int main(void)
- {
- int (*fptr)(int,int) ; //定义函数指针便令fptr。
- fptr = f;
- cout << (*fptr)(2,3) << endl;
- return 0;
- }
#include <iostream> using namespace std; int f(int x, int y) { return x+y; } int main(void) { int (*fptr)(int,int) ; //定义函数指针便令fptr。 fptr = f; cout << (*fptr)(2,3) << endl; return 0; }
在Python中,函数也是对象,并且函数对象可以被赋值给变量。通过该变量也可以调用函数,像上面一样。但是Python是动态语言,不用那么复杂的定义变量的类型,就像上面的fptr。直接可以赋值即可。
例如下面的简单的Python代码:
- def date():
- print "2014-11-5"
- f = date // f便是函数指针
- date()
- f()
def date(): print "2014-11-5" f = date // f便是函数指针 date() f()
函数对象都有一个属性__name__,可以得到函数的名字: 例如: f.__name__ 为date。
下面开始进入正题,decorator是什么,decorator是一个返回函数的高阶函数。例如,我们的一个简单的log函数,该函数在每次函数调用时做日志的工作。但是我们又不希望去修改每一个需要计入日志的函数的实现代码,那么decorator就派上用场了。
- def log(func):
- def wrapper(*args, **kw):
- print "[log] : call %s(): " %func.__name__
- return func(*args, **kw)
- return wrapper
def log(func): def wrapper(*args, **kw): print "[log] : call %s(): " %func.__name__ return func(*args, **kw) return wrapper
首先,上面的函数log的参数是一个函数对象,返回值也是一个函数。
那么怎么将log函数用起来呢?很简单,在 要传入log作为参数 的函数定义的前面前面使用 @log 关键句即可。即像下面这样:
- @log
- def date():
- print "2014-11-5"
@log def date(): print "2014-11-5"
这样每次调用date(),就相当于在调用log(date). 不添加 @log语句在date前,输出的结果是:
2014-11-5
加上@log语句之后的输出结果为:
[log] : call date()
2014-11-5
也就是说在添加了log之后,函数的调用等价与log(date),log函数的参数是date,返回的是wrapper。可以看到,wrapper可以接受任何参数的函数,在wrapper函数内部,首先打印log行,再调用原始的函数。
这里的简答的理解就是,函数在调用之前,如果被做了包裹,也就是在函数定义之前使用了@关键字,那么我们调用的就是相应的包裹函数。例如上面的调用date时调用的其实是log(date)。
这里可能存在的问题时,前面讲到说函数是个对象,有一个__name__属性,但是你会发现上述的date函数在经过装饰之后的__name__属性是wrapper。这样对于这个__name__有依赖的代码就会出现问题,因此我们需要的是 wrapper.__name__ = date.__name__的语句的功能,这个在python中可以简单使用下面的代码来实现。也就是装饰函数的完整版本:
- import functools
- def log(func):
- @functools.wraps(func)
- def wrapper(*args, **kw):
- print "call %s : " %func.__name__
- return func(*args, **kw)
- return wrapper
import functools def log(func): @functools.wraps(func) def wrapper(*args, **kw): print "call %s : " %func.__name__ return func(*args, **kw) return wrapper
使用模块functools中得wraps函数就可以实现。
上述讲述了装饰者模式,下面给出一个比较完整的例子。
- #!/usr/bin/env python
- import sys
- #decorator
- import functools
- def log(func):
- @functools.wraps(func)
- def wrapper(*args, **kw):
- print "[log] : call %s(): " %func.__name__
- f = func(*args, **kw)
- return f
- return wrapper
- date()
- print date.__name__
#!/usr/bin/env python import sys #decorator import functools def log(func): @functools.wraps(func) def wrapper(*args, **kw): print "[log] : call %s(): " %func.__name__ f = func(*args, **kw) return f return wrapper date() print date.__name__
代码结果输出为:
[log] : call date():
2014-11-5
date
如果log函数本身是有参数的话,那么decorator模式就需要再加上一层传入log函数本身的参数,代码为:
- import functools
- def log(text):
- def decorator(func):
- @functools.wraps(func)
- def wrapper(*args, **kw):
- print "%s %s" %(text,func.__name__)
- return func(*args, **kw)
- return wrapper
- return decorator
- print "Test full implementation of decorator"
- @log("paramete of log")
- def date():
- print "2014-11-5"
- date()
import functools def log(text): def decorator(func): @functools.wraps(func) def wrapper(*args, **kw): print "%s %s" %(text,func.__name__) return func(*args, **kw) return wrapper return decorator print "Test full implementation of decorator" @log("paramete of log") def date(): print "2014-11-5" date()
下面用一个最完整的例子作为结束:
- #!/usr/bin/env python
- import functools
- def log(text):
- def decorator(func):
- @functools.wraps(func)
- def wrapper(*args, **kw):
- print "%s %s()" %(text, func.__name__)
- return func(*args, **kw)
- return wrapper
- return decorator
- def log2(func):
- def decorator(*args, **kw):
- return func(*args, **kw)
- return decorator
- @log("decorator need parameter version1")
- @log("decorator need parameter version2")
- def date2(x,y):
- print "2014-11-5"
- print "x, y ", x, y
- return x
- date2 = log('execute1')(date2)
- date2 = log('execute2')(date2)
- date2 = log('execute3')(date2)
- date2(2, 3)
#!/usr/bin/env python import functools def log(text): def decorator(func): @functools.wraps(func) def wrapper(*args, **kw): print "%s %s()" %(text, func.__name__) return func(*args, **kw) return wrapper return decorator def log2(func): def decorator(*args, **kw): return func(*args, **kw) return decorator @log("decorator need parameter version1") @log("decorator need parameter version2") def date2(x,y): print "2014-11-5" print "x, y ", x, y return x date2 = log('execute1')(date2) date2 = log('execute2')(date2) date2 = log('execute3')(date2) date2(2, 3)
输出结果为:
execute3 date2()
execute2 date2()
execute1 date2()
decorator need parameter version1 date2()
decorator need parameter version2 date2()
2014-11-5
x, y 2 3
重点总结:
1、装饰器装饰完函数后 再调用函数其实是操作的里面的函数
2、@functools.wraps(func) 这个装饰器解决了这个问题