深入理解 Python 中的装饰器(Decorator)及其应用

在 Python 中,装饰器(Decorator) 是一种强大的功能,可以让我们在不修改原始函数代码的情况下,动态地为函数添加额外的功能。它广泛应用于日志记录、权限校验、缓存管理等场景,是 Python 编程中一个非常有用的工具。

本文将深入探讨 Python 中的装饰器,包括其定义、使用场景、以及一些常见的高级用法。


1. 什么是装饰器?

装饰器本质上是一个函数,它接受一个函数作为输入,返回一个新的函数。这个新的函数通常会在原始函数的基础上添加一些额外的功能。

简单例子:

def decorator(func):
    def wrapper():
        print("Before function execution.")
        func()  # 调用原始函数
        print("After function execution.")
    return wrapper

# 被装饰的函数
@decorator
def say_hello():
    print("Hello!")

say_hello()

输出

Before function execution.
Hello!
After function execution.

解释:

  • @decorator 语法是装饰器的简写形式。它相当于 say_hello = decorator(say_hello)
  • 装饰器会在原始函数执行前后添加额外的操作。

  • 2. 装饰器的应用场景

    装饰器的强大之处在于它能在不修改函数的情况下为函数添加新的功能。以下是常见的几种应用场景:

    2.1. 日志记录

    我们可以使用装饰器为函数添加日志记录功能,这样就能自动记录每次函数调用的时间、参数和返回值。

    import time
    
    def log(func):
        def wrapper(*args, **kwargs):
            start_time = time.time()
            result = func(*args, **kwargs)
            end_time = time.time()
            print(f"Function '{func.__name__}' executed in {end_time - start_time:.4f} seconds.")
            return result
        return wrapper
    
    @log
    def slow_function():
        time.sleep(2)
        return "Finished!"
    
    slow_function()
    

    输出

    Function 'slow_function' executed in 2.0001 seconds.
    

    2.2. 权限检查

    我们还可以用装饰器来检查用户是否有权限执行某个操作。

    def require_permission(func):
        def wrapper(user, *args, **kwargs):
            if user != "admin":
                print("Permission denied!")
                return
            return func(user, *args, **kwargs)
        return wrapper
    
    @require_permission
    def delete_user(user):
        print(f"User {user} deleted!")
    
    delete_user("admin")  # 有权限
    delete_user("guest")  # 没有权限
    

    输出

    User admin deleted!
    Permission denied!
    

    2.3. 缓存

    装饰器还常用于实现函数结果的缓存,即对于相同的输入,返回相同的结果,而不需要每次都重新计算。

    def cache(func):
        cached_results = {}
        def wrapper(*args):
            if args in cached_results:
                print("Fetching from cache...")
                return cached_results[args]
            result = func(*args)
            cached_results[args] = result
            return result
        return wrapper
    
    @cache
    def slow_function(n):
        print(f"Calculating {n}...")
        time.sleep(2)
        return n * 2
    
    print(slow_function(3))  # 第一次执行
    print(slow_function(3))  # 第二次从缓存中取值
    

    输出

    Calculating 3...
    6
    Fetching from cache...
    6
    

    3. 装饰器的嵌套使用

    我们可以将多个装饰器应用到同一个函数,这样可以让函数在不同层次上执行多个功能。

    def decorator1(func):
        def wrapper(*args, **kwargs):
            print("Decorator 1")
            return func(*args, **kwargs)
        return wrapper
    
    def decorator2(func):
        def wrapper(*args, **kwargs):
            print("Decorator 2")
            return func(*args, **kwargs)
        return wrapper
    
    @decorator1
    @decorator2
    def greet():
        print("Hello!")
    
    greet()
    

    输出

    Decorator 1
    Decorator 2
    Hello!
    

    解释:

  • 装饰器的应用顺序是从内到外的,即 @decorator2 先应用,然后 @decorator1 再应用。

  • 4. 带参数的装饰器

    有时我们需要传递参数给装饰器,这样可以定制装饰器的行为。我们需要使用多层嵌套来实现这一点。

    def repeat(n):
        def decorator(func):
            def wrapper(*args, **kwargs):
                for _ in range(n):
                    func(*args, **kwargs)
            return wrapper
        return decorator
    
    @repeat(3)
    def say_hello():
        print("Hello!")
    
    say_hello()
    

    输出

    Hello!
    Hello!
    Hello!
    

    解释:

  • repeat(3) 返回一个装饰器,装饰器再接收 say_hello 函数并包装起来。最终,say_hello 会被调用 3 次。

  • 5. functools.wraps 和装饰器的元信息

    当我们使用装饰器时,原始函数的元数据(如函数名、文档字符串)会被包装成装饰器中的内部函数,从而丧失。这时我们可以使用 functools.wraps 来保留这些元信息。

    import functools
    
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print("Function is called!")
            return func(*args, **kwargs)
        return wrapper
    
    @decorator
    def greet(name):
        """This function greets the person."""
        print(f"Hello, {name}!")
    
    print(greet.__name__)  # 输出 greet
    print(greet.__doc__)   # 输出 This function greets the person.
    

    输出

    greet
    This function greets the person.
    

    解释:

  • @functools.wraps(func) 确保 greet 函数的名称和文档字符串不会被装饰器中的 wrapper 函数覆盖。

  • 6. 结论

    装饰器是 Python 中一个非常强大的工具,它能让我们在不修改原始函数代码的情况下为函数添加新的功能。常见的应用场景包括日志记录、权限校验、缓存管理等。掌握装饰器的用法,将大大提高代码的灵活性和可维护性。

    装饰器的总结

  • 基本形式:通过函数嵌套实现函数功能增强。
  • 应用场景:日志、缓存、权限控制、计时等。
  • 嵌套使用:多个装饰器可以按顺序应用于一个函数。
  • 带参数的装饰器:通过外层函数接受参数,灵活控制装饰器行为。
  • 元信息保持:使用 functools.wraps 保持函数的元数据。
  • 掌握装饰器的使用,你将能编写更加简洁、可复用且具有扩展性的代码! 😃

    作者:小白8990

    物联沃分享整理
    物联沃-IOTWORD物联网 » 深入理解 Python 中的装饰器(Decorator)及其应用

    发表回复