Dangerous behavior of list(generator)
Steven D'Aprano
steve at REMOVE-THIS-cybersource.com.au
Wed Dec 30 19:01:39 EST 2009
On Wed, 30 Dec 2009 15:18:11 -0800, Tom Machinski wrote:
> Thanks for the comment and discussion guys.
>
> Bottom line, I'm going to have to remove this pattern from my code:
>
> foo = (foo for foo in foos if foo.bar).next()
I don't see why. What's wrong with it? Unless you embed it in a call to
list, or similar, it will explicitly raise StopIteration as expected.
> I used to have that a lot in cases where not finding at least one valid
> foo is an actual fatal error.
What's wrong with the obvious solution?
if not any(foo for foo in foos if foo.bar):
raise ValueError('need at least one valid foo')
> But using StopIteration to signal a fatal
> condition becomes a bug when interacting with list() as shown in the
> original post.
You shouldn't use StopIteration to signal fatal conditions, because
that's not what it is for. It's acceptable to catch it when *directly*
calling next, but otherwise you should expect that StopIteration will be
caught and suppressed by just about anything.
> It would be nice if there was a builtin for "get the first element in a
> genexp, or raise an exception (which isn't StopIteration)",
Not everything needs to be a built-in.
def get_first_or_fail(iterable_or_sequence):
it = iter(iterable_or_sequence)
try:
return it.next() # use next(it) in Python 3
except StopIteration:
raise ValueError('empty iterable')
This is perfectly usable as a helper function, or it's short enough to be
used in-line if you prefer.
--
Steven
More information about the Python-list
mailing list