Python 装饰器

装饰器的语法比较绕,之前在教程里看到过,但是实际项目中没有使用。不过使用装饰器能够更好地满足代码的开放封闭原则,还是有必要学习一下。


基本理解

首先以廖雪峰的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函数成为被装饰函数。其特点如下:

  1. log函数有一个参数:func,是传入的被装饰函数(此处为now)
  2. 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:

  1. 廖雪峰的Python教程-装饰器
  2. 你真明白 Python 装饰器么?

你可能感兴趣的:(Python 装饰器)