python之asyncio协程的回调函数返回值如何获取
import asyncioimport random
async def rnd(sleep_time):
await asyncio.sleep(sleep_time)
ret=random.randint(1,6)
print("from rnd的结果是",ret,type(ret))
return ret
def ret(x):
result = x.result()
print("收到参数为>>>>",result,type(result))
return result+10
async def main():
a1=obj.create_task(rnd(1))
a2=asyncio.ensure_future(rnd(2))
#问题a1 a2的返回值传入给ret函数,那么ret函数如果有返回值该怎么获取
b1=a1.add_done_callback(ret)
b2=a2.add_done_callback(ret)
done,pending =await asyncio.wait()
print(b1,b2)# 输出结果为 None None
obj=asyncio.get_event_loop()
obj.run_until_complete(main())
print("over")
想了解下如果回调函数也有返回值,那么我该如何获取.上面的b1 b2就是我要接受回调函数的变量,但是输出却是None.请各位大神指点
print(a1.result(), a2.result()) grekevin 发表于 2022-8-31 13:26
那个不是回调函数的返回值.
我要ret这个函数的返回值 学习一下 ```
import asyncio
async def coroutine_example():
await asyncio.sleep(1)
return 'zhihu ID: Zarten'
coro = coroutine_example()
loop = asyncio.get_event_loop()
task = loop.create_task(coro)
print('运行情况:', task)
try:
print('返回值:', task.result())
except asyncio.InvalidStateError:
print('task状态未完成,捕获了 InvalidStateError 异常')
loop.run_until_complete(task)
print('再看下运行情况:', task)
print('返回值:', task.result())
loop.close()
```
可以参考一下这个,另外,建议了解一下`coroutine`和`task`的区别
我之前看b站高天的视频,大概记录了一些
## 视频链接
asyncio的理解与入门,搞不明白协程?看这个视频就够了。
- https://b23.tv/yxQCWwb
await机制详解。再来个硬核内容,把并行和依赖背后的原理全给你讲明白
- https://b23.tv/G0bvKRk
# asyncio的理解与入门
## coroutine和task
### coroutine
async def的函数是coroutine func。
async def的函数单纯被call,会变成coroutine object,但只是单纯被call,会报错。
### coroutine的执行方式
coroutine object可以通过以下方式被正常运行
- 直接`await coroutine_obj`
- 先`create_task(coroutine_obj)`
- 将coroutine_object变成task
- 然后再`await task`
`await gather(*coroutine_objs)`
- 这里也可以传入单个coroutine object。
### coroutine和task的关系
`create_task`**显式**地将coroutine object变成task
`gather`**隐式**地将coroutine object变成task。
而直接`await`一个coroutine_object,则**不会产生**task,类似立即调用了生成器(同步执行),不会将控制权交还给event loop。
## 整个asyncio的执行方式
### event loop
另外`asyncio.run(coroutine_func)`其实是先创建了一个`event loop`,由`event loop`来控制task的执行。
- `asyncio.run(coroutine_func)`,就会产生一个task。
- event loop调度的时候最小单位是一个task。
- event loop无法直接执行一个coroutine_obj。
### 直接await
直接`await coroutine_obj`会导致`event loop`逐个发现task的时候,**直接同步执行这步代码**,不会将控制权交还给event loop。
```python
import time
import asyncio
async def nap(delay:int):
await asyncio.sleep(delay)
return f"after {delay:d} second nap finished"
async def main():
start_time = time.time()
ret = await nap(1)
ret2 = await nap(2)
print(ret)
print(ret2)
print(f"time cost: {time.time()-start_time:.1f}s")
asyncio.run(main())
```
run result
```
after 1 second nap finished
after 2 second nap finished
time cost: 3.0s
```
### create_task配合await
而通过create_task,再**逐个**await task,可以**异步执行**。
```python
import time
import asyncio
async def nap(delay:int):
await asyncio.sleep(delay)
return f"after {delay:d} second nap finished"
async def main():
start_time = time.time()
task = asyncio.create_task(nap(1))
task2 = asyncio.create_task(nap(2))
ret = await task
ret2 = await task2
print(ret)
print(ret2)
print(f"time cost: {time.time()-start_time:.1f}s")
asyncio.run(main())
```
run result
```
after 1 second nap finished
after 2 second nap finished
time cost: 2.0s
```
### gather
`await gather(coroutine_objs)`,也可以**异步执行**。
```python
import time
import asyncio
async def nap(delay:int):
await asyncio.sleep(delay)
return f"after {delay:d} second nap finished"
async def main():
start_time = time.time()
rets = await asyncio.gather(
nap(1),
nap(2),
)
ret, ret2 = rets
print(ret)
print(ret2)
print(f"time cost: {time.time()-start_time:.1f}s")
asyncio.run(main())
```
run result
```
after 1 second nap finished
after 2 second nap finished
time cost: 2.0s
```
### 总结
直接`await coutine_obj`,会导致这步代码**同步执行**。
「create_task配合await」和「gather」,可以**异步执行**,大致逻辑是。
- 会让`event loop`先收到外层的tasks。
- 在等待时,逐个发现是否还有其他task可执行。有点类似树结构,保证子节点们执行完,才能去执行父节点,实现异步执行。
### 补充asyncio.wait
相比于`gather`, `wait`可以
- 返回已完成的任务和待完成的任务,这两个序列。
- 而且传入对象必须为task的序列,不能为生成器,也不能为单个task。
- 不过如下方示例,单个task的序列是可以的。
```python
import time
import random
import asyncio
async def foo(num:int):
return num
async def main():
task = asyncio.create_task(foo(random.randint(0,100)))
dones, pendings = await asyncio.wait() # 必须是一个task的序列,不能是单个task。
# 这里说明dones,其实是完成的task
if task in dones:
result = task.result()
print(result)
print('='*30)
tasks =
dones, pendings = await asyncio.wait(tasks)
for task in dones:
print(task.result())
asyncio.run(main())
```
# await机制详解
await后面可以是coroutine object、task、future这三种。
```python
import asyncio
async def main():
await asyncio.sleep(1)
dis.dis(main())
```
run result
```
98 0 LOAD_GLOBAL 0 (asyncio)
2 LOAD_METHOD 1 (sleep)
4 LOAD_CONST 1 (1)
6 CALL_METHOD 1
8 GET_AWAITABLE
10 LOAD_CONST 0 (None)
12 YIELD_FROM
14 POP_TOP
16 LOAD_CONST 0 (None)
18 RETURN_VALUE
```
其中主要部分
```
8 GET_AWAITABLE
10 LOAD_CONST 0 (None)
12 YIELD_FROM
```
async里,当return一个值的时候,其机制类似generator。
- return,其实等同`raise StopIteration`,return的值,则设置为`StopIteration`的`Exception`的value
```python
try:
raise StopIteration('Value here')
except Exception as e:
print(e) # Value here
``` 本帖最后由 QQending 于 2022-8-31 14:39 编辑
```
import asyncio
import random
async def rnd(sleep_time):
await asyncio.sleep(sleep_time)
ret=random.randint(1,6)
print("from rnd的结果是",ret,type(ret))
return ret
def ret(x):
result = x.result()
print("收到参数为>>>>",result,type(result))
return result+10
async def main():
task = asyncio.create_task(rnd(1))
task2 =asyncio.create_task(rnd(2))
#问题a1 a2的返回值传入给ret函数,那么ret函数如果有返回值该怎么获取
dones,pendings =await asyncio.wait()
for task in dones:
print(task.result())# 输出结果为 6 1
print('over!')
asyncio.run(main())
```
我改成high-level API版本以后,你看一下。
或者
```
import asyncio
import random
from asyncio import ALL_COMPLETED
async def rnd(sleep_time):
await asyncio.sleep(sleep_time)
ret=random.randint(1,6)
print("from rnd的结果是",ret,type(ret))
return ret
def ret(x):
result = x.result()
print("收到参数为>>>>",result,type(result))
return result+10
async def main():
task = asyncio.create_task(rnd(1))
future =asyncio.ensure_future(rnd(2))
dones,pendings =await asyncio.wait(, return_when=ALL_COMPLETED)
for task in dones:
print(task.result())# 输出结果为 2 3
print('over!')
asyncio.run(main())
```
QQending 发表于 2022-8-31 14:33
```
import asyncio
import random
感谢热心解答.不过可能我的问题没描述清楚.
我有一个协程函数rnd和一个普通函数ret. 用add_done_callback让ret接收rnd的返回值,然后加10以后再进行返回.
rnd的协程函数的返回值是 1-6
ret函数的返回值是11-16
我想获取的是ret的返回值,所以只是从返回的数字就看得出上面只是获取了rnd协程函数的返回值.但是并没有我要的ret普通函数的返回值 参考下这个代码
import time
import asyncio
async def run(url):
print("开始向'%s'要数据……"%(url))
# 向百度要数据,网络IO
await asyncio.sleep(5)
data = "'%s'的数据"%(url)
print("给你数据")
return data
# 定义一个回调函数
def call_back(future):
print("call_back:", future.result())
coroutine = run("百度")
# 创建一个任务对象
task = asyncio.ensure_future(coroutine)
# 给任务添加回调,在任务结束后调用回调函数
task.add_done_callback(call_back)
loop = asyncio.get_event_loop()
loop.run_until_complete(task) 我爱猫哥 发表于 2022-8-31 15:54
参考下这个代码
import time
import asyncio
这个代码其实和我写的一样都是利用add_done_callback在协程函数运行完毕后传值给call_back函数.运行print是没问题.但是如果把你的call_back最后return一个值的话.就获取不到. 这样写执行效果是一样的
import asyncio
import random
async def rnd(sleep_time):
await asyncio.sleep(sleep_time)
ret = random.randint(1, 6)
print("from rnd的结果是", ret, type(ret))
return get_reuslt(ret)
def get_reuslt(x):
print("收到参数为>>>>", x, type(x))
return x + 10
async def main():
a1 = loop.create_task(rnd(1))
a2 = asyncio.ensure_future(rnd(2))
done, pending = await asyncio.wait()
print(a1.result(), a2.result())
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
print("over")
页:
[1]
2