当前位置: 首页 > news >正文

Python第十二节 装饰器使用详解及注意事项

前言

装饰器Python中一种强大的语法特性,它允许在不修改原函数代码的情况下,为函数添加新的功能。装饰器本质上是一个高阶函数,它接受一个函数作为参数,并返回一个新的函数。

应用场景

日志记录: 装饰器可用于记录函数的调用信息、参数和返回值。
性能分析: 可以使用装饰器来测量函数的执行时间。
权限控制: 装饰器可用于限制对某些函数的访问权限。
缓存: 装饰器可用于实现函数结果的缓存,以提高性能。

内置装饰器

@staticmethod: 将方法定义为静态方法,不需要实例化类即可调用。

@classmethod: 将方法定义为类方法,第一个参数是类本身(通常命名为 cls)。

@property: 将方法转换为属性,使其可以像属性一样访问。

1. 基本语法

    def decorator(func):def wrapper(*args, **kwargs):# 在调用原函数前的操作result = func(*args, **kwargs)# 在调用原函数后的操作return resultreturn wrapper@decoratordef my_function():pass

2. 装饰器案例分析

2.1 计时装饰器

    import timeimport functoolsdef timer(func):"""计算函数执行时间的装饰器"""@functools.wraps(func)def wrapper(*args, **kwargs):start_time = time.time()result = func(*args, **kwargs)end_time = time.time()print(f"函数 {func.__name__} 执行耗时: {end_time - start_time:.4f}秒")return resultreturn wrapper@timerdef fibonacci(n):"""计算斐波那契数列"""if n <= 1:return nreturn fibonacci(n-1) + fibonacci(n-2)# 使用print(fibonacci(10)) # 55

2.2 日志记录装饰器

    import functoolsfrom datetime import datetimedef logger(func):"""记录函数调用信息的装饰器"""@functools.wraps(func)def wrapper(*args, **kwargs):call_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")print(f"[{call_time}] 调用函数: {func.__name__}")print(f"    参数: args={args}, kwargs={kwargs}")try:result = func(*args, **kwargs)print(f"    返回值: {result}")return resultexcept Exception as e:print(f"    异常: {e}")raisereturn wrapper@loggerdef divide(a, b):return a / b# 使用divide(10, 2)divide(10, 0)  # 会记录异常

2.3 参数化装饰器

    import functoolsdef repeat(num_times):"""重复执行函数的装饰器"""def decorator_repeat(func):@functools.wraps(func)def wrapper(*args, **kwargs):for _ in range(num_times):result = func(*args, **kwargs)return resultreturn wrapperreturn decorator_repeat@repeat(num_times=3)def greet(name):print(f"Hello, {name}!")# 使用greet("Andy") # 打印 3次 # Hello Andy# Hello Andy# Hello Andy

2.4 类装饰器

类装饰器可以用于:

添加/修改类的方法或属性
拦截实例化过程
实现单例模式、日志记录、权限检查等功能

类装饰器有两种常见形式:

函数形式的类装饰器(接收类作为参数,返回新类)
类形式的类装饰器(实现 __call__ 方法,使其可调用)

    class CountCalls:"""统计函数调用次数的类装饰器"""def __init__(self, func):functools.update_wrapper(self, func)self.func = funcself.num_calls = 0def __call__(self, *args, **kwargs):self.num_calls += 1print(f"调用 {self.func.__name__}{self.num_calls} 次")return self.func(*args, **kwargs)@CountCallsdef say_hello():print("Hello!")# 使用say_hello()say_hello()print(f"总调用次数: {say_hello.num_calls}")

2.5 权限验证装饰器

    import functoolsdef require_role(required_role):"""基于角色的权限验证装饰器"""def decorator(func):@functools.wraps(func)def wrapper(user, *args, **kwargs):if user.get('role') != required_role:raise PermissionError(f"需要 {required_role} 权限,当前用户权限: {user.get('role')}")return func(user, *args, **kwargs)return wrapperreturn decorator# 用户数据users = [{'name': 'Alice', 'role': 'admin'},{'name': 'Bob', 'role': 'user'}]@require_role('admin')def delete_user(current_user, username):print(f"{current_user['name']} 删除了用户 {username}")# 使用try:delete_user(users[0], "Charlie")  # 成功delete_user(users[1], "Charlie")  # 失败except PermissionError as e:print(f"权限错误: {e}")

2.6 缓存装饰器

    import functoolsfrom typing import Anydef cache(func):"""简单的缓存装饰器"""cached_results = {}@functools.wraps(func)def wrapper(*args, **kwargs):# 创建缓存键key = str(args) + str(sorted(kwargs.items()))if key in cached_results:print(f"从缓存中获取 {func.__name__}{args} 的结果")return cached_results[key]result = func(*args, **kwargs)cached_results[key] = resultprint(f"计算 {func.__name__}{args} 的结果并缓存")return resultreturn wrapper@cachedef expensive_operation(x, y):time.sleep(1)  # 模拟耗时操作return x * y# 使用print(expensive_operation(3, 4))  # 第一次计算print(expensive_operation(3, 4))  # 从缓存获取

