[Python-checkins] r85900 - peps/trunk/pep-0380.txt
guido.van.rossum
python-checkins at python.org
Fri Oct 29 01:17:01 CEST 2010
Author: guido.van.rossum
Date: Fri Oct 29 01:17:00 2010
New Revision: 85900
Log:
Version 14 from Greg.
Modified:
peps/trunk/pep-0380.txt
Modified: peps/trunk/pep-0380.txt
==============================================================================
--- peps/trunk/pep-0380.txt (original)
+++ peps/trunk/pep-0380.txt Fri Oct 29 01:17:00 2010
@@ -7,7 +7,7 @@
Type: Standards Track
Content-Type: text/x-rst
Created: 13-Feb-2009
-Python-Version: 2.7
+Python-Version: 3.x
Post-History:
@@ -40,8 +40,8 @@
::
- 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 @@
::
- 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
@@ -71,52 +71,40 @@
the generator containing the ``yield from`` expression (the
"delegating generator").
-Furthermore, when the iterator is another generator, the subgenerator is
-allowed to execute a ``return`` statement with a value, and that value
-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 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.
-
- * The value of the ``yield from`` expression is the first argument
- to the ``StopIteration`` exception raised by the iterator when it
- terminates.
+Furthermore, when the iterator is another generator, the subgenerator
+is allowed to execute a ``return`` statement with a value, and that
+value becomes the value of the ``yield from`` expression.
+
+The full semantics of the ``yield from`` expression can be described
+in terms of the generator protocol as follows:
+
+ * 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. If the call
+ raises StopIteration, the delegating generator is resumed. Any other
+ exception is propagated to the delegating generator.
+
+ * Exceptions other than GeneratorExit thrown into the delegating
+ generator are passed to the ``throw()`` method of the iterator.
+ If the call raises StopIteration, the delegating generator is resumed.
+ Any other exception is propagated to the delegating generator.
+
+ * If a GeneratorExit exception is thrown into the delegating generator,
+ or the ``close()`` method of the delegating generator is called, then
+ the ``close()`` method of the iterator is called if it has one. If this
+ call results in an exception, it is propagated to the delegating generator.
+ Otherwise, GeneratorExit 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.
- * ``return expr`` in a generator causes ``StopIteration(expr)`` to
- be raised.
-
-
-Fine Details
-------------
-
-The implicit GeneratorExit resulting from closing the delegating
-generator is treated as though it were passed in using ``throw()``.
-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,
-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.
+ * ``return expr`` in a generator causes ``StopIteration(expr)`` to
+ be raised upon exit from the generator.
Enhancements to StopIteration
@@ -130,67 +118,70 @@
Formal Semantics
----------------
+Python 3 syntax is used in this section.
+
1. The statement
::
- RESULT = yield from EXPR
+ RESULT = yield from EXPR
is semantically equivalent to
::
- _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
+ _i = iter(EXPR)
+ try:
+ _y = next(_i)
+ except StopIteration as _e:
+ _r = _e.value
+ else:
+ while 1:
+ try:
+ _s = yield _y
+ except GeneratorExit as _e:
+ try:
+ _m = _i.close
+ except AttributeError:
+ pass
+ else:
+ _m()
+ raise _e
+ except BaseException as _e:
+ _x = sys.exc_info()
+ try:
+ _m = _i.throw
+ except AttributeError:
+ raise _e
+ else:
+ try:
+ _y = _m(*_x)
+ except StopIteration as _e:
+ _r = _e.value
+ break
+ else:
+ try:
+ if _s is None:
+ _y = next(_i)
+ else:
+ _y = _i.send(_s)
+ except StopIteration as _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.
2. In a generator, the statement
::
- 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.
@@ -199,14 +190,14 @@
::
- 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
@@ -223,13 +214,26 @@
call the new function using a ``yield from`` expression.
The behaviour of the resulting compound generator should be, as far as
-possible, exactly the same as the original unfactored generator in all
-situations, including calls to ``next()``, ``send()``, ``throw()`` and
-``close()``.
+reasonably practicable, the same as the original unfactored generator
+in all situations, including calls to ``next()``, ``send()``,
+``throw()`` and ``close()``.
The semantics in cases of subiterators other than generators has been
chosen as a reasonable generalization of the generator case.
+The proposed semantics have the following limitations with regard
+to refactoring:
+
+* A block of code that catches GeneratorExit without subsequently
+ re-raising it cannot be factored out while retaining exactly the
+ same behaviour.
+
+* Factored code may not behave the same way as unfactored code if a
+ StopIteration exception is thrown into the delegating generator.
+
+With use cases for these being rare to non-existent, it was not
+considered worth the extra complexity required to support them.
+
Finalization
------------
@@ -267,14 +271,14 @@
::
- 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
@@ -338,16 +342,46 @@
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.
+* Using a generalization of 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.
* 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
+ point at which the exception is raised. Delaying until any later
time would require storing the return value somewhere.
+Rejected Ideas
+--------------
+
+Some ideas were discussed but rejected.
+
+Suggestion: There should be some way to prevent the initial call to
+next(), or substitute it with a send() call with a specified value,
+the intention being to support the use of generators wrapped so that
+the initial next() is performed automatically.
+
+Resolution: Outside the scope of the proposal. Such generators should
+not be used with ``yield from``.
+
+Suggestion: If closing a subiterator raises StopIteration with a
+value, return that value from the ``close()`` call to the delegating
+generator.
+
+Resolution: Undesirable for a number of reasons. The purpose of closing
+a generator is to ensure proper cleanup, not to obtain a meaningful
+return value. Also, it would be unreliable unless the return value were
+stored so as to be available to subsequent close calls, which would
+cause it to persist for longer than expected.
+
+Suggestion: If ``close()`` is not to return a value, then raise an
+exception if StopIteration with a non-None value occurs.
+
+Resolution: No clear reason to do so. Ignoring a return value is not
+considered an error anywhere else in Python.
+
+
Criticisms
==========
@@ -355,7 +389,8 @@
be derived in a very different way from that of an ordinary ``yield``
expression. This suggests that some other syntax not containing the
word ``yield`` might be more appropriate, but no acceptable alternative
-has so far been proposed.
+has so far been proposed. Rejected alternatives include ``call``,
+``delegate`` and ``gcall``.
It has been suggested that some mechanism other than ``return`` in
the subgenerator should be used to establish the value returned by
@@ -364,21 +399,11 @@
function, since it would not be able to return values in the same way
as other functions.
-The use of an argument to StopIteration to pass the return value
-has been criticised as an "abuse of exceptions", without any
-concrete justification of this claim. In any case, this is only
-one suggested implementation; another mechanism could be used
-without losing any essential features of the proposal.
-
-It has been suggested that a different exception, such as
-GeneratorReturn, should be used instead of StopIteration to return a
-value. However, no convincing practical reason for this has been put
-forward, and the addition of a ``value`` attribute to StopIteration
-mitigates any difficulties in extracting a return value from a
-StopIteration exception that may or may not have one. Also, using a
-different exception would mean that, unlike ordinary functions,
-'return' without a value in a generator would not be equivalent to
-'return None'.
+The use of an exception to pass the return value has been criticised
+as an "abuse of exceptions", without any concrete justification of
+this claim. In any case, this is only one suggested implementation;
+another mechanism could be used without losing any essential features
+of the proposal.
Alternative Proposals
@@ -414,12 +439,12 @@
This document has been placed in the public domain.
-
+
..
- Local Variables:
- mode: indented-text
- indent-tabs-mode: nil
- sentence-end-double-space: t
- fill-column: 70
- coding: utf-8
- End:
+ Local Variables:
+ mode: indented-text
+ indent-tabs-mode: nil
+ sentence-end-double-space: t
+ fill-column: 70
+ coding: utf-8
+ End:
More information about the Python-checkins
mailing list