Python装饰器案例如何基础使用

wen python案例 27

Python装饰器案例如何基础使用:从零到精通的实战指南

目录导读

  1. 什么是装饰器?——概念与核心原理
  2. 装饰器的基本写法 —— 函数作为参数与返回值
  3. 简单装饰器案例 —— 日志、计时、权限检查
  4. 带参数的装饰器 —— 灵活配置功能
  5. 类装饰器 vs 函数装饰器 —— 选择依据与案例
  6. 常见陷阱与最佳实践 —— 使用 functools.wraps
  7. 问答环节 —— 高频问题与解答
  8. —— 装饰器的精髓与进阶方向

什么是装饰器?——概念与核心原理

核心思想:装饰器本质上是一个 可调用对象(通常是函数或类),它接收一个函数作为输入,并 返回一个新的函数(或修改后的函数),从而在不修改原函数代码的前提下,为其增加额外功能。

Python装饰器案例如何基础使用

为什么需要装饰器?

  • 避免重复代码(如日志、鉴权、性能分析)。
  • 保持原函数的纯净性(分离核心逻辑与横切关注点)。

小提示:在Python中,装饰器使用 语法糖实现,本质上是 new_func = decorator(old_func) 的简写。


装饰器的基本写法 —— 函数作为参数与返回值

# 最简单的装饰器:打印函数名称
def simple_decorator(func):
    def wrapper():
        print(f"调用函数:{func.__name__}")
        return func()
    return wrapper
@simple_decorator
def greet():
    print("Hello!")
greet()
# 输出:
# 调用函数:greet
# Hello!

执行流程

  1. greet 被定义时,立即传递给 simple_decorator
  2. simple_decorator 返回 wrapper 函数,替换原始 greet
  3. 调用 greet() 实际调用 wrapper()

简单装饰器案例 —— 日志、计时、权限检查

案例1:日志装饰器(用于调试)

import logging
logging.basicConfig(level=logging.INFO)
def log_call(func):
    def wrapper(*args, **kwargs):
        logging.info(f"调用 {func.__name__},参数:{args}, {kwargs}")
        result = func(*args, **kwargs)
        logging.info(f"{func.__name__} 返回:{result}")
        return result
    return wrapper
@log_call
def add(a, b):
    return a + b
add(3, 5)  # 日志自动输出调用过程

案例2:计时装饰器(性能分析)

import time
def timer(func):
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()
        print(f"{func.__name__} 耗时:{end-start:.4f}秒")
        return result
    return wrapper
@timer
def slow_func():
    time.sleep(0.5)
    return "完成"
slow_func()

案例3:权限检查装饰器

def require_auth(func):
    def wrapper(user, *args, **kwargs):
        if not user.get("is_authenticated"):
            raise PermissionError("用户未认证")
        return func(user, *args, **kwargs)
    return wrapper
@require_auth
def view_dashboard(user):
    return f"欢迎 {user['name']} 访问仪表盘"
# 调用时自动检查权限

带参数的装饰器 —— 灵活配置功能

当装饰器本身需要参数时(如打印不同级别的日志),需要 三层嵌套结构

def repeat(n=2):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(n):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator
@repeat(n=3)  # 函数被重复调用3次
def say_hello():
    print("Hello!")
say_hello()

原理@repeat(n=3) 等价于 say_hello = repeat(n=3)(say_hello),即先传入 n 得到装饰器,再用装饰器包装函数。


类装饰器 vs 函数装饰器 —— 选择依据与案例

函数装饰器(推荐用于简单场景)

  • 优点:语法直观,性能略高。
  • 缺点:状态管理需依赖闭包变量。

类装饰器(适合需要状态管理的复杂场景)

使用 __call__ 方法使类实例可调用:

class CountCalls:
    def __init__(self, func):
        self.func = func
        self.count = 0
    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"调用次数:{self.count}")
        return self.func(*args, **kwargs)
@CountCalls
def hello():
    print("Hi!")
hello()
hello()
# 输出调用计数

选择建议

  • 简单无状态 → 函数装饰器。
  • 需要保存状态(如缓存、调用计数) → 类装饰器。

常见陷阱与最佳实践 —— 使用 functools.wraps

陷阱:默认装饰器会丢失原函数的元信息(__name__, __doc__ 等)。

@timer
def add(a, b):
    """两个数相加"""
    return a + b
print(add.__name__)  # 输出 "wrapper" 而非 "add"

解决方案:使用 functools.wraps 复制原函数属性。

from functools import wraps
def timer(func):
    @wraps(func)  # 将 wrapper 的元信息还原为 func 的
    def wrapper(*args, **kwargs):
        # 装饰逻辑
        pass
    return wrapper

其他最佳实践

  • 始终让 wrapper 函数支持 *args, **kwargs 以保证通用性。
  • 考虑为装饰器编写单元测试。

问答环节 —— 高频问题与解答

Q1:装饰器可以同时多个吗?执行顺序是怎样的? A:可以,多个装饰器从下到上叠加,执行顺序相反:

@decorator_A
@decorator_B
def func(): ...
# 等价于 func = decorator_A(decorator_B(func))
# 调用时先执行 decorator_A 的外层,再进入 decorator_B

Q2:装饰器可以用于类方法吗?(@classmethod 和普通装饰器结合) A:可以,注意装饰器的 wrapper 中第一个参数需按方法类型处理(实例方法用 selfcls)。

Q3:装饰器能带可选参数吗?(如无参数时默认执行) A:可以,通过判断第一个参数是否为可调用对象:

def repeat(func=None, n=2):
    def decorator(f):
        # ... 包装逻辑
        return wrapper
    if func is None:
        return decorator
    else:
        return decorator(func)
# 使用:@repeat 或 @repeat(n=3)

Q4:装饰器性能影响大吗? A:仅增加一次函数调用开销(微秒级),可忽略不计,但装饰器内部若涉及IO或计算,则需注意。


—— 装饰器的精髓与进阶方向

核心要点

  • 装饰器是 高阶函数 的优雅应用,核心在于“包装”而不是“修改”。
  • 基础案例:日志、计时、权限、缓存。
  • 实践时永远加上 @wraps,保持函数属性完整。

进阶方向

  • 使用 functools.lru_cache 实现内存缓存。
  • 类装饰器实现单例模式。
  • 装饰器结合类型注解做参数验证。

一句话记住:装饰器 = 一句话让函数拥有超能力,且不破坏原函数结构,实际开发中,推荐先从标准库 functoolstimeit 等内置装饰器学起,逐步自己编写。

文章原创性说明:本文结合了Python官方文档、Real Python、Stack Overflow等来源的核心案例,经过重新组织与详细解释,确保内容精炼且符合SEO结构,如需转载,请保留出处链接。

抱歉,评论功能暂时关闭!