Yield From

With Python 3.3 and PEP 380 a new yield from statement got introduced that can be used to simplify chaining Iterators and Generators.

Chaining Iterators

Chaining Iterators without the new yield from statement.

def get_something_iterator():
    yield "foo"
    yield "bar"
    yield "baz"

def another_iterator():
    for i in get_something_iterator():
        yield i

for i in another_iterator():
    print(i)

Using the yield from statement this can be simplified to:

def get_something_iterator():
    yield "foo"
    yield "bar"
    yield "baz"

def another_iterator():
    yield from get_something_iterator()

for i in another_iterator():
    print(i)

Easy and straight forward. Just a simplification of the syntax.

Chaining Generators

Chaining Generators with their send, throw and close semantics is much more complicated and more prone for errors. A very simplified implementation without yield from could be:

def some_generator():
    sent = yield 0
    while sent >= 0:
        sent = yield sent
    return 'Done'

def another_generator():
    generator = some_generator()

    y = generator.send(None)

    while True:
        try:
            x = yield y
            y = generator.send(x)
        except GeneratorExit as e:
            # forward close
            generator.close()
            raise e
        except Exception as e:
            # forward throw
            generator.throw(e)

Using yield from is of course much simpler.

def some_generator():
    sent = yield 0
    while sent >= 0:
        sent = yield sent
    return 'Done'

def another_generator():
    return yield from some_generator()

Additional Semantics

But beside from the simplification it also got some additional semantically change regarding the return value.

RESULT = yield from EXPR
def some_generator():
    sent = yield 0
    while sent >= 0:
        sent = yield sent
    return 'Done'

def another_generator():
    generator = some_generator()
    try:
        y = generator.send(None)
    except StopIterator as e:
        result = e.value
    else:
        while True:
            try:
                x = yield y
                y = generator.send(x)
            except GeneratorExit as e:
                # forward close
                generator.close()
                raise e
            except Exception as e:
                # forward throw
                generator.throw(e)
            except StopIterator as e:
                result = e.value
                break
        return result

Because

y = yield from g(x)

looks much more familiar like a normal function call

y = f(x)

but shows that y is received from a coroutine.