[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?