一、含义
首先,装饰器是用来给目标函数“装饰”用的;其次,装饰器是一个函数,它返回的是一个装饰好的目标函数。
二、举例
step1:
比如,我们写了一个求平方的目标函数:
def my_pow(x):
# 求平方
print(x ** 2)
为了提示函数确实执行了,改为:
def my_pow(x):
# 求平方
print('starting my_pow...')
print( x ** 2)
如果要将很多函数都加上这个提示功能,则需要重复大量代码,于是将核心代码包装在另外一个函数add_warning中:
def add_warning(func, x):
print('starting %s...' % func.__name__)
func(x)
def my_pow(x):
# 求平方
print(x ** 2)
# 36
add_warning(my_pow, 6)
但这种方式很绕,明明想要执行my_pow,却要去执行add_warning,并且传参也不太方便。
step2:
于是,利用可变参数和关键字参数,再改一下:
def add_warning(func):
def wrapper(*args, **kwargs):
print('starting %s...' % func.__name__)
return func(*args, **kwargs)
return wrapper
def my_pow(x):
# 求平方
print(x ** 2)
my_pow = add_warning(my_pow)
# 36
my_pow(6)
上面的代码中,add_warning就是一个装饰器,它是一个函数,把计算平方的核心代码包裹在wrapper函数中,然后装饰器把wrapper函数返回就可以了。
step3:
再进一步,为了不傻逼一样地将装饰器add_warning在代码中显式地赋给my_pow函数,于是高大上地改为如下这种形式:
def add_warning(func):
def wrapper(*args, **kwargs):
print('starting %s...' % func.__name__)
return func(*args, **kwargs)
return wrapper
@add_warning
def my_pow(x):
# 求平方
print(x ** 2)
# 36
my_pow(6)
这样,赋值操作就交给python解释器去完成了。
3、question
你可能会问,为什么不将step2中wrapper函数去掉,直接改为:
def add_warning(func, *args, **kwargs):
print('starting %s...' % func.__name__)
return func(*args, **kwargs)
def my_pow(x):
# 求平方
print(x ** 2)
my_pow = add_warning(my_pow)
# 36
my_pow(6)
因为,此时执行add_warning(my_pow)后,返回的并不是一个装饰好的函数,而是func执行后的具体值(结果),这与我们的目标不符。当然上面这段代码也会报错。。。