我正在使用PyMemoize库来缓存协程。我修饰了协程,但当 Python 调用它时,我得到:
PyMemoize
TypeError: can't pickle coroutine objects
发生这种情况是因为PyMemoize内部尝试 pickle 协程并将其存储在 Redis 中。为此,它使用shelve.Shelf,而后者又使用pickle。问题是,由于未知原因,pickle不支持 pickle 协程。
shelve.Shelf
pickle
我尝试使用 pickle 来处理协程,dill并且成功了。如何告知shelve将其用作dill序列化后端?
dill
shelve
我尝试过 monkey-patch shelve,但是没有用(我不知道为什么):
import shelve from dill import Pickler, Unpickler shelve.Pickler = Pickler shelve.Unpickler = Unpickler
在 Python 的标准库中,shelve 模块是基于 pickle 的,因此默认情况下它只支持 pickle。但是,shelve 可以通过自定义 pickle 类来更改其序列化行为。这意味着你可以替换 pickle 的 Pickler 和 Unpickler,例如使用 dill,从而支持协程等不受 pickle 支持的对象。
Pickler
Unpickler
以下是解决方案步骤:
你需要定义一个自定义的 Shelf 子类,明确使用 dill 的 Pickler 和 Unpickler:
Shelf
import shelve import dill class DillShelf(shelve.Shelf): def __init__(self, filename, flag='c', protocol=None, writeback=False): super().__init__(dill.open(filename, flag), protocol=protocol, writeback=writeback) # 用法示例 with DillShelf('cache.db') as db: async def my_coroutine(): return "Hello, coroutine!" db['my_coroutine'] = my_coroutine # DillShelf 支持存储协程 print(db['my_coroutine']) # 输出协程对象
如果你希望在 PyMemoize 中直接替换默认的 shelve 序列化,可以通过配置来完成。以下是一个示例实现:
import dill from pymemoize.decorators import memoize # 自定义序列化与反序列化方法 def dill_serializer(obj): return dill.dumps(obj) def dill_deserializer(data): return dill.loads(data) # 在 `memoize` 中注册 @memoize(serializer=dill_serializer, deserializer=dill_deserializer) async def my_coroutine(x): return x * 2 # 测试 import asyncio async def main(): result = await my_coroutine(5) # 缓存调用 print(result) # 输出 10 asyncio.run(main())
这种方法可以避免直接 monkey-patch 标准库,同时保持代码的灵活性和可维护性。