122 lines
3.5 KiB
Plaintext
122 lines
3.5 KiB
Plaintext
PEP: 3142
|
|
Title: Add a "while" clause to generator expressions
|
|
Version: $Revision$
|
|
Last-Modified: $Date$
|
|
Author: Gerald Britton <gerald.britton@gmail.com>
|
|
Status: Rejected
|
|
Type: Standards Track
|
|
Content-Type: text/x-rst
|
|
Created: 12-Jan-2009
|
|
Python-Version: 3.0
|
|
Post-History:
|
|
Resolution: https://mail.python.org/pipermail/python-dev/2013-May/126136.html
|
|
|
|
|
|
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`) is a concise method to serve
|
|
dynamically-generated objects to list comprehensions (:pep:`202`).
|
|
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`)::
|
|
|
|
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.
|
|
|
|
|
|
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:
|