153

Is there a straight-forward expression that can produce an infinite iterator?

This is a purely theoretical question. No need for a "practical" answer here :)


For example, it is easy to use a generator expression to make a finite iterator:

my_gen = (0 for i in xrange(42))

However, to make an infinite one I need to "pollute" my namespace with a bogus function:

def _my_gen():
    while True:
        yield 0
my_gen = _my_gen()

Doing things in a separate file and import-ing later doesn't count.


I also know that itertools.repeat does exactly this. I'm curious if there is a one-liner solution without that.

user2357112
  • 260,549
  • 28
  • 431
  • 505
hugomg
  • 68,213
  • 24
  • 160
  • 246

7 Answers7

283

itertools provides three infinite iterators:

I don't know of any others in the standard library.


Since you asked for a one-liner:

__import__("itertools").count()
user2357112
  • 260,549
  • 28
  • 431
  • 505
Katriel
  • 120,462
  • 19
  • 136
  • 170
  • 35
    Re: repeat(x, times=∞) - there is no `∞` symbol for whoever wondered - omitting the argument makes repeat run for ever – Mr_and_Mrs_D Apr 02 '16 at 13:21
  • 1
    Upvoted because (while ncoghlan's answer directly addresses the OP's question) this is more generally applicable. – Huw Walters Jan 23 '18 at 08:26
  • 8
    This is a heck of a lot more readable than the `iter(int, 1)` incantation. Too bad `itertools` doesn't have an `endlessly()` method whose sole purpose is doing this; `itertools.count()` isn't all that readable either. – BallpointBen Sep 04 '18 at 16:17
169
for x in iter(int, 1): pass
  • Two-argument iter = zero-argument callable + sentinel value
  • int() always returns 0

Therefore, iter(int, 1) is an infinite iterator. There are obviously a huge number of variations on this particular theme (especially once you add lambda into the mix). One variant of particular note is iter(f, object()), as using a freshly created object as the sentinel value almost guarantees an infinite iterator regardless of the callable used as the first argument.

ncoghlan
  • 40,168
  • 10
  • 71
  • 80
  • 4
    very interesting way to use `iter` with property of `int` which we many a times forget. – Senthil Kumaran Apr 21 '11 at 03:53
  • 4
    you can use this magic recipe to simulate `itertools.count`: `count = lambda start=0, step=1: (start + i*step for i, _ in enumerate(iter(int, 1)))` – Coffee_Table Aug 13 '18 at 23:43
  • 15
    Just to explain what is going on here: When the `iter`-function is called with two arguments, it behaves a little different than normally: `iter(callable, sentinel) -> iterator`. Argument 1, `callable` is called for every iteration of the iterator, *until* it returns the value of `sentinel`. However, as `int()` will always return `0`, we can call `int()` forever and never reach 1. This will in effect produce an infinite list of `0`'s – Olsgaard Apr 14 '20 at 08:47
  • Note that while very concise, the overhead here is not trivial. You pay the overhead of the callable each iteration, which can trigger object creation each time if doing something like a dict. This is ~2x as slow as just using an already initialized while-true generator yielding a pre-allocated singleton object. – Matviy Kotoniy Jan 03 '23 at 17:58
26

you can iterate over a callable returning a constant always different than iter()'s sentinel

g1=iter(lambda:0, 1)
sigjuice
  • 28,661
  • 12
  • 68
  • 93
user237419
  • 8,829
  • 4
  • 31
  • 38
  • 15
    I both love and hate this... I love that it accomplishes what I want in so few characters, but hate how nobody is ever going to look at it and know what it's supposed to do. – ArtOfWarfare Jun 05 '17 at 02:33
  • 2
    knowing the syntax of `iter` (here with extra sentinel) and the syntax of `lambda` (here without any passed parameters, just `return 0`), the only place to hate is that enigmatic `g1`. – Sławomir Lenart Mar 11 '19 at 17:56
10

Your OS may provide something that can be used as an infinite generator. Eg on linux

for i in (0 for x in open('/dev/urandom')):
    print i

obviously this is not as efficient as

for i in __import__('itertools').repeat(0)
    print i
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
7

Quite ugly and crazy (very funny however), but you can build your own iterator from an expression by using some tricks (without "polluting" your namespace as required):

{ print("Hello world") for _ in
    (lambda o: setattr(o, '__iter__', lambda x:x)
            or setattr(o, '__next__', lambda x:True)
            or o)
    (type("EvilIterator", (object,), {}))() } 
Thomas Baruchel
  • 7,236
  • 2
  • 27
  • 46
  • 1
    @Faissaloo Indeed... You may find a still more insane expression on an old page I wrote: https://baruchel.github.io/python/2018/06/20/python-exceptions-in-lambda/ – Thomas Baruchel Jul 21 '20 at 17:03
  • No lambda tricks required. You may simply set the lambda functions whithin the type call: `type("LokisInfinity", (), {'__iter__': lambda x:x, '__next__': lambda x:True})()` fits neatly into 80 chars – Teck-freak May 10 '23 at 06:05
6

None that doesn't internally use another infinite iterator defined as a class/function/generator (not -expression, a function with yield). A generator expression always draws from anoter iterable and does nothing but filtering and mapping its items. You can't go from finite items to infinite ones with only map and filter, you need while (or a for that doesn't terminate, which is exactly what we can't have using only for and finite iterators).

Trivia: PEP 3142 is superficially similar, but upon closer inspection it seems that it still requires the for clause (so no (0 while True) for you), i.e. only provides a shortcut for itertools.takewhile.

  • As I suspected... Can we be sure then that there isn't a readily available infinite generator to abuse? (Sadly, xrange(0,1,-1) doesn't work...) – hugomg Apr 20 '11 at 23:50
  • 2
    @missingno: `from itertools import repeat, count, cycle` probably counts as "readily available" to most people. – ncoghlan Apr 21 '11 at 03:33
  • 1
    Oops, I forgot about 2-argument `iter`. Infinite iterators are actually available as a builtin - see my answer :) – ncoghlan Apr 21 '11 at 03:46
2

Maybe you could use decorators like this for example:

def generator(first):
    def wrap(func):
        def seq():
            x = first
            while True:
                yield x
                x = func(x)
        return seq
    return wrap

Usage (1):

@generator(0)
def blah(x):
    return x + 1

for i in blah():
    print i

Usage (2)

for i in generator(0)(lambda x: x + 1)():
    print i

I think it could be further improved to get rid of those ugly (). However it depends on the complexity of the sequence that you wish to be able to create. Generally speaking if your sequence can be expressed using functions, than all the complexity and syntactic sugar of generators can be hidden inside a decorator or a decorator-like function.

julx
  • 8,694
  • 6
  • 47
  • 86
  • 12
    OP asks for a oneliner and you present a 10-line decorator with triple-nested `def` and closure? ;) –  Apr 20 '11 at 22:28
  • 2
    @delnan Well but if you define the decorator once, you can have your one liners, can't you? As I understand the purpose is to have each additional infinite generator implemented in one line. And this is what is presented here. You can have `(2^x)`, you can have `(x)`. If you improve it a little possibly also fibonacci, etc. – julx Apr 20 '11 at 22:33
  • Doesn't answer my question, but then how can you not love all those fluffy closures? BTW, I'm pretty sure you can get rid of the extra parens by getting rid of `seq` and indenting back the code directly to `wrap` – hugomg Apr 20 '11 at 23:54