234 votes

Comment savoir si un générateur est vide dès le départ ?

Existe-t-il un moyen simple de tester si le générateur n'a pas d'éléments, comme par exemple peek , hasNext , isEmpty ou quelque chose de ce genre ?

0 votes

Corrigez-moi si je me trompe, mais si vous pouviez faire une solution vraiment générique pour tout Dans le cas d'un générateur, cela équivaudrait à placer des points d'arrêt sur les déclarations de rendement et à avoir la possibilité de "revenir en arrière". Cela signifierait-il qu'il faut cloner le cadre de la pile sur les déclarations de rendement et le restaurer sur StopIteration ?

0 votes

Eh bien, je suppose que les restaurer StopIteration ou non, mais au moins StopIteration vous dirait qu'il est vide. Ouais, j'ai besoin de dormir...

4 votes

Je pense que je sais pourquoi il veut ça. Si vous faites du développement web avec des modèles, et que vous passez la valeur de retour dans un modèle comme Cheetah ou autre, une liste vide [] est commodément Falsey de sorte que vous pouvez faire une vérification if sur elle et faire un comportement spécial pour quelque chose ou rien. Les générateurs sont vrais même s'ils ne produisent aucun élément.

146voto

John Fouhy Points 14700

Suggestion :

def peek(iterable):
    try:
        first = next(iterable)
    except StopIteration:
        return None
    return first, itertools.chain([first], iterable)

Utilisation :

res = peek(mysequence)
if res is None:
    # sequence is empty.  Do stuff.
else:
    first, mysequence = res
    # Do something with first, maybe?
    # Then iterate over the sequence:
    for element in mysequence:
        # etc.

3 votes

Je ne comprends pas bien l'intérêt de renvoyer deux fois le premier élément dans le fichier return first, itertools.chain([first], rest) .

11 votes

@njzk2 Je voulais faire une opération de "peek" (d'où le nom de la fonction). wiki "peek est une opération qui retourne la valeur du haut de la collection sans enlever la valeur des données"

2 votes

Cela ne fonctionnera pas si le générateur est conçu pour produire Aucun. def gen(): for pony in range(4): yield None if pony == 2 else pony

77voto

David Berger Points 5459

La réponse simple à votre question : non, il n'y a pas de moyen simple. Il existe un grand nombre de solutions de contournement.

Il ne devrait pas y avoir de moyen simple, étant donné ce que sont les générateurs : un moyen de produire une séquence de valeurs sans garder la séquence en mémoire . Il n'y a donc pas de retour en arrière.

Vous pourriez écrire une fonction has_next ou même la glisser dans un générateur en tant que méthode avec un décorateur fantaisie si vous le souhaitiez.

2 votes

Je savais qu'il n'y avait aucun moyen de trouver la longueur d'un générateur, mais j'ai pensé que j'avais peut-être manqué un moyen de trouver s'il va initialement générer quelque chose.

1 votes

Oh, et pour référence, j'ai essayé de mettre en œuvre ma propre suggestion de "décorateur fantaisiste". DIFFICILE. Apparemment copy.deepcopy ne fonctionne pas sur les générateurs.

0 votes

Vous ne pouvez pas trouver la longueur d'un générateur - vous pouvez seulement exécuter le générateur, et générer la séquence entière en mémoire et voir quelle est la longueur de la séquence.

49voto

razz0 Points 71

Une façon simple est d'utiliser le paramètre optionnel de l'option suivant() qui est utilisé si le générateur est épuisé (ou vide). Par exemple :

iterable = some_generator()

_exhausted = object()

if next(iterable, _exhausted) == _exhausted:
    print('generator is empty')

Edit : Corrigé le problème signalé dans le commentaire de mehtunguh.

1 votes

Non. C'est incorrect pour tout générateur où la première valeur obtenue est fausse.

8 votes

Utilisez un object() au lieu de class pour le rendre plus court d'une ligne : _exhausted = object() ; if next(iterable, _exhausted) is _exhausted:

1 votes

Pourquoi les objets et tout ça ? Tout simplement : if next(itreable,-1) == -1 alors la gen est vide !

23voto

vezult Points 3812

La meilleure approche, à mon avis, serait d'éviter un test spécial. La plupart du temps, l'utilisation d'un générateur est le test :

thing_generated = False

# Nothing is lost here. if nothing is generated, 
# the for block is not executed. Often, that's the only check
# you need to do. This can be done in the course of doing
# the work you wanted to do anyway on the generated output.
for thing in my_generator():
    thing_generated = True
    do_work(thing)

Si cela ne suffit pas, vous pouvez toujours effectuer un test explicite. A ce stade, thing contiendra la dernière valeur générée. Si rien n'a été généré, elle sera indéfinie - à moins que vous n'ayez déjà défini la variable. Vous pouvez vérifier la valeur de thing mais ce n'est pas très fiable. Au lieu de cela, il suffit de définir un drapeau dans le bloc et de le vérifier par la suite :

if not thing_generated:
    print "Avast, ye scurvy dog!"

4 votes

Cette solution tentera de consommer l'ensemble du générateur, ce qui le rendra inutilisable pour les générateurs infinis.

2 votes

@ViktorStískala : Je ne vois pas où vous voulez en venir. Il serait stupide de tester si un générateur infini produit des résultats.

0 votes

Je voulais signaler que votre solution pourrait contenir une rupture dans la boucle for, car vous ne traitez pas les autres résultats et il est inutile qu'ils soient générés. range(10000000) est un générateur fini (Python 3), mais vous n'avez pas besoin de parcourir tous les éléments pour savoir s'il génère quelque chose.

8voto

Ali Afshar Points 22836

Je déteste offrir une deuxième solution, surtout une que je n'utiliserais pas moi-même, mais, si vous devez absolument avait de faire cela et de ne pas consommer le générateur, comme dans d'autres réponses :

def do_something_with_item(item):
    print item

empty_marker = object()

try:
     first_item = my_generator.next()     
except StopIteration:
     print 'The generator was empty'
     first_item = empty_marker

if first_item is not empty_marker:
    do_something_with_item(first_item)
    for item in my_generator:
        do_something_with_item(item)

Maintenant, je n'aime vraiment pas cette solution, car je crois que ce n'est pas ainsi que les générateurs doivent être utilisés.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X