yeild from 后面的函数应该返回什么?

用asyncio实现了一个协程:

import threading
import asyncio

@asyncio.coroutine
def hello():
    print('Hello world! (%s)' % threading.currentThread())
    print(1)
    # asyncio.sleep(1)返回None
    r = yield from asyncio.sleep(1)
    print('Hello again! (%s)' % threading.currentThread())
    print(2)

loop = asyncio.get_event_loop()
tasks = [hello(), hello()]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

输出:

Hello world! (<_MainThread(MainThread, started 6404)>)
1
Hello world! (<_MainThread(MainThread, started 6404)>)
1
Hello again! (<_MainThread(MainThread, started 6404)>)
2
Hello again! (<_MainThread(MainThread, started 6404)>)
2

可以看到是同一个进程运行了两个协程,并且asyncio.sleep(1)这个函数模拟了一个一秒的IO等待,这就很迷了,我理解的是,程序运行到yeild from 就运行asyncio.sleep(1)这个函数,然后等待IO返回,并去运行了另外一个hello(),待asyncio.sleep(1)运行完毕又接着运行hello()接下来的代码
我就想了:

**可不可以自己写个asyncio.sleep(1)函数啊,返回自己定义的值,赋值给r?**

但是并不是我想的那么简单,报错了,我写的如下:

import threading
import asyncio
import time

def foo():
    # 返回'1'
    time.sleep(1)
    return '1'

@asyncio.coroutine
def hello():
    print('Hello world! (%s)' % threading.currentThread())
    print(1)
    r = yield from foo()
    print('Hello again! (%s)' % threading.currentThread())
    print(2)

loop = asyncio.get_event_loop()
tasks = [hello(), hello()]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

报错如下:

Hello world! (<_MainThread(MainThread, started 6832)>)
1
Hello world! (<_MainThread(MainThread, started 6832)>)
1
Task exception was never retrieved
future: <Task finished coro=<hello() done, defined at test3.py:12> exception=RuntimeError("Task got bad yield: '1'",)>
Traceback (most recent call last):
  File "test3.py", line 16, in hello
    r = yield from foo()
RuntimeError: Task got bad yield: '1'
Task exception was never retrieved
future: <Task finished coro=<hello() done, defined at test3.py:12> exception=RuntimeError("Task got bad yield: '1'",)>
Traceback (most recent call last):
  File "test3.py", line 16, in hello
    r = yield from foo()
RuntimeError: Task got bad yield: '1'
    TypeError: 'int' object is not iterable

看提示是要返回一个iterable,改了一下:

def foo():
    # 返回一个iter
    time.sleep(1)
    return iter('1')

报错:

Hello world! (<_MainThread(MainThread, started 6576)>)
1
Hello world! (<_MainThread(MainThread, started 6576)>)
1
Task exception was never retrieved
future: <Task finished coro=<hello() done, defined at test3.py:12> exception=RuntimeError("Task got bad yield: '1'",)>
Traceback (most recent call last):
  File "test3.py", line 16, in hello
    r = yield from foo()
RuntimeError: Task got bad yield: '1'
Task exception was never retrieved
future: <Task finished coro=<hello() done, defined at test3.py:12> exception=RuntimeError("Task got bad yield: '1'",)>
Traceback (most recent call last):
  File "test3.py", line 16, in hello
    r = yield from foo()
RuntimeError: Task got bad yield: '1'

看不懂RuntimeError: Task got bad yield: '1',求大佬解释

帮我写个

def foo():
    ??????

要是我的理解有错也欢迎指出,谢谢各位

20200629更新
以前只是为了学习看了协程, 这个东西被吹的的说的效率多高, 实际也没见谁真的使用, 而且python协程还在更新, 虽然有asyncio模块, 但是各种装饰器, 关键词的写法很多, 感觉比较混乱, 听说在python 3.10下会把装饰器的写法给删除

然后还有就是, 吹啊, 到处说啥python协程神器, 怎么快啊什么的, 我看的教程基本全是用

asyncio.sleep(1)

然后其他比如网络请求, 用的是专门为协程设计的网络请求包, 比如什么

connect = asyncio.open_connection(host, 80)

还有什么

aiohttp 包

对, 都可以用, 只是比如你以前用的request的程序, 如果用多线程, 多进程, 那么直接套就可以了, 但是如果你想用协程, 对不起, 没有, 你得自己去找, 或者自己写。

所以说我选择暂时不去深究, 毕竟性能够, 而且感觉把线程换协程效率也没提升很多, 且容易出一些未知的错误, 毕竟我才疏学浅, 有点不懂。

所以说协程这东西到底是用来干嘛的? 我只是觉得太费劲了, 不值得学这个东西

可能话说的有点重了, 欢迎沟通交流

阅读 4.8k
1 个回答

你既然在异步框架用了协程,在hello前有异步装饰器,那么你在yiled from后面跟的这个函数必须要有框架的异步装饰器进行装饰。就像如下:

@asyncio.coroutine
def foo():
    time.sleep(1)
    return "1"

想更深入地了解装饰器的作用,你就需要自己去搜搜资料学了。一般异步框架的话,只要这么装饰起来用就OK了。

推荐问题