浙大疏锦行
今日内容核心:装饰器(Decorator)
1. 装饰器是什么?
- 本质:一个函数,它接收另一个函数作为参数,返回一个新函数(通常叫
wrapper),在不修改原函数代码的前提下,为原函数增加额外功能(如计时、日志、权限校验等)。 - 目的:遵循DRY 原则(Don’t Repeat Yourself),将重复的辅助逻辑(如计时)抽离出来,让核心函数更干净、可读性更强。
2. 装饰器的基本结构(无参数、无返回值)
defdisplay_time(func):defwrapper():# 前置操作start=time.time()func()# 调用原函数# 后置操作print(time.time()-start)returnwrapper# 返回新函数,而非执行它- 使用
@display_time语法糖,等价于prime_nums = display_time(prime_nums)。 - 调用
prime_nums()时,实际执行的是wrapper()。
3. 带参数的被装饰函数如何处理?
wrapper(*args, **kwargs)接收任意位置参数和关键字参数,然后原样传给func(*args, **kwargs)。- 这样装饰器就可以复用于任何参数形式的函数。
4. 带返回值的被装饰函数如何处理?
wrapper中需要result = func(...)并return result,保证原函数的返回值能传递出去。
5. 执行顺序(单步调试会看到)
- 定义
display_time - 定义
prime_nums,并立即应用@display_time→ 执行display_time(prime_nums),得到wrapper - 此时
prime_nums这个名字被重新绑定到wrapper - 调用
prime_nums()→ 进入wrapper→ 执行计时 → 调用原始的prime_nums(即func)→ 打印质数 → 输出耗时
你笔记中range(2, num)的运用
在判断素数is_prime(num)中:
foriinrange(2,num):ifnum%i==0:returnFalserange(2, num)生成从 2 到num-1的整数序列,用于测试所有可能的除数。- 如果
num能被其中任何一个数整除,则不是素数;否则是素数。 - 注意:
num=2时,range(2,2)为空,循环不执行,直接返回True(所以笔记中单独处理了num==2)。
装饰器与普通函数计时对比
- 普通写法:计时代码混在
prime_nums内部,干扰核心逻辑。 - 装饰器写法:计时逻辑独立成
display_time,prime_nums只关心找质数,代码更清晰,且计时功能可复用于其他函数。
今日总结一句话
装饰器是“函数的函数”,通过
@语法糖将原函数包装,在不改动原函数内部代码的情况下为其增加额外能力。理解其本质是func = decorator(func)的替换过程,并掌握*args, **kwargs传递参数以及return返回值,就能应对绝大多数场景。
今日作业:
def logger(func):
def wrapper(*args, **kwargs):
print(f"开始执行函数 {func.name},参数: {args}, {kwargs}“)
result = func(*args, **kwargs)
print(f"函数 {func.name} 执行完毕,返回值: {result}”)
return result
return wrapper
@logger
def multiply(a, b):
return a * b
multiply(2, 3)