[Python-ideas] Possible PEP 380 tweak
Nick Coghlan
ncoghlan at gmail.com
Sat Oct 30 06:05:20 CEST 2010
On Sat, Oct 30, 2010 at 1:47 PM, Guido van Rossum <guido at python.org> wrote:
> I agree that you've poked a hole in my proposal. If we can change the
> expansion of yield-from to restore the equivalency between gtally()
> and the simplest gtally2(), thereby restoring the original refactoring
> principle, we might be able to save it. Otherwise I declare defeat.
> Right now I am too tired to think of such an expansion, but I recall
> trying my hand at one a few nights ago and realizing that I'd
> introduced another problem. So this does not look too hopeful,
> especially since I really don't like extending GeneratorExit for the
> purpose.
I tried to make the original version work as well, but always ran into
one of two problems:
- breaking GeneratorExit for resource cleanup
- "leaking" inner return values so they looked like they came from the
outer function.
Here's a crazy idea though. What if gtally2() could be written as follows:
def gtally2():
return from gtally()
If we make the generator tail call explicit, then the interpreter can
do the right thing (i.e. raise StopIteration-with-a-value instead of
reraising GeneratorExit) and we don't need to try to shoehorn two
different sets of semantics into the single yield-from construct.
To give some formal semantics to the new statement:
# RETURN FROM semantics
_i = iter(EXPR)
_m, _a = next, (_i,)
# _m is a function or a bound method;
# _a is a tuple of arguments to call _m with;
# both are set to other values further down
while 1:
# Move the generator along
# Unlike YIELD FROM, we allow StopIteration to
# escape, since this is a tail call
_y = _m(*_a)
# Yield _y and process what came back in
try:
_s = yield _y
except GeneratorExit as _e:
# Request to exit
try:
# Don't reuse _m, since we're bailing out of the loop
_close = _i.close
except AttributeError:
pass
else:
# Unlike YIELD FROM, we use StopIteration
# to return the value of the inner close() call
raise StopIteration(_close())
# If there is no inner close() attribute, return None
raise StopIteration
except BaseException as _e:
# An exception was thrown in; pass it along
_a = sys.exc_info()
try:
_m = _i.throw
except AttributeError:
# Can't throw it in; throw it back out
raise _e
else:
# A value was sent in; pass it along
if _s is None:
_m, _a = next, (_i,)
else:
_m, _a = _i.send, (_s,)
# Unlike YIELD FROM, this is a statement, so there is no RESULT
Summary of the differences between return from and yield from:
- statement, not an expression
- an inner StopIteration is allowed to propogate
- a thrown in GeneratorExit is converted to StopIteration
Cheers,
Nick.
--
Nick Coghlan | ncoghlan at gmail.com | Brisbane, Australia
More information about the Python-ideas
mailing list