Bootstrap

Python 装饰器

Python 装饰器是一种强大且优雅的工具,它允许我们在不修改原始函数代码的情况下,增加或改变函数的功能。装饰器的使用可以显著提高代码的复用性和可读性,是 Python 编程中不可或缺的一部分。

装饰器的基本概念

装饰器本质上是一个函数,它接受一个函数作为参数并返回一个新的函数。通过装饰器,我们可以在函数执行前后添加额外的逻辑,而不需要修改函数本身的代码。这种特性使得装饰器非常适合用于 日志记录性能测试事务处理权限校验等场景。

装饰器的定义和使用

定义装饰器

要定义一个装饰器,我们需要编写一个函数,该函数接受一个函数作为参数,并返回一个新的函数。以下是一个简单的装饰器示例:

def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

在这个例子中,my_decorator 是一个装饰器函数,它接受一个函数 func 作为参数。wrapper 函数是装饰器内部定义的一个新函数,它在调用 func 之前和之后分别打印了一些信息。最后,my_decorator 返回 wrapper 函数。

使用装饰器

要使用装饰器,我们可以将函数传递给装饰器,或者使用 @ 语法糖来简化这一过程。以下是如何使用上面定义的装饰器:

def say_hello():
    print("Hello!")

# 使用装饰器
say_hello = my_decorator(say_hello)
say_hello()

输出:

Something is happening before the function is called.
Hello!
Something is happening after the function is called.

使用 @ 语法糖可以更简洁地应用装饰器:

@my_decorator
def say_hello():
    print("Hello!")

say_hello()

输出与之前相同。

带参数的装饰器

装饰器也可以接受参数,这需要在装饰器外层再定义一个函数来接收这些参数。以下是一个带参数的装饰器示例:

def repeat(num_times):
    def decorator_repeat(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator_repeat

@repeat(num_times=3)
def greet(name):
    print(f"Hello {name}")

greet("Alice")

输出:

Hello Alice
Hello Alice
Hello Alice

在这个例子中,repeat 是一个带参数的装饰器,它接受一个参数 num_times,表示要重复执行被装饰函数的次数。decorator_repeat 是装饰器内部定义的函数,它接受被装饰的函数 func 作为参数。wrapper 函数是实际执行被装饰函数的函数,它会根据 num_times 的值多次调用 func

装饰器的使用场景

日志记录

装饰器非常适合用于日志记录。我们可以在函数执行前后记录日志信息,以便跟踪程序的运行情况。

import logging

def log_decorator(func):
    def wrapper(*args, **kwargs):
        logging.info(f"Calling function: {func.__name__}")
        result = func(*args, **kwargs)
        logging.info(f"Function {func.__name__} executed successfully")
        return result
    return wrapper

@log_decorator
def calculate_sum(a, b):
    return a + b

calculate_sum(3, 4)

在这个例子中,log_decorator 装饰器会在被装饰函数执行前后记录日志信息,包括函数的名称和执行状态。

性能测试

装饰器也可以用于性能测试。我们可以通过装饰器来计算函数的执行时间,从而评估函数的性能。

import time

def timer_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Function {func.__name__} took {end_time - start_time} seconds to execute")
        return result
    return wrapper

@timer_decorator
def calculate_factorial(n):
    if n == 0:
        return 1
    else:
        return n * calculate_factorial(n - 1)

calculate_factorial(5)

在这个例子中,timer_decorator 装饰器会计算被装饰函数的执行时间,并打印出来。

事务处理

在数据库操作中,装饰器可以用于事务处理。我们可以在函数执行前后进行事务的开启和提交,确保数据的一致性和完整性。

def transaction_decorator(func):
    def wrapper(*args, **kwargs):
        print("Starting transaction")
        result = func(*args, **kwargs)
        print("Committing transaction")
        return result
    return wrapper

@transaction_decorator
def update_database(data):
    print(f"Updating database with data: {data}")

update_database("new_data")

在这个例子中,transaction_decorator 装饰器会在被装饰函数执行前后进行事务的开启和提交。

权限校验

装饰器还可以用于权限校验。我们可以在函数执行前进行权限检查,确保用户具有执行该函数的权限。

def admin_required(func):
    def wrapper(*args, **kwargs):
        user = get_current_user()
        if user.is_admin:
            return func(*args, **kwargs)
        else:
            raise PermissionError("Admin permission required")
    return wrapper

@admin_required
def delete_user(user_id):
    print(f"Deleting user with ID: {user_id}")

delete_user(123)

在这个例子中,admin_required 装饰器会检查当前用户是否具有管理员权限,如果没有,则抛出 PermissionError 异常。

装饰器的注意事项

保持原始函数的元信息

在使用装饰器时,我们可能会丢失原始函数的一些元信息,如函数名、文档字符串等。为了解决这个问题,我们可以使用 functools.wraps 装饰器来保留原始函数的元信息。

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("Something is happening before the function is called.")
        result = func(*args, **kwargs)
        print("Something is happening after the function is called.")
        return result
    return wrapper

@my_decorator
def say_hello():
    """Prints a greeting message."""
    print("Hello!")

print(say_hello.__name__)  # 输出:say_hello
print(say_hello.__doc__)   # 输出:Prints a greeting message.

在这个例子中,@wraps(func) 装饰器会将原始函数 say_hello 的元信息复制到 wrapper 函数上,从而保留了函数名和文档字符串。

处理带参数的函数

装饰器内部的 wrapper 函数需要接受任意参数和关键字参数,以便能够装饰带参数的函数。

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("Something is happening before the function is called.")
        result = func(*args, **kwargs)
        print("Something is happening after the function is called.")
        return result
    return wrapper

@my_decorator
def greet(name, age):
    print(f"Hello {name}, you are {age} years old.")

greet("Alice", 30)

在这个例子中,wrapper 函数接受任意参数 *args 和关键字参数 **kwargs,从而能够装饰带参数的函数 greet

多个装饰器的顺序

当一个函数被多个装饰器装饰时,装饰器的执行顺序是从近到远,即离函数定义最近的装饰器先执行。以下是一个示例:

def decorator1(func):
    def wrapper1():
        print("Decorator 1 before")
        func()
        print("Decorator 1 after")
    return wrapper1

def decorator2(func):
    def wrapper2():
        print("Decorator 2 before")
        func()
        print("Decorator 2 after")
    return wrapper2

@decorator1
@decorator2
def say_hello():
    print("Hello!")

say_hello()

输出:

Decorator 1 before
Decorator 2 before
Hello!
Decorator 2 after
Decorator 1 after

在这个例子中,@decorator1 离函数定义最近,所以它先执行,然后是 @decorator2

总结

Python 装饰器是一种非常强大的工具,它可以帮助我们以一种简洁和可读的方式扩展函数的功能。通过装饰器,我们可以在不修改原始函数代码的情况下,增加或改变函数的行为。装饰器在日志记录、性能测试、事务处理、权限校验等场景中都有广泛的应用。
在使用装饰器时,我们需要注意保持原始函数的元信息,处理带参数的函数,以及理解多个装饰器的执行顺序。

;