Last active
January 13, 2025 03:29
-
-
Save Ryaang/88693c067941f4628a3d3a7bb10f3db1 to your computer and use it in GitHub Desktop.
支持同步和异步函数的持久化缓存装饰器,用于缓存计算结果,避免重复计算。缓存结果存储在指定目录中,后续调用时直接读取。自动适配同步和异步函数。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from diskcache import Cache | |
| import functools | |
| import asyncio | |
| from typing import Any, Callable, TypeVar, Union, Awaitable | |
| import time | |
| import logging | |
| import hashlib | |
| import json | |
| T = TypeVar('T') | |
| def persistent_cache( | |
| cache_dir: str = '.cache', | |
| expire: int = None, # 过期时间(秒) | |
| tag: str = None, # 缓存标签 | |
| max_size: int = 2 * 1024 * 1024 * 1024, # 2GB | |
| eviction_policy: str = 'least-recently-used', | |
| verbose: bool = False | |
| ): | |
| """持久化缓存装饰器,支持同步和异步函数 | |
| Args: | |
| cache_dir: 缓存文件存储目录 | |
| expire: 缓存过期时间(秒) | |
| tag: 缓存标签,用于批量管理缓存 | |
| """ | |
| # 创建缓存实例 | |
| cache = Cache(cache_dir, size_limit=max_size) | |
| def decorator(func: Callable[..., Union[T, Awaitable[T]]]) -> Callable[..., Union[T, Awaitable[T]]]: | |
| """实际的装饰器函数""" | |
| def make_cache_key(*args, **kwargs): | |
| """生成缓存键的哈希值""" | |
| def serialize_arg(arg): | |
| """序列化参数""" | |
| if isinstance(arg, (dict, list, set)): | |
| return json.dumps(arg, sort_keys=True) | |
| return str(arg) | |
| # 构建键的组成部分 | |
| key_parts = [ | |
| func.__name__, # 函数名 | |
| tuple(serialize_arg(arg) for arg in args), # 位置参数 | |
| frozenset((k, serialize_arg(v)) for k, v in sorted(kwargs.items())) # 关键字参数 | |
| ] | |
| # 将键转换为字符串并编码 | |
| key_str = json.dumps(str(key_parts), sort_keys=True) | |
| # 使用 SHA-256 生成哈希 | |
| hash_obj = hashlib.sha256(key_str.encode('utf-8')) | |
| return hash_obj.hexdigest() | |
| @functools.wraps(func) | |
| def sync_wrapper(*args, **kwargs) -> T: | |
| """同步函数的包装器""" | |
| # 使用函数名和参数作为键 | |
| key = make_cache_key(*args, **kwargs) | |
| # 尝试获取缓存 | |
| result = cache.get(key) | |
| if result is not None: | |
| if verbose: | |
| logging.debug(f"Cache hit for: {func.__name__}") | |
| return result | |
| # 执行函数并缓存结果 | |
| result = func(*args, **kwargs) | |
| cache.set(key, result, expire=expire, tag=tag) | |
| return result | |
| @functools.wraps(func) | |
| async def async_wrapper(*args, **kwargs) -> T: | |
| """异步函数的包装器""" | |
| key = make_cache_key(*args, **kwargs) | |
| try: | |
| result = cache.get(key) | |
| if result is not None: | |
| if verbose: | |
| logging.debug(f"Cache hit for: {func.__name__} (key: {key[:8]}...)") | |
| return result | |
| result = await func(*args, **kwargs) | |
| try: | |
| cache.set(key, result, expire=expire, tag=tag) | |
| except Exception as e: | |
| logging.warning(f"Failed to cache result for key {key[:8]}...: {e}") | |
| return result | |
| except Exception as e: | |
| logging.error(f"Cache error for key {key[:8]}...: {e}") | |
| return await func(*args, **kwargs) | |
| return async_wrapper if asyncio.iscoroutinefunction(func) else sync_wrapper | |
| return decorator | |
| if __name__ == "__main__": | |
| # 异步函数 | |
| @persistent_cache(cache_dir='.pcache') | |
| async def expensive_function(a: str, b: str) -> str: | |
| return a + b | |
| # 运行并计时 | |
| import time | |
| start_time = time.time() | |
| result = asyncio.run(expensive_function("a"*10000, "b"*10000)) | |
| end_time = time.time() | |
| print(f"Time taken: {end_time - start_time} seconds") | |
| # print(result) |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
持久化缓存装饰器
这是一个支持同步和异步函数的持久化缓存装饰器,用于缓存函数计算结果,避免重复计算,提升性能。缓存结果会被存储在指定目录中,后续调用时直接从缓存读取。
核心功能:
使用方法:
@persistent_cache(cache_dir='.pcache')装饰需要缓存的函数。await调用;对于同步函数,正常调用即可。示例:
适用场景:
注意事项: