目 录CONTENT

文章目录

Python装饰器七大技巧:写出更简洁优雅的代码

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

📢 转载信息

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

原文作者:Jason Brownlee


Python装饰器七大技巧:写出更简洁优雅的代码

Python 装饰器(Decorators)是语言中一个非常强大且令人兴奋的特性,它允许你在不修改函数源代码的情况下,向函数添加新的功能。本文将介绍七个实用的 Python 装饰器技巧,帮助你写出更简洁、更具表现力的代码。

本文的所有示例代码均假定你已经导入了 `functools` 模块,该模块在编写包装函数时非常有用。

from functools |
  functools.wraps

1. 装饰一个函数

这是最简单的装饰器形式。它接受一个函数作为参数,返回一个包装后的函数。

考虑一个需要计算函数执行时间的场景。我们可以创建一个装饰器来完成这个任务。

import time

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

@timer
def long_running_function(x):
    time.sleep(x)

long_running_function(2)
# Output: Function long_running_function took 2.0004 seconds

这种模式非常常见,但它有一个小问题:它会丢失原始函数的元数据(如 `__name__` 和 `__doc__`)。为了解决这个问题,我们使用 `functools.wraps`。

from functools import wraps

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

2. 装饰带有参数的函数

有时我们需要装饰器接受参数,例如,在日志中包含一个自定义消息。

要使装饰器接受参数,我们需要在装饰器之上再添加一个包装层,这个包装层负责接收参数并返回实际的装饰器(即上一个例子中的 `timer` 函数)。

from functools import wraps

def log_execution(message):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print(f'Starting {func.__name__}: {message}')
            result = func(*args, **kwargs)
            print(f'Finished {func.__name__}')
            return result
        return wrapper
    return decorator

@log_execution(message='Processing data')
def process_data(data):
    return [item * 2 for item in data]

print(process_data([1, 2, 3]))
# Output:
# Starting process_data: Processing data
# Finished process_data
# [2, 4, 6]

3. 装饰返回值的函数

我们可以修改函数的返回值。例如,确保所有返回的字符串都转换为大写。

from functools import wraps

def make_uppercase(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        if isinstance(result, str):
            return result.upper()
        return result
    return wrapper

@make_uppercase
def greet(name):
    return f'hello {name}'

print(greet('world'))
# Output: HELLO WORLD

4. 装饰类的方法(Class Methods)

装饰类的方法与装饰普通函数非常相似,但需要注意处理 `self` 参数。

from functools import wraps

def log_method_call(func):
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        print(f'Calling method: {func.__name__} on instance {self}')
        result = func(self, *args, **kwargs)
        return result
    return wrapper

class MyClass:
    def __init__(self, value):
        self.value = value

    @log_method_call
    def update_value(self, new_value):
        self.value = new_value
        return self.value

instance = MyClass(10)
instance.update_value(20)
# Output: Calling method: update_value on instance <__main__.MyClass object at 0x...>

5. 使用类作为装饰器

装饰器也可以用类来实现。这在需要维护状态或实现更复杂的逻辑时非常有用。类装饰器必须实现 `__call__` 方法来包装被装饰的函数,并可选地实现 `__init__` 来接收参数。

from functools import wraps

class TimerClass:
    def __init__(self, func):
        self.func = func
        wraps(func)(self)

    def __call__(self, *args, **kwargs):
        t0 = time.time()
        result = self.func(*args, **kwargs)
        t1 = time.time()
        print(f'Class Decorator: {self.func.__name__} took {t1 - t0:.4f} seconds')
        return result

@TimerClass
def heavy_computation(n):
    sum(range(n))

heavy_computation(1000000)
# Output: Class Decorator: heavy_computation took 0.0225 seconds

6. 装饰器链(Chaining Decorators)

Python 允许你将多个装饰器应用于同一个函数。装饰器的应用顺序是从下到上(或从内到外)。

# 假设我们有前面定义的 @make_uppercase 和 @timer 装饰器

@timer
@make_uppercase
def format_and_run(text):
    print(f"Original: {text}")
    return text.lower()

result = format_and_run("HELLO WORLD")
print(f"Final Result: {result}")

# 执行顺序:
# 1. format_and_run('HELLO WORLD') 被 @make_uppercase 装饰
# 2. 结果被 @timer 装饰

# Output:
# Original: HELLO WORLD
# Class Decorator: format_and_run took 0.0000 seconds
# Final Result: hello world

注意:输出可能与你预期的不同,因为装饰器的执行顺序会影响打印内容。在这种情况下,`@timer` 包裹了 `@make_uppercase` 的结果。

7. 使用类装饰器和参数

结合第 2 节和第 5 节的技巧,我们可以创建一个带参数的类装饰器。这需要一个外部函数来接收参数,并返回一个类实例。

from functools import wraps

class LimitCalls:
    def __init__(self, max_calls):
        self.max_calls = max_calls
        self.call_count = 0

    def __call__(self, func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            if self.call_count < self.max_calls:
                self.call_count += 1
                print(f'Call {self.call_count} of {self.max_calls}')
                return func(*args, **kwargs)
            else:
                print(f'Function {func.__name__} has been called too many times.')
                return None
        return wrapper

@LimitCalls(max_calls=2)
def limited_function(x):
    print(f'Running with {x}')
    return x

limited_function(1)
limited_function(2)
limited_function(3)

# Output:
# Call 1 of 2
# Running with 1
# Call 2 of 2
# Running with 2
# Function limited_function has been called too many times.

掌握这些装饰器技巧可以显著提高 Python 代码的模块化、可读性和复用性。




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

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

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

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

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

0

评论区