[Python-ideas] PEP 380 alternative: A yielding function

Guido van Rossum guido at python.org
Tue Jul 27 20:01:12 CEST 2010


Have you really thought through the implementation in CPython? When a
non-generator function is called, there's usually a C stack frame in
the way which prevents yielding.

--Guido

On Tue, Jul 27, 2010 at 6:18 PM, Anders J. Munch <2010 at jmunch.dk> wrote:
> Looking at PEP 380 (http://www.python.org/dev/peps/pep-0380/), the
> need for yield forwarding syntax comes from the inability to delegate
> yielding functionality in the usual way. For example, if you have a
> recurring pattern including yields, like this (this is a toy example,
> please don't take it for more than that):
>
>  if a:
>      yield a
>  if b:
>      yield b
>
> you cannot do the extract function refactoring in the usual way:
>
>     def yield_if_true(x):
>      if x:
>          yield x
>  yield_if_true(a)
>  yield_if_true(b)
>
> because yield_if_true would become a generator.
>
> PEP 380 addresses this by making the workaround - "for x in
> yield_if_true(a): yield x" - easier to write.
>
> But suppose you could address the source instead?  Suppose you could
> write yield_if_true in such a way that it did not become a generator
> despite yielding?
>
> Syntactically this could be done with a yielding *function* in
> addition to the yield statement/expression, either as a builtin or a
> function in the sys module.  Let's call it 'yield_' , for lack of a
> better name.  The function would yield the nearest generator on the
> call stack.
>
> Now the example would work with a slight modifiction:
>
>  def yield_if_true(x):
>      if x:
>          yield_(x)
>  yield_if_true(a)
>  yield_if_true(b)
>
> The real benefits from a yield_ function come with recursive
> functions.  A recursive tree traversal that yields from the leaf nodes
> currently suffers from a performance penalty: Every yield is repeated
> as many times as the depth of the tree, turning a O(n) traversal
> algorithm into an O(n lg(n)) algorithm.  PEP 380 does not change that.
>
> But a yield_ function could be O(1), regardless of the forwarding
> depth.
>
> To be fair, a clever implementation might be able to short-circuit a
> 'yield from' chain and achieve the same thing.
>
> Two main drawbacks of yield_:
> - Difficulty of implementation. Generators would need to keep an
>  entire stack branch alive, instead of merely a single frame, and if
>  that somehow affects the efficiency of simple generators, that would
>  be bad.
> - 'the nearest generator on the call stack' is sort of a dynamic
>  scoping thing, which has its problems.  For example, if you forget
>  to make the relevant function a generator (the "if 0: yield None"
>  trick might have been needed but was forgotten), then the yield
>  would trickle up to some random generator higher up, with confusing
>  results.  And if you use yield_ in a callback, well, let's just say
>  that an interesting case too.
>
> All the same, if a yield_ function is practical, I think it is a
> better way to address the problems that motivate PEP 380.  I'm guessing you
> could implement 'yield from' as a pure-Python
> function using yield_, making yield_ strictly more powerful, although
> I couldn't say for sure as I haven't studied the enhanced generator
> protocol.
>
> regards, Anders
>
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> http://mail.python.org/mailman/listinfo/python-ideas
>



-- 
--Guido van Rossum (python.org/~guido)



More information about the Python-ideas mailing list