目 录CONTENT

文章目录

掌握7个Python装饰器技巧,写出更简洁优雅的代码

青云TOP
2025-10-09 / 0 评论 / 0 点赞 / 0 阅读 / 0 字

📢 转载信息

原文链接:https://machinelearningmastery.com/7-python-decorator-tricks-to-write-cleaner-code/

原文作者:Jason Brownlee


Python 装饰器是一种非常强大的工具,它可以让你在不修改函数源代码的情况下,为其添加额外的功能。掌握这些技巧,可以帮助你编写出更简洁、更具可读性的 Python 代码。

本文将介绍 7 个实用的 Python 装饰器技巧,助你提升代码质量。

1. 装饰器基础回顾

在深入技巧之前,我们先快速回顾一下装饰器的基本概念。装饰器本质上是一个函数,它接收另一个函数作为参数,返回一个新的函数,这个新函数会包装原函数的功能。

下面是一个最简单的装饰器示例:

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("在函数执行前执行...")
        result = func(*args, **kwargs)
        print("在函数执行后执行...")
        return result
    return wrapper

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

# 调用时,say_hello() 实际上执行的是 wrapper 函数
say_hello()

输出结果:

在函数执行前执行...
Hello!
在函数执行后执行...

2. 装饰器进阶:传递参数的装饰器

有时你需要装饰器本身也能接收参数。这需要我们在装饰器外再包裹一层函数,用于接收装饰器参数,并返回一个标准的装饰器函数。

def repeat(num_times):
    def decorator_repeat(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator_repeat

@repeat(num_times=3)
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")

效果:函数 `greet` 会被执行 3 次。

3. 使用 `functools.wraps` 保持函数元数据

当使用装饰器时,被装饰的函数会丢失其原始的名称 (`__name__`)、文档字符串 (`__doc__`) 等元数据。这是因为装饰器返回的是内部的 `wrapper` 函数。

使用 `functools.wraps` 可以解决这个问题。它是一个内置的装饰器,用于将原始函数的元数据复制到包装函数上。

import functools

def log_calls(func):
    @functools.wraps(func)  # 关键:添加此行
    def wrapper(*args, **kwargs):
        print(f"Calling function: {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log_calls
def calculate_sum(a, b):
    """计算两个数的和。"""
    return a + b

print(calculate_sum.__name__)  # 输出: calculate_sum (没有 @wraps 会输出 wrapper)
print(calculate_sum.__doc__)   # 输出: 计算两个数的和。

4. 类作为装饰器:实现状态管理

虽然函数是装饰器的常见形式,但使用类也可以实现装饰器。当你的装饰器需要维护内部状态(比如计数器、缓存等)时,使用类会更加清晰和方便。类装饰器需要实现 `__init__` 和 `__call__` 方法。

  • `__init__(self, func)`:接收被装饰的函数作为参数。
  • `__call__(self, *args, **kwargs)`:在函数被调用时执行,接收函数的实际参数。
class CounterDecorator:
    def __init__(self, func):
        self.func = func
        self.count = 0

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"{self.func.__name__} has been called {self.count} times.")
        return self.func(*args, **kwargs)

@CounterDecorator
def process_data(data):
    print(f"Processing: {data}")

process_data("Set A")
process_data("Set B")

5. 装饰器栈:同时应用多个装饰器

Python 允许你在一个函数上堆叠多个装饰器。装饰器的应用顺序是从下往上(**先应用最下面的装饰器,再应用上面的装饰器**)。

@decorator_b
@decorator_a
def my_function():
    pass

这等同于执行:`my_function = decorator_b(decorator_a(my_function))`。

6. 装饰器链中的参数传递与返回

在装饰器链中,确保每个包装器都能正确地接收和传递参数,并返回最终结果至关重要。如果一个包装器不返回 `func(*args, **kwargs)` 的结果,那么后续的装饰器将接收到错误的值(通常是 `None`)。

最佳实践: 始终使用 `*args` 和 `**kwargs` 来接收所有位置和关键字参数,并确保返回函数调用的结果。

7. 编写可重用的、通用的装饰器

为了最大化装饰器的复用性,它们应该尽量做到“纯净”(Pure),即减少对全局状态的依赖。如果装饰器需要配置,最好通过装饰器参数(如技巧 2 所示)来提供配置,而不是硬编码在装饰器内部。

一个通用且干净的装饰器应该只做一件事,并且做好。例如,一个专门用于计时执行时间的装饰器:

import time

def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        t1 = time.time()
        result = func(*args, **kwargs)
        t2 = time.time()
        print(f'{func.__name__} took {t2 - t1:.4f} seconds.')
        return result
    return wrapper

@timer
def slow_operation(n):
    time.sleep(n)

slow_operation(0.5)

总结

Python 装饰器是编写简洁、模块化代码的利器。通过掌握参数传递、`functools.wraps` 的使用,以及了解函数装饰器和类装饰器的应用场景,你可以更有效地增强代码的功能,同时保持代码的优雅和可维护性。





🚀 想要体验更好更全面的AI调用?

欢迎使用青云聚合API,约为官网价格的十分之一,支持300+全球最新模型,以及全球各种生图生视频模型,无需翻墙高速稳定,小白也可以简单操作。

青云聚合API官网https://api.qingyuntop.top

支持全球最新300+模型:https://api.qingyuntop.top/pricing

详细的调用教程及文档:https://api.qingyuntop.top/about

0

评论区