Yielding a chain of values
Peter Hansen
peter at engcorp.com
Sun Aug 28 16:05:03 EDT 2005
Talin wrote:
> I'm finding that a lot of places within my code, I want to return the
> output of a generator from another generator. Currently the only method
> I know of to do this is to explicitly loop over the results from the
> inner generator, and yield each one:
>
> for x in inner():
> yield x
>
> I was wondering if there was a more efficient and concise way to do
> this. And if there isn't, then what about extending the * syntax used
> for lists, i.e.:
>
> yield *inner()
It's not the first time it's been suggested. You can check the archives
perhaps for past discussions.
I think syntax like that could be a good idea too, for readability
reasons perhaps, but I don't think it's really important for efficiency
reasons. If you think about it, this involves one stack frame being set
up for the call to the generator, and then a series of quick context
switches (or whatever they're called in this situation) between the
current stack frame and the inner() one, as each yielded value is
produced, yield, then re-yielded up to the calling frame (with another
quick context switch). No additional stack frames are generated, and
very few byte codes are involved:
>>> def f():
... for x in inner():
... yield x
...
>>> dis.dis(f)
2 0 SETUP_LOOP 21 (to 24)
3 LOAD_GLOBAL 0 (inner)
6 CALL_FUNCTION 0
9 GET_ITER
>> 10 FOR_ITER 10 (to 23)
13 STORE_FAST 0 (x)
3 16 LOAD_FAST 0 (x)
19 YIELD_VALUE
20 JUMP_ABSOLUTE 10
...
Yes, that is some overhead, but unless you are going many levels deep
(and that's usually a design smell of some kind) this isn't likely to be
noticed amongst the rest of the code which is presumably doing something
non-trivial to produce the values in the first place, and to consume
them ultimately.
The basic loop could be handled entirely from C with an appropriate
syntax addition as you suggest, but executing those four byte code
instructions is very quick, and there is no repeated (Python) function
call overhead as you would expect if inner() were not a generator.
-Peter
More information about the Python-list
mailing list