3. 多个装饰器的使用

    def decorator1(func):@functools.wraps(func)def wrapper(*args, **kwargs):print("装饰器1 - 前")result = func(*args, **kwargs)print("装饰器1 - 后")return resultreturn wrapperdef decorator2(func):@functools.wraps(func)def wrapper(*args, **kwargs):print("装饰器2 - 前")result = func(*args, **kwargs)print("装饰器2 - 后")return resultreturn wrapper@decorator1@decorator2def example():print("原始函数")# 使用example()# 输出:# 装饰器1 - 前# 装饰器2 - 前# 原始函数# 装饰器2 - 后# 装饰器1 - 后

4. 使用注意事项

4.1 保留元信息

错误做法:

def bad_decorator(func):def wrapper(*args, **kwargs):return func(*args, **kwargs)return wrapper@bad_decorator
def my_function():"""这是一个测试函数"""passprint(my_function.__name__)  # 输出: wrapper
print(my_function.__doc__)   # 输出: None

正确做法:

    import functoolsdef good_decorator(func):@functools.wraps(func)def wrapper(*args, **kwargs):return func(*args, **kwargs)return wrapper@good_decoratordef my_function():"""这是一个测试函数"""passprint(my_function.__name__)  # 输出: my_functionprint(my_function.__doc__)   # 输出: 这是一个测试函数

4.2 处理带参数的装饰器

import functoolsdef smart_decorator(condition=True):def decorator(func):@functools.wraps(func)def wrapper(*args, **kwargs):if condition:print("装饰器功能启用")else:print("装饰器功能禁用")return func(*args, **kwargs)return wrapperreturn decorator# 使用
@smart_decorator(condition=True)
def function1():print("函数1执行")@smart_decorator(condition=False)
def function2():print("函数2执行")

4.3 避免装饰器副作用

def safe_decorator(func):"""安全的装饰器,处理异常"""@functools.wraps(func)def wrapper(*args, **kwargs):try:return func(*args, **kwargs)except Exception as e:print(f"函数 {func.__name__} 执行出错: {e}")# 可以选择返回默认值或重新抛出异常return Nonereturn wrapper@safe_decorator
def risky_operation():return 1 / 0  # 这会引发异常result = risky_operation()  # 不会崩溃,会打印错误信息

5. 实际应用场景

5.1 Web框架中的路由装饰器

    class Router:def __init__(self):self.routes = {}def route(self, path):def decorator(func):self.routes[path] = funcreturn funcreturn decoratorrouter = Router()@router.route("/home")def home():return "Home Page"@router.route("/about")def about():return "About Page"# 使用print(router.routes)  # 输出: {'/home': <function home>, '/about': <function about>}

5.2 数据库事务装饰器

def transaction(db_connection):def decorator(func):@functools.wraps(func)def wrapper(*args, **kwargs):try:result = func(*args, **kwargs)db_connection.commit()print("事务提交成功")return resultexcept Exception as e:db_connection.rollback()print(f"事务回滚: {e}")raisereturn wrapperreturn decorator

总结

装饰器是用途:

增强函数功能:添加日志、计时、缓存等功能

代码复用:将通用功能抽象为装饰器

保持代码整洁:避免修改原函数代码

实现AOP:面向切面编程

最佳实践:

  1. 始终使用 @functools.wraps 保留函数元信息
  2. 为装饰器编写清晰的文档字符串
  3. 考虑装饰器的性能和副作用
  4. 在团队中建立装饰器使用规范

通过合理使用装饰器,可以大幅提高代码的可维护性和可读性。

http://www.dtcms.com/a/471729.html

相关文章:

  • 传媒类网站模板做网站怎么赚流量
  • 网站建设 技术方案模板wordpress 外国主机
  • 惠州市博罗县建设局网站双线网站选服务器
  • spring ai用法
  • linux系统服务器怎么做网站外贸网站建设注意事项
  • c做网站教程哈尔滨学网页设计
  • 什么网站是专门做艺术字的网站一定要备案
  • 二手房网站排行屯济宁做网站公司
  • 内存频率重要吗?对游戏影响大不大?玖合异刃DDR5 8000Mhz评测
  • mem 设备控制 GPIO - C程序通过sysfs文件系统使用GPIO中断
  • 简约风格装修seo排名如何
  • 有关使用AVX,EIGEN等加速方法过程中cmake选项的说明
  • 二手书交易网站开发背景WordPress发邮件4.4.1
  • 【项目开发Trip第2站】casbin库与身份权限划分
  • POET 宣布投资7500万美元
  • wordpress底部插件郑州seo顾问热狗网
  • 韩国网站免费模板美丽定制 网站模板
  • 栾城网站制作产品推广策划案
  • 如何做网站联盟营销网站设计中的js
  • Wazuh vs. 安全洋葱:开源SOC核心平台用哪个呢?
  • 容桂网站制作价位晋江论坛手机版
  • 有做网站看病的吗用vs2010做网站应用程序脱机
  • 如何评价一个网站的网站建设林和西网站建设
  • 云服务器的应用场景
  • 网站开发需要什么工程师软装工作室
  • 怎么建立一个网站好文创产品设计方案模板
  • 提高网站排名怎么做小程序定制开发公司前十名
  • ins做甜品网站手车做网课网站
  • 【AI图片生成】图片生成,这里特别注意根据实际需要换成对应适合自己需求的图片大小和尺寸
  • 框架--Lombok