[Python-checkins] CVS: python/nondist/peps pep-0255.txt,1.10,1.11
Tim Peters
tim_one@users.sourceforge.net
Sat, 23 Jun 2001 01:53:23 -0700
Update of /cvsroot/python/python/nondist/peps
In directory usw-pr-cvs1:/tmp/cvs-serv28002/peps
Modified Files:
pep-0255.txt
Log Message:
Lotsa changes: more details and examples covering recent decisions wrt
return stmts, try/except, try/finally; more Q&A. I expect this is the
last major revision of this PEP, so I'm going to post it again too.
Index: pep-0255.txt
===================================================================
RCS file: /cvsroot/python/python/nondist/peps/pep-0255.txt,v
retrieving revision 1.10
retrieving revision 1.11
diff -C2 -r1.10 -r1.11
*** pep-0255.txt 2001/06/21 16:56:41 1.10
--- pep-0255.txt 2001/06/23 08:53:21 1.11
***************
*** 11,15 ****
Created: 18-May-2001
Python-Version: 2.2
! Post-History: 14-Jun-2001
--- 11,15 ----
Created: 18-May-2001
Python-Version: 2.2
! Post-History: 14-Jun-2001, 23-Jun-2001
***************
*** 119,123 ****
! Specification
A new statement is introduced:
--- 119,123 ----
! Specification: Yield
A new statement is introduced:
***************
*** 126,133 ****
"yield" is a new keyword, so a future statement[8] is needed to phase
! this in. [XXX spell this out]
The yield statement may only be used inside functions. A function that
! contains a yield statement is called a generator function.
When a generator function is called, the actual arguments are bound to
--- 126,138 ----
"yield" is a new keyword, so a future statement[8] is needed to phase
! this in. [XXX spell this out -- but new keywords have ripple effects
! across tools too, and it's not clear this can be forced into the future
! framework at all -- it's not even clear that Python's parser alone can
! be taught to swing both ways based on a future stmt]
The yield statement may only be used inside functions. A function that
! contains a yield statement is called a generator function. A generator
! function is an ordinary function object in all respects, but has the
! new CO_GENERATOR flag set in the code object's co_flags member.
When a generator function is called, the actual arguments are bound to
***************
*** 154,157 ****
--- 159,171 ----
call.
+ Restriction: A yield statement is not allowed in the try clause of a
+ try/finally construct. The difficulty is that there's no guarantee
+ the generator will ever be resumed, hence no guarantee that the finally
+ block will ever get executed; that's too much a violation of finally's
+ purpose to bear.
+
+
+ Specification: Return
+
A generator function can also contain return statements of the form:
***************
*** 162,175 ****
the bodies of non-generator functions nested within the generator).
! When a return statement is encountered, nothing is returned, but a
! StopIteration exception is raised, signalling that the iterator is
! exhausted. The same is true if control flows off the end of the
! function. Note that return means "I'm done, and have nothing
! interesting to return", for both generator functions and non-generator
! functions.
- Generators and Exception Propagation
If an unhandled exception-- including, but not limited to,
StopIteration --is raised by, or passes through, a generator function,
--- 176,216 ----
the bodies of non-generator functions nested within the generator).
! When a return statement is encountered, control proceeds as in any
! function return, executing the appropriate finally clauses (if any
! exist). Then a StopIteration exception is raised, signalling that the
! iterator is exhausted. A StopIteration exception is also raised if
! control flows off the end of the generator without an explict return.
!
! Note that return means "I'm done, and have nothing interesting to
! return", for both generator functions and non-generator functions.
!
! Note that return isn't always equivalent to raising StopIteration: the
! difference lies in how enclosing try/except constructs are treated.
! For example,
!
! >>> def f1():
! ... try:
! ... return
! ... except:
! ... yield 1
! >>> print list(f1())
! []
!
! because, as in any function, return simply exits, but
!
! >>> def f2():
! ... try:
! ... raise StopIteration
! ... except:
! ... yield 42
! >>> print list(f2())
! [42]
+ because StopIteration is captured by a bare "except", as is any
+ exception.
+ Specification: Generators and Exception Propagation
+
If an unhandled exception-- including, but not limited to,
StopIteration --is raised by, or passes through, a generator function,
***************
*** 193,197 ****
File "<stdin>", line 2, in f
ZeroDivisionError: integer division or modulo by zero
! >>> k.next() # and the generator function cannot be resumed
Traceback (most recent call last):
File "<stdin>", line 1, in ?
--- 234,238 ----
File "<stdin>", line 2, in f
ZeroDivisionError: integer division or modulo by zero
! >>> k.next() # and the generator cannot be resumed
Traceback (most recent call last):
File "<stdin>", line 1, in ?
***************
*** 200,234 ****
! Yield and Try/Except/Finally
! While "yield" is a control-flow statement, and in most respects acts
! like a "return" statement from the caller's point of view, within a
! generator it acts more like a callback function. In particular, it has
! no special semantics with respect to try/except/finally. This is best
! illustrated by a contrived example; the primary lesson to take from
! this is that using yield in a finally block is a dubious idea!
! >>> def g():
... try:
... yield 1
! ... 1/0 # raises exception
! ... yield 2 # we never get here
... finally:
! ... yield 3 # yields, and we raise the exception *next* time
! ... yield 4 # we never get here
! >>> k = g()
! >>> k.next()
! 1
! >>> k.next()
! 3
! >>> k.next() # as if "yield 3" were a callback, exception raised now
! Traceback (most recent call last):
! File "<stdin>", line 1, in ?
! File "<stdin>", line 4, in g
! ZeroDivisionError: integer division or modulo by zero
! >>> k.next() # unhandled exception terminated the generator
! Traceback (most recent call last):
! File "<stdin>", line 1, in ?
! StopIteration
>>>
--- 241,276 ----
! Specification: Try/Except/Finally
! As noted earlier, yield is not allowed in the try clause of a try/
! finally construct. A consequence is that generators should allocate
! critical resources with great care. There is no restriction on yield
! otherwise appearing in finally clauses, except clauses, or in the try
! clause of a try/except construct:
! >>> def f():
... try:
... yield 1
! ... try:
! ... yield 2
! ... 1/0
! ... yield 3 # never get here
! ... except ZeroDivisionError:
! ... yield 4
! ... yield 5
! ... raise
! ... except:
! ... yield 6
! ... yield 7 # the "raise" above stops this
! ... except:
! ... yield 8
! ... yield 9
! ... try:
! ... x = 12
... finally:
! ... yield 10
! ... yield 11
! >>> print list(f())
! [1, 2, 4, 5, 8, 9, 10, 11]
>>>
***************
*** 300,303 ****
--- 342,349 ----
print
+ Both output blocks display:
+
+ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
+
Q & A
***************
*** 313,317 ****
implementation in Jython requires that the compiler be able to
determine potential suspension points at compile-time, and a new
! keyword makes that easy.
Q. Why allow "return" at all? Why not force termination to be spelled
--- 359,390 ----
implementation in Jython requires that the compiler be able to
determine potential suspension points at compile-time, and a new
! keyword makes that easy. The CPython referrence implementation also
! exploits it heavily, to detect which functions *are* generator-
! functions (although a new keyword in place of "def" would solve that
! for CPython -- but people asking the "why a new keyword?" question
! don't want any new keyword).
!
! Q: Then why not some other special syntax without a new keyword? For
! example, one of these instead of "yield 3":
!
! return 3 and continue
! return and continue 3
! return generating 3
! continue return 3
! return >> , 3
! from generator return 3
! return >> 3
! return << 3
! >> 3
! << 3
!
! A: Did I miss one <wink>? Out of hundreds of messages, I counted two
! suggesting such an alternative, and extracted the above from them.
! It would be nice not to need a new keyword, but nicer to make yield
! very clear -- I don't want to have to *deduce* that a yield is
! occurring from making sense of a previous senseless sequence of
! keywords or operators. Still, if this attracts enough interest,
! proponents should settle on a single consensus suggestion, and Guido
! will Pronounce on it.
Q. Why allow "return" at all? Why not force termination to be spelled
***************
*** 324,327 ****
--- 397,403 ----
forcing everyone to work at that level, though. "return" means "I'm
done" in any kind of function, and that's easy to explain and to use.
+ Note that "return" isn't always equivalent to "raise StopIteration"
+ in try/except construct, either (see the "Specification: Return"
+ section).
Q. Then why not allow an expression on "return" too?