Python 装饰器(Decorators)
- 什么是装饰器?
装饰器(Decorator)本质上是一个 修改其他函数功能的函数。它的核心思想是:不修改原函数代码,动态添加新功能。比如:
记录函数执行时间
检查用户权限
缓存计算结果
自动重试失败操作
- 理解函数是“对象”
在 Python 中,函数也是对象,可以像变量一样传递。这是装饰器的核心基础!
示例 1:函数赋值给变量
python
复制
def say_hello():
print("Hello!")
将函数赋值给变量
greet = say_hello
调用变量(实际调用函数)
greet() # 输出: Hello!
示例 2:函数作为参数传递
python
复制
def call_twice(func):
func() # 第一次调用
func() # 第二次调用
call_twice(say_hello)
# 输出:
# Hello!
# Hello!
- 最简单的装饰器
假设我们要在函数执行前后打印日志:
步骤 1:定义一个装饰器函数
python
复制
def log_decorator(func):
def wrapper():
print("函数开始执行...")
func() # 调用原函数
print("函数执行完毕!")
return wrapper # 返回新函数
步骤 2:使用装饰器
python
复制
@log_decorator # 语法糖(等价于 say_hello = log_decorator(say_hello)
def say_hello():
print("Hello!")
say_hello()
# 输出:
# 函数开始执行...
# Hello!
# 函数执行完毕!
- 装饰器的执行过程
当使用 @log_decorator 时,实际发生了以下步骤:
传递函数:log_decorator(say_hello) 被调用,参数是原函数 say_hello
返回新函数:log_decorator 返回内部定义的 wrapper 函数
替换原函数:say_hello 变量指向新的 wrapper 函数
- 处理带参数的函数
如果被装饰的函数需要参数,怎么办?让 wrapper 接受参数并传递给原函数。
示例:记录函数参数
python
复制
def log_args(func):
def wrapper(*args, **kwargs): # 接受任意参数
print(f"参数: args={args}, kwargs={kwargs}")
return func(*args, **kwargs) # 传递参数给原函数
return wrapper
@log_args
def add(a, b):
return a + b
print(add(3, b=5))
# 输出:
# 参数: args=(3,), kwargs={'b': 5}
8
- 装饰器带参数
如果装饰器本身需要参数(比如指定日志级别),需要再包裹一层。
示例:根据日志级别打印
python
复制
def log_level(level): # 外层函数接受参数
def decorator(func): # 装饰器函数
def wrapper(*args, **kwargs):
print(f"[{level}] 函数开始执行...")
result = func(*args, **kwargs)
print(f"[{level}] 函数执行完毕!")
return result
return wrapper
return decorator # 返回装饰器
@log_level("INFO") # 等价于 add = log_level("INFO")(add)
def add(a, b):
return a + b
add(2, 3)
# 输出:
# [INFO] 函数开始执行...
# [INFO] 函数执行完毕!
- 保留原函数的信息
使用装饰器后,原函数的名称(name)和文档(doc)会被替换为 wrapper。用 functools.wraps 解决这个问题:
python
复制
from functools import wraps
def log_decorator(func):
@wraps(func) # 保留原函数信息
def wrapper(*args, **kwargs):
print("开始执行...")
result = func(*args, **kwargs)
print("执行完毕!")
return result
return wrapper
@log_decorator
def say_hello():
"""打招呼的函数"""
print("Hello!")
print(say_hello.__name__) # 输出: say_hello(而不是 wrapper)
print(say_hello.__doc__) # 输出: 打招呼的函数
- 类装饰器
除了函数,还可以用类实现装饰器。通过实现 call 方法:
python
复制
class CountCalls:
def __init__(self, func):
self.func = func
self.calls = 0
def __call__(self, *args, **kwargs):
self.calls += 1
print(f"函数被调用了 {self.calls} 次")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
say_hello() # 输出: 函数被调用了 1 次 → Hello!
say_hello() # 输出: 函数被调用了 2 次 → Hello
!
9. 实际应用场景
装饰器在 Python 中广泛应用,例如:
Web框架:@app.route(“/”)(Flask/Django)
权限验证:@login_required
性能测试:计算函数执行时间
缓存:@lru_cache(内置装饰器)
总结
核心思想:装饰器通过“函数嵌套”和“函数作为参数”实现功能扩展。
关键点:
使用 @decorator 语法糖
处理参数:*args 和 **kwargs
保留原函数信息:@wraps(func)
类装饰器:实现 call 方法
试着写几个自己的装饰器(比如记录执行时间),就能快速掌握这个概念!