Add PEP 3142.
This commit is contained in:
parent
08ea236bbb
commit
d5afafae8c
|
@ -0,0 +1,127 @@
|
||||||
|
PEP: 3142
|
||||||
|
Title: Add a "while" clause to generator expressions
|
||||||
|
Version: $Revision$
|
||||||
|
Last-Modified: $Date$
|
||||||
|
Author: Gerald Britton <gerald.britton@gmail.com>
|
||||||
|
Status: Draft
|
||||||
|
Type: Standards Track
|
||||||
|
Content-Type: text/plain
|
||||||
|
Created: 12-Jan-2009
|
||||||
|
Python-Version: 3.0
|
||||||
|
Post-History:
|
||||||
|
|
||||||
|
|
||||||
|
Abstract
|
||||||
|
|
||||||
|
This PEP proposes an enhancement to generator expressions, adding a
|
||||||
|
"while" clause to complement the existing "if" clause.
|
||||||
|
|
||||||
|
|
||||||
|
Rationale
|
||||||
|
|
||||||
|
A generator expression (PEP 289 [1]) is a concise method to serve
|
||||||
|
dynamically-generated objects to list comprehensions (PEP 202 [2]).
|
||||||
|
Current generator expressions allow for an "if" clause to filter
|
||||||
|
the objects that are returned to those meeting some set of
|
||||||
|
criteria. However, since the "if" clause is evaluated for every
|
||||||
|
object that may be returned, in some cases it is possible that all
|
||||||
|
objects would be rejected after a certain point. For example:
|
||||||
|
|
||||||
|
g = (n for n in range(100) if n*n < 50)
|
||||||
|
|
||||||
|
which is equivalent to the using a generator function
|
||||||
|
(PEP 255 [3]):
|
||||||
|
|
||||||
|
def __gen(exp):
|
||||||
|
for n in exp:
|
||||||
|
if n*n < 50:
|
||||||
|
yield n
|
||||||
|
g = __gen(iter(range(10)))
|
||||||
|
|
||||||
|
would yield 0, 1, 2, 3, 4, 5, 6 and 7, but would also consider
|
||||||
|
the numbers from 8 to 99 and reject them all since n*n >= 50 for
|
||||||
|
numbers in that range. Allowing for a "while" clause would allow
|
||||||
|
the redundant tests to be short-circuited:
|
||||||
|
|
||||||
|
g = (n for n in range(100) while n*n < 50)
|
||||||
|
|
||||||
|
would also yield 0, 1, 2, 3, 4, 5, 6 and 7, but would stop at 8
|
||||||
|
since the condition (n*n < 50) is no longer true. This would be
|
||||||
|
equivalent to the generator function:
|
||||||
|
|
||||||
|
def __gen(exp):
|
||||||
|
for n in exp:
|
||||||
|
if n*n < 50:
|
||||||
|
yield n
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
g = __gen(iter(range(100)))
|
||||||
|
|
||||||
|
Currently, in order to achieve the same result, one would need to
|
||||||
|
either write a generator function such as the one above or use the
|
||||||
|
takewhile function from itertools:
|
||||||
|
|
||||||
|
from itertools import takewhile
|
||||||
|
g = takewhile(lambda n: n*n < 50, range(100))
|
||||||
|
|
||||||
|
The takewhile code achieves the same result as the proposed syntax,
|
||||||
|
albeit in a longer (some would say "less-elegant") fashion. Also,
|
||||||
|
the takewhile version requires an extra function call (the lambda
|
||||||
|
in the example above) with the associated performance penalty.
|
||||||
|
A simple test shows that:
|
||||||
|
|
||||||
|
for n in (n for n in range(100) if 1): pass
|
||||||
|
|
||||||
|
performs about 10% better than:
|
||||||
|
|
||||||
|
for n in takewhile(lambda n: 1, range(100)): pass
|
||||||
|
|
||||||
|
though they achieve similar results. (The first example uses a
|
||||||
|
generator; takewhile is an iterator). If similarly implemented,
|
||||||
|
a "while" clause should perform about the same as the "if" clause
|
||||||
|
does today.
|
||||||
|
|
||||||
|
The reader may ask if the "if" and "while" clauses should be
|
||||||
|
mutually exclusive. There are good examples that show that there
|
||||||
|
are times when both may be used to good advantage. For example:
|
||||||
|
|
||||||
|
p = (p for p in primes() if p > 100 while p < 1000)
|
||||||
|
|
||||||
|
should return prime numbers found between 100 and 1000, assuming
|
||||||
|
I have a primes() generator that yields prime numbers.
|
||||||
|
|
||||||
|
Adding a "while" clause to generator expressions maintains the
|
||||||
|
compact form while adding a useful facility for short-circuiting
|
||||||
|
the expression.
|
||||||
|
|
||||||
|
|
||||||
|
Acknowledgements
|
||||||
|
|
||||||
|
Raymond Hettinger first proposed the concept of generator
|
||||||
|
expressions in January 2002.
|
||||||
|
|
||||||
|
|
||||||
|
References
|
||||||
|
|
||||||
|
[1] PEP 289: Generator Expressions
|
||||||
|
http://www.python.org/dev/peps/pep-0289/
|
||||||
|
|
||||||
|
[2] PEP 202: List Comprehensions
|
||||||
|
http://www.python.org/dev/peps/pep-0202/
|
||||||
|
|
||||||
|
[3] PEP 255: Simple Generators
|
||||||
|
http://www.python.org/dev/peps/pep-0255/
|
||||||
|
|
||||||
|
|
||||||
|
Copyright
|
||||||
|
|
||||||
|
This document has been placed in the public domain.
|
||||||
|
|
||||||
|
|
||||||
|
Local Variables:
|
||||||
|
mode: indented-text
|
||||||
|
indent-tabs-mode: nil
|
||||||
|
sentence-end-double-space: t
|
||||||
|
fill-column: 70
|
||||||
|
coding: utf-8
|
||||||
|
End:
|
Loading…
Reference in New Issue