Python 装饰器:优雅增强函数功能的魔法工具

Python 装饰器:优雅增强函数功能的魔法工具
 
一、为什么需要装饰器?
 
在软件开发中,我们经常需要为函数添加额外功能,比如日志记录、性能统计、权限验证等。如果直接修改函数代码,会违反「开闭原则」(对扩展开放,对修改关闭),导致代码冗余且难以维护。Python 装饰器(Decorator)正是为解决这类问题而生的语法糖,它能在不改变原函数代码的前提下,动态增强函数功能。
 
二、装饰器基础:高阶函数与闭包
 
1. 高阶函数(Higher-Order Function)
 
满足以下至少一个条件的函数称为高阶函数:
 
- 接收一个或多个函数作为参数
- 返回一个函数作为结果
 
示例:接收函数作为参数
 
def greet(func):
    func()

def say_hello():
    print("Hello!")

greet(say_hello)  # 输出:Hello!
 
 
2. 闭包(Closure)
 
如果内部函数引用了外部函数的变量,并且外部函数返回了内部函数,那么这个内部函数就形成了闭包。闭包允许函数在外部作用域之外访问内部变量。
 
示例:闭包实现计数器
 
def counter():
    count = 0
    def increment():
        nonlocal count  # 声明使用外部函数的变量
        count += 1
        print(count)
    return increment

counter_func = counter()
counter_func()  # 输出:1
counter_func()  # 输出:2
 
 
三、装饰器的本质:用闭包包装函数
 
装饰器本质上是一个高阶函数,它接收一个函数作为输入,返回一个增强后的新函数作为输出,新函数通常会在执行原函数前后添加额外逻辑。
 
1. 无参数函数的装饰器
 
需求:为函数添加日志记录,打印函数执行前后的信息
 
def log_decorator(func):
    def wrapper():
        print(f"开始执行 {func.__name__}")
        func()  # 调用原函数
        print(f"{func.__name__} 执行完毕")
    return wrapper

@log_decorator  # 等价于 say_hello = log_decorator(say_hello)
def say_hello():
    print("Hello, Decorator!")

say_hello()
 
 
输出:
 
开始执行 say_hello
Hello, Decorator!
say_hello 执行完毕
 
 
2. 处理带参数的函数
 
如果被装饰的函数有参数,需要让  wrapper  函数接收任意参数:
 
def log_decorator(func):
    def wrapper(*args, **kwargs):  # 适配任意位置参数和关键字参数
        print(f"开始执行 {func.__name__},参数:{args}, {kwargs}")
        result = func(*args, **kwargs)  # 传递参数给原函数
        print(f"{func.__name__} 执行完毕,返回值:{result}")
        return result  # 返回原函数的返回值
    return wrapper

@log_decorator
def add(a, b):
    return a + b

add(2, 3)
 
 
输出:
 
开始执行 add,参数:(2, 3), {}
add 执行完毕,返回值:5
 
 
四、进阶用法:带参数的装饰器与类装饰器
 
1. 带参数的装饰器(三层函数结构)
 
如果装饰器本身需要接收参数(如日志级别、路径等),需要再嵌套一层函数:
 
def log_decorator_with_param(level):  # 装饰器参数
    def decorator(func):  # 接收被装饰的函数
        def wrapper(*args, **kwargs):
            print(f"[{level}] 开始执行 {func.__name__}")
            func(*args, **kwargs)
            print(f"[{level}] {func.__name__} 执行完毕")
        return wrapper
    return decorator  # 返回真正的装饰器

@log_decorator_with_param(level="DEBUG")  # 等价于 say_hello = log_decorator_with_param("DEBUG")(say_hello)
def say_hello():
    print("Hello!")

say_hello()
 
 
输出:
 
[DEBUG] 开始执行 say_hello
Hello!
[DEBUG] say_hello 执行完毕
 
 
2. 类装饰器(使用  __call__  方法)
 
除了函数装饰器,也可以用类实现装饰器,通过  __call__  方法让实例可调用:
 
class ClassDecorator:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print(f"类装饰器开始执行 {self.func.__name__}")
        self.func(*args, **kwargs)
        print(f"类装饰器执行完毕")

@ClassDecorator
def say_bye():
    print("Goodbye!")

say_bye()
 
 
输出:
 
类装饰器开始执行 say_bye
Goodbye!
类装饰器执行完毕
 
 
五、关键工具: functools.wraps  保留函数元信息
 
装饰器会导致原函数的元信息(如名称、文档字符串)被覆盖,可通过  functools.wraps  修复:
 
from functools import wraps

def log_decorator(func):
    @wraps(func)  # 保留原函数的元信息
    def wrapper(*args, **kwargs):
        """包装函数的文档字符串"""
        print(f"执行 {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log_decorator
def say_hello():
    """这是一个打招呼的函数"""
    print("Hello!")

print(say_hello.__name__)  # 输出:say_hello(而非 wrapper)
print(say_hello.__doc__)   # 输出:这是一个打招呼的函数(而非包装函数的文档)
 
 
六、常见应用场景
 
1. 日志记录
 
记录函数的调用时间、参数、返回值,方便调试和监控。
 
2. 性能分析
 
计算函数执行耗时,定位性能瓶颈:
 
import time

def timer(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        print(f"{func.__name__} 耗时 {time.time() - start:.4f} 秒")
        return result
    return wrapper
 
 
3. 权限验证
 
在函数执行前检查用户权限,避免非法调用:
 
def require_auth(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        if not current_user.is_authenticated:
            raise PermissionError("需要登录才能访问")
        return func(*args, **kwargs)
    return wrapper
 
 
4. 缓存结果
 
使用  lru_cache  装饰器缓存函数结果,避免重复计算(Python 内置装饰器):
 
from functools import lru_cache

@lru_cache(maxsize=128)
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)
 
 
七、最佳实践与注意事项
 
1. 保持装饰器的纯度:避免在装饰器中修改被装饰函数的内部状态,专注于功能增强。
2. 合理使用嵌套:带参数的装饰器可能导致多层嵌套,保持代码简洁,必要时添加注释。
3. 注意执行时机:装饰器在函数定义时立即执行(加载时),而非调用时。
4. 优先使用内置装饰器:如  @property 、 @classmethod 、 @staticmethod  等,遵循 Python 惯例。
 
八、总结
 
装饰器是 Python 中极具灵活性的特性,它通过高阶函数和闭包实现了代码的横向扩展,让我们可以优雅地复用逻辑、分离关注点。掌握装饰器的核心原理(高阶函数、闭包、元编程)后,你可以在日志、性能、权限等场景中高效提升代码质量。记住用  functools.wraps  保留函数元信息,让代码更健壮易读。多写多练,你会逐渐体会到装饰器带来的编程乐趣!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值