161 lines
5.3 KiB
Plaintext
161 lines
5.3 KiB
Plaintext
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
|
|
Python-Version: 2.5
|
|
Post-History:
|
|
|
|
|
|
Abstract
|
|
|
|
This PEP proposes to enhance generators by providing mechanisms for
|
|
raising exceptions and sharing data with running generators.
|
|
|
|
|
|
|
|
Rationale
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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(),
|
|
genraise(), raiseinto(), and flush(). None of these fit as well
|
|
as throw().
|
|
|
|
Note B: The full throw() syntax should exactly match raise's syntax:
|
|
|
|
throw([expression, [expression, [expression]]])
|
|
|
|
Accordingly, it should be implemented to handle all of the following:
|
|
|
|
raise string g.throw(string)
|
|
raise string, data g.throw(string,data)
|
|
raise class, instance g.throw(class,instance)
|
|
raise instance g.throw(instance)
|
|
raise g.throw()
|
|
|
|
|
|
|
|
References
|
|
|
|
[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
|
|
|
|
[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:
|