[Python-ideas] [Python-Dev] yield * (Re: Missing operator.call)

Steven D'Aprano steve at pearwood.info
Tue Feb 10 00:04:04 CET 2009


Antoine Pitrou wrote:
> Steven D'Aprano <steve at ...> writes:
>> Marcin 'Qrczak' Kowalczyk wrote:
>>> On Sun, Feb 8, 2009 at 23:20, Steven D'Aprano <steve at ...> wrote:
>>>
>>>> And the cost is small:
>>>>
>>>> [steve <at> ando ~]$ python -m timeit -s "seq = range(500)" "(3*x for x in
> seq if
>>>> x%2 == 1)"
>>>> 1000000 loops, best of 3: 0.611 usec per loop
>>> Because generators are lazy and you don't run it into completion.
>> We were talking about the cost of *making* the generator, not the cost 
>> of running it to completion.
> 
> No, I was talking about the cost of running it to completion. 

Perhaps I misunderstood you, because what you actually said was:

"But the former will be slower than the latter, because it constructs an
intermediate generator only to yield it element by element."

Since a for loop will also yield element by element, the only difference 
I saw was constructing the generator expression, which is cheap.

 > A generator is
> executed in a separated frame. Therefore, if you "yield from" a generator, there
> is a frame switch at each iteration between the generator frame and the frame of
> the "yield from".
> Which is not the case with an inline "for" loop containing a "yield".

I'm afraid I don't understand the relevance of this. If it's a 
criticism, it's a criticism of generators in general, not of the 
proposed syntax. Don't we already carry the cost of the frame switch 
when iterating over a generator?

for el in generator:
     yield el

If that is replaced with the proposed syntax

yield from generator

what's the difference, performance-wise? In both cases, you can optimise 
by unrolling the generator into an inline for loop, at the cost of 
readability, convenience, and the ability to pass generator objects around.

In Python 2.4 at least, the optimisation is not to be sneered at (modulo 
the usual warnings about premature optimisation): unrolling is about 
30-40% faster:

$ python -m timeit -s "def f():" -s "    for x in (i+1 for i in 
xrange(20)): yield x" "list(f())"  # using gen expr
100000 loops, best of 3: 12.9 usec per loop

$ python -m timeit -s "def f():" -s "    for x in xrange(20): yield x+1" 
"list(f())"  # unrolled into body of the loop
100000 loops, best of 3: 8.09 usec per loop


Since people are already choosing to use generator expressions instead 
of unrolling them into for loops, I don't believe that your objection is 
relevant to the proposal. "yield from expression" would (presumably) be 
a shorter, neater way of saying "for x in expression: yield x" except 
that it doesn't create a new name x.



-- 
Steven




More information about the Python-ideas mailing list