web-dev-qa-db-fra.com

comment choisir un seul élément d'un générateur (en python)?

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)
  ...
182
Alexandros

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)
260
Sven Marnach

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.

Note sur 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.

28
kxr

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.

2
g.d.d.c
generator = myfunct()
while True:
   my_element = generator.next()

assurez-vous d'attraper l'exception levée après la prise du dernier élément

0
John