142

Currently I'm doing this:

try:
    something = next(iterator)
    # ...
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? I only need to check for the first item.

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
Bastien Léonard
  • 60,478
  • 20
  • 78
  • 95

8 Answers8

179
if any(True for _ in iterator):
    print('iterator had at least one element')
if all(False for _ in iterator):
    print('iterator was empty')

Note that this will consume the first element of the iterable if it has at least one element.

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
Jochen Ritzel
  • 104,512
  • 31
  • 200
  • 194
  • This seems to work for me, with a re.finditer. You can test that any stops at first success easily: run `any((x > 100 for x in xrange(10000000)))` and then run `any((x > 10000000 for x in xrange(100000000)))` -- the second should take much longer. – chbrown Apr 18 '12 at 19:53
  • 1
    This works for the case of "at least x" `sum(1 for _ in itertools.islice(iterator, max_len)) >= max_len` – Dave Butler Jan 07 '14 at 19:24
  • 14
    Similarly if you need to check if the iterator is empty, one could use `all(False for _ in iterator)` will check if the iterator is empty. (all returns True if the iterator is empty, otherwise it stops when it sees the first False element) – KGardevoir Apr 03 '14 at 21:56
  • 52
    The big problem with this solution is that you can't actually use the returned value from the iterator if it's not empty, right? – Ken Williams Jan 03 '17 at 03:55
  • 10
    NOTE! This consumes the first item in the iterator, so it's lost, gone, you can't use it anymore. – ovimunt Feb 04 '21 at 22:31
  • 2
    You can use `if list(iterator) == []` or `if not any(iterator)` to check if it's empty, or `if any(iterator)` to check if it contains something – Break Feb 08 '21 at 07:49
62

Pass a sentinel value as the default value to next():

sentinel = object()

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

You could also use any value which you "know" (based on application considerations) that the iterator can't possibly yield as the sentinel value.

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 2
    Nice! For my use case, `if not next(iterator, None):` was sufficient as I was sure None would not be part of the items. Thanks for pointing me in the right direction! – wasabigeek Dec 18 '19 at 13:20
  • 2
    @wasabi Remember that `not` will return True for any falsy object, such as empty lists, False, and zero. `is not None` is safer, and in my opinion clearer. – Kyuuhachi Jan 11 '20 at 02:43
28

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
  • 278,309
  • 50
  • 514
  • 539
  • 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). – rossipedia Jun 24 '10 at 22:59
  • 1
    Thanks, I think I'm going to use `next()`. – Bastien Léonard Jun 24 '10 at 23:07
  • 1
    @Bastien, fine, but do so with an appropriate _sentinel_ (see my answer). – Alex Martelli Jun 24 '10 at 23:14
  • 3
    There's a huge memory leak in this solution. The `tee` in itertools will have to keep every single element from the original iterator in case `any_check` ever needs to advance. This is worse than just converting the original iterator to a list. – Rafał Dowgird Mar 03 '11 at 08:54
  • 1
    @RafałDowgird *This is worse than just converting the original iterator to a list.* Not really – think about infinite sequences. – Piotr Dobrogost Mar 15 '16 at 19:02
  • @AlexMartelli - `None` may be a perfectly fine sentinel, e.g. in @BastienLéonard's original use case of checking whether a SQL query has valid results. – Ken Williams Jan 03 '17 at 03:58
16

The best way to do that is with a peekable from more_itertools.

from more_itertools import peekable
iterator = peekable(iterator)
if iterator:
    # Iterator is non-empty.
else:
    # Iterator is empty.

Just beware if you kept refs to the old iterator, that iterator will get advanced. You have to use the new peekable iterator from then on. peekable expects to be the only bit of code modifying that old iterator.

enigmaticPhysicist
  • 1,518
  • 16
  • 21
7

you can use:

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

but it's a bit nonexplanatory for the code reader

mykhal
  • 19,175
  • 11
  • 72
  • 80
6

What about:

In [1]: i=iter([])

In [2]: bool(next(i,False))
Out[2]: False

In [3]: i=iter([1])

In [4]: bool(next(i,False))
Out[4]: True
Neil
  • 7,042
  • 9
  • 43
  • 78
0

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]
  • This seems like the only answer which actually meets the original requirement (w/o non-standard libraries): it lets one check whether there are remaining elements WITHOUT CONSUMING ELEMENTS. Not sure why it's been down-voted. …Of course it only works on your own `LookaheadIterator`s (you can't use it with the iterator you get back from a standard-library), but even so it seems to meet OP's use-case. – not-just-yeti Jul 17 '21 at 18:58
-4

A little late, but... You could turn the iterator into a list and then work with that list:

# Create a list of objects but runs out the iterator.
l = [_ for _ in iterator]

# If the list is not empty then the iterator had elements; else it was empty.
if l :
    pass # Use the elements of the list (i.e. from the iterator)
else :
    pass # Iterator was empty, thus list is empty.
Jens
  • 8,423
  • 9
  • 58
  • 78
  • 4
    This is inefficient because it enumerates the entire list. Won't work for infinite generators. – a06e Dec 22 '15 at 15:41
  • @becko: Agreed. But that doesn't seem to be the case in the original question. – Jens Dec 22 '15 at 18:58
  • 3
    Another problem is that the iterator could generate an infinite amount of objects which can result in memory overflow, and the fact that the program will never reach the next statement – Willem Van Onsem Feb 22 '16 at 11:45