前言:python新手,如有错误欢迎指正
其实装饰器本身不难理解,不带参数的装饰器可以用2层嵌套的闭包实现,带参数的就是3层闭包实现,这篇文章主要记录下为什么使用闭包的原因和如果不这么用会出现的问题。
(主要是我自己犯过的一些理解错误,也可以当作自己的错题集)
首先要了解的是@修饰符,官方文档的解释是这样的,有兴趣的可以去看看,对照着谷歌翻译还是不难看懂的
@dec2
@dec1
def func(arg1, arg2, ...):
pass
This is equivalent to:
def func(arg1, arg2, ...):
pass
func = dec2(dec1(func))
所以@相当于把下面的函数当做参数传递给@后面的函数,这个是基本的定义,下面我们从最简单的装饰器开始入手
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的作用是now=log(now)
相当于now=wrapper(),由于函数闭包的延迟运算性质,wrapper会在调用now()的时候运行
我们来调试一下看看,这里我在wrapper上面加了一行输出,然后断点调试
def log(func):
print 'run decorator'
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def now():
print('2015-3-25')
if __name__=='__main__':
now()
在顺序运行到@log时跳入了log内
后面的执行顺序和我们预期的一样,log直接返回wrapper给now,然后在main中使用了新的now
其实通俗点讲,装饰器这里的2层嵌套的作用在于把原函数和新功能封装起来之后返回给原函数的引用。
下面是我曾经的一个疑问,可以不用嵌套来完成装饰器吗?
def log(func):
print 'run log'
return func
@log
def now(x):
print('run now %d' % x)
if __name__=='__main__':
now(123)
得出来的结果是这样的
run log
run now 123
Process finished with exit code 0
乍一看没问题,但是我们仔细分析一下,now这个引用指向的还是原来的now(),
原函数实际上并没有加上新功能,我的错误在于把这里的run log错误的当做了之前装饰器中
call name的功能,实际上这里的run log在上文中对应的是run decorator的作用,我们在下面加一行输出就能证实这点
if __name__=='__main__':
now(123)
now(456)
结果如下
run log
run now 123
run now 456
Process finished with exit code 0
到此,2层嵌套的装饰器就算真正理解了,不仅是怎么用,还有为什么这么用。
至于带参数的装饰器为什么需要3层嵌套,我自己尝试出来的结果是传参需要,试过了2层嵌套
来实现传参,但是全都是参数出错,有兴趣的可以自己尝试一下,这部分等我彻底搞清楚了再
写出来