1. Python装饰器:函数的高级玩法
装饰器是Python中最优雅的语法糖之一,它允许我们在不修改原函数代码的情况下,为函数添加新功能。这种设计模式在Web框架(如Flask、Django)中被广泛使用,特别是在路由注册和权限验证场景中。
1.1 装饰器基础原理
装饰器的本质是一个高阶函数,它接收一个函数作为参数,并返回一个新的函数。这个新函数通常会包装原始函数,在调用前后执行额外的逻辑。
python复制def my_decorator(func):
def wrapper():
print("函数执行前")
func()
print("函数执行后")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
当调用say_hello()时,实际上调用的是wrapper()函数,它会先打印"函数执行前",然后调用原始say_hello,最后打印"函数执行后"。
1.2 装饰器的常见应用场景
- 日志记录:自动记录函数调用信息
- 性能测试:计算函数执行时间
- 权限验证:检查用户是否有权限执行操作
- 缓存:存储函数计算结果避免重复计算
- 错误处理:统一处理函数可能抛出的异常
提示:装饰器会改变函数的元信息(如
__name__),可以使用functools.wraps来保留原始函数的元数据。
2. 登录注册系统的装饰器实现
2.1 用户认证装饰器
在Web开发中,很多路由需要用户登录后才能访问。我们可以用装饰器来实现这个功能:
python复制from functools import wraps
users = {
'admin': '123456',
'user1': 'password'
}
current_user = None
def login_required(func):
@wraps(func)
def wrapper(*args, **kwargs):
if current_user is None:
print("请先登录!")
return login()
return func(*args, **kwargs)
return wrapper
2.2 权限控制装饰器
更进一步,我们可以实现基于角色的权限控制:
python复制def permission_required(permission):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if current_user and current_user['role'] == permission:
return func(*args, **kwargs)
print(f"需要{permission}权限!")
return None
return wrapper
return decorator
3. 完整登录注册系统实现
3.1 用户数据存储
我们先实现一个简单的用户存储系统:
python复制import json
from functools import wraps
class UserSystem:
def __init__(self):
self.users = {}
self.current_user = None
self.load_users()
def load_users(self):
try:
with open('users.json', 'r') as f:
self.users = json.load(f)
except FileNotFoundError:
self.users = {'admin': {'password': 'admin123', 'role': 'admin'}}
def save_users(self):
with open('users.json', 'w') as f:
json.dump(self.users, f)
3.2 注册功能实现
python复制def register(self, username, password, role='user'):
if username in self.users:
print("用户名已存在!")
return False
self.users[username] = {
'password': password,
'role': role
}
self.save_users()
print("注册成功!")
return True
3.3 登录功能实现
python复制def login(self, username, password):
if username not in self.users:
print("用户名不存在!")
return False
if self.users[username]['password'] != password:
print("密码错误!")
return False
self.current_user = {
'username': username,
'role': self.users[username]['role']
}
print("登录成功!")
return True
4. 装饰器在实际项目中的应用技巧
4.1 带参数的装饰器
有时候我们需要装饰器本身也能接收参数:
python复制def repeat(num_times):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for _ in range(num_times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(num_times=3)
def greet(name):
print(f"Hello {name}")
4.2 类装饰器
装饰器也可以使用类来实现:
python复制class CountCalls:
def __init__(self, func):
self.func = func
self.num_calls = 0
def __call__(self, *args, **kwargs):
self.num_calls += 1
print(f"调用次数: {self.num_calls}")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("Hello!")
4.3 多个装饰器的叠加
装饰器可以叠加使用,执行顺序是从下往上:
python复制@decorator1
@decorator2
@decorator3
def my_function():
pass
这等价于:decorator1(decorator2(decorator3(my_function)))
5. 常见问题与解决方案
5.1 装饰器导致函数签名改变
问题:使用装饰器后,函数的__name__和__doc__等元信息会被覆盖。
解决方案:使用functools.wraps装饰器:
python复制from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
5.2 装饰器与静态方法冲突
问题:在类中使用装饰器时,可能与@staticmethod或@classmethod冲突。
解决方案:确保装饰器位于最外层:
python复制class MyClass:
@my_decorator
@staticmethod
def my_method():
pass
5.3 调试装饰器函数
问题:调试时难以追踪装饰器内部的函数调用。
解决方案:
- 使用
pdb设置断点 - 打印函数调用信息
- 使用
inspect模块检查函数参数
python复制import inspect
def debug_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"调用函数: {func.__name__}")
print(f"参数: {inspect.signature(func)}")
return func(*args, **kwargs)
return wrapper
6. 性能优化与高级技巧
6.1 使用lru_cache实现缓存
Python标准库中的functools.lru_cache是一个实用的装饰器,可以缓存函数结果:
python复制from functools import lru_cache
@lru_cache(maxsize=128)
def expensive_function(x):
# 耗时计算
return result
6.2 异步函数装饰器
对于异步函数,装饰器需要稍作修改:
python复制def async_decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
print("异步装饰器前")
result = await func(*args, **kwargs)
print("异步装饰器后")
return result
return wrapper
6.3 装饰器工厂模式
当需要根据配置动态生成装饰器时,可以使用工厂模式:
python复制def decorator_factory(config):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if config.get('debug'):
print(f"调试信息: 调用{func.__name__}")
return func(*args, **kwargs)
return wrapper
return decorator
@decorator_factory({'debug': True})
def my_function():
pass
7. 实际项目中的装饰器应用
7.1 Flask路由装饰器
Flask框架使用装饰器来定义路由:
python复制from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return "Hello World!"
7.2 Django权限装饰器
Django提供了几个内置的权限装饰器:
python复制from django.contrib.auth.decorators import login_required, permission_required
@login_required
def my_view(request):
pass
@permission_required('polls.can_vote')
def vote(request):
pass
7.3 自定义API限流装饰器
实现一个简单的API调用频率限制装饰器:
python复制import time
from functools import wraps
def rate_limit(calls_per_minute):
interval = 60 / calls_per_minute
last_called = 0
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
nonlocal last_called
elapsed = time.time() - last_called
if elapsed < interval:
time.sleep(interval - elapsed)
last_called = time.time()
return func(*args, **kwargs)
return wrapper
return decorator
8. 装饰器的测试与调试
8.1 单元测试装饰器
测试装饰器时,需要测试装饰后的函数行为:
python复制import unittest
def double_result(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs) * 2
return wrapper
@double_result
def add(a, b):
return a + b
class TestDecorator(unittest.TestCase):
def test_double_result(self):
self.assertEqual(add(2, 3), 10)
self.assertEqual(add(-1, 1), 0)
8.2 性能测试装饰器
可以创建一个专门用于测量函数执行时间的装饰器:
python复制import time
from functools import wraps
def timing_decorator(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
8.3 调试装饰器
创建一个在调试时打印详细信息的装饰器:
python复制def debug_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"调用函数: {func.__name__}")
print(f"位置参数: {args}")
print(f"关键字参数: {kwargs}")
result = func(*args, **kwargs)
print(f"返回值: {result}")
return result
return wrapper
9. 装饰器设计模式的最佳实践
9.1 保持装饰器简单
每个装饰器应该只负责一个功能,遵循单一职责原则。如果需要多个功能,可以使用多个装饰器叠加。
9.2 正确处理函数参数
装饰器应该能够处理任何类型的函数参数,包括:
- 位置参数
- 关键字参数
- 可变参数(*args, **kwargs)
- 默认参数
9.3 考虑装饰器的性能影响
装饰器会在每次函数调用时增加额外的开销。对于性能敏感的代码,应该:
- 尽量减少装饰器中的逻辑
- 避免在装饰器中进行耗时的操作
- 考虑使用缓存或其他优化技术
9.4 文档和类型提示
为装饰器添加适当的文档字符串和类型提示:
python复制from typing import Callable, TypeVar, Any
import functools
T = TypeVar('T', bound=Callable[..., Any])
def log_calls(func: T) -> T:
"""记录函数调用的装饰器"""
@functools.wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
print(f"调用 {func.__name__}")
return func(*args, **kwargs)
return wrapper
10. 登录注册系统的完整实现
结合前面介绍的装饰器知识,我们来实现一个完整的登录注册系统:
python复制import json
from functools import wraps
class AuthSystem:
def __init__(self):
self.users = {}
self.current_user = None
self.load_users()
def load_users(self):
try:
with open('users.json', 'r') as f:
self.users = json.load(f)
except FileNotFoundError:
self.users = {'admin': {'password': 'admin123', 'role': 'admin'}}
def save_users(self):
with open('users.json', 'w') as f:
json.dump(self.users, f, indent=4)
def register(self, username, password, role='user'):
if username in self.users:
print("用户名已存在!")
return False
self.users[username] = {
'password': password,
'role': role
}
self.save_users()
print("注册成功!")
return True
def login(self, username, password):
if username not in self.users:
print("用户名不存在!")
return False
if self.users[username]['password'] != password:
print("密码错误!")
return False
self.current_user = {
'username': username,
'role': self.users[username]['role']
}
print("登录成功!")
return True
def logout(self):
self.current_user = None
print("已退出登录")
def login_required(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
if not self.current_user:
print("请先登录!")
return None
return func(*args, **kwargs)
return wrapper
def role_required(self, role):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
if not self.current_user:
print("请先登录!")
return None
if self.current_user['role'] != role:
print(f"需要{role}权限!")
return None
return func(*args, **kwargs)
return wrapper
return decorator
# 使用示例
auth = AuthSystem()
@auth.login_required
def user_profile():
print(f"欢迎, {auth.current_user['username']}")
@auth.role_required('admin')
def admin_panel():
print("管理员面板")
这个实现展示了如何将装饰器应用于实际的用户认证系统,包括:
- 基本的用户注册和登录功能
- 登录状态检查装饰器
- 基于角色的权限控制装饰器
- 用户数据的持久化存储
在实际项目中,你可以根据需要扩展这个基础实现,添加更多功能如密码加密、会话管理、多因素认证等。
