Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What kind of objects `yield from` can be used with?

Initially (PEP 380), yield from syntax was introduced to be used for delegating to a "subgenerator." Later it was used with now deprecated generator-based coroutines.

I cannot find out what kind of objects yield from can be applied to in general. My first conjecture was that it only requires __iter__ method on the object to return an iterator. Indeed, the following works with 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)))

However, it also works with some awaitables, like asyncio.sleep(1), which do not have __iter__ method.

What is the general rule? What determines if an object can be given as an argument to yield from form?

like image 636
Alexey Avatar asked May 20 '20 13:05

Alexey


People also ask

What is the use of yield keyword in JavaScript?

In simpler words, the yield keyword will convert an expression that is specified along with it to a generator object and return it to the caller. Hence, if you want to get the values stored inside the generator object, you need to iterate over it. It will not destroy the local variables’ states.

What happens when a yield method returns an IEnumerable type object?

When a yield method returns an IEnumerable type object, this object implements both IEnumerable and IEnumerator. Casting this object to IEnumerator produces a generator that is useless until the GetEnumerator method is called. At the same time, if a generator seems ‘dead’, it may suddenly start working after the GetEnumerator method call.

What happens when you use the yield keyword inside a generator?

When you use a yield keyword inside a generator function, it returns a generator object instead of values. In fact, it stores all the returned values inside this generator object in a local state. If you have used the return statement, which returned an array of values, this would have consumed a lot of memory.

When to use yield in Python?

Methods that use yield can really simplify your life sometimes. Behind this magic exists an entire class the compiler generated, which is why I recommend you use the yield feature only when it is significantly more convenient that, for example, LINQ.


1 Answers

You can check how CPython evaluates that statement. From this follows it needs to be either a coroutine or an iterable:

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();
}
like image 63
a_guest Avatar answered Nov 10 '22 07:11

a_guest