Python中的装饰器

Python中的装饰器

本文解释了Python中的装饰器。

YouTube Video

Python中的装饰器

Python装饰器是一种强大的功能,用于为函数或方法添加额外的功能。装饰器允许您在不修改现有代码的情况下添加新功能,从而提高代码的可重用性和可维护性。

装饰器的基础知识

Python装饰器通过将函数作为参数并返回具有新增功能的新函数来工作。使用装饰器,您可以轻松地为函数添加前置处理或后置处理。

没有装饰器时,修改函数的行为需要直接编辑函数本身。使用装饰器,您可以在不修改原始实现的情况下扩展功能。

装饰器的基本结构

 1def my_decorator(func):
 2    def wrapper():
 3        print("Before the function call")
 4        func()
 5        print("After the function call")
 6    return wrapper
 7
 8@my_decorator
 9def say_hello():
10    print("Hello!")
11
12say_hello()
  • 在这个例子中,my_decorator 函数作为装饰器,对 say_hello 函数进行了封装。使用 @my_decorator 语法应用装饰器,当调用 say_hello() 时,装饰器提供的前置和后置处理会自动执行。

装饰器的工作原理

装饰器按以下步骤工作。

  1. 装饰器将一个函数(或方法)作为参数。

  2. 它定义了一个包装函数以执行原始函数。

  3. 包装函数在执行原始函数之前或之后执行额外处理。

  4. 装饰器返回包装函数。因此,原始函数被替换为一个新的、经过装饰的函数。

带参数函数的装饰器

将装饰器应用于带参数的函数时,包装函数必须进行调整以接受这些参数。

 1def my_decorator(func):
 2    def wrapper(*args, **kwargs):
 3        print("Before the function call")
 4        result = func(*args, **kwargs)
 5        print("After the function call")
 6        return result
 7    return wrapper
 8
 9@my_decorator
10def greet(name):
11    print(f"Hello, {name}!")
12
13greet("Alice")
  • 通过使用 *args**kwargs,可以处理接受任意数量参数和关键字参数的函数。这使得装饰器能够通用于任何类型参数的函数。

装饰器的应用示例

用于日志记录的装饰器

装饰器通常用于添加日志记录功能。例如,通过创建一个在函数执行前后记录日志的装饰器,可以记录函数被调用的时间以及执行所需的时间。

 1import time
 2
 3def log_time(func):
 4    def wrapper(*args, **kwargs):
 5        start_time = time.time()
 6        print(f"Calling function '{func.__name__}'...")
 7        result = func(*args, **kwargs)
 8        end_time = time.time()
 9        print(f"Function '{func.__name__}' completed in {end_time - start_time} seconds")
10        return result
11    return wrapper
12
13@log_time
14def long_task(duration):
15    time.sleep(duration)
16    print("Task completed!")
17
18long_task(2)
  • 在此示例中,log_time 装饰器计算函数的执行时间并将其输出为日志。long_task 函数被装饰器包裹,其执行时间在运行时被记录。

用于权限管理的装饰器

装饰器也可以用于权限管理。例如,可以通过检查用户是否拥有特定权限来限制处理操作。

 1def requires_permission(user_role):
 2    def decorator(func):
 3        def wrapper(*args, **kwargs):
 4            if user_role != "admin":
 5                print("Permission denied!")
 6                return
 7            return func(*args, **kwargs)
 8        return wrapper
 9    return decorator
10
11@requires_permission("admin")
12def delete_user(user_id):
13    print(f"User {user_id} has been deleted.")
14
15delete_user(123)  # Executed
16delete_user = requires_permission("guest")(delete_user)
17delete_user(456)  # Permission denied!
  • requires_permission 装饰器根据用户的角色限制函数的执行。通过将此类逻辑集中到装饰器中,可以防止权限管理逻辑分散于代码中,从而提高可读性。

嵌套装饰器

可以应用多个装饰器。当对单个函数应用多个装饰器时,它们会按照从上到下的顺序执行。

 1def uppercase(func):
 2    def wrapper(*args, **kwargs):
 3        result = func(*args, **kwargs)
 4        return result.upper()
 5    return wrapper
 6
 7def exclaim(func):
 8    def wrapper(*args, **kwargs):
 9        result = func(*args, **kwargs)
10        return result + "!"
11    return wrapper
12
13@uppercase
14@exclaim
15def greet(name):
16    return f"Hello, {name}"
17
18print(greet("Alice"))  # "HELLO, ALICE!"
  • 在此示例中,两个装饰器被应用于 greet 函数。@exclaim 装饰器在字符串末尾添加一个感叹号,然后 @uppercase 将字符串转换为大写。

用于类方法的装饰器

装饰器也可以用于类方法。当需要控制类中方法的行为时,这尤其有用。将装饰器应用于类方法时,需要注意像 selfcls 这样的参数。

 1def log_method_call(func):
 2    def wrapper(self, *args, **kwargs):
 3        print(f"Calling method '{func.__name__}'...")
 4        return func(self, *args, **kwargs)
 5    return wrapper
 6
 7class MyClass:
 8    @log_method_call
 9    def greet(self, name):
10        print(f"Hello, {name}")
11
12obj = MyClass()
13obj.greet("Bob")
  • 在此示例中,log_method_call 装饰器被应用于 greet 方法,在调用该方法时输出日志。

使用装饰器时的注意事项

使用装饰器时需要注意一些事项。装饰器可能会更改原始函数的名称和文档字符串(例如,__name____doc__),因此推荐使用 functools.wraps 来保留这些信息。

 1import functools
 2
 3# Decorator without functools.wraps
 4def bad_decorator(func):
 5    def wrapper(*args, **kwargs):
 6        print("Before the function call")
 7        return func(*args, **kwargs)
 8    return wrapper
 9
10# Decorator with functools.wraps
11def good_decorator(func):
12    @functools.wraps(func)
13    def wrapper(*args, **kwargs):
14        print("Before the function call")
15        return func(*args, **kwargs)
16    return wrapper
17
18# Apply decorators
19@bad_decorator
20def my_function_bad():
21    """This is the original docstring for my_function_bad."""
22    print("Executing my_function_bad")
23
24@good_decorator
25def my_function_good():
26    """This is the original docstring for my_function_good."""
27    print("Executing my_function_good")
28
29# Run functions
30print("=== Without functools.wraps ===")
31my_function_bad()
32print("Function name:", my_function_bad.__name__)
33print("Docstring:", my_function_bad.__doc__)
34
35print("\n=== With functools.wraps ===")
36my_function_good()
37print("Function name:", my_function_good.__name__)
38print("Docstring:", my_function_good.__doc__)
  • 使用 @functools.wraps 可以确保原始函数的元数据正确地传递到包装函数。

总结

Python装饰器是一种非常强大的工具,可以简洁地为函数和方法添加额外的功能。它们可以在各种场景中使用,以减少代码重复性并提高代码的可维护性和可重用性。了解装饰器的工作原理后,您可以编写出更高效且更灵活的代码。

您可以在我们的YouTube频道上使用Visual Studio Code跟随上述文章进行学习。 请也查看我们的YouTube频道。

YouTube Video