Raymond's latest update. Note Status is still Draft until
confirmation of Accepted status comes from Guido.
This commit is contained in:
parent
ade4c48fa4
commit
b17080888c
184
pep-0279.txt
184
pep-0279.txt
|
@ -12,7 +12,7 @@ Post-History:
|
|||
|
||||
Abstract
|
||||
|
||||
This PEP introduces three orthogonal (not mutually exclusive) ideas
|
||||
This PEP introduces two orthogonal (not mutually exclusive) ideas
|
||||
for enhancing the generators introduced in Python version 2.2 [1].
|
||||
The goal is to increase the convenience, utility, and power
|
||||
of generators.
|
||||
|
@ -49,7 +49,7 @@ Rationale
|
|||
|
||||
The next steps in the evolution of generators are:
|
||||
|
||||
1. Add a new builtin function, indexed() which was made possible
|
||||
1. Add a new builtin function, iterindexed() which was made possible
|
||||
once iterators and generators became available. It provides
|
||||
all iterables with the same advantage that iteritems() affords
|
||||
to dictionaries -- a compact, readable, reliable index notation.
|
||||
|
@ -58,19 +58,25 @@ Rationale
|
|||
that provides a simple way to convert a list comprehension into
|
||||
a generator whenever memory issues arise.
|
||||
|
||||
3. Add a generator method to enable 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.
|
||||
|
||||
All of the suggestions are designed to take advantage of the
|
||||
existing implementation and require little additional effort to
|
||||
incorporate. Each is backward compatible and requires no new
|
||||
keywords. The three generator tools go into Python 2.3 when
|
||||
keywords. The two generator tools go into Python 2.3 when
|
||||
generators become final and are not imported from __future__.
|
||||
|
||||
|
||||
|
||||
BDFL Pronouncements
|
||||
|
||||
1. The new built-in function is ACCEPTED. There needs to be further
|
||||
discussion on the best name for the function.
|
||||
|
||||
2. Generator comprehensions are REJECTED. The rationale is that
|
||||
the benefits are marginal since generators can already be coded directly
|
||||
and the costs are high because implementation and maintenance require
|
||||
major efforts with the parser.
|
||||
|
||||
|
||||
Reference Implementation
|
||||
|
||||
There is not currently a CPython implementation; however, a simulation
|
||||
|
@ -91,15 +97,16 @@ Reference Implementation
|
|||
|
||||
|
||||
|
||||
Specification for a new builtin:
|
||||
Specification for a new builtin [ACCEPTED PROPOSAL]:
|
||||
|
||||
def indexed(collection, start=0, stop=None):
|
||||
'Generates an indexed series: (0,seqn[0]), (1,seqn[1]) ...'
|
||||
gen = iter(collection)
|
||||
cnt = start
|
||||
while stop is None or cnt<stop:
|
||||
yield (cnt, gen.next())
|
||||
cnt += 1
|
||||
|
||||
def iterindexed(collection):
|
||||
'Generates an indexed series: (0,seqn[0]), (1,seqn[1]) ...'
|
||||
i = 0
|
||||
it = iter(collection)
|
||||
while 1:
|
||||
yield (i, it.next())
|
||||
i += 1
|
||||
|
||||
|
||||
Note A: PEP 212 Loop Counter Iteration [2] discussed several
|
||||
|
@ -115,7 +122,7 @@ Specification for a new builtin:
|
|||
|
||||
There are other PEPs which touch on related issues: integer iterators,
|
||||
integer for-loops, and one for modifying the arguments to range and
|
||||
xrange. The indexed() proposal does not preclude the other proposals
|
||||
xrange. The iterindexed() proposal does not preclude the other proposals
|
||||
and it still meets an important need even if those are adopted -- the need
|
||||
to count items in any iterable. The other proposals give a means of
|
||||
producing an index but not the corresponding value. This is especially
|
||||
|
@ -129,19 +136,46 @@ Specification for a new builtin:
|
|||
main argument for a builtin was that the function is destined to be
|
||||
part of a core programming style, applicable to any object with an
|
||||
iterable interface. Just as zip() solves the problem of looping
|
||||
over multiple sequences, the indexed() function solves the loop
|
||||
over multiple sequences, the iterindexed() function solves the loop
|
||||
counter problem.
|
||||
|
||||
If only one builtin is allowed, then indexed() is the most important
|
||||
If only one builtin is allowed, then iterindexed() is the most important
|
||||
general purpose tool, solving the broadest class of problems while
|
||||
improving program brevity, clarity and reliability.
|
||||
|
||||
|
||||
Note C: Various alternative names have been proposed:
|
||||
|
||||
iterindexed()-- five syllables is a mouthfull
|
||||
index() -- nice verb but could be confused the .index() method
|
||||
indexed() -- widely liked however adjectives should be avoided
|
||||
count() -- direct and explicit but often used in other contexts
|
||||
itercount() -- direct, explicit and hated by more than one person
|
||||
enumerate() -- a contender but doesn't mention iteration or indices
|
||||
iteritems() -- conflicts with key:value concept for dictionaries
|
||||
|
||||
|
||||
Note D: This function was originally proposed with optional start and
|
||||
stop arguments. GvR pointed out that the function call
|
||||
iterindexed(seqn,4,6) had an alternate, plausible interpretation as a
|
||||
slice that would return the fourth and fifth elements of the sequence.
|
||||
To avoid the ambiguity, the optional arguments were dropped eventhough
|
||||
it meant losing flexibity as a loop counter. That flexiblity was most
|
||||
important for the common case of counting from one, as in:
|
||||
for linenum, line in iterindexed(source): print linenum, line
|
||||
|
||||
|
||||
Comments from GvR: filter and map should die and be subsumed into list
|
||||
comprehensions, not grow more variants. I'd rather introduce builtins
|
||||
that do iterator algebra (e.g. the iterzip that I've often used as
|
||||
an example).
|
||||
|
||||
I like the idea of having some way to iterate over a sequence and
|
||||
its index set in parallel. It's fine for this to be a builtin.
|
||||
|
||||
I don't like the name "indexed"; adjectives do not make good
|
||||
function names. Maybe iterindexed()?
|
||||
|
||||
Comments from Ka-Ping Yee: I'm also quite happy with everything you
|
||||
proposed ... and the extra builtins (really 'indexed' in particular)
|
||||
are things I have wanted for a long time.
|
||||
|
@ -158,8 +192,8 @@ Specification for a new builtin:
|
|||
obsolete. Adding a separate module for iterator utilities seems like
|
||||
a good idea.
|
||||
|
||||
Comments from the Community: The response to the indexed() proposal has
|
||||
been close to 100% favorable. Almost everyone loves the idea.
|
||||
Comments from the Community: The response to the iterindexed() proposal
|
||||
has been close to 100% favorable. Almost everyone loves the idea.
|
||||
|
||||
Author response: Prior to these comments, four builtins were proposed.
|
||||
After the comments, xmap xfilter and xzip were withdrawn. The one
|
||||
|
@ -173,7 +207,7 @@ Specification for a new builtin:
|
|||
|
||||
|
||||
|
||||
Specification for Generator Comprehensions:
|
||||
Specification for Generator Comprehensions [REJECTED PROPOSAL]:
|
||||
|
||||
If a list comprehension starts with a 'yield' keyword, then
|
||||
express the comprehension with a generator. For example:
|
||||
|
@ -237,6 +271,15 @@ Specification for Generator Comprehensions:
|
|||
language, where most algorithms use lazy infinite sequences, and I
|
||||
just don't think that's where its future lies.
|
||||
|
||||
I don't think it's worth the trouble. I expect it will take a lot
|
||||
of work to hack it into the code generator: it has to create a
|
||||
separate code object in order to be a generator. List
|
||||
comprehensions are inlined, so I expect that the generator
|
||||
comprehension code generator can't share much with the list
|
||||
comprehension code generator. And this for something that's not
|
||||
that common and easily done by writing a 2-line helper function.
|
||||
IOW the ROI isn't high enough.
|
||||
|
||||
Comments from Ka-Ping Yee: I am very happy with the things you have
|
||||
proposed in this PEP. I feel quite positive about generator
|
||||
comprehensions and have no reservations. So a +1 on that.
|
||||
|
@ -277,103 +320,6 @@ Specification for Generator Comprehensions:
|
|||
embedded yield.
|
||||
|
||||
|
||||
|
||||
Specification for Generator Exception Passing:
|
||||
|
||||
Add a .throw(exception) method to the generator interface:
|
||||
|
||||
def logger():
|
||||
start = time.time()
|
||||
log = []
|
||||
try:
|
||||
while 1:
|
||||
log.append( time.time() - start )
|
||||
yield log[-1]
|
||||
except WriteLog:
|
||||
return 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. This is a true deficiency. 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 [1]. Without .throw(), the current work-around
|
||||
forces the resolution or clean-up code to be moved outside the generator.
|
||||
|
||||
|
||||
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 seem to fit
|
||||
as well as throw().
|
||||
|
||||
|
||||
Note B: The 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()
|
||||
|
||||
|
||||
Comments from GvR: I'm not convinced that the cleanup problem that
|
||||
this is trying to solve exists in practice. I've never felt the need
|
||||
to put yield inside a try/except. I think the PEP doesn't make enough
|
||||
of a case that this is useful.
|
||||
|
||||
Comments from Ka-Ping Yee: I agree that the exception issue needs to
|
||||
be resolved and [that] you have suggested a fine solution.
|
||||
|
||||
Comments from Neil Schemenauer: The exception passing idea is one I
|
||||
hadn't thought of before and looks interesting. If we enable the
|
||||
passing of values back, then we should add this feature too.
|
||||
|
||||
Comments for Magnus Lie Hetland: Even though I cannot speak for the
|
||||
ease of implementation, I vote +1 for the exception passing mechanism.
|
||||
|
||||
Comments from the Community: The response has been mostly favorable. One
|
||||
negative comment from GvR is shown above. The other was from Martin von
|
||||
Loewis who was concerned that it could be difficult to implement and
|
||||
is withholding his support until a working patch is available. To probe
|
||||
Martin's comment, I checked with the implementers of the original
|
||||
generator PEP for an opinion on the ease of implementation. They felt that
|
||||
implementation would be straight-forward and could be grafted onto the
|
||||
existing implementation without disturbing its internals.
|
||||
|
||||
Author response: When the sole use of generators is to simplify writing
|
||||
iterators for lazy producers, then the odds of needing generator
|
||||
exception passing are slim. If, on the other hand, generators
|
||||
are used to write lazy consumers, create coroutines, generate output
|
||||
streams, or simply for their marvelous capability for restarting a
|
||||
previously frozen state, THEN the need to raise exceptions will
|
||||
come up frequently.
|
||||
|
||||
I'm no judge of what is truly Pythonic, but am still astonished
|
||||
that there can exist blocks of code that can't be excepted to or
|
||||
through, that the try/finally combination is blocked, and that the
|
||||
only work-around is to rewrite as a class and move the exception
|
||||
code out of the function or method being excepted.
|
||||
|
||||
|
||||
|
||||
References
|
||||
|
||||
[1] PEP 255 Simple Generators
|
||||
|
|
Loading…
Reference in New Issue