Existe uma expressão para um gerador infinito?

119

Existe uma expressão geradora direta que pode produzir elementos infinitos?

Esta é uma questão puramente teórica. Não há necessidade de uma resposta "prática" aqui :)


Por exemplo, é fácil fazer um gerador finito:

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

No entanto, para fazer um infinito, preciso "poluir" meu namespace com uma função falsa:

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

Fazer as coisas em um arquivo separado e fazer importmais tarde não conta.


Eu também sei que itertools.repeatfaz exatamente isso. Estou curioso para saber se existe uma solução de uma linha sem isso.

hugomg
fonte
9
Na verdade, você não precisa poluir seu namespace ... basta nomear a função my_gene depois fazer my_gen = my_gen().
6502 de
2
você também pode usar del _my_gense não quiser confundir os dois
John La Rooy

Respostas:

133
for x in iter(int, 1): pass
  • Dois argumentos iter= zero argumento chamável + valor sentinela
  • int() sempre retorna 0

Portanto, iter(int, 1)é um iterador infinito. Obviamente, há um grande número de variações sobre este tema em particular (especialmente quando você adiciona lambdaà mistura). Uma variante de nota particular é iter(f, object()), como usar um objeto recém-criado como o valor sentinela quase garante um iterador infinito, independentemente do chamável usado como o primeiro argumento.

ncoghlan
fonte
3
maneira muito interessante de usar itercom propriedade da intqual muitas vezes esquecemos.
Senthil Kumaran,
3
você pode usar esta receita 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ó para explicar o que está acontecendo aqui: Quando o iter-função é chamado com dois argumentos, ele se comporta um pouco diferente do que normalmente: iter(callable, sentinel) -> iterator. O argumento 1 callableé chamado para cada iteração do iterador, até que retorne o valor de sentinel. No entanto, como int()sempre retornará 0, podemos ligar int()para sempre e nunca chegar a 1. Isso produzirá uma lista infinita de 0's
Olsgaard
217

itertools fornece três geradores infinitos:

Não conheço nenhum outro na biblioteca padrão.


Já que você pediu um one-liner:

__import__("itertools").count()
Katriel
fonte
18
Re: repeat (x, times = ∞) - não há símbolo para quem quer saber - omitir o argumento faz repetir rodar para sempre
Mr_and_Mrs_D
Votos positivos porque (embora a resposta de ncoghlan trate diretamente da pergunta do OP) é ​​mais aplicável em geral.
Huw Walters
Isso é muito mais legível do que o iter(int, 1)encantamento. Uma pena itertoolsnão ter um endlessly()método cujo único propósito é fazer isso; itertools.count()não é tão legível.
BallpointBen
19

você pode iterar sobre um callable retornando uma constante sempre diferente da sentinela de iter ()

g1=iter(lambda:0, 1)
user237419
fonte
8
Eu amo e odeio isso ... Eu amo que ele realize o que eu quero em tão poucos personagens, mas odeio como ninguém vai olhar para ele e saber o que ele deve fazer.
ArtOfWarfare
1
sabendo a sintaxe de iter(aqui com sentinela extra) e a sintaxe de lambda(aqui sem nenhum parâmetro passado, apenas return 0), o único lugar para odiar é esse enigmático g1.
Sławomir Lenart
@ SławomirLenart homens nunca entendem. era apenas embaraçosamente pequeno, então esguichei 1g.
user237419
8

Seu sistema operacional pode fornecer algo que pode ser usado como um gerador infinito. Por exemplo, no Linux

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

obviamente, isso não é tão eficiente quanto

for i in __import__('itertools').repeat(0)
    print i
John La Rooy
fonte
11
A solução / dev / urandom depende de \naparecerem de vez em quando ... Desonesto! :)
hugomg
5

Nenhum que não use internamente outro iterador infinito definido como uma classe / função / gerador (não -expressão, uma função com yield). Uma expressão geradora sempre extrai de outra iterável e não faz nada além de filtrar e mapear seus itens. Você não pode ir de itens finitos para infinitos com apenas mape filter, você precisa while(ou um forque não termina, que é exatamente o que não podemos ter usando apenasfor iteradores e finitos).

Curiosidades: PEP 3142 é superficialmente semelhante, mas após uma inspeção mais detalhada, parece que ainda requer a forcláusula (portanto, não (0 while True)para você), ou seja, fornece apenas um atalho para itertools.takewhile.


fonte
Como eu suspeitava ... Podemos ter certeza de que não há um gerador infinito prontamente disponível para abuso? (Infelizmente, xrange (0,1, -1) não funciona ...)
hugomg de
2
@missingno: from itertools import repeat, count, cycleprovavelmente conta como "prontamente disponível" para a maioria das pessoas.
ncoghlan
1
Ups, esqueci-me dos 2 argumentos iter. Os iteradores infinitos estão realmente disponíveis como embutidos - veja minha resposta :)
ncoghlan
5

Bastante feio e louco (muito engraçado no entanto), mas você pode construir seu próprio iterador a partir de uma expressão usando alguns truques (sem "poluir" seu namespace conforme necessário):

{ 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
fonte
Você claramente ama LISP
Faissaloo
1
@Faissaloo Certamente ... Você pode encontrar uma expressão ainda mais insana em uma página antiga que escrevi: baruchel.github.io/python/2018/06/20/…
Thomas Baruchel
2

Talvez você possa usar decoradores como este, por exemplo:

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

Acho que poderia ser melhorado para se livrar dessas coisas feias (). No entanto, depende da complexidade da sequência que você deseja criar. Falando de maneira geral, se sua sequência pode ser expressa usando funções, então toda a complexidade e açúcar sintático dos geradores podem ser escondidos dentro de um decorador ou de uma função do tipo decorador.

julx
fonte
9
OP pede um oneliner e você apresenta um decorador de 10 linhas com aninhamento triplo defe fechamento? ;)
2
@delnan Bem, mas se você definir o decorador uma vez, você pode ter seus one liners, não pode? Pelo que entendi, o objetivo é ter cada gerador infinito adicional implementado em uma linha. E é isso que é apresentado aqui. Você pode ter (2^x), você pode ter (x). Se você melhorar um pouco, possivelmente também fibonacci, etc.
julho de
Não responde minha pergunta, mas então como você pode não amar todos aqueles fechos fofos? A propósito, tenho certeza de que você pode se livrar dos parênteses extras eliminando seqe recuando o código diretamente parawrap
hugomg de