装饰器的语法比较绕,之前在教程里看到过,但是实际项目中没有使用。不过使用装饰器能够更好地满足代码的开放封闭原则,还是有必要学习一下。
基本理解
首先以廖雪峰的Python教程中的例子介绍装饰器的基本语法:
def log(func):
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def now():
print('2015-3-25')
其中log函数通过@log
作为装饰器,now函数成为被装饰函数。其特点如下:
- log函数有一个参数:func,是传入的被装饰函数(此处为now)
- log函数内部定义了一个新的函数wrapper,并将其作为返回值。wrapper函数的返回值是传入函数now的执行。
为什么有这样的特点,我们可以从装饰器的执行流程说起。首先我们知道函数在没有被调用之前其内部代码不会被执行。那么有一个问题,上述代码执行了哪些?根据你真明白 Python 装饰器么?,这里从@log
开始,有两个操作:1. 执行log函数。 2. 将其返回值赋值给被装饰函数(now)(理解了这个也就理解了更加复杂的写法的基础)。所以上述代码块在定义后,执行的语句有:@log--> def log(func)-->return wrapper
。因此接下来,在调用now()
后才会执行wrapper中的内容,即:
>>> now()
call now():
2015-3-25
这里还想记录下自己的一个错误理解,一开始看到说装饰器会将返回值赋给被装饰函数,觉得可以省略掉其中的wrapper函数,直接将func返回。
def log(func):
print('call %s():' % func.__name__)
return func # return func()
@log
def now():
print('2015-3-25')
犯这个错误显然就是没有理解好函数的调用流程以及装饰器的原理。
复杂用法
1. 被装饰函数需要传入参数
由于被装饰函数(now)实际上已经被装饰器内部的函数(wrapper)所替代,所以可以想象到,wrapper在定义时需要和被装饰函数传入相同的参数(或者利用args和*kw来接受所有参数),并在返回时将参数赋值给被装饰函数。如:
def log(func):
def wrapper(*args,**kw):
# do something
return now(*args,**kw)
return wrapper
@log
def now(arg1,arg2,arg3):
print('2018-2-14')
2. 装饰器需要传入参数
对于无参数情况下,被装饰函数等价于: now = log(now)
,有参数情况下,想要实现的效果是now=log('string')(now)
。此时从数学角度来说,可以令newlog=log('string')
,即可将新问题化简为原问题。回到代码问题上,已知log
函数是一个以被装饰函数为参数的函数,那么 newlog
函数就需要返回一个这样的函数即可,就是是将原本的两重嵌套转化为三重嵌套。实例如下:
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log('execute')
def now():
print('2015-3-25')
3. 装饰器在被装饰函数前后起作用
传统装饰器的用途可以用作函数运行前权限检查,调用信息打印等,但是如果希望函数运行完之后仍然能进行一些操作,应该怎么做呢?其实也很简单,我们可以将被装饰函数的执行放到wrapper
函数中,返回一个后处理函数即可。实例如下:
def Before(request,kargs):
print 'before'
def After(request,kargs):
print 'after'
def Filter(before_func,after_func):
def outer(main_func):
def wrapper(request,kargs):
before_result = before_func(request,kargs)
if(before_result != None):
return before_result;
main_result = main_func(request,kargs)
if(main_result != None):
return main_result;
after_result = after_func(request,kargs)
if(after_result != None):
return after_result;
return wrapper
return outer
@Filter(Before, After)
def Index(request,kargs):
print 'index'
文章实例代码来自以下参考,如有侵权,立刻删除...
Reference:
- 廖雪峰的Python教程-装饰器
- 你真明白 Python 装饰器么?