项目解析1、登录验证用户是否存在 储备知识 Python 之 decorator装饰器

 

下面是我对 装饰器 这一小节的总结, 以及自己的理解。

注:【本文中的代码参考上述教程】

很多时候我会把Python的很多语法与C++相融合,在C++中,函数的名称即为函数的地址,我们可以通过定义成为"函数指针"的变量,并且将函数名称赋值给该变量,那么我们在调用函数的时候,就可以直接使用该变量调用函数。

例如下面的C++的代码就是一个简单的函数指针的定义以及调用的过程。

 

  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. int f(int x, int y)  
  5. {  
  6.     return x+y;  
  7. }  
  8.   
  9. int main(void)  
  10. {  
  11.     int (*fptr)(int,int) ; //定义函数指针便令fptr。  
  12.     fptr = f;  
  13.     cout << (*fptr)(2,3) << endl;  
  14.     return 0;  
  15. }  
#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代码:

 

  1. def date():  
  2.     print "2014-11-5"  
  3. f = date // f便是函数指针  
  4. date()  
  5. f()  
def date():
    print "2014-11-5"
f = date // f便是函数指针
date()
f()

函数对象都有一个属性__name__,可以得到函数的名字: 例如: f.__name__ 为date。

 

下面开始进入正题,decorator是什么,decorator是一个返回函数的高阶函数。例如,我们的一个简单的log函数,该函数在每次函数调用时做日志的工作。但是我们又不希望去修改每一个需要计入日志的函数的实现代码,那么decorator就派上用场了。

 

  1. def log(func):  
  2.     def wrapper(*args, **kw):  
  3.         print "[log] : call %s(): " %func.__name__  
  4.         return func(*args, **kw)  
  5.     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 关键句即可。即像下面这样:

 

  1. @log   
  2. def date():  
  3.     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中可以简单使用下面的代码来实现。也就是装饰函数的完整版本:

 

  1. import functools  
  2. def log(func):  
  3.     @functools.wraps(func)  
  4.     def wrapper(*args, **kw):  
  5.         print "call %s : " %func.__name__  
  6.         return func(*args, **kw)  
  7.     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函数就可以实现。

 


上述讲述了装饰者模式,下面给出一个比较完整的例子。

 

  1. #!/usr/bin/env python  
  2. import sys  
  3.   
  4. #decorator  
  5.   
  6. import functools  
  7. def log(func):  
  8.     @functools.wraps(func)  
  9.     def wrapper(*args, **kw):  
  10.         print "[log] : call %s(): " %func.__name__  
  11.         f = func(*args, **kw)  
  12.         return f  
  13.     return wrapper  
  14.   
  15. date()  
  16. 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函数本身的参数,代码为:

 

  1. import functools  
  2. def log(text):  
  3.     def decorator(func):  
  4.         @functools.wraps(func)  
  5.         def wrapper(*args, **kw):  
  6.             print "%s %s" %(text,func.__name__)  
  7.             return func(*args, **kw)  
  8.         return wrapper  
  9.     return decorator  
  10.   
  11. print "Test full implementation of decorator"  
  12. @log("paramete of log")  
  13. def date():  
  14.     print "2014-11-5"  
  15. 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()

 


此时调用log函数就等价于调用 log("parameter of log")(date) ,函数log的返回值是decorator函数,再将date作为参数传递给decorator函数,实现调用。

下面用一个最完整的例子作为结束:

 

  1. #!/usr/bin/env python  
  2.   
  3. import functools  
  4. def log(text):  
  5.     def decorator(func):  
  6.         @functools.wraps(func)  
  7.         def wrapper(*args, **kw):  
  8.             print "%s %s()" %(text, func.__name__)  
  9.             return func(*args, **kw)  
  10.         return wrapper  
  11.     return decorator  
  12.   
  13. def log2(func):  
  14.     def decorator(*args, **kw):  
  15.         return func(*args, **kw)  
  16.     return decorator  
  17.   
  18. @log("decorator need parameter version1")   
  19. @log("decorator need parameter version2")   
  20. def date2(x,y):  
  21.     print "2014-11-5"  
  22.     print "x, y ", x, y  
  23.     return x  
  24.   
  25.   
  26. date2 = log('execute1')(date2)  
  27. date2 = log('execute2')(date2)  
  28. date2 = log('execute3')(date2)  
  29. 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)   这个装饰器解决了这个问题

posted @ 2017-06-08 23:00  若时光搁浅  阅读(325)  评论(0编辑  收藏  举报