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.
for x in iter(int, 1): pass
iter
= argument à zéro argument + valeur sentinelleint()
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.
itertools
fournit trois générateurs infinis:
count(start=0, step=1)
: 0, 1, 2, 3, 4, ...
cycle(p)
: p [0], p [1], ..., p [-1], p [0], ...
repeat(x, times=∞)
: x, x, x, x, ...
Je n'en connais pas d'autres dans la bibliothèque standard.
Depuis que vous avez demandé un one-liner:
__import__("itertools").count()
vous pouvez parcourir un callable retournant une constante toujours différente de la sentinelle d'iter ()
g1=iter(lambda:0,1)
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
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
.
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,), {}))() }
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.