[Python-Dev] Whatever happened to 'nonlocal x = y'?

Nathaniel Smith njs at pobox.com
Fri Jan 5 17:59:00 EST 2018


On Fri, Jan 5, 2018 at 7:47 AM, Guido van Rossum <guido at python.org> wrote:
> I don't recall (though someone with more time might find the discussion in
> the archives or on the tracker). It was never implemented and I think it
> shouldn't be. So we might as well update the PEP. It wouldn't be
> particularly useful, since (by definition) the function that declares the
> nonlocal variable is not its owner, and hence it's unlikely to make sense to
> initialize it here. The same reasoning applies to global BTW.

The reason I got curious and looked into it is that recently I've been
finding myself using it a lot for passing values back out of
concurrent functions (examples below). So it does have use cases, but
I agree that it's not clear how much value is really added by saving a
line here. Maybe in a year or two if this style catches on as
idiomatic then it'd be worth revisiting.

#######

Example: run several functions, return the value of the one that
finishes first (or non-deterministic if several finish at ~the same
time):

async def race(*async_fns):
    async with trio.open_nursery() as nursery:
        winning_value = None

        async def driver(async_fn):
            nonlocal winning_value
            winning_value = await async_fn()
            # we're done, so cancel competitors
            nursery.cancel_scope.cancel()

        for async_fn in async_fns:
            nursery.start_soon(driver, async_fn)

    return winner

#######

Example: an async iterator version of zip, with concurrent evaluation
of the different iterators (based on an idea from github user @matham:
https://github.com/python-trio/trio/issues/393):

async def async_zip(*aiterables):
    aiterators = [aiterable.__aiter__() for aiterable in aiterables]
    done = False
    while True:
        items = [None] * len(aiterators)

        async def fill_in(i):
            try:
                items[i] = await aiterators[i].__anext__()
            except StopAsyncIteration:
                nonlocal done
                done = True

        async with trio.open_nursery() as nursery:
            for i in range(len(aiterators)):
                nursery.start_soon(fill_in, i)

        if done:
            break

        yield tuple(items)

-n

-- 
Nathaniel J. Smith -- https://vorpus.org


More information about the Python-Dev mailing list