如何在 Python 中使用装饰器修改函数行为

PythonPythonBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

Python 装饰器是一个强大的工具,它允许你在不改变函数核心功能的情况下修改函数的行为。在本教程中,我们将深入探讨装饰器的世界,并探索如何使用它们来提高 Python 代码的性能、日志记录和整体功能。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/AdvancedTopicsGroup(["Advanced Topics"]) python/FunctionsGroup -.-> python/function_definition("Function Definition") python/FunctionsGroup -.-> python/arguments_return("Arguments and Return Values") python/FunctionsGroup -.-> python/lambda_functions("Lambda Functions") python/AdvancedTopicsGroup -.-> python/decorators("Decorators") python/AdvancedTopicsGroup -.-> python/context_managers("Context Managers") subgraph Lab Skills python/function_definition -.-> lab-415777{{"如何在 Python 中使用装饰器修改函数行为"}} python/arguments_return -.-> lab-415777{{"如何在 Python 中使用装饰器修改函数行为"}} python/lambda_functions -.-> lab-415777{{"如何在 Python 中使用装饰器修改函数行为"}} python/decorators -.-> lab-415777{{"如何在 Python 中使用装饰器修改函数行为"}} python/context_managers -.-> lab-415777{{"如何在 Python 中使用装饰器修改函数行为"}} end

理解 Python 装饰器

Python 装饰器是一项强大的特性,它使你能够在不改变函数源代码的情况下修改其行为。它们是一种用另一个函数来包装一个函数的方式,为原始函数添加额外的功能。

什么是装饰器?

装饰器是一种修改函数或类行为的方式。它们使用 @ 符号定义,后面跟着装饰器函数,并放置在函数或类定义之前。

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

def uppercase(func):
    def wrapper():
        result = func()
        return result.upper()
    return wrapper

@uppercase
def say_hello():
    return "hello"

print(say_hello())  ## 输出:HELLO

在这个示例中,uppercase 装饰器函数接受一个函数 func 作为参数,并返回一个新的函数 wrapper,该函数调用 func 然后将结果转换为大写。

装饰器如何工作

装饰器通过在运行时修改函数的行为来工作。当你将一个装饰器应用于一个函数时,原始函数会被替换为装饰器函数的结果。这意味着当你调用被装饰的函数时,实际上调用的是装饰器返回的包装函数。

应用装饰器的过程可以分解为以下步骤:

  1. 定义装饰器函数,它接受一个函数作为参数并返回一个新函数。
  2. 使用 @ 符号将装饰器应用于一个函数。
  3. 当调用被装饰的函数时,执行装饰器返回的包装函数而不是原始函数。

使用装饰器的好处

装饰器有几个好处:

  1. 代码复用:装饰器允许你在多个函数中复用相同的功能,使你的代码更符合 DRY(不要重复自己)原则。
  2. 关注点分离:装饰器帮助你将函数的核心功能与你想要添加的额外功能分离,使你的代码更模块化且易于维护。
  3. 灵活性:可以轻松地为函数添加或移除装饰器,从而轻松启用或禁用某些行为。
  4. 可读性:装饰器使你的代码更具可读性和自我文档化,因为装饰器名称清楚地表明了添加到函数的额外功能。

装饰器的常见用例

装饰器可用于各种场景,包括:

  • 日志记录:为函数添加日志记录功能。
  • 缓存:缓存函数的结果以提高性能。
  • 认证:检查用户是否有权限访问函数。
  • 计时:测量函数的执行时间。
  • 错误处理:为函数提供自定义错误处理。

在下一节中,我们将探讨如何在 Python 中应用装饰器来修改函数行为。

应用装饰器来修改函数行为

既然我们已经对装饰器是什么以及它们如何工作有了基本的了解,那么让我们来探索如何使用它们在 Python 中修改函数行为。

向装饰器传递参数

装饰器也可以接受参数,这使你能够自定义它们的行为。下面是一个装饰器的示例,它接受一个参数来控制输出的大小写:

def case_converter(case):
    def decorator(func):
        def wrapper():
            result = func()
            if case == "upper":
                return result.upper()
            elif case == "lower":
                return result.lower()
            else:
                return result
        return wrapper
    return decorator

@case_converter("upper")
def say_hello():
    return "hello"

print(say_hello())  ## 输出:HELLO

@case_converter("lower")
def say_goodbye():
    return "GOODBYE"

print(say_goodbye())  ## 输出:goodbye

在这个示例中,case_converter 装饰器接受一个参数 case,它决定输出是应该转换为大写还是小写。

