Python 装饰器(Decorators)
Python 装饰器【函数闭包(function closure)的语法糖(Syntactic sugar)】
1. 函数闭包(function closure)
多个函数耦合
import time
# 函数逻辑(查找奇数)和辅助功能(记录时间)耦合在一起了,
# 缺点:不方便修改,容易引起bug
# 能不能将辅助功能从主要功能函数中抽离出来?
def print_odds():
# 输出0~100之间所有奇数,并统计函数执行时间
start_time = time.clock()
#起始时间
#查找并输出所有奇数
for i in range(100):
if i % 2 == 1:
print(i)
end_time = time.clock()
#结束时间
print("it takes {} s to find all the odds".format(end_time - start_time))
if __name__ == '__main__':
print_odds()
解耦
import time
# 解耦,函数职责分离
# 缺点:要通过辅助函数来调用主要功能函数,不方便
def count_time(func):
start_time = time.clock()
func()
end_time = time.clock()
# 结束时间
print("it takes {} s to find all the odds".format(end_time - start_time))
def print_odds():
# 输出0~100之间所有奇数,并统计函数执行时间
# 查找并输出所有奇数
for i in range(100):
if i % 2 == 1:
print(i)
if __name__ == '__main__':
count_time(print_odds)
对应使用者来说应该只知道主要函数,辅助函数应该隐藏
函数闭包:一个函数,其参数和返回值都是函数
- 用于增强函数功能
- 面向切面编程(AOP)
import time
def print_odds():
# 输出0~100之间所有奇数,并统计函数执行时间
# 查找并输出所有奇数
for i in range(100):
if i % 2 == 1:
print(i)
# 闭包本质上是一个函数
# 闭包函数的传入参数和返回值也都是函数
# 闭包函数的返回函数是对传入函数进行增强后的结果
def count_time_wrapper(func):
# 闭包
# 用于增强函数 func:给函数 func 增加统计时间的功能
def improve_func():
start_time = time.clock()
func()
end_time = time.clock()
# 结束时间
print("it takes {} s to find all the odds".format(end_time - start_time))
return improve_func # 返回函数对象
if __name__ == '__main__':
# 调用 count_time_wrapper 增强函数
print_odds = count_time_wrapper(print_odds)
print_odds()
2. 语法糖(Syntactic sugar)
3. 装饰器(decorator)
无形参,无返回值
import time
def count_time_wrapper(func):
# 闭包
# 用于增强函数 func:给函数 func 增加统计时间的功能
def improve_func():
start_time = time.clock()
func()
end_time = time.clock()
# 结束时间
print("it takes {} s to find all the odds".format(end_time - start_time))
return improve_func
@count_time_wrapper
def print_odds():
# 输出0~100之间所有奇数,并统计函数执行时间
# 查找并输出所有奇数
for i in range(100):
if i % 2 == 1:
print(i)
if __name__ == '__main__':
# 装饰器等价于在第一次调用函数时执行以下语句:
# print_odds = count_time_wrapper(print_odds)
print_odds()
解决带参数和返回值场景
import time
def count_time_wrapper(func):
# 闭包
# 用于增强函数 func:给函数 func 增加统计时间的功能
def improve_func(*args, **kwargs): # 增强函数应该把接收到的所有参数传给原函数
start_time = time.clock()
ret = func(*args, **kwargs) # 执行函数
end_time = time.clock()
# 结束时间
print("it takes {} s to find all the odds".format(end_time - start_time))
return ret # 增强函数的返回值应该是原函数的返回值
return improve_func
@count_time_wrapper
def print_odds():
# 输出0~100之间所有奇数,并统计函数执行时间
# 查找并输出所有奇数
for i in range(100):
if i % 2 == 1:
print(i)
# @count_time_wrapper
def count_odds(lim = 100):
cnt = 0
for i in range(lim):
if i % 2 == 1:
cnt+=1
return cnt
if __name__ == '__main__':
print(count_odds(lim=100000)) # 装饰前函数能正常返回,能接受参数
print('-------------')
count_odds = count_time_wrapper(count_odds)
print('-------------')
print(count_odds(lim=100000)) # 装饰后函数能正常返回,能接受参数
多个装饰器的执行顺序
4. *args 和 **kwargs
*args 和 **kwargs 是 Python 中用于处理可变数量参数的语法。
- *args
- *args 允许你传递可变数量的位置参数给一个函数。
- 当函数被调用时,所有额外的位置参数会被收集到一个元组中。
def example(*args):
for arg in args:
print(arg)
example(1, 2, 3)
# 输出:
# 1
# 2
# 3
- **kwargs
**kwargs 允许你传递可变数量的关键字参数给一个函数。
这些额外的关键字参数会被收集到一个字典中。
def example(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
example(name="Alice", age=30)
# 输出:
# name: Alice
# age: 30
4. 常见面试题
def wraper1(func1):
print('set func1') # 在 wrapper1 装饰函数时输出
def improved_func1():
print('call func1') # 在 wrapper1 装饰过的函数被调用时输出
func1()
return improved_func1
def wraper2(func2):
print('set func2') # 在 wrapper2 装饰函数时输出
def improved_func2():
print('call func2') # 在 wrapper2 装饰过的函数被调用时输出
func2()
return improved_func2
@wraper1
@wraper2
def original_func():
pass
if __name__ == '__main__':
original_func()
print('------')
original_func()