Dangerous behavior of list(generator)
steve at REMOVE-THIS-cybersource.com.au
Thu Dec 31 01:01:39 CET 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.
it = iter(iterable_or_sequence)
return it.next() # use next(it) in Python 3
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.
More information about the Python-list