装饰带参数的函数

装饰器也可用于修改带参数的函数的行为。下面是一个示例:

def log_function_call(func):
    def wrapper(*args, **kwargs):
        print(f"调用 {func.__name__},参数为 args={args} 和 kwargs={kwargs}")
        return func(*args, **kwargs)
    return wrapper

@log_function_call
def add_numbers(a, b):
    return a + b

print(add_numbers(2, 3))  ## 输出:
## 调用 add_numbers,参数为 args=(2, 3) 和 kwargs={}
## 5

在这个示例中,log_function_call 装饰器包装了 add_numbers 函数,并在执行原始函数之前记录函数调用。

堆叠装饰器

装饰器也可以堆叠,这使你能够将多个装饰器应用于单个函数。下面是一个示例:

def uppercase(func):
    def wrapper():
        result = func()
        return result.upper()
    return wrapper

def exclaim(func):
    def wrapper():
        result = func()
        return result + "!"
    return wrapper

@exclaim
@uppercase
def say_hello():
    return "hello"

print(say_hello())  ## 输出:HELLO!

在这个示例中,say_hello 函数被 uppercaseexclaim 两个装饰器修饰。装饰器按照列出的顺序应用,因此 uppercase 装饰器先应用,exclaim 装饰器后应用。

通过使用装饰器,你可以轻松修改函数的行为而不改变其核心功能。这使你的代码更模块化、可复用且易于维护。

高级装饰器技术与用例

如你所见,装饰器是在 Python 中修改函数行为的强大工具。在本节中,我们将探索一些更高级的装饰器技术和用例。

装饰类

装饰器也可用于修改类的行为。下面是一个装饰器的示例,它为一个类添加了一个日志记录方法:

def log_class_methods(cls):
    class LoggedClass(cls):
        def __getattribute__(self, attr):
            if callable(super(LoggedClass, self).__getattribute__(attr)):
                def logged_method(*args, **kwargs):
                    print(f"调用方法 {attr}")
                    return super(LoggedClass, self).__getattribute__(attr)(*args, **kwargs)
                return logged_method
            return super(LoggedClass, self).__getattribute__(attr)
    return LoggedClass

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

    def do_something(self):
        print(f"对值 {self.value} 执行某些操作")

obj = MyClass(42)
obj.do_something()  ## 输出:调用方法 do_something
                   ## 对值 42 执行某些操作

在这个示例中,log_class_methods 装饰器接受一个类作为参数,并返回一个新类,该新类用一个日志记录函数包装了原始类的所有方法。

带有状态的装饰器

装饰器也可以在函数调用之间维护状态。这对于缓存、速率限制或其他有状态操作可能很有用。下面是一个缓存函数结果的装饰器示例:

def cache(func):
    cache = {}
    def wrapper(*args):
        if args in cache:
            print("返回缓存结果")
            return cache[args]
        else:
            result = func(*args)
            cache[args] = result
            return result
    return wrapper

@cache
def fibonacci(n):
    if n <= 1:
        return n
    else:
        return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(10))  ## 输出:计算 fibonacci(10)
                     ## 55
print(fibonacci(10))  ## 输出:返回缓存结果
                     ## 55

在这个示例中,cache 装饰器维护一个函数调用参数及其相应结果的字典。当调用被装饰的函数时,装饰器首先检查结果是否已经缓存,如果是,则返回缓存的结果。否则,它计算结果并将其存储在缓存中以供将来使用。

装饰器工厂

有时,你可能希望创建可以用参数配置的装饰器。这可以使用装饰器工厂来实现,装饰器工厂是一个返回装饰器的函数。下面是一个示例:

def repeat(n):
    def decorator(func):
        def wrapper():
            result = ""
            for _ in range(n):
                result += func()
            return result
        return wrapper
    return decorator

@repeat(3)
def say_hello():
    return "你好 "

print(say_hello())  ## 输出:你好 你好 你好

在这个示例中,repeat 函数是一个装饰器工厂,它接受一个参数 n 并返回一个装饰器,该装饰器包装原始函数,调用它 n 次并连接结果。

这些高级装饰器技术展示了 Python 中装饰器的灵活性和强大功能。通过使用装饰器,你可以创建可复用、模块化且易于维护的代码,这些代码可以轻松扩展和定制以满足你的需求。

总结

在本教程结束时,你将对 Python 装饰器以及如何有效地应用它们来修改函数行为有扎实的理解。你将学习到高级技术并探索实际应用案例,从而能够编写更高效、易读且易于维护的 Python 代码。