一、为什么需要装饰器?

想象一个场景:你有多个函数,需要在每个函数执行前后打印日志、计算执行时间或者进行权限验证。如果在每个函数里都重复写这些逻辑,代码会变得冗余且难以维护。装饰器正是为了解决这类问题而生 —— 它将这些通用逻辑抽取出来,通过 “装饰” 的方式动态附加到函数上。

二、装饰器的基础:函数是 “一等公民”

在 Python 中,函数是一等公民(First-Class Citizen),这意味着:

  • 函数可以作为参数传递给另一个函数
  • 函数可以作为返回值从另一个函数返回
  • 函数可以赋值给变量

这是装饰器实现的核心基础。我们先从一个简单的例子入手:

python

运行

def greet():
    print("Hello, World!")

# 将函数赋值给变量
greet_func = greet
greet_func()  # 输出: Hello, World!

三、闭包:装饰器的 “前身”

闭包(Closure)是指一个函数内部定义了另一个函数,内部函数引用了外部函数的变量,并且外部函数返回了内部函数。闭包是装饰器的关键组成部分:

python

运行

def outer_function(msg):
    # 外部函数变量
    message = msg

    def inner_function():
        # 内部函数引用外部变量
        print(message)
    
    # 返回内部函数(不执行)
    return inner_function

# 调用外部函数,得到内部函数的引用
my_func = outer_function("Hello, Closure!")
my_func()  # 输出: Hello, Closure!

四、手写第一个装饰器

基于闭包,我们可以实现一个简单的装饰器,用于计算函数的执行时间:

python

运行

import time

def timer_decorator(func):
    def wrapper():
        start_time = time.time()
        func()  # 执行原函数
        end_time = time.time()
        print(f"函数执行耗时: {end_time - start_time:.4f}秒")
    return wrapper

# 使用装饰器
@timer_decorator
def slow_function():
    time.sleep(1)
    print("函数执行完毕")

slow_function()

这里的@timer_decorator是 Python 的语法糖,等价于slow_function = timer_decorator(slow_function)

五、进阶:装饰带参数的函数

如果原函数带有参数,我们的装饰器需要能接收并传递这些参数。可以通过*args**kwargs来实现:

python

运行

def timer_decorator(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 result  # 返回原函数结果
    return wrapper

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

print(add(2, 3))  # 输出: 5 和 函数执行耗时

六、更高级:带参数的装饰器

有时候我们需要给装饰器本身传递参数,这时候需要再嵌套一层函数:

python

运行

def log_decorator(level="INFO"):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"[{level}] 函数 {func.__name__} 开始执行")
            result = func(*args, **kwargs)
            print(f"[{level}] 函数 {func.__name__} 执行完毕")
            return result
        return wrapper
    return decorator

# 使用带参数的装饰器
@log_decorator(level="DEBUG")
def multiply(a, b):
    return a * b

multiply(2, 3)

七、类装饰器:另一种实现方式

除了函数装饰器,Python 还支持类装饰器。类装饰器通过__call__方法实现:

python

运行

class TimerDecorator:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        start_time = time.time()
        result = self.func(*args, **kwargs)
        end_time = time.time()
        print(f"类装饰器: 函数执行耗时 {end_time - start_time:.4f}秒")
        return result

@TimerDecorator
def subtract(a, b):
    return a - b

print(subtract(5, 3))

八、最佳实践:使用 functools.wraps

装饰器会 “掩盖” 原函数的元信息(如函数名、文档字符串)。使用functools.wraps可以保留原函数的元信息:

python

运行

from functools import wraps

def timer_decorator(func):
    @wraps(func)  # 保留原函数元信息
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"执行耗时: {end_time - start_time:.4f}秒")
        return result
    return wrapper

@timer_decorator
def example_function():
    """这是一个示例函数的文档字符串"""
    pass

print(example_function.__name__)  # 输出: example_function(而不是wrapper)
print(example_function.__doc__)   # 输出: 这是一个示例函数的文档字符串

九、总结

装饰器是 Python 中强大且优雅的特性,通过它我们可以实现代码的复用和解耦。从基础的函数装饰器到进阶的带参数装饰器、类装饰器,掌握这些技巧能让你的代码更加 Pythonic。希望本文能帮助你解锁装饰器的 “魔法”,写出更优雅的代码!

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