使用真实示例理解 Python 的 Decorator

摘要:Python 中的装饰器是一项功能,允许开发人员在不更改源代码的情况下修改或扩展函数或方法的行为。修饰器由 @ 符号表示,广泛用于日志记录、验证、缓存和其他增强功能。

Python 中的装饰器是一项功能,允许开发人员在不更改源代码的情况下修改或扩展函数或方法的行为。修饰器由 @ 符号表示,广泛用于日志记录、验证、缓存和其他增强功能。

装饰器是一个高阶函数,它将另一个函数作为输入,向其添加功能,并返回修改后的函数。本质上,它 “包装” 了一个函数,在不改变其核心逻辑的情况下改变其行为。

装饰器的基本语法:

@decorator_namedef function_to_decorate: pass

@decorator_name 等同于编写:

function_to_decorate = decorator_name(function_to_decorate)

日志记录是装饰器的常见用例。让我们创建一个简单的装饰器,在调用函数时进行记录。

文件结构:

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

log_decorator.py:

import datetimedef 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_calldef add_numbers(a, b): return a + bif __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 1515

2. 当 Python 解释器看到 @log_function_call 时,它会自动将 add_numbers 函数替换为 log_function_call(add_numbers) 的结果。

3. 在 log_function_call 中,创建了一个新的包装器函数。此包装器:

记录对 add_numbers 的调用,打印函数名称及其参数。执行原始 add_numbers 函数并存储其结果。在返回函数调用结果之前记录该结果。

4. add_numbers 函数现在被包装器“包装”,因此每当调用 add_numbers 时,它都会首先经过 log_function_call 逻辑。

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

设置 Flask:

pip install flask

文件结构:

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

app/decorators.py 中:

from flask import request, jsonifydef 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

应用程序/routes.py:

from flask import Flask, jsonifyfrom .decorators import require_api_keyapp = Flask(__name__)@app.route("/secure-data", methods=["GET"])@require_api_keydef secure_data: return jsonify({"data": "This is secure data!"})if __name__ == "__main__": app.run(debug=True)

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

python -m app.routes

使用 curl 等工具使用适当的 API 密钥发送请求:

@require_api_keydef secure_data: return jsonify({"data": "This is secure data!"})@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 函数本身的情况下强制执行前提条件 (authentication)。这种关注点分离确保了代码更清晰、模块化程度更高。

当您想要存储昂贵的函数调用的结果以供重用时,缓存非常有用。

文件结构:

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

cache_decorator.py:

import functoolsdef 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@cachedef fibonacci(n): if n

2. 缓存装饰器创建一个包装函数,该函数:

检查传递给 fibonacci 的参数是否已缓存在 memo 字典中。如果结果被缓存,它将检索该值并跳过重新计算它,从而提高性能。如果结果没有缓存,则通过调用原始斐波那契函数来计算结果,将结果存储在 memo 字典中,然后返回结果。

3. 装饰器透明地管理缓存,因此你可以使用斐波那契,而不必担心主代码中的缓存逻辑。这对于计算成本高昂的递归函数特别有用。

在所有三个示例中,@ 装饰器通过将目标函数包装在另一个函数(包装器)中来修改目标函数的行为。此包装器可以:

添加新功能(例如,日志记录、身份验证)。修改输入或输出。强制执行前提条件(例如,检查 API 密钥)。提高性能(例如,缓存)。

从本质上讲,装饰器充当调用方和原始函数之间的中间人,在不更改函数源代码的情况下增强或更改行为。这种方法使代码保持干净、模块化且易于维护。

来源:自由坦荡的湖泊AI一点号

相关推荐