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:
yield v
for v in g:
yield v
However, if the subgenerator is to interact properly with the caller
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
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
protocol as follows:
* Any values that the iterator yields are passed directly to the
caller.
* Any values that the iterator yields are passed directly to the
caller.
* Any values sent to the delegating generator using ``send()``
are passed directly to the iterator. If the sent value is None,
the iterator's ``next()`` method is called. If the sent value is
not None, the iterator's ``send()`` method is called. Any exception
resulting from attempting to call ``next`` or ``send`` is raised
in the delegating generator.
* Any values sent to the delegating generator using ``send()``
are passed directly to the iterator. If the sent value is None,
the iterator's ``next()`` method is called. If the sent value is
not None, the iterator's ``send()`` method is called. Any exception
resulting from attempting to call ``next`` or ``send`` is raised
in the delegating generator.
* Exceptions passed to the ``throw()`` method of the delegating
generator are forwarded to the ``throw()`` method of the iterator.
If the iterator does not have a ``throw()`` method, its ``close()``
method is called if it has one, then the thrown-in exception is
raised in the delegating generator. Any exception resulting from
attempting to call these methods (apart from one case noted below)
is raised in the delegating generator.
* Exceptions passed to the ``throw()`` method of the delegating
generator are forwarded to the ``throw()`` method of the iterator.
If the iterator does not have a ``throw()`` method, its ``close()``
method is called if it has one, then the thrown-in exception is
raised in the delegating generator. Any exception resulting from
attempting to call these methods (apart from one case noted below)
is raised in the delegating generator.
* The value of the ``yield from`` expression is the first argument
to the ``StopIteration`` exception raised by the iterator when it
terminates.
* The value of the ``yield from`` expression is the first argument
to the ``StopIteration`` exception raised by the iterator when it
terminates.
* ``return expr`` in a generator causes ``StopIteration(expr)`` to
be raised.
* ``return expr`` in a generator causes ``StopIteration(expr)`` to
be raised.
Fine Details
@ -113,8 +113,9 @@ An iterator having a ``throw()`` method is expected to recognize
this as a request to finalize itself.
If a call to the iterator's ``throw()`` method raises a StopIteration
exception, and it is *not* the same exception object that was thrown
in, its value is returned as the value of the ``yield from`` expression
exception, and it is *not* the same exception object that was thrown in,
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.
@ -133,51 +134,48 @@ Formal Semantics
::
RESULT = yield from EXPR
RESULT = yield from EXPR
is semantically equivalent to
::
_i = iter(EXPR)
try:
try:
_y = _i.next()
except StopIteration, _e:
_r = _e.value
else:
while 1:
try:
_s = yield _y
except:
_m = getattr(_i, 'throw', None)
if _m is not None:
_x = sys.exc_info()
try:
_y = _m(*_x)
except StopIteration, _e:
if _e is _x[1]:
raise
else:
_r = _e.value
break
else:
_m = getattr(_i, 'close', None)
if _m is not None:
_m()
raise
else:
try:
if _s is None:
_y = _i.next()
else:
_y = _i.send(_s)
except StopIteration, _e:
_r = _e.value
break
finally:
del _i
RESULT = _r
_i = iter(EXPR)
try:
_y = _i.next()
except StopIteration, _e:
_r = _e.value
else:
while 1:
try:
_s = yield _y
except:
_m = getattr(_i, 'throw', None)
if _m is not None:
_x = sys.exc_info()
try:
_y = _m(*_x)
except StopIteration, _e:
if _e is _x[1] or isinstance(_x[1], GeneratorExit):
raise
else:
_r = _e.value
break
else:
_m = getattr(_i, 'close', None)
if _m is not None:
_m()
raise
else:
try:
if _s is None:
_y = _i.next()
else:
_y = _i.send(_s)
except StopIteration, _e:
_r = _e.value
break
RESULT = _r
except that implementations are free to cache bound methods for the 'next',
'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
::
raise StopIteration(value)
raise StopIteration(value)
except that, as currently, the exception cannot be caught by ``except``
clauses within the returning generator.
@ -201,14 +199,14 @@ clauses within the returning generator.
::
class StopIteration(Exception):
class StopIteration(Exception):
def __init__(self, *args):
if len(args) > 0:
self.value = args[0]
else:
self.value = None
Exception.__init__(self, *args)
def __init__(self, *args):
if len(args) > 0:
self.value = args[0]
else:
self.value = None
Exception.__init__(self, *args)
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
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
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
call
::
y = yield from g(x)
y = yield from g(x)
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
@ -341,13 +339,13 @@ value of the ``close()`` call to the subgenerator. However, the proposed
mechanism is attractive for a couple of reasons:
* Using the StopIteration exception makes it easy for other kinds
of iterators to participate in the protocol without having to
grow an extra attribute or a close() method.
of iterators to participate in the protocol without having to
grow an extra attribute or a close() method.
* It simplifies the implementation, because the point at which the
return value from the subgenerator becomes available is the same
point at which StopIteration is raised. Delaying until any later
time would require storing the return value somewhere.
return value from the subgenerator becomes available is the same
point at which StopIteration is raised. Delaying until any later
time would require storing the return value somewhere.
Criticisms