web-dev-qa-db-fra.com

Existe-t-il une expression pour un générateur infini?

Existe-t-il une expression de générateur simple qui peut produire des éléments infinis?

Il s'agit d'une question purement théorique. Pas besoin d'une réponse "pratique" ici :)


Par exemple, il est facile de faire un générateur fini:

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

Cependant, pour en créer un infini, je dois "polluer" mon espace de noms avec une fonction bidon:

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

Faire des choses dans un fichier séparé et import- plus tard ne compte pas.


Je sais aussi que itertools.repeat fait exactement cela. Je suis curieux de savoir s'il existe une solution à un revêtement sans cela.

102
hugomg
for x in iter(int, 1): pass
  • iter = argument à zéro argument + valeur sentinelle
  • int() renvoie toujours 0

Par conséquent, iter(int, 1) est un itérateur infini. Il y a évidemment un grand nombre de variations sur ce thème particulier (surtout une fois que vous ajoutez lambda dans le mix). Une variante de note particulière est iter(f, object()), car l'utilisation d'un objet fraîchement créé car la valeur sentinelle garantit presque un itérateur infini indépendamment de l'appelable utilisé comme premier argument.

118
ncoghlan

itertools fournit trois générateurs infinis:

Je n'en connais pas d'autres dans la bibliothèque standard.


Depuis que vous avez demandé un one-liner:

__import__("itertools").count()
178
Katriel

vous pouvez parcourir un callable retournant une constante toujours différente de la sentinelle d'iter ()

g1=iter(lambda:0,1)
16
user237419

Votre système d'exploitation peut fournir quelque chose qui peut être utilisé comme un générateur infini. Par exemple sur Linux

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

évidemment ce n'est pas aussi efficace que

for i in __import__('itertools').repeat(0)
    print i
6
John La Rooy

Aucun qui n'utilise pas en interne un autre itérateur infini défini comme une classe/fonction/générateur (pas -expression, une fonction avec yield). Une expression de générateur puise toujours dans un autre itérable et ne fait que filtrer et mapper ses éléments. Vous ne pouvez pas passer d'éléments finis à des éléments infinis avec seulement map et filter, vous avez besoin de while (ou d'un for qui ne se termine pas, c'est exactement ce que nous ne pouvons pas avoir en utilisant seulement for et des itérateurs finis).

Anecdote: PEP 3142 est superficiellement similaire, mais à y regarder de plus près, il semble qu'il nécessite toujours la clause for (donc pas de (0 while True) pour vous), c'est-à-dire ne fournit qu'un raccourci pour itertools.takewhile.

5
user395760

Assez laid et fou (très drôle cependant), mais vous pouvez créer votre propre itérateur à partir d'une expression en utilisant quelques astuces (sans "polluer" votre espace de noms comme requis):

{ 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,), {}))() } 
4
Thomas Baruchel

Vous pourriez peut-être utiliser des décorateurs comme celui-ci par exemple:

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

Utilisation (1):

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

for i in blah():
    print i

Utilisation (2)

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

Je pense qu'il pourrait être encore amélioré pour se débarrasser de ces vilains (). Cependant, cela dépend de la complexité de la séquence que vous souhaitez pouvoir créer. De manière générale, si votre séquence peut être exprimée à l'aide de fonctions, alors toute la complexité et le sucre syntaxique des générateurs peuvent être cachés dans un décorateur ou une fonction de type décorateur.

2
julkiewicz