views:

163

answers:

6

Currently I'm doing this:

try:
    something = iterator.next()
    # ...
except StopIteration:
    # ...

But I would like an expression that I can place inside a simple if statement. Is there anything built-in which would make this code look less clumsy?

any() returns False if an iterable is empty, but it will potentially iterate over all the items if it's not. I only need it to check the first item.


Someone asks what I'm trying to do. I have written a function which executes an SQL query and yields its results. Sometimes when I call this function I just want to know if the query returned anything and make a decision based on that.

A: 

__length_hint__ estimates the length of list(it) - it's private method, though:

x = iter( (1, 2, 3) )
help(x.__length_hint__)
      1 Help on built-in function __length_hint__:
      2 
      3 __length_hint__(...)
      4     Private method returning an estimate of len(list(it)).
The MYYN
not guaranteed for every iterator.>>> def it():... yield 1... yield 2... yield 3... >>> i = it()>>> i.__length_hint__Traceback (most recent call last): File "<stdin>", line 1, in <module>AttributeError: 'generator' object has no attribute '__length_hint__'
superjoe30
It's also probably legal for it to return 0 for an iterator that has more than zero entries, since it's just a hint.
Glenn Maynard
+7  A: 

This isn't really cleaner, but it shows a way to package it in a function losslessly:

def has_elements(iter):
  from itertools import tee
  iter, any_check = tee(iter)
  try:
    any_check.next()
    return True, iter
  except StopIteration:
    return False, iter

has_el, iter = has_elements(iter)
if has_el:
  # not empty

This isn't really pythonic, and for particular cases, there are probably better (but less general) solutions, like the next default.

first = next(iter, None)
if first:
  # Do something

This isn't general because None can be a valid element in many iterables.

Matthew Flaschen
This is probably the best way of doing this. However, it would help to know what the OP is trying to do? There's probably a more elegant solution (this IS Python, after all).
Bryan Ross
Thanks, I think I'm going to use `next()`.
Bastien Léonard
@Bastien, fine, but do so with an appropriate _sentinel_ (see my answer).
Alex Martelli
+2  A: 

you can use:

if zip([None], iterator):
    # ...
else:
    # ...

but it's a bit nonexplanatory for the code reader

mykhal
.. (you can use any 1-item iterable instead of [None])
mykhal
+1  A: 

This is an overkill iterator wrapper that generally allows to check whether there's a next item (via conversion to boolean). Of course pretty inefficient.

class LookaheadIterator ():

    def __init__(self, iterator):
        self.__iterator = iterator
        try:
            self.__next      = next (iterator)
            self.__have_next = True
        except StopIteration:
            self.__have_next = False

    def __iter__(self):
        return self

    def next (self):
        if self.__have_next:
            result = self.__next
            try:
                self.__next      = next (self.__iterator)
                self.__have_next = True
            except StopIteration:
                self.__have_next = False

            return result

        else:
            raise StopIteration

    def __nonzero__(self):
        return self.__have_next

x = LookaheadIterator (iter ([]))
print bool (x)
print list (x)

x = LookaheadIterator (iter ([1, 2, 3]))
print bool (x)
print list (x)

Output:

False
[]
True
[1, 2, 3]
doublep
+7  A: 

In Python 2.6+, if name sentinel is bound to a value which the iterator can't possibly yield,

if next(iterator, sentinel) is sentinel:
    print('iterator was empty')

If you have no idea of what the iterator might possibly yield, make your own sentinel (e.g. at the top of your module) with

sentinel = object()

Otherwise, you could use, in the sentinel role, any value which you "know" (based on application considerations) that the iterator can't possibly yield.

Alex Martelli
+2  A: 

any won't go beyond the first element if it's True. In case the iterator yields something false-ish you can write any(True for _ in iterator).

THC4k
Mmh nice, I didn't think of that.
Bastien Léonard
I don't know why this answer didn't get more upvotes.
Bastien Léonard
Bastien Léonard: Me neither, it is a nice idea =)
THC4k