[Python-ideas] x=(yield from) confusion [was:Yet another alternative name for yield-from]

Jacob Holm jh at improva.dk
Wed Apr 8 17:04:34 CEST 2009


Jacob Holm wrote:
> Even assuming every relevant object implemented the pattern I suggest, 
> it is still not possible to use yield-from to write something like 
> itertools.dropwhile and have it delegate all send and throw calls 
> correctly. To make that possible, you need exactly the same thing that 
> you need for pre-started coroutines: The ability to replace the next() 
> call made by the yield-from expression with something else. Give me 
> that, and you will also have removed the need for a special pattern 
> for coroutines that should be usable with yield-from.

To be clear, I think the best way of handling this is to add a read-only 
property to generator objects holding the latest value yielded, and let 
yield-from use that when present instead of calling next(). (This is not 
a new idea, I am just explaining the consequences as I see them). The 
property can be cleared when the frame is released, so there should be 
no issues with that.

With that property, the dropwhile example becomes trivial:

def dropwhile(predicate, iterable):
    it = iter(iterable)
    v = next(it)
    while predicate(v):
        v = next(it)
    return yield from it  # Starts by yielding the last value checked, which is v.


More interesting (to me) is that the following helpers allow you to call 
a pre-started generator using yield-from in the 3 special ways I 
mentioned *without* needing the generator constructor to take any magic 
arguments.

def first_yielding(value, iterable):
    it = iter(iterable)
    try:
        s = yield value
    except GeneratorExit:
        it.close()
    except BaseException as e:
        it.throw(e) # sets the property so yield-from will use that first
    else:
        it.send(s)  # sets the property so yield-from will use that first
    return yield from it

def first_sending(value, iterable):
    it = iter(iterable)
    it.send(value)  # sets the property so yield-from will use that first
    return yield from it

def first_throwing(exc, iterable):
    it = iter(iterable)
    it.throw(exc)   # sets the property so yield-from will use that first
    return yield from it


# Yield None (first value yielded by a @coroutine) as first value
yield from example(*args, **kwargs)

# Yield 42 as first value
yield from first_yielding(42, example(*args, **kwargs))

# Treat the first next() as a send(42)
yield from first_sending(42, example(*args, **kwargs))

# Treat the first next() as a throw(ValueError(42))
yield from first_throwing(ValueError(42), example(*args, **kwargs))


So no new syntax needed, and coroutines are easily callable without the 
constructor needing extra magic arguments. Also, I am sure the property 
has other unrelated uses. What's not to like?


- Jacob




More information about the Python-ideas mailing list