J'ai une fonction de générateur comme celle-ci:
def myfunct():
...
yield result
La manière habituelle d'appeler cette fonction serait:
for r in myfunct():
dostuff(r)
Ma question, est-il possible d'obtenir un seul élément du générateur quand je le souhaite? Par exemple, j'aimerais faire quelque chose comme:
while True:
...
if something:
my_element = pick_just_one_element(myfunct())
dostuff(my_element)
...
Créer un générateur en utilisant
g = myfunct()
Chaque fois que vous souhaitez un article, utilisez
next(g)
(ou g.next()
dans Python 2.5 ou inférieur).
Si le générateur se ferme, il augmentera StopIteration
. Vous pouvez soit intercepter cette exception si nécessaire, soit utiliser l'argument default
pour next()
:
next(g, default_value)
Pour sélectionner un seul élément d'un générateur, utilisez break
dans une instruction for
ou list(itertools.islice(gen, 1))
.
Selon votre exemple (littéralement), vous pouvez faire comme:
while True:
...
if something:
for my_element in myfunct():
dostuff(my_element)
break
else:
do_generator_empty()
Si vous voulez "récupérez un seul élément de la) [une fois généré] n générateur à ma guise" (Je suppose que 50% de l'objectif initial et l'intention la plus courante), :
gen = myfunct()
while True:
...
if something:
for my_element in gen:
dostuff(my_element)
break
else:
do_generator_empty()
De cette façon, l'utilisation explicite de generator.next()
peut être évitée et le traitement de fin de saisie ne nécessite pas (cryptique) StopIteration
le traitement des exceptions ou des comparaisons de valeurs par défaut supplémentaires.
La section d'instructions else:
de for
n'est nécessaire que si vous souhaitez faire quelque chose de spécial en cas de fin de générateur.
next()
/.next()
:En Python3, la méthode .next()
a été renommée .__next__()
pour une bonne raison: elle est considérée comme de bas niveau (PEP 3114). Avant Python 2.6, la fonction intégrée next()
n'existait pas. Et il a même été discuté de déplacer next()
vers le module operator
(ce qui aurait été sage), en raison de son besoin rare et de l’inflation discutable des noms intégrés.
Utiliser next()
sans défaut est encore une pratique de bas niveau - jeter ouvert le StopIteration
cryptique comme un verrou dans le code d'une application normale. Et utiliser next()
avec sentinel par défaut - ce qui devrait au mieux être la seule option pour un next()
directement dans builtins
- est limité et donne souvent lieu à une logique/lecture illisible non Pythonic.
En bout de ligne: Utiliser next () devrait être très rare - comme avec les fonctions du module operator
. Utiliser for x in iterator
, islice
, list(iterator)
et d'autres fonctions acceptant un itérateur de manière transparente est la manière naturelle d'utiliser des itérateurs au niveau de l'application - et est toujours possible. next()
est de bas niveau, un concept supplémentaire, non évident - comme le montre la question de ce fil. Alors que par exemple L'utilisation de break
dans for
est conventionnelle.
Je ne crois pas qu'il existe un moyen pratique de récupérer une valeur arbitraire à partir d'un générateur. Le générateur fournira une méthode next () pour se parcourir, mais la séquence complète n'est pas produite immédiatement pour économiser de la mémoire. C'est la différence fonctionnelle entre un générateur et une liste.
generator = myfunct()
while True:
my_element = generator.next()
assurez-vous d'attraper l'exception levée après la prise du dernier élément