[Python-ideas] return value of yield expressions

Jacob Holm jh at improva.dk
Tue Sep 13 13:02:16 CEST 2011


On 2011-09-13 05:24, H. Krishnan wrote:
> Hi,
> I should start off by saying that my knowledge of PEP342 is less than
> a week old, and I cannot claim any use-case for what I am about to
> propose.

That's OK.  Welcome to the wonderful world of PEP342 generators :)

> The PEP itself says "In effect, a yield-expression is like an inverted
> function call; the argument to yield is in fact returned (yielded)
> from the currently executing function, and the "return value" of yield
> is the argument passed in via send()."
> One could, I guess, wrap a function to be 'called' via this mechanism:
> def genwrap(func):
>    def gen():
>        ret = None
>        while True:
>            args, kwds = (yield ret)
>            ret = func(*args, **kwds)
>            del args, kwds
>    g = gen()
>    g.next()
>    return g
> f = genwrap(func)
> f.send( (args, kwds) )
> However, 'send' takes only one argument (and hence the poor syntax in
> the last statement).
> Has the option of the return value of the yield expression treated
> very much like function arguments been discussed?
> (a1, a2, a3 = default, *args, **kwds) = (yield ret)
> 'send' could support positional and keyword arguments.
> I guess this particular form would break backward compatibility but
> there might be other alternatives.

There are really two independent proposals here.  I'll call them
"function argument unpacking" and "allow arbitrary arguments to send".

To start with the latter, I don't think that is going to fly.  All
arguments to send must be returned as a single value by yield or cause
most of the examples in the PEP380 discussion to fail.  Allowing
arbitrary arguments to send would have to change that value in a
backward-incompatible way when sending a single argument.

However, you don't really need to change send itself to improve the
situation.  A small helper function like:

def send(*args, **kwds):
    if not args:
        raise TypeError(
            'send() takes at least 1 positional argument (0 given)'
    return args[0].send((args[1:], kwds))

would turn your "f.send( (args, kwds) )" into "send(f, *args, **kwds)",
which is already much nicer syntax.  I would be +0 on adding such a
helper as builtin, similar to the "next" builtin.

Your other proposal is really independent of generators I think.  I too
would like to see a way to do "function argument unpacking".

args, kwds = (yield ret)  # any expression really
(a1, a2, a3, *args), kwds = (lambda a1,a2,a3=default,*args, **kwds:
                             (a1,a2,a3)+args, kwds
                            )(*args, **kwds)

is about the shortest way I can come up with that works today, and that
is way too much repetition for my taste.  If locals() were writable (a
change that is unlikely to happen btw), this could be reduced to:

args, kwds = (yield ret)  # any expression really
    (lambda a1,a2,a3=default,*args,**kwds:locals())(*args, **kwds)

which would avoid some of the duplication but is really not that more

Your version seems like a nice extension of the regular tuple unpacking
for the special case of (args, kwds) tuples.  The main problem is how to
distinguish the normal tuple unpacking from the new form.  E.g. does

   a, b = (), {'a':1, 'b':2}

result in a==() and b=={'a':1, 'b':2} or in a==1 and b==2?  (it would
have to be the first, for obvious backward-compatibility reasons)

A possible solution would be:

   *(a1, a2, a3 = default, *args, **kwds) = (yield ret)

Ie, let '*(argument list) = args, kwds' be the syntax for "function
argument unpacking".

I am not sure if this is parseable within the constraints that we have
for the python parser, but I think it would be.  It is currently invalid
syntax, so should be backwards compatible.

To summarize

-1 to "allow arbitrary arguments to generator.send".
+0 to adding a builtin "send" helper function as described above.
+1 for "function argument unpacking".

Best regards

- Jacob

More information about the Python-ideas mailing list