本质上来说, 就是闭包加python的一个语法糖.
亲们,让我们从头说起:
python中一切皆对象,方法也是一个对象.
def foo():
print 'this is func foo'
isinstance(foo, object)
True
所以TA可以被作为方法的形参传进去, 也可以作为return value被return.
我们先记下这一点, 后面会详细说明.
内嵌方法(nested function):
python允许在方法内部定义一个方法, 内部方法可以引用操作外部方法的变量.
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
输出如下:
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)
我们把上面的例子稍微改一改:
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()
输出如下:
this is outer func
这次我们把outer()的返回值改成inner这个方法本身。是不是看起来有点怪怪的?前面已经提到,方法在python里本身也是一个对象,so, why not?
从输出可以看出,调用outer()之后,inner方法并没有被执行。这也是好理解的。在python中,“()”是调用操作符,既然outer返回的是inner而不是inner(),说明inner就没有被call,所以它没有执行。cool,到这里虽然看起来有些复杂,但还是在比较好理解的范围之内。
我们继续:
print closure()
输出:
this is inner func
outer para in inner is 1
1
这个是不是就有点难理解了?outer方法已经被执行完了,那么para的生命周期已经结束,inner怎么还可以用它?不报错?
** 这就是python对闭包的支持。**
如果我们dir一个方法(dir(closure)),会看到有func_closure这么一个内置的属性。如果此方法是一个闭包,那就会把闭包里用到的对象塞到这个属性里。
print closure.func_closure
输出:
(<cell at 0x7f42d46dd280: int object at 0x16cd158>,)
这个int对象就是我们例子里可爱的para.
装饰器(decorator):
升级一下上个例子, 给outer改个名字,同时传个参数。
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()
输出:
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)。
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()
一样的输出:
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