Python装饰器与带参数装饰器解析
本文介绍Python装饰器与带参数装饰器,以及其与高阶函数的关系。
"""Python3 默认 unicode 编码,可以写中文以下代码请在 Python3 中运行"""
import time
import inspect
import sys
在 Python 中,装饰器是一个特殊的函数,它的输入必须是一个函数。
下面这个装饰器输入是一个函数X,然后返回值是函数Y,函数Y会将函数X运行 1000 次并且打印出其耗时。
def 装饰器(函数X):
def 函数Y(*X的参数, **X的kw参数):
start = time.time()
times = 1000
for _ in range(times):
ans = 函数X(*X的参数, **X的kw参数)
end = time.time()
print('run {} times use {}'.format(times, end - start))
return ans
return 函数Y
@装饰器
def sum1(array):
return sum(array)
此时的 sum1 等同于
>>> def sum1(array):
return sum(array)
>>> sum1 = 装饰器(sum1)
>>> sum1(array) 等同于 函数Y(array)
这里的 <函数Y> 是在 <装饰器> 的闭包环境中
也就是先将 的内容绑定给 <函数X>
再将 <装饰器> 的返回值 <函数Y> 绑定给 sum1
所以 运行 sum1(array) 会将
sum(array) 运行 1000 次并且打印其耗时
下面这个带参数装饰器,其返回值是一个装饰器,用这个装饰器装饰函数之后,可以运行该函数指定次数并且打印耗时。
def 带参数装饰器(times):
def 新装饰器(函数X):
def 函数Y(*X的参数, **X的kw参数):
start = time.time()
for _ in range(times):
# 运行次数由 带参数装饰器 的参数值决定
ans = 函数X(*X的参数, **X的kw参数)
end = time.time()
print('run {} times use {}'.format(times, end - start))
return ans
return 函数Y
return 新装饰器
@带参数装饰器(500)
def sum2(array):
return sum(array)
此时的 sum2 等同于
>>> def sum2(array):
return sum(array)
>>> sum2 = 带参数装饰器(500)(sum2)
>>> sum2(array) 等同于 函数Y(array)
这里的 <新装饰器> 是在 <带参数装饰器> 的闭包环境中
而 <函数Y> 又在 <新装饰器> 的闭包环境中
也就是先将 <新装饰器> 中的 times 变量设定为 500
然后将 的内容绑定给 <函数X>
最后将 <新装饰器> 的返回值 <函数Y> 绑定给 sum2
所以 运行 sum2(array) 会将
sum(array) 运行 times=500 次并且打印其耗时
Python的装饰器只限定了输入是函数,不对其输出进行任何限定。
def 坏蛋装饰器(函数X):
return "坏蛋装饰器返回值"
@坏蛋装饰器
def sum4(array):
return sum(array)
此时的 sum4 等同于
>>> def sum4(array):
return sum(array)
>>> sum4 = 坏蛋装饰器(sum4) = "坏蛋装饰器返回值"
>>> sum4(array) 会直接报错
下面代码测试下这些装饰器的作用,以及比较装饰器写法于高阶函数写法的不同。
def sum3(array):
return sum(array)
def test_sum():
array = list(range(1000))
# 装饰器写法在此!
print('装饰器写法结果:')
ans1 = sum1(array)
ans2 = sum2(array)
# 高阶函数写法在此!
# sum1 于 装饰器(sum3) 对应
# sum2 于 带参数装饰器(100)(sum3) 对应
print('高阶函数写法结果:')
ans3 = 装饰器(sum3)(array)
ans4 = 带参数装饰器(100)(sum3)(array)
新的装饰器 = 带参数装饰器(200)
装饰后函数 = 新的装饰器(sum3)
ans5 = 装饰后函数(array)
assert ans1 == ans2 == ans3 == ans4 == ans5
print('坏蛋装饰器的结果:')
print(type(sum4), sum4)
print('pass test')
if __name__ == "__main__":
test_sum()
运行结果:
装饰器写法结果:
run 1000 times use 0.009413957595825195
run 500 times use 0.009354591369628906
高阶函数写法结果:
run 1000 times use 0.015649080276489258
run 100 times use 0.0019309520721435547
run 200 times use 0.0029439926147460938
坏蛋装饰器的结果:
坏蛋装饰器返回值
pass test
Python中装饰器本身是一类特殊的高阶函数。其中:装饰器(使用@符号在函数定义前):输入:任意一个函数,例如上文的函数X
输出:没有特定要求,通常是该装饰器内定义的函数,例如上文的函数Y
函数Y输入通常是X的参数,这样方便调用函数X
函数Y输出是没有特定要求,可以与函数X的结果相同或不同
装饰器的输出会与函数X的名称绑定,例如函数sum4不再是函数而是坏蛋装饰器返回的字符串
带参数装饰器:输入:改变装饰器行为的变量
输出为装饰器
带参数装饰器更加灵活,可以对装饰器的行为进行修改