New version from Greg.

This commit is contained in:
Guido van Rossum 2009-03-27 19:33:33 +00:00
parent eb61d88000
commit dcbd51055b
1 changed files with 80 additions and 82 deletions

View File

@ -40,8 +40,8 @@ much difficulty using a loop such as
:: ::
for v in g: for v in g:
yield v yield v
However, if the subgenerator is to interact properly with the caller However, if the subgenerator is to interact properly with the caller
in the case of calls to ``send()``, ``throw()`` and ``close()``, things in the case of calls to ``send()``, ``throw()`` and ``close()``, things
@ -63,7 +63,7 @@ generator:
:: ::
yield from <expr> yield from <expr>
where <expr> is an expression evaluating to an iterable, from which an where <expr> is an expression evaluating to an iterable, from which an
iterator is extracted. The iterator is run to exhaustion, during which iterator is extracted. The iterator is run to exhaustion, during which
@ -78,30 +78,30 @@ becomes the value of the ``yield from`` expression.
In general, the semantics can be described in terms of the iterator In general, the semantics can be described in terms of the iterator
protocol as follows: protocol as follows:
* Any values that the iterator yields are passed directly to the * Any values that the iterator yields are passed directly to the
caller. caller.
* Any values sent to the delegating generator using ``send()`` * Any values sent to the delegating generator using ``send()``
are passed directly to the iterator. If the sent value is None, are passed directly to the iterator. If the sent value is None,
the iterator's ``next()`` method is called. If the sent value is the iterator's ``next()`` method is called. If the sent value is
not None, the iterator's ``send()`` method is called. Any exception not None, the iterator's ``send()`` method is called. Any exception
resulting from attempting to call ``next`` or ``send`` is raised resulting from attempting to call ``next`` or ``send`` is raised
in the delegating generator. in the delegating generator.
* Exceptions passed to the ``throw()`` method of the delegating * Exceptions passed to the ``throw()`` method of the delegating
generator are forwarded to the ``throw()`` method of the iterator. generator are forwarded to the ``throw()`` method of the iterator.
If the iterator does not have a ``throw()`` method, its ``close()`` If the iterator does not have a ``throw()`` method, its ``close()``
method is called if it has one, then the thrown-in exception is method is called if it has one, then the thrown-in exception is
raised in the delegating generator. Any exception resulting from raised in the delegating generator. Any exception resulting from
attempting to call these methods (apart from one case noted below) attempting to call these methods (apart from one case noted below)
is raised in the delegating generator. is raised in the delegating generator.
* The value of the ``yield from`` expression is the first argument * The value of the ``yield from`` expression is the first argument
to the ``StopIteration`` exception raised by the iterator when it to the ``StopIteration`` exception raised by the iterator when it
terminates. terminates.
* ``return expr`` in a generator causes ``StopIteration(expr)`` to * ``return expr`` in a generator causes ``StopIteration(expr)`` to
be raised. be raised.
Fine Details Fine Details
@ -113,8 +113,9 @@ An iterator having a ``throw()`` method is expected to recognize
this as a request to finalize itself. this as a request to finalize itself.
If a call to the iterator's ``throw()`` method raises a StopIteration If a call to the iterator's ``throw()`` method raises a StopIteration
exception, and it is *not* the same exception object that was thrown exception, and it is *not* the same exception object that was thrown in,
in, its value is returned as the value of the ``yield from`` expression and the original exception was not GeneratorExit, then the value of the
new exception is returned as the value of the ``yield from`` expression
and the delegating generator is resumed. and the delegating generator is resumed.
@ -133,51 +134,48 @@ Formal Semantics
:: ::
RESULT = yield from EXPR RESULT = yield from EXPR
is semantically equivalent to is semantically equivalent to
:: ::
_i = iter(EXPR) _i = iter(EXPR)
try: try:
try: _y = _i.next()
_y = _i.next() except StopIteration, _e:
except StopIteration, _e: _r = _e.value
_r = _e.value else:
else: while 1:
while 1: try:
try: _s = yield _y
_s = yield _y except:
except: _m = getattr(_i, 'throw', None)
_m = getattr(_i, 'throw', None) if _m is not None:
if _m is not None: _x = sys.exc_info()
_x = sys.exc_info() try:
try: _y = _m(*_x)
_y = _m(*_x) except StopIteration, _e:
except StopIteration, _e: if _e is _x[1] or isinstance(_x[1], GeneratorExit):
if _e is _x[1]: raise
raise else:
else: _r = _e.value
_r = _e.value break
break else:
else: _m = getattr(_i, 'close', None)
_m = getattr(_i, 'close', None) if _m is not None:
if _m is not None: _m()
_m() raise
raise else:
else: try:
try: if _s is None:
if _s is None: _y = _i.next()
_y = _i.next() else:
else: _y = _i.send(_s)
_y = _i.send(_s) except StopIteration, _e:
except StopIteration, _e: _r = _e.value
_r = _e.value break
break RESULT = _r
finally:
del _i
RESULT = _r
except that implementations are free to cache bound methods for the 'next', except that implementations are free to cache bound methods for the 'next',
'send' and 'throw' methods of the iterator upon first use. 'send' and 'throw' methods of the iterator upon first use.
@ -186,13 +184,13 @@ except that implementations are free to cache bound methods for the 'next',
:: ::
return value return value
is semantically equivalent to is semantically equivalent to
:: ::
raise StopIteration(value) raise StopIteration(value)
except that, as currently, the exception cannot be caught by ``except`` except that, as currently, the exception cannot be caught by ``except``
clauses within the returning generator. clauses within the returning generator.
@ -201,14 +199,14 @@ clauses within the returning generator.
:: ::
class StopIteration(Exception): class StopIteration(Exception):
def __init__(self, *args): def __init__(self, *args):
if len(args) > 0: if len(args) > 0:
self.value = args[0] self.value = args[0]
else: else:
self.value = None self.value = None
Exception.__init__(self, *args) Exception.__init__(self, *args)
Rationale Rationale
@ -249,7 +247,7 @@ so to an unfactored one in all Python implementations.
The assumption made is that, in the majority of use cases, the subiterator The assumption made is that, in the majority of use cases, the subiterator
will not be shared. The rare case of a shared subiterator can be will not be shared. The rare case of a shared subiterator can be
accommodated by means of a wrapper that blocks ``throw()`` and ``send()`` accommodated by means of a wrapper that blocks ``throw()`` and ``close()``
calls, or by using a means other than ``yield from`` to call the calls, or by using a means other than ``yield from`` to call the
subiterator. subiterator.
@ -269,14 +267,14 @@ Using the proposed syntax, a statement such as
:: ::
y = f(x) y = f(x)
where f is an ordinary function, can be transformed into a delegation where f is an ordinary function, can be transformed into a delegation
call call
:: ::
y = yield from g(x) y = yield from g(x)
where g is a generator. One can reason about the behaviour of the where g is a generator. One can reason about the behaviour of the
resulting code by thinking of g as an ordinary function that can be resulting code by thinking of g as an ordinary function that can be
@ -341,13 +339,13 @@ value of the ``close()`` call to the subgenerator. However, the proposed
mechanism is attractive for a couple of reasons: mechanism is attractive for a couple of reasons:
* Using the StopIteration exception makes it easy for other kinds * Using the StopIteration exception makes it easy for other kinds
of iterators to participate in the protocol without having to of iterators to participate in the protocol without having to
grow an extra attribute or a close() method. grow an extra attribute or a close() method.
* It simplifies the implementation, because the point at which the * It simplifies the implementation, because the point at which the
return value from the subgenerator becomes available is the same return value from the subgenerator becomes available is the same
point at which StopIteration is raised. Delaying until any later point at which StopIteration is raised. Delaying until any later
time would require storing the return value somewhere. time would require storing the return value somewhere.
Criticisms Criticisms