1
votes

I am on Python 3.6.7.

I just noticed that a for loop over an empty list does not loop even once. After some thought, that made some sense to me. I.e. a loop over a zero-sized (empty) object returns zero iterations.

iterable = []
for element in iterable:
    pass
print(element)
>>> NameError: name 'element' is not defined

This means that a test inside the loop will not be executed if len(iterable) == 0.

iterable = []
for element in iterable:
    assert isinstance(element, int)
#nothing happens

Then how can I catch this situation?

Is there a compact built-in way to raise an error when my loop does not run because the iterable is empty?

Catching this particular situation requires manually:

  • testing that the iterator is non-empty before the loop
  • testing that the element was defined, after the loop. Neither method seems elegant to me

And I may end up having this test in every single for loop.

assert len(iterable) > 0
#loop

or

#loop
assert "element" in dir() #?
3
instead of assert len(iterator) > 0 you can just use assert iterator - empty iterators are Falsey: bool([]) == False. - CDJB
Note that iterators have neither length nor boolean sense by default. Iterables do, but they are a tiny subset of things that act as iterators. - MisterMiyagi
What exactly is your question? Do you want to catch empty iterators, do you want to assert the nested type of an empty iterator, do you want an example where empty iterators are useful, do you want to raise an exception on empty loops, ...? - MisterMiyagi
MisterMiyagi I have removed my consideration about use of empty iterators. This makes the question asked more clear (Is there a compact built-in way to raise an error when my loop does not run because the iterable is empty?) - Andrea Massetti
MisterMyagi I edited and replaced iterator with iterable - Andrea Massetti

3 Answers

1
votes

There is no builtin way to directly check for empty iterators. Empty iterators are generally not considered exceptional. However, you can define compact helpers to explicitly check for empty iterators.


Detecting empty iterators up-front is generally not possible. In some cases, one can guess from the source of an iterator whether it is empty -- for example, iterators of sequences and mappings are empty if their parent is boolean false. However, iterators themselves do not have any indication for content and may in fact be "empty" unless iterated over.

The only reliable means is to check whether an iterator provides items while iterating. One can define a wrapper iterator that raises an error if an iterator provides no items at all.

def non_empty(iterable):
    """Helper to ensure that ``iterable`` is not empty during iteration"""
    iterator = iter(iterable)
    try:
        yield next(iterator)  # explicitly check first item
    except StopIteration:
        raise LookupError(f'{iterable} is empty') from None
    yield from iterator       # forward iteration of later items

Such a helper can be wrapped around both iterables and iterators, and works in for loops, explicit iter iterators and any other iteration scenario.

>>> iterable = []
>>> for element in non_empty(iterable):
...    assert isinstance(element, int)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in non_empty
LookupError: [] is empty
2
votes

You can use the Statements, and else Clauses on Loops.

Loop statements may have an else clause; it is executed when the loop terminates through exhaustion of the iterable.

For example:

iterator = []
for element in iterator:
    print('This wont print..')
else:
    assert iterator

This will results with:

Traceback (most recent call last):
  File "<pyshell#2>", line 4, in <module>
    assert iterator
AssertionError
2
votes

Why do you need to do something if the loop wont run because the list is empty? It's perfectly normal behavior. If you expect for some reason to have at least one item (maybe having no items in array is a measure of error in your program) then check if len(iterator) == 0 as you suggested earlier. Its a perfectly valid way to do it.

By the way exeptions are the way to go in those cases when you are checking the values of variables etc. Asserts are more of the hard-coded info for critical failures and SHOULD NOT be used for reporting usual errors as they cant be handled.