简介
Python 装饰器是一个强大的工具,它允许你在不改变函数核心逻辑的情况下修改函数的行为。在本教程中,我们将探讨装饰器语法,并学习如何使用它来增强 Python 函数的功能。从基本的装饰技术到高级方法,你将全面了解 Python 编程语言中这个多功能的特性。
Python 装饰器是一个强大的工具,它允许你在不改变函数核心逻辑的情况下修改函数的行为。在本教程中,我们将探讨装饰器语法,并学习如何使用它来增强 Python 函数的功能。从基本的装饰技术到高级方法,你将全面了解 Python 编程语言中这个多功能的特性。
Python 装饰器是一项强大的语言特性,它使你能够在不改变函数源代码的情况下修改其行为。它们提供了一种用额外功能包装函数的方式,以简洁且可复用的方式增强或改变其行为。
Python 中的装饰器是一种通过用另一个函数包装来 “装饰” 函数的方式。包装函数可以在调用原始函数之前或之后添加额外功能,甚至修改原始函数的参数和返回值。
装饰器使用 @
符号定义,后面跟着装饰器函数的名称,放在函数定义之前。这种语法是将装饰器应用于函数的一种简写方式。
下面是一个简单的装饰器示例,它记录传递给函数的参数:
def log_arguments(func):
def wrapper(*args, **kwargs):
print(f"调用 {func.__name__},参数为 args={args},kwargs={kwargs}")
return func(*args, **kwargs)
return wrapper
@log_arguments
def add_numbers(a, b):
return a + b
result = add_numbers(2, 3)
print(result)
输出:
调用 add_numbers,参数为 args=(2, 3),kwargs={}
5
在这个示例中,log_arguments
装饰器包装了 add_numbers
函数,在调用原始函数之前添加了记录参数的功能。
装饰器有几个好处:
装饰器在 Python 中有广泛的用途,包括:
在下一节中,我们将更详细地探讨如何创建和使用装饰器。
装饰器函数的基本结构如下:
def decorator_function(func):
def wrapper(*args, **kwargs):
## 在调用原始函数之前执行某些操作
result = func(*args, **kwargs)
## 在调用原始函数之后执行某些操作
return result
return wrapper
装饰器函数接受一个函数作为参数,并返回一个包装原始函数的新函数。包装函数可以在调用原始函数之前和/或之后执行额外的任务。
下面是一个简单的装饰器示例,它记录函数的执行时间:
import time
def measure_time(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:.2f} 秒。")
return result
return wrapper
@measure_time
def fibonacci(n):
if n <= 1:
return n
else:
return (fibonacci(n-1) + fibonacci(n-2))
fibonacci(30)
输出:
函数 fibonacci 执行耗时 0.31 秒。
装饰器也可以接受参数,这提供了更大的灵活性和定制性。为此,你需要创建一个装饰器工厂,它是一个返回装饰器函数的函数。
下面是一个装饰器示例,它允许你指定函数应该重试的次数:
def retry(max_retries):
def decorator(func):
def wrapper(*args, **kwargs):
retries = 0
while retries < max_retries:
try:
return func(*args, **kwargs)
except Exception as e:
print(f"函数 {func.__name__} 执行失败。正在重试... (第 {retries + 1}/{max_retries} 次尝试)")
retries += 1
raise Exception(f"函数 {func.__name__} 在 {max_retries} 次重试后仍失败。")
return wrapper
return decorator
@retry(max_retries=3)
def divide(a, b):
return a / b
try:
result = divide(10, 0)
except Exception as e:
print(e)
输出:
函数 divide 执行失败。正在重试... (第 1/3 次尝试)
函数 divide 执行失败。正在重试... (第 2/3 次尝试)
函数 divide 执行失败。正在重试... (第 3/3 次尝试)
函数 divide 在 3 次重试后仍失败。
在这个示例中,retry
装饰器工厂接受一个 max_retries
参数,该参数用于确定在引发异常之前装饰函数应该重试的次数。
装饰器可以堆叠,允许你将多层功能应用于单个函数。装饰器的应用顺序很重要,因为它决定了包装函数的执行顺序。
def uppercase(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result.upper()
return wrapper
def reverse(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result[::-1]
return wrapper
@uppercase
@reverse
def greet(name):
return f"Hello, {name}!"
print(greet("LabEx"))
输出:
!XEbal,OLLEH
在这个示例中,greet
函数首先用 uppercase
装饰器进行装饰,然后再用 reverse
装饰器进行装饰。最终结果是一个函数,它返回大写且反转后的问候语。
装饰器的顺序很重要,因为它决定了包装函数的执行顺序。在这种情况下,uppercase
装饰器首先应用,然后 reverse
装饰器应用于 uppercase
装饰器的结果。
除了基于函数的装饰器外,Python 还支持基于类的装饰器。当你需要在装饰器中维护状态或配置信息时,这种方法会很有用。
下面是一个基于类的装饰器示例,它缓存函数的结果:
class cache:
def __init__(self, func):
self.func = func
self.cache = {}
def __call__(self, *args, **kwargs):
key = str(args) + str(kwargs)
if key in self.cache:
return self.cache[key]
else:
result = self.func(*args, **kwargs)
self.cache[key] = result
return result
@cache
def fibonacci(n):
if n <= 1:
return n
else:
return (fibonacci(n-1) + fibonacci(n-2))
print(fibonacci(30))
print(fibonacci(30))
输出:
832040
832040
在这个示例中,cache
类用作装饰器。当装饰器应用于函数时,会调用 __init__
方法,每次调用被装饰的函数时,会调用 __call__
方法。cache
字典用于存储之前函数调用的结果,这样后续使用相同参数的调用可以从缓存中获取结果。
装饰器工厂是一种创建可以用参数定制的装饰器的方法。这使你能够创建更灵活、可复用的装饰器。
下面是一个装饰器工厂示例,它允许你指定函数可以接受的最大参数数量:
def max_arguments(max_args):
def decorator(func):
def wrapper(*args, **kwargs):
if len(args) + len(kwargs) > max_args:
raise ValueError(f"函数 {func.__name__} 最多只能接受 {max_args} 个参数。")
return func(*args, **kwargs)
return wrapper
return decorator
@max_arguments(max_args=3)
def greet(name, greeting, exclamation="!"):
return f"{greeting}, {name}{exclamation}"
print(greet("LabEx", "Hello"))
print(greet("LabEx", "Hello", "?"))
print(greet("LabEx", "Hello", "?", "extra"))
输出:
Hello, LabEx!
Hello, LabEx?
ValueError: 函数 greet 最多只能接受 3 个参数。
在这个示例中,max_arguments
装饰器工厂接受一个 max_args
参数,该参数用于确定被装饰函数可以接受的最大参数数量。然后,decorator
函数用参数验证逻辑包装原始函数。
当你将装饰器应用于函数时,原始函数的元数据(如名称、文档字符串和签名)会丢失。如果你在诸如 Web 框架或命令行界面等元数据很重要的上下文中使用该函数,这可能会有问题。
为了保留原始函数的元数据,你可以使用 functools.wraps
装饰器,它将相关元数据从原始函数复制到包装函数。
from functools import wraps
def uppercase(func):
@wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result.upper()
return wrapper
@uppercase
def greet(name):
"""返回给定名称的问候语。"""
return f"Hello, {name}!"
print(greet.__name__)
print(greet.__doc__)
输出:
greet
返回给定名称的问候语。
在这个示例中,wraps
装饰器用于将 greet
函数的 __name__
和 __doc__
属性复制到 wrapper
函数。这确保了被装饰的函数保留其原始元数据,这对于各种用例可能很重要。
Python 装饰器是一种强大且灵活的语言特性,它允许你以简洁且可复用的方式修改函数的行为。通过理解基本概念、常见用例和高级技术,你可以利用装饰器编写更模块化、可维护且富有表现力的 Python 代码。
请记住,有效使用装饰器的关键是在添加功能与保留原始函数的意图和行为之间取得平衡。通过实践和创造力,你可以在 Python 项目中充分发挥装饰器的潜力。
在本教程结束时,你将扎实掌握 Python 装饰器以及如何使用它们来扩展函数的功能。你将学习基本语法,探索各种装饰器技术,并了解如何创建自己的自定义装饰器以满足特定需求。掌握 Python 装饰器将使你能够编写更高效、模块化和可维护的代码,使你成为更熟练的 Python 程序员。