【问题标题】:What kind of objects `yield from` can be used with?可以使用什么样的对象`yield from`?
【发布时间】:2020-09-06 21:40:34
【问题描述】:

最初 (PEP 380),yield from 语法被引入用于委托给“子生成器”。后来它与现在 deprecated 基于生成器的协程一起使用。

我不知道yield from 一般可以应用于什么样的对象。我的第一个猜想是它只需要对象上的__iter__ 方法来返回一个迭代器。事实上,以下内容适用于 Python 3.8:

class C:
    def __init__(self, n):
        self.n = n

    def __iter__(self):
        return iter(range(self.n))

def g(n):
    yield from C(n)

print(tuple(g(3)))

但是,它也适用于一些等待对象,例如 asyncio.sleep(1),它们没有 __iter__ 方法。

一般规则是什么?什么决定了一个对象是否可以作为yield from 表单的参数?

【问题讨论】:

  • 您走在正确的道路上,但也许yield from asyncio.sleep(1) 让您感到困惑。 yield from 是 Python 3.4 的语法等价于 Python 3.5 await。查看 Python 3.4 asyncio: 18.5.9.3. Concurrency and multithreading 的文档。
  • @Felipe,你的意思是yield from有两个完全不相关的意思吗?它只有两个,还是更多?在我给出的代码示例中,您不能将yield from 替换为await,因此它们并不总是等价的。
  • @Felipe,我看不到您指出我的问题的文档在哪里得到了回答。
  • 上面链接文档的第一段。 “一个事件循环在一个线程中运行,并在同一个线程中执行所有回调和任务。当一个任务在事件循环中运行时,没有其他任务在同一个线程中运行。但是当任务使用yield from时,该任务被挂起,事件循环执行下一个任务。”
  • 异步编程在 Python 中是一个完全不同的概念(不同于生成器)。在当今世界,您使用关键字 await 来利用异步函数,但在 3.4 之前,您将使用 yield from 而不是 await(这就是为什么您可能会看到 yield from 散布在不支持异步函数的代码中似乎有发电机)。

标签: python python-3.x generator coroutine yield-from


【解决方案1】:

您可以查看 CPython evaluates that statement 的方式。由此可见,它需要是协程或可迭代的:

case TARGET(GET_YIELD_FROM_ITER): {
    /* before: [obj]; after [getiter(obj)] */
    PyObject *iterable = TOP();
    PyObject *iter;
    if (PyCoro_CheckExact(iterable)) {
        /* `iterable` is a coroutine */
        if (!(co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) {
            /* and it is used in a 'yield from' expression of a
               regular generator. */
            Py_DECREF(iterable);
            SET_TOP(NULL);
            _PyErr_SetString(tstate, PyExc_TypeError,
                             "cannot 'yield from' a coroutine object "
                             "in a non-coroutine generator");
            goto error;
        }
    }
    else if (!PyGen_CheckExact(iterable)) {
        /* `iterable` is not a generator. */
        iter = PyObject_GetIter(iterable);
        Py_DECREF(iterable);
        SET_TOP(iter);
        if (iter == NULL)
            goto error;
    }
    PREDICT(LOAD_CONST);
    DISPATCH();
}

【讨论】:

  • 那么,真的有两个不相关的意思……你知道是否一直如此,或者历史上所有的协程,包括asyncio.sleep,都有__iter__方法?
  • @Alexey 那时协程和生成器之间并没有真正的区别。正如3.4 docs read“协程是遵循某些约定的生成器。”。不存在单独的协程类型。 PEP 492 描述了对 asyncawait 语法的更改,它也提供了关于旧行为的信息。 PEP 3156 还提供了有关该主题的信息(例如,在这些文档中搜索 yield from,这是一篇长篇大论)。
  • 我想看看如何使用生成器实现并发,但显然为时已晚,因为实现发生了变化。我想我必须降级到 Python 3.3。
猜你喜欢
  • 2018-02-22
  • 2016-06-01
  • 2016-07-06
  • 1970-01-01
  • 2016-01-07
  • 2023-01-25
  • 1970-01-01
  • 2015-07-28
  • 2015-08-08
相关资源
最近更新 更多