一篇文章学会Python中的装饰器 装饰器是Python中一种非常重要的函数式编程工具。它们允许开发人员将函数作为参数传递给另一个函数,并在执行函数之前或之后对其进行修改。在本文中,我们将深入研究Python中的装饰器,并了解如何使用它们进行函数修饰。 1.装饰器的定义 装饰器实际上是一个函数或类,它接受另一个函数作为参数并返回另一个函数。使用装饰器,您可以在不修改函数源代码的情况下修改函数的行为。这使得您可以轻松地添加新的功能,例如日志记录或性能评估。 下面是一个简单的装饰器示例,它通过在函数执行之前和之后打印消息来记录函数调用: ``` def log_calls(func): def wrapper(*args, **kwargs): print("Calling {} with arguments {} and {}".format(func.__name__, args, kwargs)) result = func(*args, **kwargs) print("Returning {} from {}".format(result, func.__name__)) return result return wrapper @log_calls def add(x, y): return x + y result = add(2, 3) print(result) ``` 在上面的示例中,我们定义了一个名为“log_calls”的装饰器,它接受一个函数“func”作为参数并返回另一个函数“wrapper”。“wrapper”函数在函数“func”执行之前和之后打印消息,并最终返回函数“func”的结果。我们然后使用装饰器将“add”函数修饰为“log_calls”函数的输出。 当我们运行以上代码,输出如下: ``` Calling add with arguments (2, 3) and {} Returning 5 from add 5 ``` 2.装饰器的语法 上面的示例中,我们使用了Python的语法糖来应用装饰器。实际上,您也可以使用以下语法将装饰器应用于函数: ``` def add(x, y): return x + y add = log_calls(add) ``` 这将把函数“add”传递给装饰器“log_calls”,并将返回值赋值给同名函数变量“add”。使用这种语法,您可以在没有语法糖的情况下使用装饰器。 3.多个装饰器 您可以在一个函数上应用多个装饰器。在这种情况下,装饰器将按照内部到外部的顺序应用于函数。例如,以下代码将应用两个装饰器:log_calls和memoize(用于缓存函数的结果): ``` def log_calls(func): def wrapper(*args, **kwargs): print("Calling {} with arguments {} and {}".format(func.__name__, args, kwargs)) result = func(*args, **kwargs) print("Returning {} from {}".format(result, func.__name__)) return result return wrapper def memoize(func): cache = {} def wrapper(*args): if args in cache: return cache[args] result = func(*args) cache[args] = result return result return wrapper @log_calls @memoize def fibonacci(n): if n < 2: return n return fibonacci(n-1) + fibonacci(n-2) result = fibonacci(10) print(result) ``` 在上面的代码中,我们定义了两个装饰器:log_calls和memoize。我们然后将它们应用于函数“fibonacci”,并使用“@”符号按照从内到外的顺序来进行装饰。由于“memoize”装饰器首先被应用,因此它将缓存函数的结果,而“log_calls”装饰器将记录函数的调用。 当我们运行以上代码,输出如下: ``` Calling wrapper with arguments (10,) and {} Calling wrapper with arguments (9,) and {} Calling wrapper with arguments (8,) and {} Calling wrapper with arguments (7,) and {} Calling wrapper with arguments (6,) and {} Calling wrapper with arguments (5,) and {} Calling wrapper with arguments (4,) and {} Calling wrapper with arguments (3,) and {} Calling wrapper with arguments (2,) and {} Returning 1 from wrapper Returning 0 from wrapper Returning 1 from wrapper Calling wrapper with arguments (1,) and {} Calling wrapper with arguments (0,) and {} Returning 1 from wrapper Returning 2 from wrapper Calling wrapper with arguments (1,) and {} Calling wrapper with arguments (0,) and {} Returning 1 from wrapper Returning 3 from wrapper Calling wrapper with arguments (2,) and {} Calling wrapper with arguments (1,) and {} Calling wrapper with arguments (0,) and {} Returning 2 from wrapper Returning 5 from wrapper ``` 4.带有参数的装饰器 有时,您可能需要在一个装饰器中传递参数。例如,您可能希望使装饰器只记录特定的函数参数。在这种情况下,您可以定义一个装饰器函数,并将参数传递给它。然后,您可以通过将装饰器函数返回装饰器本身来创建一个实际的装饰器。 以下是一个示例,其中定义了一个名为“log_calls_with”的装饰器函数,该函数接受一个参数“arg”,并返回一个装饰器函数,该函数记录特定参数的函数调用: ``` def log_calls_with(arg): def log_calls(func): def wrapper(*args, **kwargs): if arg in args or arg in kwargs: print("Calling {} with arguments {} and {}".format(func.__name__, args, kwargs)) result = func(*args, **kwargs) print("Returning {} from {}".format(result, func.__name__)) return result return wrapper return log_calls @log_calls_with(2) def add(x, y): return x + y result = add(2, 3) print(result) result = add(4, 5) print(result) ``` 在上面的代码中,我们定义了一个名为“log_calls_with”的装饰器函数,它接受一个参数“arg”,并返回一个名为“log_calls”的装饰器函数。这个装饰器函数记录包含“arg”参数的函数调用。我们然后使用“@”符号将“log_calls_with”应用于函数“add”,并指定“arg”参数为“2”。 当我们运行以上代码,输出如下: ``` Calling add with arguments (2, 3) and {} Returning 5 from add 5 Calling add with arguments (4, 5) and {} Returning 9 from add 9 ``` 本文中我们学习了Python中装饰器的定义、语法和多个装饰器、带有参数的装饰器的应用,其应用范围还远不于此。装饰器是Python中一个非常强大的工具,能够帮助我们写出更优雅、模块化和可维护的代码。