Python 装饰器完全指南:从入门到实战应用
Python 装饰器完全指南:从入门到实战应用
引言
在 Python 编程中,装饰器(Decorator)是最强大也最容易被误解的特性之一。许多初学者在看到 INLINE_CODE_0 符号时感到困惑,而经验丰富的开发者则利用装饰器编写出优雅、可维护的代码。本文将带你从装饰器的基本原理出发,逐步掌握其高级用法,并通过实际案例展示如何在真实项目中应用这一强大工具。
装饰器的本质是一个函数,它接受另一个函数作为参数,并返回一个新的函数。通过装饰器,我们可以在不修改原函数代码的前提下,为其添加额外的功能。这种设计模式完美体现了 Python 的"开放 - 封闭原则"——对扩展开放,对修改封闭。
一、装饰器的基础原理
1.1 函数即对象
要理解装饰器,首先要理解 Python 中"函数是一等公民"的概念。在 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!
1.2 第一个装饰器
基于上述概念,我们可以创建最简单的装饰器:
def my_decorator(func):
def wrapper(*args, **kwargs):
print("函数执行前")
result = func(*args, **kwargs)
print("函数执行后")
return result
return wrapper
@my_decorator
def say_hello(name):
print(f"Hello, {name}!")
say_hello("Alice")
# 输出:
# 函数执行前
# Hello, Alice!
# 函数执行后
INLINE_CODE_1 语法糖等价于 INLINE_CODE_2。装饰器在函数定义时立即执行,返回的 INLINE_CODE_3 函数替换了原函数。
1.3 保留函数元信息
上述装饰器有一个问题:原函数的 INLINE_CODE_4 和 INLINE_CODE_5 会被 wrapper 覆盖。使用 INLINE_CODE_6 可以解决这个问题:
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("函数执行前")
return func(*args, **kwargs)
return wrapper
@my_decorator
def say_hello(name):
"""向用户打招呼"""
print(f"Hello, {name}!")
print(say_hello.__name__) # 输出:say_hello(而不是 wrapper)
print(say_hello.__doc__) # 输出:向用户打招呼
二、带参数的装饰器
2.1 装饰器工厂
当装饰器本身需要参数时,我们需要创建"装饰器工厂"——一个返回装饰器的函数:
def repeat(times):
"""重复执行函数的装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for i in range(times):
print(f"第 {i + 1} 次执行")
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
2.2 可选参数的装饰器
更复杂的场景是装饰器既有可选参数又有默认行为:
def optional_decorator(_func=None, *, prefix="INFO"):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"[{prefix}] 开始执行 {func.__name__}")
return func(*args, **kwargs)
return wrapper
if _func is None:
return decorator
else:
return decorator(_func)
@optional_decorator
def func1():
pass
@optional_decorator(prefix="DEBUG")
def func2():
pass
三、类装饰器
装饰器不仅可以装饰函数,还可以装饰类:
def singleton(cls):
"""单例模式装饰器"""
instances = {}
@wraps(cls)
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class Database:
def __init__(self):
self.connection = "DB Connection"
db1 = Database()
db2 = Database()
print(db1 is db2) # 输出:True(同一个实例)
四、实际应用场景
4.1 日志记录
import logging
from datetime import datetime
logging.basicConfig(level=logging.INFO)
def log_execution(func):
@wraps(func)
def wrapper(*args, **kwargs):
logging.info(f"[{datetime.now()}] 调用 {func.__name__}")
try:
result = func(*args, **kwargs)
logging.info(f"[{datetime.now()}] {func.__name__} 执行成功")
return result
except Exception as e:
logging.error(f"[{datetime.now()}] {func.__name__} 执行失败:{e}")
raise
return wrapper
@log_execution
def process_data(data):
return [x * 2 for x in data]
4.2 性能分析
import time
from functools import wraps
def timing(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
@timing
def slow_function():
time.sleep(1)
return "完成"
4.3 权限验证
def require_auth(func):
@wraps(func)
def wrapper(user, *args, **kwargs):
if not user.is_authenticated:
raise PermissionError("用户未认证")
if not user.has_permission(func.__name__):
raise PermissionError("用户无权限")
return func(user, *args, **kwargs)
return wrapper
@require_auth
def delete_resource(user, resource_id):
# 删除资源
pass
4.4 缓存(记忆化)
def memoize(func):
cache = {}
@wraps(func)
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
@memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(50)) # 瞬间完成
4.5 重试机制
import random
import time
def retry(max_attempts=3, delay=1, backoff=2):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
attempts = 0
current_delay = delay
while attempts < max_attempts:
try:
return func(*args, **kwargs)
except Exception as e:
attempts += 1
if attempts >= max_attempts:
raise
print(f"尝试 {attempts}/{max_attempts} 失败,{current_delay}秒后重试")
time.sleep(current_delay)
current_delay *= backoff
return wrapper
return decorator
@retry(max_attempts=5, delay=2)
def unstable_api():
if random.random() < 0.7:
raise ConnectionError("网络错误")
return "成功"
4.6 事务管理
def transaction(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 开始事务
print("BEGIN TRANSACTION")
try:
result = func(*args, **kwargs)
print("COMMIT")
return result
except Exception:
print("ROLLBACK")
raise
return wrapper
@transaction
def transfer_money(from_account, to_account, amount):
# 转账逻辑
pass
五、装饰器栈
多个装饰器可以叠加使用,执行顺序是从下到上(从内到外):
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: 后
六、最佳实践与注意事项
6.1 始终使用 @wraps
忘记使用 INLINE_CODE_7 是常见错误,会导致调试困难和文档丢失。
6.2 保持装饰器单一职责
每个装饰器只做一件事,复杂的装饰器应该拆分成多个小装饰器组合使用。
6.3 注意装饰器执行时机
装饰器在模块导入时执行,不要在装饰器中执行耗时操作。
6.4 处理异步函数
对于异步函数,需要使用 INLINE_CODE_8 相关的装饰器:
import asyncio
from functools import wraps
def async_timing(func):
@wraps(func)
async def wrapper(*args, **kwargs):
start = time.perf_counter()
result = await func(*args, **kwargs)
end = time.perf_counter()
print(f"{func.__name__} 执行时间:{end - start:.6f} 秒")
return result
return wrapper
@async_timing
async def fetch_data():
await asyncio.sleep(1)
return "data"
结语
装饰器是 Python 中最优雅的特性之一,它让我们能够以声明式的方式为函数添加功能,极大地提高了代码的可读性和可维护性。从简单的日志记录到复杂的权限控制,从性能优化到事务管理,装饰器的应用场景无处不在。
掌握装饰器的关键在于理解其本质——高阶函数的应用。一旦你理解了函数可以作为参数传递和返回,装饰器的神秘面纱就会被揭开。建议从简单的装饰器开始练习,逐步尝试更复杂的场景,最终你将能够灵活运用装饰器编写出更加 Pythonic 的代码。
记住:优秀的装饰器应该是透明的、可组合的、并且遵循单一职责原则。当你发现自己在多个函数中重复相同的代码模式时,就是考虑使用装饰器的最佳时机。