One-liner to check whether an iterator yields at least one element?
Question:
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.
Answers:
__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)).
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.
you can use:
if zip([None], iterator):
# ...
else:
# ...
but it’s a bit nonexplanatory for the code reader
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]
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.
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)
.
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.
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
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.
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.
__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)).
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.
you can use:
if zip([None], iterator):
# ...
else:
# ...
but it’s a bit nonexplanatory for the code reader
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]
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.
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)
.
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.
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
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.