python装饰器笔记
装饰器本质是一个函数,装饰器装饰一个函数,其实就是返回另一个函数,强调装饰器本质是函数以及返回的是函数,这个比较重要,因为装饰器语法看起来挺唬人的,其实也就是把对函数的操作绕了两三下,没啥高大上的内容
1、多层装饰器:
def makeBold(fun): print("***** 1 *******") def wrapped(): print("------ 1 ------") return "<b> " + fun() + " <\\b>" return wrapped def makeItalic(fun): print("***** 2 *******") def wrapped(): print("------- 2 ------") return "<i> " + fun() + " <\i>" return wrapped @makeBold @makeItalic def fun(): return "hello world" print(fun()) 输出: ***** 2 ******* ***** 1 ******* ------ 1 ------ ------- 2 ------ <b> <i> hello world <\i> <\b>
由此可见,解释器解释过程为:执行到@makeBold时,发现底下仍然是个装饰器,于是跳过先执行@makeItalic,等完成@makeItalic对fun的装饰(结果是返回一个函数),再返过去用@makeBold装饰第一步返回的函数
2、带参数的装饰器
带参数的装饰器其实就是一个装饰器“生成器”,或者说装饰器工厂,他返回的是一个常规的生成器,用返回的这个装饰器去装饰待装饰的函数。
比如functools.wraps就是一个带参数的装饰器。下面记录下对functools.wraps的原理理解。
一般装饰器会改变函数的一些属性,比如:
def log(func): def log_wrap(*args, **kargs): """this is log_wrap""" return func(*args, **kargs) return log_wrap @log def func(): """print a string""" pass print(func.__name__) print(func.__doc__)
输出:
log_wrap
this is log_wrap
这是因为func函数经@log装饰后,函数名func已经指向了log_wrap这个函数,所以原来的那个func的属性都丢失了,取而代之的是log_wrap的各个属性,但是实际应用中这是我们不愿看到的,因为一个函数经装饰后,不仅要看起来功能增强了,而且还要看起来函数还是原来的那个函数,即要保持原来函数的属性不变,要实现这个功能,可以做如下修改:
def log(func): def log_wrap(*args, **kargs): """this is log_wrap""" return func(*args, **kargs) log_wrap.__name__ = func.__name__ log_wrap.__doc__ = func.__doc__ return log_wrap @log def func(): """print a string""" pass 输出: func print a string
这下就对了,装饰前后,原函数的属性都是一样的。python提供了functools.wraps这个装饰器来替我们完成这件事,只需要如下使用,即可保证被装饰的函数属性不变:
from functools import wraps def log(func): @wraps(func) # 在这里加一个装饰器即可保证原函数的属性不变 def log_wrap(*args, **kargs): """this is log_wrap""" return func(*args, **kargs) return log_wrap @log def func(): """print a string""" pass print(func.__name__) print(func.__doc__) 输出: func print a string
wraps装饰器的源码没细看了,根据它的作用,以及它是个带参数的装饰器,所以自己简单实现了一下这个装饰器:
def wraps(f): def wrapper(func): def inner(*args, **kwargs): return func(*args, **kwargs) inner.__name__ = f.__name__ inner.__doc__ = f.__doc__ return inner return wrapper def log(func): @wraps(func) # 返回一个装饰器,用返回的装饰器装饰log_wrap,还是返回一个函数 def log_wrap(*args, **kargs): """this is log_wrap""" return func(*args, **kargs) return log_wrap @log def func(): """print a string""" pass print(func.__name__) print(func.__doc__) 输出: func print a string
效果是一样的。
@wraps(func)这句其实返回的是一个装饰器,用这个返回的装饰器装饰log_wrap,返回的还是一个函数