Python的装饰器其实就是“函数是第一公民”的一种体现,即函数可以作为另一个函数的参数传入也可以作为函数的返回值返回。
一个标准的装饰器一般是这样:
def outter(func):
def inner(*args, **kwargs):
print(f'函数的名字:{func.__name__}')
print(f'函数的位置参数:{args}')
return func(*args, **kwargs)
return inner
这里定义了一个函数,名称是outter,它的参数是第二个函数也是未来将被执行的目标函数func。在outter中有第三个函数inner,这个函数是outter的内部函数,同时inner也将作为outter函数的返回值。
因为闭包的作用,当outter函数执行完毕返回inner后,inner函数中引用的参数func依然存在并可以被使用。
下面看outter的使用:
def outter(func):
def inner(*args, **kwargs):
print(f'函数的名字:{func.__name__}')
print(f'函数的位置参数:{args}')
return func(*args, **kwargs)
return inner
def greeting(name):
print(f'你好啊{name}')
inner_func = outter(greeting)
inner_func('老张')
将greeting函数作为参数传入outter函数。
当outter执行结束时,返回值(即内部函数inner)赋予了变量inner_func。随后通过inner_func变量调用inner函数并传入字符串参数老张。在闭包的作用下,inner函数在执行的时候依然可以访问到gretting函数的信息。inner函数打印了greeting函数的名称和自己的位置参数信息,最后inner函数调用了greeting函数并传入参数,并将greeting函数执行的返回值(greeting函数的返回值是None)作为自己的返回值。
最终上面代码的控制台输出信息是:
函数的名字:greeting
函数的位置参数:('老张',)
你好啊老张
outter函数就是一个装饰器,它起到的作用就是对func函数进行切片,在func执行前或后进行额外的操作。
Python还特别为装饰器提供了@便捷语法,我们可以把上面的两次调用变成一次对greeting函数的调用:
def outter(func):
def inner(*args, **kwargs):
print(f'函数的名字:{func.__name__}')
print(f'函数的位置参数:{args}')
return func(*args, **kwargs)
return inner
@outter
def greeting(name):
print(f'你好啊{name}')
greeting('老张')
上面是装饰器的标准用法,下面看两种装饰器的额外用法:
第一种:装饰器带参数。适用于根据装饰器参数作出不同切片选择的场合。看示例代码:
def outter_with_param(country):
def outter(func):
def inner(*args, **kwargs):
print(f'函数的名字:{func.__name__}')
print(f'函数的位置参数:{args}')
if country.lower() in ['china', 'chinese']:
print('你好', end=' ')
func(*args, **kwargs)
else:
print('hello', end=' ')
func(*args, **kwargs)
return inner
return outter
@outter_with_param('China')
def greeting(name):
print(f'{name}')
greeting('老张')
最外面又套了一层outter_with_param函数,这个outter_with_param就是带参数的装饰器。在闭包的作用下,inner函数调用外部的func和country都可以访问到。
装饰器的切片作用就是根据装饰器参数country值的不同,选择不同的问候语言:你好或者hello。
当示例的代码被执行时,最终的打印结果是:
函数的名字:greeting
函数的位置参数:('老张',)
你好 老张
第二种:多个装饰器
def outter1(func):
def inner1(*args):
result = func(*args)
result = result**2
print(f'inner1的计算结果是{result}')
return result
return inner1
def outter2(func):
def inner2(*args):
result = func(*args)
result = result * 2
print(f'inner2的计算结果是{result}')
return result
return inner2
def my(a, b):
return a + b
现在有两个装饰器outter1和outter2。
装饰器outter1的作用是将参数func函数的计算结果平方后再返回,而outter2的作用是将参数func函数的计算结果翻一倍再返回。
现在将两个装饰器都叠加在函数my上面:
@outter2
@outter1
def my(a,b):
return a+b
my(3,5)
此时屏幕上会显示什么内容呢?
上述写法与:
inner2 = outter2(outter1(my))
inne2(3,5)
效果完全一样。
inner2引用的是outter2中的内部函数inner2,当调用inner2(3,5)的时候,会调用outter2的参数函数,也就是:
outter1(my)(3,5)
inner2会等待outter1(my)(3,5)的结果,并将该结果翻一倍,打印到屏幕并作为自己的返回值。
outter1(my)(3,5)的值是多少呢?这是一种连续调用的写法,等同于:
inner1 = outter1(my)
inner1(3,5)
inner1引用的是outter1函数的内部函数inner1。inner函数会将my函数的返回结果平方。inner1执行时屏幕上打印inner1的计算结果是64并返回64。
inner2拿到返回的结果64后,将这个结果翻一倍,屏幕上打印inner2的计算结果是128并返回128。
所以代码最终运行的结果是:
inner1的计算结果是64
inner2的计算结果是128
由此也不能推断出,写成如下形式的执行情况了:
@outter1
@outter2
my(a,b):
return a+b
my(3,5)
inner2翻倍my函数的执行结果,屏幕上打印inner2的计算结果是16并返回16
inner1拿到inner2返回的结果16后,再将这个结果平方,屏幕上打印inner1的计算结果是256并返回256
inner2的计算结果是16
inner1的计算结果是256