python的装饰器: decorator

本质上来说, 就是闭包加python的一个语法糖.
亲们,让我们从头说起:

python中一切皆对象,方法也是一个对象.
1
2
3
4
5
def foo():
print 'this is func foo'

isinstance(foo, object)
True

所以TA可以被作为方法的形参传进去, 也可以作为return value被return.
我们先记下这一点, 后面会详细说明.

内嵌方法(nested function):

python允许在方法内部定义一个方法, 内部方法可以引用操作外部方法的变量.

1
2
3
4
5
6
7
8
9
10
11
def outer():
para = 1
print 'this is outer func'
def inner():
print 'this is inner func'
print 'outer para in inner is %s' % para
return para
return inner()

print outer()
print para

输出如下:

1
2
3
4
5
this is outer func
this is inner func
outer para in inner is 1
1
NameError: name 'para' is not defined

一个方法内部变量的生命周期, 应该是在执行这个方法时被define, 方法执行完毕后被delete. 在这个例子里print outer()之后, para必定不能再被使用, 如果再加一个print para, 必定会报错. okay, 目前为止, 一切都是按我们预想的来, 看起来也很好理解.

闭包(closures)

我们把上面的例子稍微改一改:

1
2
3
4
5
6
7
8
9
10
def outer(): 
para = 1
print 'this is outer func'
def inner():
print 'this is inner func'
print 'outer para in inner is %s' % para
return para
return inner

closure = outer()

输出如下:

1
this is outer func

这次我们把outer()的返回值改成inner这个方法本身。是不是看起来有点怪怪的?前面已经提到,方法在python里本身也是一个对象,so, why not?
从输出可以看出,调用outer()之后,inner方法并没有被执行。这也是好理解的。在python中,“()”是调用操作符,既然outer返回的是inner而不是inner(),说明inner就没有被call,所以它没有执行。cool,到这里虽然看起来有些复杂,但还是在比较好理解的范围之内。
我们继续:

1
print closure()

输出:

1
2
3
this is inner func
outer para in inner is 1
1

这个是不是就有点难理解了?outer方法已经被执行完了,那么para的生命周期已经结束,inner怎么还可以用它?不报错?
这就是python对闭包的支持。
如果我们dir一个方法(dir(closure)),会看到有func_closure这么一个内置的属性。如果此方法是一个闭包,那就会把闭包里用到的对象塞到这个属性里。

1
print closure.func_closure

输出:

1
(<cell at 0x7f42d46dd280: int object at 0x16cd158>,)

这个int对象就是我们例子里可爱的para.

装饰器(decorator):

升级一下上个例子, 给outer改个名字,同时传个参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
def decor(func):
print 'this is func decor'
def inner():
print 'this is inner func'
print 'call func in inner.'
func()
return inner

def foo():
print 'this is func foo'

foo = decor(foo)
foo()

输出:

1
2
3
4
this is func decor
this is inner func
call func in inner.
this is func foo

改名之后的方法叫decor,加了个参数func。把decor(foo)重新赋值给foo, 并执行foo。
结果是原有的foo被执行,还有我们的inner。是不是很酷,相当于给foo加了个包装(wrapper)。
我们离胜利仅剩一步!
@这个符号是python对闭包采用的一个语法糖(syntax suger)。

1
2
3
4
5
6
7
8
9
10
11
12
13
def decor(func):
print 'this is func decor'
def inner():
print 'this is inner func'
print 'call func in inner.'
func()
return inner
@decor
def foo():
print 'this is func foo'

# foo = decor(foo)
foo()

一样的输出:

1
2
3
4
this is func decor
this is inner func
call func in inner.
this is func foo

用了@语法糖,就相当于去掉了foo = decor(foo)这一步,让代码看起来更优雅,更readable。
That’s it。这就是装饰器。
如需转载, 请注明出处!谢谢

还可以用class来做装饰器,请参考:class decorator
也可以给装饰器传参数,请参考:decorator with arguments