如何在你的项目中使用python的装饰器?

Python装饰器允许开发人员在不修改源代码的情况下修改和扩展函数的行为。装饰器以@符号表示,并广泛用于日志记录、验证、缓存和其他增强功能。在本文中,我们将探讨Python@装饰器,深入研究真实世界示例,并演示如何使用它。

什么是装饰器?

装饰器是一个高阶函数,接受另一个函数作为输入,为其添加功能,然后返回修改后的函数。本质上,它“包装”了一个函数,改变其行为而不改变其核心逻辑。

装饰器的基本语法:

@decorator_name
def function_to_decorate():
    pass

这个写法可能会让初学者感到疑惑。其实上面的代码可以有一个等价的写法,即将被装饰的函数(function_to_decorate)以参数的形式传给装饰函数(decorator_name),如下所示:

function_to_decorate = decorator_name(function_to_decorate)

项目实践

实践1:日志记录

日志记录在项目实践中很常见,下面直接上代码。

文件结构:

project/
├── log_decorator.py
├── app.py

定义装饰器函数log_decorator.py:

import datetime

def log_function_call(func):
    def wrapper(*args, **kwargs):
        print(f"{datetime.datetime.now()}: Calling {func.__name__} with {args} and {kwargs}")
        result = func(*args, **kwargs)
        print(f"{datetime.datetime.now()}: {func.__name__} returned {result}")
        return result
    return wrapper

app.py:

from log_decorator import log_function_call

@log_function_call
def add_numbers(a, b):
    return a + b

if __name__ == "__main__":
    print(add_numbers(5, 10))

运行 app.py 时的输出:

2024-12-12 14:30:15.123456: Calling add_numbers with (5, 10) and {}
2024-12-12 14:30:15.123456: add_numbers returned 15
15

代码解释:

@log_function_call
def add_numbers(a, b):
    return a + b

装饰器的作用:

  1. @log_function_call将 log_function_call装饰器应用于add_numbers函数。
  2. 当 Python 解释器看到 @log_function_call 时,它会自动用 log_function_call(add_numbers) 的结果替换 add_numbers函数。
  3. 在 log_function_call 内部,创建了一个新的 wrapper函数。这个 wrapper
    • 记录对 add_numbers 的调用,打印函数名称及其参数。
    • 执行原始的 add_numbers 函数并存储其结果。
    • 在返回结果之前记录函数调用的结果。
  4. 现在 add_numbers 函数被 wrapper “包装”起来,所以每次调用 add_numbers 时,它会首先经过 log_function_call 的逻辑。

实践2:Web应用程序的身份验证

在web应用中,装饰器可以用于强制执行用户身份验证。让我们为一个简单的 Flask Web 应用程序演示这一点。

安装Flask:

pip install flask

文件结构:

project/
├── app/
│   ├── __init__.py
│   ├── decorators.py
│   ├── routes.py

app/decorators.py:

from flask import request, jsonify

def require_api_key(func):
    def wrapper(*args, **kwargs):
        api_key = request.headers.get("x-api-key")
        if api_key != "mysecurekey123":
            return jsonify({"error": "Unauthorized"}), 401
        return func(*args, **kwargs)
    return wrapper

app/routes.py:

from flask import Flask, jsonify
from .decorators import require_api_key

app = Flask(__name__)

@app.route("/secure-data", methods=["GET"])
@require_api_key
def secure_data():
    return jsonify({"data": "This is secure data!"})

if __name__ == "__main__":
    app.run(debug=True)

使用以下命令运行应用程序:

python -m app.routes

使用类似 curl 的工具发送带有正确 API 密钥的请求:

curl -H "x-api-key: mysecurekey123" http://127.0.0.1:5000/secure-data

代码解释:

@require_api_key
def secure_data():
    return jsonify({"data": "This is secure data!"})

装饰器的作用:

  1. @require_api_key 将 require_api_key装饰器应用于 secure_data函数。

  2. require_api_key装饰器用额外逻辑包装 secure_data函数。具体来说:

    • 检查HTTP请求中是否存在 x-api-key 头,并验证它。
    • 如果密钥无效或缺失,包装函数将立即返回未经授权的响应(401)。
    • 如果密钥有效,包装函数继续调用原始的 secure_data 函数,后者以JSON形式返回安全数据。
  3. 该装饰器强制执行一个前提条件(身份验证),而不改变 secure_data 函数本身。这种关注分离确保了更清洁、更模块化的代码。

实践3:缓存结果

当你在项目过程中可能会遇到一些计算代价比较高的的函数,这个时候你就可以将结果缓存起来。而装饰器就可以满足你的需求。

文件结构:

project/
├── cache_decorator.py
├── app.py

装饰器函数cache_decorator.py

import functools

def cache(func):
    memo = {}

    @functools.wraps(func)
    def wrapper(*args):
        if args in memo:
            print("Returning cached result")
            return memo[args]
        print("Calculating result")
        result = func(*args)
        memo[args] = result
        return result
    return wrapper

app.py:

from cache_decorator import cache

@cache
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

if __name__ == "__main__":
    print(fibonacci(10))
    print(fibonacci(10))  # 这将返回缓存的结果

实践 3 代码解释:

@cache
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)
  1. @cache 将装饰器应用于fibonacci函数。

  2. cache 装饰器创建了一个 wrapper函数:

    • 检查传递给 fibonacci的参数是否已经在 memo 字典中缓存。
    • 如果结果已缓存,则检索值并跳过重新计算,提高性能。
    • 如果结果未缓存,它通过调用原始的fibonacci 函数来计算结果,将结果存储在 memo 字典中,然后返回结果。
  3. 该装饰器透明地管理缓存,因此您可以在主代码中使用 fibonacci 而不必担心缓存逻辑。这对于计算昂贵的递归函数特别有用。

写在最后

Python 装饰器是一种强大的工具,允许开发人员在不改变源代码的情况下修改和扩展函数的行为。通过@符号表示,装饰器在日志记录、验证、缓存等方面得到广泛应用。装饰器基本语法简单明了,实际上是高阶函数,接受一个函数作为输入,为其添加功能并返回修改后的函数。本文通过实际项目实践展示了装饰器的应用。从日志记录到身份验证再到结果缓存,每个示例都展示了装饰器如何“包装”函数,添加额外功能,而不改变函数的核心逻辑。这种灵活性使得代码更清晰、更模块化,同时方便扩展和修改函数的行为。装饰器的使用减少了重复代码,提高了代码的可维护性和可读性。

来源:harvey的网络日志

THE END