“yield from”可以与哪些对象一起使用?

Ale*_*xey 6 python generator coroutine python-3.x yield-from

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

我不知道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)))
Run Code Online (Sandbox Code Playgroud)

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

一般规则是什么?是什么决定了一个对象是否可以作为yield fromform的参数给出?

a_g*_*est 3

您可以检查 CPython 如何计算该语句。由此可见,它必须是协程或可迭代的:

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();
}
Run Code Online (Sandbox Code Playgroud)

  • @Alexey 当时协程和生成器之间没有真正的区别。正如 [3.4 文档所示](https://docs.python.org/3.4/library/asyncio-task.html#coroutines):*“协程是遵循某些约定的生成器。”*。不存在单独的协程类型。[PEP 492](https://www.python.org/dev/peps/pep-0492/) 描述了对“async”和“await”语法的更改,对于旧行为也提供了信息。[PEP 3156](https://www.python.org/dev/peps/pep-3156/) 还提供了有关该主题的信息(例如,在这些文档中搜索“yield from”,这是一个很长的阅读过程)。 (2认同)