¿Existe una expresión para un generador infinito?

119

¿Existe una expresión generadora sencilla que pueda producir elementos infinitos?

Ésta es una cuestión puramente teórica. No hay necesidad de una respuesta "práctica" aquí :)


Por ejemplo, es fácil hacer un generador finito:

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

Sin embargo, para hacer uno infinito, necesito "contaminar" mi espacio de nombres con una función falsa:

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

Hacer las cosas en un archivo separado y importhacerlas más tarde no cuenta.


También sé que itertools.repeathace exactamente esto. Tengo curiosidad por saber si hay una solución de una sola línea sin eso.

hugomg
fuente
9
En realidad, no necesitas contaminar tu espacio de nombres ... solo nombra la función my_geny luego hazlo my_gen = my_gen().
6502
2
también puedes usarlo del _my_gensi no quieres confundir a los dos
John La Rooy

Respuestas:

133
for x in iter(int, 1): pass
  • Dos argumentos iter= cero argumentos invocables + valor centinela
  • int() siempre vuelve 0

Por tanto, iter(int, 1)es un iterador infinito. Obviamente, hay una gran cantidad de variaciones sobre este tema en particular (especialmente una vez que lo agregas lambdaa la mezcla). Una variante de particular interés es iter(f, object())que el uso de un objeto recién creado como valor centinela casi garantiza un iterador infinito independientemente del invocable utilizado como primer argumento.

ncoghlan
fuente
3
una forma muy interesante de usar itercon propiedad intque muchas veces olvidamos.
Senthil Kumaran
3
puedes usar esta receta mágica para simular itertools.count:count = lambda start=0, step=1: (start + i*step for i, _ in enumerate(iter(int, 1)))
Coffee_Table
1
Sólo para explicar lo que está pasando aquí: Cuando el iter-función se llama con dos argumentos, se comporta un poco diferente de lo normal: iter(callable, sentinel) -> iterator. callableSe llama al argumento 1 para cada iteración del iterador, hasta que devuelve el valor de sentinel. Sin embargo, como int()siempre volverá 0, podemos llamar int()para siempre y nunca llegar a 1. Esto, en efecto, producirá una lista infinita de 0's
Olsgaard
217

itertools proporciona tres generadores infinitos:

No conozco a ningún otro en la biblioteca estándar.


Ya que pediste una sola línea:

__import__("itertools").count()
Katriel
fuente
18
Re: repeat (x, times = ∞) - no hay símbolo para quien se preguntó - omitir el argumento hace que se repita para siempre
Mr_and_Mrs_D
Se votó a favor porque (aunque la respuesta de ncoghlan aborda directamente la pregunta del OP) esto es de aplicación más general.
Huw Walters
Esto es mucho más legible que el iter(int, 1)encantamiento. Lástima itertoolsque no tenga un endlessly()método cuyo único propósito sea hacer esto; itertools.count()tampoco es tan legible.
BallpointBen
19

puede iterar sobre un invocable devolviendo una constante siempre diferente al centinela de iter ()

g1=iter(lambda:0, 1)
user237419
fuente
8
Amo y odio esto ... Amo que logra lo que quiero en tan pocos personajes, pero odio cómo nadie va a mirarlo y saber lo que se supone que debe hacer.
ArtOfWarfare
1
conociendo la sintaxis de iter(aquí con centinela extra) y la sintaxis de lambda(aquí sin ningún parámetro pasado, solo return 0), el único lugar para odiar es ese enigmático g1.
Sławomir Lenart
@ SławomirLenart, los hombres nunca lo entienden. es solo que era vergonzosamente pequeño, así que eché 1 g.
user237419
8

Su sistema operativo puede proporcionar algo que se puede utilizar como un generador infinito. Por ejemplo, en linux

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

obviamente esto no es tan eficiente como

for i in __import__('itertools').repeat(0)
    print i
John La Rooy
fuente
11
La solución / dev / urandom depende de que \naparezcan s de vez en cuando ... ¡Devious! :)
hugomg
5

Ninguno que no use internamente otro iterador infinito definido como una clase / función / generador (no -expresión, una función con yield). Una expresión generadora siempre se basa en otro iterable y no hace más que filtrar y mapear sus elementos. No puede pasar de elementos finitos a infinitos con solo mapy filter, lo necesita while(o un forque no termina, que es exactamente lo que no podemos tener usando solo foriteradores finitos).

Trivia: PEP 3142 es superficialmente similar, pero tras una inspección más cercana parece que todavía requiere la forcláusula (así que no (0 while True)para usted), es decir, solo proporciona un atajo para itertools.takewhile.


fuente
Como sospechaba ... ¿Podemos estar seguros entonces de que no hay un generador infinito fácilmente disponible para abusar? (Lamentablemente, xrange (0,1, -1) no funciona ...)
hugomg
2
@missingno: from itertools import repeat, count, cycleprobablemente cuenta como "fácilmente disponible" para la mayoría de las personas.
ncoghlan
1
Vaya, me olvidé de los 2 argumentos iter. Los iteradores infinitos están realmente disponibles como incorporados; vea mi respuesta :)
ncoghlan
5

Bastante feo y loco (sin embargo, muy divertido), pero puedes construir tu propio iterador a partir de una expresión usando algunos trucos (sin "contaminar" tu espacio de nombres como sea necesario):

{ 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
fuente
Claramente amas LISP
Faissaloo
1
@Faissaloo De hecho ... Puede encontrar una expresión aún más loca en una página vieja que escribí: baruchel.github.io/python/2018/06/20/…
Thomas Baruchel
2

Tal vez podrías usar decoradores como este, por ejemplo:

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

Uso (1):

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

for i in blah():
    print i

Uso (2)

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

Creo que se podría mejorar aún más para deshacernos de esos feos (). Sin embargo, depende de la complejidad de la secuencia que desee poder crear. En términos generales, si su secuencia se puede expresar usando funciones, entonces toda la complejidad y el azúcar sintáctico de los generadores se puede ocultar dentro de un decorador o una función similar al decorador.

julx
fuente
9
OP pide un delineador y presenta un decorador de 10 líneas con triple anidado defy cierre? ;)
2
@delnan Bueno, pero si defines el decorador una vez, puedes tener tus frases breves, ¿no? Según tengo entendido, el propósito es tener cada generador infinito adicional implementado en una línea. Y esto es lo que se presenta aquí. Puedes tener (2^x), puedes tener (x). Si se mejora un poco, posiblemente, también de Fibonacci, etc
julx
No responde a mi pregunta, pero entonces, ¿cómo no amar todos esos cierres esponjosos? Por cierto, estoy bastante seguro de que puede deshacerse de los parientes adicionales al deshacerse seqy sangrar el código directamente awrap
hugomg