装饰器Decorator(函数的装饰)
一。LEGB函数作用域的优先级和解析
函数是function的一个对象,被调用完后内部变量就会被回收,被引用的除外(例如return的变量)
1. local :函数内部作用域
2. enclosing :函数内部和内嵌函数之间(一般是闭包使用)
3. global :全局作用域
4. build-in :内置作用域(Python自带的)
当多个作用域有相同的变量时,优先级高的会覆盖优先级低的,优先级从高到低1到4.
二。闭包
1. 什么是闭包
>>> def my_func(val): ... def func(): ... print val ... return func ... >>> f = my_func('a') >>> f() a >>>
在my_func这个函数里,内嵌的func函数就是闭包,而闭包调用了enclosing作用域的val,my_func函数调用完后,val理应被回收了,但却还能被闭包调用,这是因为闭包的__closure__属性。
2. 闭包的__closure__属性
>>> def my_func(val): ... print '%x' % id(val) ... def func(): ... print val ... return func ... >>> f = my_func('a') 10ce56580 >>> print f.__closure__ (<cell at 0x10cf52980: str object at 0x10ce56580>,) >>>
可以看到闭包的__closure__属性保存了一个内存地址,而这个内存地址就是my_func函数里变量val的内存地址。这就是val没被回收的原因,这是闭包的一个强大的作用之一。
三。装饰器(Decorator): 在代码运行期间动态增加功能的方式。
1. 用闭包装饰函数my_func_01,为他添加打印的b的功能
>>> def my_func(fun): ... def func(): ... print 'b' ... return fun() ... return func ... >>> def my_func_01(): ... print 'a' ... >>> my_func_01 = my_func(my_func_01) >>> my_func_01() b a >>>
2. 用语法糖@简化闭包的装饰
>>> def my_func(fun): ... def func(): ... print 'b' ... return fun() ... return func ... >>> @my_func ... def my_func_01(): ... print 'a' ... >>> my_func_01() b a >>>
所以可知道@my_func就等于 my_func_01 = my_func(my_func_01)
四。简单函数装饰
为my_fun(a) 函数添加一个打印功能 print 'add_fun'
@add_log 就相当于 my_fun = add_log(my_fun)
>>> def add_log(func): ... def wrapper(*args,**kw): ... print 'add_fun' ... return func(*args,**kw) ... return wrapper ... >>> @add_log ... def my_fun(a): ... print 'my_fun' ... return 'a' ... >>> my_fun(4) add_fun my_fun 'a' >>>
my_fun 指向了新的函数 wrapper(*args,**kw) , return func(*args,**kw) 而不是 return func ,这样一来,my_fun 所传入的参数就要和 my_fun(a) 的参数统一起来(当然也可以这样 my_fun(*args,**kw)) , 然后返回 func(*args,**kw) 的执行结果给 my_fun 变量,就实现了装饰作用。
五。带参数的装饰器
可以传入参数的装饰器,也可以这样 def add_log(*args,**kw): 来传多个参数。
@add_log('qwe') 就相当于 my_fun = add_log('wer')(my_fun) , 也就相当于 my_fun1 = add_log('wer') 和 @my_fun1 结合.
>>> def add_log(text): ... def log_decorator(func): ... def wrapper(*args,**kw): ... print 'add_fun' ... print text ... return func(*args,**kw) ... return wrapper ... return log_decorator ... >>> @add_log('qwe') ... def my_fun(a): ... print 'my_fun' ... return 'a' ... >>> my_fun(4) add_fun qwe my_fun 'a' >>>
六。完善装饰器
my_fun 指向了新的函数 ,那一些属性就不是原来 my_fun 函数的了,这对有些依赖函数签名的代码执行就会出错(其实并不懂啥是依赖函数签名的代码0.0)。
>>> my_fun.__name__ 'wrapper' >>>
就要在装饰器里添加 wrapper.__name__ = my_fun.__name__ 和 wrapper.__doc__ = my_fun.__doc__ 等代码,可以直接用Python内置的functools.wraps来处理。
>>> import functools >>> def add_log(func): ... @functools.wraps(func) ... def wrapper(*args,**kw): ... print 'add_fun' ... return func(*args,**kw) ... return wrapper ... >>> @add_log ... def my_fun(a): ... print 'my_fun' ... return 'a' ... >>> print my_fun.__name__ my_fun >>>
最后需要指出,由于我们把原函数签名改成了(*args, **kw),因此,无法获得原函数的原始参数信息。即便我们采用固定参数来装饰只有一个参数的函数
四。参考
https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386819879946007bbf6ad052463ab18034f0254bf355000
http://www.imooc.com/code/6067
https://www.imooc.com/learn/581