如何在你的项目中使用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
装饰器的作用:
-
@log_function_call
将log_function_call
装饰器应用于add_numbers
函数。 -
当 Python 解释器看到 @log_function_call
时,它会自动用log_function_call(add_numbers)
的结果替换add_numbers
函数。 -
在 log_function_call
内部,创建了一个新的wrapper
函数。这个wrapper
:-
记录对 add_numbers
的调用,打印函数名称及其参数。 -
执行原始的 add_numbers
函数并存储其结果。 -
在返回结果之前记录函数调用的结果。
-
-
现在 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!"})
装饰器的作用:
-
@require_api_key
将require_api_key
装饰器应用于secure_data
函数。 -
require_api_key
装饰器用额外逻辑包装secure_data
函数。具体来说:-
检查HTTP请求中是否存在 x-api-key 头,并验证它。 -
如果密钥无效或缺失,包装函数将立即返回未经授权的响应(401)。 -
如果密钥有效,包装函数继续调用原始的 secure_data 函数,后者以JSON形式返回安全数据。
-
-
该装饰器强制执行一个前提条件(身份验证),而不改变 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)
-
@cache
将装饰器应用于fibonacci
函数。 -
cache
装饰器创建了一个wrapper
函数:-
检查传递给 fibonacci
的参数是否已经在memo
字典中缓存。 -
如果结果已缓存,则检索值并跳过重新计算,提高性能。 -
如果结果未缓存,它通过调用原始的 fibonacci
函数来计算结果,将结果存储在memo
字典中,然后返回结果。
-
-
该装饰器透明地管理缓存,因此您可以在主代码中使用 fibonacci
而不必担心缓存逻辑。这对于计算昂贵的递归函数特别有用。
写在最后
Python 装饰器是一种强大的工具,允许开发人员在不改变源代码的情况下修改和扩展函数的行为。通过@
符号表示,装饰器在日志记录、验证、缓存等方面得到广泛应用。装饰器基本语法简单明了,实际上是高阶函数,接受一个函数作为输入,为其添加功能并返回修改后的函数。本文通过实际项目实践展示了装饰器的应用。从日志记录到身份验证再到结果缓存,每个示例都展示了装饰器如何“包装”函数,添加额外功能,而不改变函数的核心逻辑。这种灵活性使得代码更清晰、更模块化,同时方便扩展和修改函数的行为。装饰器的使用减少了重复代码,提高了代码的可维护性和可读性。
来源:harvey的网络日志