Search code examples
pythonpython-3.xgeneratoryield

Valid, but weird use of yield inside list


I was playing with generators a bit, when I suddenly wrote this piece of code.

Although I have written it, I don't know why it is valid, or why it works.

>>> x = (lambda : [(yield 1), (yield 2)])()
>>> next(x)
1
>>> next(x)
2
>>> next(x)
StopIteration

Solution

  • You created a generator function, with the lambda syntax. yield is just another expression, so can be used inside a lambda.

    You basically did this:

    def foo():
        return [(yield 1), (yield 2)]
    
    x = foo()
    

    but all in a single expression.

    Both yield expressions return None, so the StopIteration result value is set to [None, None]:

    >>> x = (lambda : [(yield 1), (yield 2)])()
    >>> next(x), next(x)
    (1, 2)
    >>> try:
    ...     next(x)
    ... except StopIteration as si:
    ...     print(si.value)
    ...
    [None, None]
    

    Instead of using next(), you could use generator.send() to make the yield values return something other than None:

    >>> x = (lambda : [(yield 1), (yield 2)])()
    >>> next(x)  # advance to first yield
    1
    >>> x.send(42)   # set return value for first yield, continue to second
    2
    >>> try:
    ...     x.send(81)   # set return value for second yield
    ... except StopIteration as si:
    ...     print(si.value)
    ...
    [42, 81]
    

    It works, because it is legal Python syntax. It's not very useful however.