理解Python的装饰器

看Flask文档时候看到关于cache的装饰器,有这么一段代码:

def cached(timeout=5 * 60, key=’view/%s’):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            cache_key = key % request.path
            rv = cache.get(cache_key)
            if rv is not None:
                return rv
            rv = f(*args, **kwargs)
            cache.set(cache_key, rv, timeout=timeout)
            return rv
        return decorated_function
    return decorator

之前也略懂装饰器,但是仔细一看发现了区别。

一般的装饰器代码是这样的:

def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if g.user is None:
            return redirect(url_for(’login’, next=request.url))
        return f(*args, **kwargs)
    return decorated_function

看出区别了吗?

第一个里面有两层def,第二个只有一层!

我的好奇心一下就上来了,非要把这个东西搞明白不可。

 

研究了一会总算研究明白了,下面就开始讲解吧。

 

首先理解一般的装饰器到底是怎么工作的:

def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if g.user is None:
            return redirect(url_for(’login’, next=request.url))
        return f(*args, **kwargs)
    return decorated_function


@login_required
def test():
    print "haha"

我们用login_required装饰了test函数,那么实际上执行的语句可以理解成这样:

test = login_required(test)

也就是说,现在test变成了login_required(test)的执行结果,也就是变成了decorated_function函数。

所以我们每次执行test的时候,其实执行的就是decorated_function,而原来的test函数就变成了decorated_function中的f函数。实现了装饰功能。

 

下面我们看看两层嵌套的装饰器到底是怎么工作的:

def cached(timeout=5 * 60, key=’view/%s’):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            cache_key = key % request.path
            rv = cache.get(cache_key)
            if rv is not None:
                return rv
            rv = f(*args, **kwargs)
            cache.set(cache_key, rv, timeout=timeout)
            return rv
        return decorated_function
    return decorator


@cached(100)
def test():
    pass

好像没什么区别?

再仔细看看!

 

@cached(100)

这里多了个参数!

 

是的,秘密就在这个参数。

加了参数之后,装饰器的装饰过程变成了两步:

cached_temp = cached(100)
test = cached_temp(test)

Python内部肯定没有cached_temp这个变量,这是我加的。不过实际的执行过程就是这么个顺序,这下就一目了然了吧!

 

首先运行cached(100),这时候返回的是第一层def,也就是真正的装饰器。

然后运行cached_temp(test),这时候返回了第二层def,这步就和一般的装饰器一样了。

 

 

我们总结一下,嵌套两层def的目的就是实现“带参装饰器”,相当于可以通过参数来定制一个装饰器,然后再应用到函数上。

 

明白了吗?

posted @ 2014-03-21 15:36  numbbbbb  阅读(423)  评论(0编辑  收藏  举报