我需要一个新的处理程序,该处理程序使用另一个现有的处理程序来一次处理多个请求:
async def process_multiple_queries(request):
init_request = request.clone()
body = await request.json()
for query in body["queries"]:
@asyncio.coroutine
def fake_json(self):
return query
# Replace request body on new fake json
R = init_request.clone()
R.json = fake_json.__get__(R, Request)
# Process request in the background task
asyncio.ensure_future(process_query(R))
async def process_query(request):
body = await request.json()
# ... Process body ...
return web.json_response({"result": "OK"})
但是,所有计划任务的结果都等于上一个任务的结果,即process_query
中传递的所有请求的主体仅等于上一个请求的主体。
但是,如果我在asyncio.sleep(5)
之后添加asyncio.ensure_future(process_query(R))
,则所有任务都会得到正确处理并产生不同的结果。
如何解决?
答案 0 :(得分:0)
这是well-known Python陷阱,其中的闭包按名称而不是按值捕获变量。结果,fake_json
协程的所有实例都引用相同的query
变量,因此最终都引用了它的 last 已知值。
要解决此问题,您需要更改定义:
@asyncio.coroutine
def fake_json(self):
return query
表示捕获query
的值的公式,例如:
@asyncio.coroutine
def fake_json(self, query=query):
return query
await asyncio.sleep(5)
之所以有用,是因为它允许process_query
在query
的值从脚下改变之前运行到完成。与await process_query(R)
会得到相同的效果。
与问题无关的两点:
@asyncio.coroutine
装饰器是deprecated,并且没有理由在新代码中使用它(除非您特别希望与3.5之前版本的Python兼容)。相反,只需使用fake_json
定义async def
。
您无需调用__get__
即可将self
绑定到fake_json
,甚至没有使用它。您可以定义一个不fake_json
的{{1}},而只需分配self
。