<jupyter_start><jupyter_text>Yield From - Closing and Return Just as we can send `next` and `send` through a delegator, we can also send `close`.How does this affect the delegator and the subgenerator?Let's take a look.<jupyter_code>def subgen():
    try:
        while True:
            received = yield
            print(received)
    finally:
        print('subgen: closing...')
def delegator():
    s = subgen()
    yield from s
    yield 'delegator: subgen closed'
    print('delegator: closing...')
d = delegator()
next(d)<jupyter_output><empty_output><jupyter_text>At this point, both the delegator and the subgenerator are primed and suspended:<jupyter_code>from inspect import getgeneratorstate, getgeneratorlocals
getgeneratorlocals(d)
s = getgeneratorlocals(d)['s']
print(getgeneratorstate(d))
print(getgeneratorstate(s))<jupyter_output>GEN_SUSPENDED
GEN_SUSPENDED<jupyter_text>We can send data to the delegator:<jupyter_code>d.send('hello')<jupyter_output>hello<jupyter_text>We can even send data directly to the subgenerator since we now have a handle on it:<jupyter_code>s.send('python')<jupyter_output>python<jupyter_text>In fact, we can close it too:<jupyter_code>s.close()<jupyter_output>subgen: closing...<jupyter_text>So, what is the state of the delegator now?<jupyter_code>getgeneratorstate(d)<jupyter_output><empty_output><jupyter_text>But the subgenerator closed, so let's see what happens when we call `next` on `d`:<jupyter_code>next(d)<jupyter_output><empty_output><jupyter_text>As you can see, the generator code resume right after the `yield from`, and we can do this one more time to close the delegator:<jupyter_code>next(d)<jupyter_output>delegator: closing...<jupyter_text>OK, so this is what happens when the subgenerator closes (directly or indirectly) - the delegator simply resumes running right after the `yield from` when we call `next`.But what happens if we close the delegator instead of just closing the subgenerator?<jupyter_code>d = delegator()
next(d)
s = getgeneratorlocals(d)['s']
print(getgeneratorstate(d))
print(getgeneratorstate(s))
d.close()<jupyter_output>subgen: closing...<jupyter_text>As you can see the subgenerator also closed. Is the delegator closed too?<jupyter_code>print(getgeneratorstate(d))
print(getgeneratorstate(s))<jupyter_output>GEN_CLOSED
GEN_CLOSED<jupyter_text>Yes. So closing the delegator will close not only the delegator itself, but also close the currently active subgenerator (if any). We should notice that when we closed the subgenerator directly no apparent exception was raised in our context.What happens if the subgenerator returns something when it closes?<jupyter_code>def subgen():
    try:
        while True:
            received = yield
            print(received)
    finally:
        print('subgen: closing...')
        return 'subgen: return value'
s = subgen()
next(s)
s.send('hello')
s.close()<jupyter_output>hello
subgen: closing...<jupyter_text>Hmmm, the `StopIteration` exception was silenced. Let's do this a different way, since we know the `StopIteration` exception should contain the return value:<jupyter_code>s = subgen()
next(s)
s.send('hello')
s.throw(GeneratorExit, 'force exit')<jupyter_output>hello
subgen: closing...<jupyter_text>OK, so now we can see that the `StopIteration` exception contains the return value.The `yield from` actually captures that value as it's return value - in other words `yield from` is not just a statement, it is in fact, like `yield`, also an expression.Let's see how that works:<jupyter_code>def subgen():
    try:
        yield 1
        yield 2
    finally:
        print('subgen: closing...')
        return 100
def delegator():
    s = subgen()
    result = yield from s
    print('subgen returned:', result)
    yield 'delegator suspended'
    print('delegator closing')
d = delegator()
next(d)
next(d)
next(d)<jupyter_output>subgen: closing...
subgen returned: 100
{'fraction_non_alphanumeric': 0.0986959856814114, 'fraction_numerical': 0.002045512656609563, 'mean_word_length': 5.08398133748056, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '93884', 'license': 'Apache-2.0', 'n_tokens_mistral': 1259, 'n_tokens_neox': 1234, 'n_words': 397, 'path': 'dd_1/Part 2/Section 12 - Generator Based Coroutines/10 - Yield From - Closing and Return.ipynb', 'repo_name': 'rebekka-halal/bg', 'size': '3911'}