参考资料:(大神的作品)
http://www.cnblogs.com/Jerry-Chou/archive/2012/05/23/python-decorator-explain.html
https://serholiu.com/python-closures
闭包其实就是在一个函数中嵌套另一个函数的定义。闭包的作用:包括了外部函数的局部变量,这些局部变量在外部函数返回后也继续存在,并能被内部函数引用。
实例如下:
def fun_closule(y):
"""闭包的作用: 当外部函数返回了, 外部函数的局部变量还可以被内部函数引用"""
print 'id(num): %X', id(y)
def tmp(x):
return x * y
print 'id(tmp): %X', repr(tmp)
return tmp
if __name__ == '__main__':
closule = fun_closule(4)
print 'id(closule): ', repr(closule)
#当删除了fun_closule对象后, 外部参数还可以被内部函数引用.
del fun_closule
print closule(2)
结果如下:
id(num): %X 140186159726672
id(tmp): %X
id(closule):
8
从结果可以看出,当fun_closule(4) 执行后, 创建和返回了 closule
这个函数对象,内存地址是0x101c5f398,
并且发现tmp内部函数和它的内存地址相同,即closule只是这个函数对象的一个引用。
现在知道闭包是怎么一回事了,那就到看看闭包到底是怎么回事的时候了。Python 中函数也是对象,所以函数也有很多属性,和闭包相关的就是 __closure__
属性。__closure__
属性定义的是一个包含 cell 对象的元组,其中元组中的每一个 cell 对象用来保存作用域中变量的值。
>>> closule.__closure__
(,)
>>> type(closule.__closure__[0])
>>> closule.__closure__[0].cell_contents
4 |
就如刚才所说,在 closule
的 __closure__
属性中有外部函数变量y 的引用,通过内存地址可以发现,引用的都是同一个y。如果没用形成闭包,则 __closure__
属性为 None
。
装饰器其实是用闭包来实现的。
简单的一个例子:
def handle_exception(fn):
def _decorate():
print 'before'
fn()
print 'after'
return _decorate
# @handle_exception
def hello():
print 'hello'
if __name__ == '__main__':
# print hello()
res = handle_exception(hello)
print res()
使用装饰器,提供一个语法糖@(Syntax Sugar), 如下:
import functools
def handle_exception(fn):
def _decorate():
print 'before'
fn()
print 'after'
return _decorate
@handle_exception
def hello():
print 'hello'
if __name__ == '__main__':
print hello()
返回结果:
➜ test python decorate_sample.py
before
hello
after
None
解释: 上述执行的程序返回的都是同一个结果。 在有装饰器下,Python解释器调用hello时,他会将hello转换为handle_exception(hello)()。也就是说真正执行的是_decorate这个函数。
可以用闭包的概念来解释,res和_decorate函数的内存地址是一样的,装饰器其实是闭包的特例, 其外部函数传的参数是函数名而已。
def handle_exception(num=None):
def decorator(fn):
@functools.wraps(fn)
def __decorator(*args, **kw):
print "before response"
fn(*args, **kw)
print "after respond”, num
return __decorator
return decorator
@handle_exception(num)
def func(a):
print "func "
if __name__ == '__main__':
func(10)
结果如下:
➜ test python decorate_test.py
before response
func
after respond 10
解释:
其实函数传参, 就是一个原有基础上在外面加一个函数,形成一个闭包,这样函数的参数就是一个闭包的外部参数,不会随着环境改变。
def logger(fn):
spec = inspect.getargspec(fn)
print '\nlogger spec: ', spec
def _decorator(*args, **kw):
print "before logger"
fn(*args, **kw)
print "after logger"
return _decorator
def handle_exception(fn):
spec = inspect.getargspec(fn)
print '\nhandle_exception spec: ', spec
def __decorator(self=None, req=None, **kw):
print "before response"
fn(self, req, **kw)
print "after respone", type
return __decorator
@logger
@handle_exception
def func(self, h):
print "func "
if __name__ == '__main__':
func(10, 20) # 等价于下面的,如果把装饰器去掉
# logger(handle_exception(func))()
返回结果:
➜ test python decorate_test.py
handle_exception spec: ArgSpec(args=['self', 'h'], varargs=None, keywords=None, defaults=None)
logger spec: ArgSpec(args=['self', 'req'], varargs=None, keywords='kw', defaults=None)
before logger
before response
func
after respone
after logger
解释: import inspect主要的作用是检查函数的参数名。可以断点一下,查看程序执行的顺序,有多个装饰器的时候执行顺序是由近到远, 在函数调用的时候, 从远--》近--》函数--》近--》远, 为什么会出现这样的情况呢?
调用fun(10, 20)函数最后变成了logger函数调用,等价于函数logger(handle_exception(func))(), 参数有handle_exception函数名等,函数名是logger,所以先执行logger里面的_decorator函数,然后依次执行。
为什么import inspect检查的参数名不一样呢?
logger spec:
从上面的结果可以得到如果把handle_exception装饰器和 hello函数当做一个整体的话,logger装饰器中fn的参数其实是def__decorator(self, req, **kw):里面的参数。
可以这样理解整体为handle_exception(hello)(self, req, **kw), 而handle_exception(hello)和__decorator函数的内存地址是一样的。 所以最后得出__decorator(self, req, **kw)。
handle_exception spec:
handle_exception里面的fn其实就是func函数名, 所以参数就是func(self, h)。
实例:
from decorator import decorator
def logger(fn):
spec = inspect.getargspec(fn)
print '\nlogger spec: ', spec
def _decorator(*args, **kw):
print "before logger"
fn(*args, **kw)
print "after logger"
return _decorator
@decorator
def handle_exception(fn, self=None, req=None, **kw):
spec = inspect.getargspec(fn)
print '\nhandle_exception spec: ', spec
print "before response"
fn(self, req, **kw)
print "after respone", type
@logger
@handle_exception
def func(self, h):
print "func "
if __name__ == '__main__':
func(10, 20)
# logger(handle_exception(func))()
结果如下:
➜ test python decorate_test.py
logger spec: ArgSpec(args=['self', 'h'], varargs=None, keywords=None, defaults=None)
before logger
handle_exception spec: ArgSpec(args=['self', 'h'], varargs=None, keywords=None, defaults=None)
before response
func
after respone
after logger
将之前实例的handle_exception 换成如上的情况。更加简便,原来还可以这样用。
注意:看inspect查询参数的结果, 这点比较重要, handle_exception和func当做一个整体, 显示的参数名是func的参数名,所以加了异常处理装饰器后不影响logger装饰器的获得的参数名。
场景:
@logger(category='医药资源-疾病列表-获取疾病详细信息', description='获取疾病详细信息', performance=True)
@http.route('/isleep/disease/get', type='http', auth='user')
@handle_exception
def isleep_disease_get(self, **kw):
需要在http.route装饰器中间加一个handle_exception装饰器,同时route装饰器源码里面有个是对接口参数名验证的地方。。怎么办? 可以用上面的方法。