897 字
4 分钟
【Python】Python中的修饰器

Python 装饰器(Decorator)详解#

什么是装饰器?#

装饰器本质上是一个函数,它接收一个函数作为参数,并返回一个新的函数。它可以在不修改原函数代码的情况下,增强或改变函数的行为。

# 装饰器本质
@decorator
def func():
pass
# 等价于
decorator(func)

最简单的装饰器示例#

def my_decorator(func):
def wrapper():
print("函数执行前...")
func()
print("函数执行后...")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()

输出:

函数执行前...
Hello!
函数执行后...

装饰器的执行流程#

┌─────────────────────────────────────────────────────────┐
│ @my_decorator │
│ def say_hello(): │
│ print("Hello!") │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ 1. Python 解释器看到 @my_decorator │
│ 2. 调用 my_decorator(say_hello) │
│ 3. 返回 wrapper 函数 │
│ 4. say_hello = wrapper │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ 调用 say_hello() 实际上是调用 wrapper() │
└─────────────────────────────────────────────────────────┘

使用场景#

1. 计时器 - 测量函数执行时间#

import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} 执行耗时: {end - start:.4f} 秒")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
print("完成!")
slow_function()
# 输出:
# 完成!
# slow_function 执行耗时: 1.0012 秒

2. 日志记录#

def log(func):
def wrapper(*args, **kwargs):
print(f"调用函数: {func.__name__}, 参数: {args}, {kwargs}")
result = func(*args, **kwargs)
print(f"函数返回: {result}")
return result
return wrapper
@log
def add(a, b):
return a + b
add(3, 5)
# 输出:
# 调用函数: add, 参数: (3, 5), {}
# 函数返回: 8

3. 权限验证#

def require_login(func):
def wrapper(user, *args, **kwargs):
if not user.get('is_logged_in'):
print("请先登录!")
return None
return func(user, *args, **kwargs)
return wrapper
@require_login
def view_profile(user):
print(f"欢迎, {user['name']}!")
view_profile({'name': 'Alice', 'is_logged_in': False}) # 请先登录!
view_profile({'name': 'Alice', 'is_logged_in': True}) # 欢迎, Alice!

4. 缓存(记忆化)#

def cache(func):
cached_results = {}
def wrapper(*args):
if args in cached_results:
print(f"从缓存获取: {args}")
return cached_results[args]
result = func(*args)
cached_results[args] = result
return result
return wrapper
@cache
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10)) # 第一次计算
print(fibonacci(10)) # 从缓存获取

5. 重试机制#

import random
def retry(max_attempts=3):
def decorator(func):
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"第 {attempt + 1} 次尝试失败: {e}")
print("所有尝试均失败!")
return wrapper
return decorator
@retry(max_attempts=3)
def unstable_api():
if random.random() < 0.7:
raise ConnectionError("网络错误")
return "成功!"
unstable_api()

带参数的装饰器#

def repeat(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def greet(name):
print(f"Hello, {name}!")
greet("World")
# 输出:
# Hello, World!
# Hello, World!
# Hello, World!

结构对比:

普通装饰器(2层) 带参数的装饰器(3层)
───────────────── ─────────────────────
def decorator(func): def decorator_factory(arg):
def wrapper(): def decorator(func):
... def wrapper():
return wrapper ...
return wrapper
return decorator

类装饰器#

装饰器也可以是一个类:

class CountCalls:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"{self.func.__name__} 被调用了 {self.count} 次")
return self.func(*args, **kwargs)
@CountCalls
def say_hi():
print("Hi!")
say_hi() # say_hi 被调用了 1 次 \n Hi!
say_hi() # say_hi 被调用了 2 次 \n Hi!

多个装饰器叠加#

@decorator1
@decorator2
@decorator3
def func():
pass
# 等价于
func = decorator1(decorator2(decorator3(func)))

执行顺序: 从下往上装饰,从上往下执行

保留原函数信息#

使用 functools.wraps 保留原函数的元信息:

from functools import wraps
def my_decorator(func):
@wraps(func) # 保留原函数的 __name__, __doc__ 等
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""这是文档字符串"""
pass
print(example.__name__) # 输出: example(而不是 wrapper)
print(example.__doc__) # 输出: 这是文档字符串

总结#

使用场景示例
日志/调试记录函数调用信息
性能监控计时、计数
权限控制登录验证、角色检查
缓存避免重复计算
重试机制网络请求失败重试
输入验证参数类型检查
注册机制Flask 路由、d2l 的 add_to_class

装饰器是 Python 中实现 AOP(面向切面编程) 的优雅方式,让你能够将横切关注点(如日志、缓存、权限)与业务逻辑分离!

【Python】Python中的修饰器
http://onemom.top/posts/python_decorater/
作者
onemotre
发布于
2025-12-25
许可协议
CC BY-NC-SA 4.0