[Python-ideas] Saving state in list/generator comprehension
Steven D'Aprano
steve at pearwood.info
Mon Jun 11 04:01:38 CEST 2012
Andrew Carter wrote:
> Forgive me for any problems in this e-mail as I'm new to this mailing list.
>
> I thought it might be nice to be able to somehow save a state in
> list/generator comprehensions,
> a side effect of this (although not the intended goal) is it would make
> reduce feasible in a clean manner as the final result would just be the
> state.
reduce already exists; in Python 2, it is a built-in available at all times,
in Python 3 it has been banished to the functools module.
What is your use-case for this? "Saving state" is a means to an end. The
beauty of list comprehensions and generator expressions is that they are
intentionally quite simple and limited. If you need something more complex,
write a function or generator. Not everything has to be a (very-long and
unreadable) one-linear.
reduce already exists, but if it didn't, you could write it quite easily.
Here's a version with optional starting value which yields the intermediate
results:
import itertools
_MISSING = object() # sentinel value
def foldl(func, iterable, start=_MISSING):
# foldr is left as an exercise :-)
if start is _MISSING:
it = iter(iterable)
else:
it = itertools.chain([start], iterable)
a = next(it) # raises if iterable is empty and start not given
try:
b = next(it)
except StopIteration:
yield a
return
a = func(a, b)
yield a
for b in it:
a = func(a, b)
yield a
Modifying this to return just the last value is easy, and in fact is simpler
than the above:
def foldl(func, iterable, start=_MISSING):
if start is _MISSING:
it = iter(iterable)
else:
it = itertools.chain([start], iterable)
a = next(it)
for b in it:
a = func(a, b)
return a
[...]
> Some of the pros of doing it this way is that because with/as are already
> keywords in python backwards compatibility shouldn't be an issue,
That's not an argument in favour of your request. That's merely the lack of
one specific argument against it. There are an infinite number of things which
could be done that won't break backwards compatibility, but that doesn't mean
we should do them all. What positive arguments in favour of your proposal do
you have? What does your proposal allow us to do that we can't already do, or
at least do better?
> p.s. Is there a built-in way to get the last element from a generator
> (perhaps even with a default) a quick google search did not reveal one?
The same as you would get the last element from any iterator, not just
generators: iterate over it as quickly as possible, keeping only the last
value seen. Because generator values are generated lazily as needed, there's
no direct way to skip to the last value, or get random access to them.
In pure Python:
for x in iterator:
pass
This may be faster:
collections.deque(iterator, maxlen=1)[0]
Of course, both examples assume that the iterator or generator yields at least
one value, and is not infinite.
--
Steven
More information about the Python-ideas
mailing list