Python 装饰器完全指南:从入门到实战应用
Python 装饰器完全指南:从入门到实战应用
引言
在 Python 编程的世界里,装饰器(Decorator)无疑是最具魅力也最令人困惑的特性之一。许多初学者第一次看到 INLINE_CODE_0 符号时都会感到困惑:这到底是什么?为什么函数上面要加这个?它又是如何工作的?
装饰器的本质是一个高阶函数——它接受一个函数作为输入,并返回一个新的函数。这种设计模式让你能够在不修改原始函数代码的前提下,为其添加额外的功能。这正是 Python 中"开放 - 封闭原则"(对扩展开放,对修改封闭)的完美体现。
本文将带你从零开始理解装饰器,通过大量可运行的代码示例,掌握这一强大工具,并学会在实际项目中灵活运用。
一、装饰器的基础概念
1.1 什么是装饰器?
简单来说,装饰器是一个"包装器"。想象你买了一件礼物,你会用包装纸把它包起来,添加一些装饰。礼物本身没有改变,但它现在有了额外的"功能"——更美观、更适合送礼。
装饰器对函数的作用也是如此:它不改变函数的核心逻辑,但可以为函数添加额外的行为,比如:
- 记录日志
- 性能计时
- 权限验证
- 缓存结果
- 异常处理
1.2 从函数开始理解
要理解装饰器,首先需要理解三个概念:
1. 函数是一等公民
在 Python 中,函数可以像变量一样被传递:
def greet(name):
return f"Hello, {name}!"
# 函数可以赋值给变量
say_hello = greet
print(say_hello("Alice")) # 输出:Hello, Alice!
# 函数可以作为参数传递
def call_function(func, arg):
return func(arg)
print(call_function(greet, "Bob")) # 输出:Hello, Bob!
# 函数可以作为返回值
def create_greeter(prefix):
def greeter(name):
return f"{prefix}, {name}!"
return greeter
say_hi = create_greeter("Hi")
print(say_hi("Charlie")) # 输出:Hi, Charlie!
2. 嵌套函数
函数内部可以定义另一个函数:
def outer():
print("这是外层函数")
def inner():
print("这是内层函数")
return inner
func = outer()
func() # 输出:这是内层函数
3. 闭包
内层函数可以访问外层函数的变量:
def make_multiplier(factor):
def multiply(number):
return number * factor
return multiply
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 输出:10
print(triple(5)) # 输出:15
理解了这三点,你就已经掌握了装饰器的基础!
二、手写第一个装饰器
2.1 最简单的装饰器
让我们从一个最简单的例子开始:
def my_decorator(func):
def wrapper():
print("函数执行前")
func()
print("函数执行后")
return wrapper
def say_hello():
print("Hello!")
# 手动应用装饰器
say_hello = my_decorator(say_hello)
say_hello()
输出:
函数执行前
Hello!
函数执行后
2.2 使用 @ 语法糖
Python 提供了更简洁的语法:
def my_decorator(func):
def wrapper():
print("函数执行前")
func()
print("函数执行后")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
INLINE_CODE_1 等价于 INLINE_CODE_2。
2.3 处理函数参数
实际应用中,函数通常有参数。装饰器需要能够处理任意参数:
def my_decorator(func):
def wrapper(*args, **kwargs):
print(f"调用函数:{func.__name__}")
print(f"参数:args={args}, kwargs={kwargs}")
result = func(*args, **kwargs)
print(f"返回值:{result}")
return result
return wrapper
@my_decorator
def add(a, b):
return a + b
@my_decorator
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
add(3, 5) # 输出完整的调用信息
greet("Alice", greeting="Hi")
2.4 保留函数元信息
使用装饰器后,函数的 INLINE_CODE_3 和 INLINE_CODE_4 会被 wrapper 覆盖。使用 INLINE_CODE_5 可以保留原始信息:
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@my_decorator
def documented_function():
"""这是一个有文档的函数"""
pass
print(documented_function.__name__) # 输出:documented_function
print(documented_function.__doc__) # 输出:这是一个有文档的函数
三、实用装饰器示例
3.1 日志装饰器
记录函数的调用信息,便于调试和监控:
from functools import wraps
import logging
# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
def log_call(func):
@wraps(func)
def wrapper(*args, **kwargs):
logging.info(f"调用 {func.__name__}, 参数:{args}, {kwargs}")
try:
result = func(*args, **kwargs)
logging.info(f"{func.__name__} 执行成功")
return result
except Exception as e:
logging.error(f"{func.__name__} 执行失败:{e}")
raise
return wrapper
@log_call
def divide(a, b):
return a / b
divide(10, 2)
# divide(10, 0) # 会记录异常
3.2 性能计时装饰器
测量函数执行时间,帮助定位性能瓶颈:
from functools import wraps
import time
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
end = time.perf_counter()
print(f"{func.__name__} 执行时间:{end - start:.6f} 秒")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
@timer
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
slow_function()
fibonacci(10)
3.3 缓存装饰器(Memoization)
缓存函数结果,避免重复计算:
from functools import wraps
def memoize(func):
cache = {}
@wraps(func)
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
print(f"计算 {args} 并缓存结果")
else:
print(f"从缓存获取 {args} 的结果")
return cache[args]
return wrapper
@memoize
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(5)) # 会计算并缓存
print(fibonacci(5)) # 直接从缓存获取
print(fibonacci(6)) # 只需要计算新值
3.4 重试装饰器
在网络请求等不稳定操作中自动重试:
from functools import wraps
import time
import random
def retry(max_attempts=3, delay=1):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
last_exception = None
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
last_exception = e
print(f"尝试 {attempt + 1}/{max_attempts} 失败:{e}")
if attempt < max_attempts - 1:
time.sleep(delay)
raise last_exception
return wrapper
return decorator
@retry(max_attempts=3, delay=0.5)
def unstable_operation():
if random.random() < 0.7: # 70% 概率失败
raise ConnectionError("网络不稳定")
return "操作成功"
try:
result = unstable_operation()
print(result)
except Exception as e:
print(f"最终失败:{e}")
3.5 权限验证装饰器
在 Web 应用中验证用户权限:
from functools import wraps
class User:
def __init__(self, name, role):
self.name = name
self.role = role
# 模拟当前用户
current_user = None
def require_role(*allowed_roles):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if current_user is None:
raise PermissionError("用户未登录")
if current_user.role not in allowed_roles:
raise PermissionError(
f"需要权限:{allowed_roles}, 当前权限:{current_user.role}"
)
return func(*args, **kwargs)
return wrapper
return decorator
@require_role("admin", "manager")
def delete_user(user_id):
print(f"删除用户 {user_id}")
@require_role("admin")
def shutdown_system():
print("系统关闭")
# 测试
current_user = User("Alice", "admin")
delete_user(123) # 成功
shutdown_system() # 成功
current_user = User("Bob", "user")
# delete_user(456) # 抛出权限错误
四、带参数的装饰器
有时候装饰器本身需要参数,比如指定重试次数、缓存时间等。这需要三层嵌套:
from functools import wraps
def repeat(times):
"""重复执行函数指定次数"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
results = []
for _ in range(times):
results.append(func(*args, **kwargs))
return results
return wrapper
return decorator
@repeat(3)
def roll_dice():
import random
return random.randint(1, 6)
print(roll_dice()) # 输出 3 个随机数的列表
记忆技巧:
- 无参数装饰器:2 层函数(decorator + wrapper)
- 有参数装饰器:3 层函数(param_decorator + decorator + wrapper)
五、类装饰器
装饰器不仅可以装饰函数,还可以装饰类:
def singleton(cls):
"""单例模式装饰器"""
instances = {}
@wraps(cls)
def wrapper(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return wrapper
@singleton
class Database:
def __init__(self):
self.connection = "数据库连接"
print("创建数据库连接")
db1 = Database()
db2 = Database()
print(db1 is db2) # 输出:True(同一个实例)
六、装饰器栈(多个装饰器)
一个函数可以应用多个装饰器,从下往上执行:
def decorator_a(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("装饰器 A - 前")
result = func(*args, **kwargs)
print("装饰器 A - 后")
return result
return wrapper
def decorator_b(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("装饰器 B - 前")
result = func(*args, **kwargs)
print("装饰器 B - 后")
return result
return wrapper
@decorator_a
@decorator_b
def hello():
print("Hello!")
hello()
输出顺序:
装饰器 A - 前
装饰器 B - 前
Hello!
装饰器 B - 后
装饰器 A - 后
七、实际项目中的应用场景
7.1 Web 框架中的装饰器
Flask 和 Django 等框架广泛使用装饰器:
# Flask 路由装饰器
@app.route("/users/<int:user_id>")
@require_login
@cache_response(timeout=300)
def get_user(user_id):
return db.get_user(user_id)
7.2 API 速率限制
from functools import wraps
import time
from collections import defaultdict
def rate_limit(max_calls, period):
calls = defaultdict(list)
def decorator(func):
@wraps(func)
def wrapper(client_id, *args, **kwargs):
now = time.time()
# 清理过期记录
calls[client_id] = [t for t in calls[client_id] if now - t < period]
if len(calls[client_id]) >= max_calls:
raise Exception("请求频率超限")
calls[client_id].append(now)
return func(client_id, *args, **kwargs)
return wrapper
return decorator
@rate_limit(max_calls=5, period=60) # 每分钟最多 5 次
def api_call(client_id, endpoint):
print(f"调用 API: {endpoint}")
7.3 事务管理
from functools import wraps
def transaction(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("BEGIN TRANSACTION")
try:
result = func(*args, **kwargs)
print("COMMIT")
return result
except Exception as e:
print(f"ROLLBACK: {e}")
raise
return wrapper
@transaction
def transfer_money(from_account, to_account, amount):
print(f"从 {from_account} 转账 {amount} 到 {to_account}")
# 如果中间出错,会自动回滚
八、最佳实践与注意事项
8.1 始终使用 @wraps
忘记使用 INLINE_CODE_6 是常见错误,会导致调试困难:
# 错误示例
def bad_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
# 正确示例
def good_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
8.2 保持装饰器单一职责
每个装饰器只做一件事,便于组合和维护:
# 不好:一个装饰器做太多事
@log_and_cache_and_retry
def my_function():
pass
# 好:多个单一职责的装饰器
@log_call
@cache_result
@retry_on_failure
def my_function():
pass
8.3 注意装饰器的性能开销
复杂装饰器可能影响性能,在热点代码中谨慎使用:
# 避免在循环中重复应用复杂装饰器
for i in range(1000000):
decorated_function(i) # 每次调用都有装饰器开销
结语
装饰器是 Python 中最优雅的特性之一,它让你能够以声明式的方式为函数添加功能,保持代码的整洁和可维护性。
本文从基础概念讲起,通过大量可运行的示例,展示了装饰器的各种应用场景。希望你能在实践中灵活运用这些技巧,写出更优雅的 Python 代码。
记住:装饰器的核心思想是不修改原有代码,通过包装来扩展功能。这正是软件设计中"开放 - 封闭原则"的完美体现。
下一步,你可以尝试:
- 为自己的项目创建自定义装饰器
- 阅读 Flask、Django 等框架的源码,学习它们如何使用装饰器
- 探索 INLINE_CODE_7 模块中的内置装饰器(如 INLINE_CODE_8、INLINE_CODE_9)
Happy Coding!