python-peps/pep-0288.txt

156 lines
5.1 KiB
Plaintext
Raw Normal View History

PEP: 288
Title: Generators Attributes and Exceptions
Version: $Revision$
Last-Modified: $Date$
Author: python@rcn.com (Raymond D. Hettinger)
Status: Draft
Type: Standards Track
Created: 21-Mar-2002
2005-01-02 16:41:54 -05:00
Python-Version: 2.5
Post-History:
Abstract
2005-01-02 16:41:54 -05:00
This PEP proposes to enhance generators by providing mechanisms for
raising exceptions and sharing data with running generators.
Rationale
2005-01-02 16:41:54 -05:00
Currently, only class based iterators can provide attributes and
exception handling. However, class based iterators are harder to
write, less compact, less readable, and slower. A better solution
is to enable these capabilities for generators.
2005-01-02 16:41:54 -05:00
Enabling attribute assignments allows data to be passed to and from
running generators. The approach of sharing data using attributes
pervades Python. Other approaches exist but are somewhat hackish
in comparison.
2005-01-02 16:41:54 -05:00
Another evolutionary step is to add a generator method to allow
exceptions to be passed to a generator. Currently, there is no
clean method for triggering exceptions from outside the generator.
Also, generator exception passing helps mitigate the try/finally
2005-01-02 16:41:54 -05:00
prohibition for generators. The need is especially acute for
generators needing to flush buffers or close resources upon termination.
The two proposals are backwards compatible and require no new
keywords. They are being recommended for Python version 2.5.
Specification for Generator Attributes
Essentially, the proposal is to emulate attribute writing for classes.
The only wrinkle is that generators lack a way to refer to instances of
2005-01-02 16:41:54 -05:00
themselves. So, the proposal is to provide a function for discovering
the reference. For example:
def mygen(filename):
self = mygen.get_instance()
myfile = open(filename)
for line in myfile:
if len(line) < 10:
continue
self.pos = myfile.tell()
yield line.upper()
g = mygen('sample.txt')
line1 = g.next()
print 'Position', g.pos
Uses for generator attributes include:
1. Providing generator clients with extra information (as shown
above).
2. Externally setting control flags governing generator operation
(possibly telling a generator when to step in or step over
data groups).
3. Writing lazy consumers with complex execution states
(an arithmetic encoder output stream for example).
4. Writing co-routines (as demonstrated in Dr. Mertz's articles [1]).
The control flow of 'yield' and 'next' is unchanged by this
2005-01-02 16:41:54 -05:00
proposal. The only change is that data can passed to and from the
generator. Most of the underlying machinery is already in place,
only the access function needs to be added.
Specification for Generator Exception Passing:
Add a .throw(exception) method to the generator interface:
def logger():
start = time.time()
log = []
try:
while True:
2005-01-02 16:41:54 -05:00
log.append(time.time() - start)
yield log[-1]
except WriteLog:
writelog(log)
g = logger()
for i in [10,20,40,80,160]:
testsuite(i)
g.next()
g.throw(WriteLog)
There is no existing work-around for triggering an exception
inside a generator. It is the only case in Python where active
code cannot be excepted to or through.
Generator exception passing also helps address an intrinsic
limitation on generators, the prohibition against their using
2005-01-02 16:41:54 -05:00
try/finally to trigger clean-up code [2].
Note A: The name of the throw method was selected for several
reasons. Raise is a keyword and so cannot be used as a method
name. Unlike raise which immediately raises an exception from the
current execution point, throw will first return to the generator
and then raise the exception. The word throw is suggestive of
putting the exception in another location. The word throw is
already associated with exceptions in other languages.
Alternative method names were considered: resolve(), signal(),
2005-01-02 16:41:54 -05:00
genraise(), raiseinto(), and flush(). None of these fit as well
as throw().
2005-01-02 23:59:21 -05:00
Note B: To keep the throw() syntax simple only the instance
version of the raise syntax would be supported (no variants for
"raise string" or "raise class, instance").
2005-01-02 23:59:21 -05:00
Calling "g.throw(instance)" would correspond to writing
"raise instance" immediately after the most recent yield.
References
2005-01-02 16:41:54 -05:00
[1] Dr. David Mertz's draft columns for Charming Python:
http://gnosis.cx/publish/programming/charming_python_b5.txt
http://gnosis.cx/publish/programming/charming_python_b7.txt
2005-01-02 16:41:54 -05:00
[2] PEP 255 Simple Generators:
http://www.python.org/peps/pep-0255.html
[3] Proof-of-concept recipe:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/164044
Copyright
This document has been placed in the public domain.
Local Variables:
mode: indented-text
indent-tabs-mode: nil
fill-column: 70
End: