40

I was playing around with iterables and more specifically the yield operator in Python. While using test driven development to start writing a new iterable, I wondered what is the shortest code that could make this simple test for an iterable to pass:

def test():
    for x in my_iterable():
        pass

The shortest version I could think of was:

def my_iterable():
    for i in []:
        yield i

Is it possible to write a simpler, shorter or more beautiful (pythonic) version?

Adam Lindberg
  • 16,447
  • 6
  • 65
  • 85
  • 1
    Note that if your test iterable is empty and doesn't produce anything -- as shown and in most or all of the answers -- your test won't exercise any of the code in the `for` loop. – martineau May 16 '12 at 17:08

6 Answers6

43

Yes, there is:

return iter([])
James Youngman
  • 3,623
  • 2
  • 19
  • 21
29

Another solution, in Python 3, is to use the new yield from syntax:

def empty_gen():
    yield from ()

Which is readable, and keep empty_gen as a generator.

aluriak
  • 5,559
  • 2
  • 26
  • 39
  • 7
    **This is it.** While answers like `lambda: iter(())` satisfy man's instinctual drive for cleverness, they're also unreadable and fail to produce an actual *generator.* In particular, `isinstance(iter(()), types.GeneratorType) == False`. As the relative success of [`mypy`](http://mypy-lang.org/) shows, types sometimes *do* matter. Always `yield from`, Python 3 people. – Cecil Curry Aug 08 '17 at 06:20
14

You can use the lambda and iter functions to create an empty iterable in Python.

my_iterable = lambda: iter(())
octopusgrabbus
  • 10,555
  • 15
  • 68
  • 131
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
11

How about

my_iterable = str

this passes your test.

To speak seriously, Iterable in the collections module provides:

def __iter__(self):
    while False:
        yield None

This can be considered "most pythonic" because this is what python itself uses.

Note that technically all answers so far provide iterators (__iter__ + next), not iterables (just __iter__).

georg
  • 211,518
  • 52
  • 313
  • 390
2
def do_yield():
    return
    yield None

if usage of yield is important for you, one of the other answers otherwise.

glglgl
  • 89,107
  • 13
  • 149
  • 217
0

Another answer, as I provide a completely new solution with a different approach.

In one of by libraries, I have an EmptyIterator such as

class EmptyIter(object):
    __name__ = 'EmptyIter'
    """Iterable which is False and empty"""
    def __len__(self): return 0
    def next(self): raise StopIteration # even that is redundant
    def __getitem__(self, index): raise IndexError

It is an alternative approach which uses the following properties:

  • alternative iteration via the sequence protocol (see here)
  • alternative "falseness" protocol as described here.
glglgl
  • 89,107
  • 13
  • 149
  • 217