Corrigez-moi si je me trompe, mais si vous pouviez apporter une solution vraiment générique à n'importe quel générateur, ce serait l'équivalent de définir des points d'arrêt sur les instructions yield et d'avoir la capacité de "reculer". Cela signifierait-il cloner le cadre de la pile sur les rendements et les restaurer sur StopIteration?
Eh bien, je suppose que les restaurer StopIteration ou non, mais au moins StopIteration vous dirait qu'il était vide. Ouais, j'ai besoin de dormir ...
4
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 quelque chose du genre, la liste vide []est idéalement Falsey afin que vous puissiez faire une vérification si et faire un comportement spécial pour quelque chose ou rien. Les générateurs sont vrais même s'ils ne donnent aucun élément.
jpsimons
Voici mon cas d'utilisation ... J'utilise glob.iglob("filepattern")un modèle générique fourni par l'utilisateur et je souhaite avertir l'utilisateur si le modèle ne correspond à aucun fichier. Bien sûr, je peux contourner ce problème de différentes manières, mais il est utile de pouvoir tester proprement si l'itérateur est venu vide ou non.
La réponse simple à votre question: non, il n'y a pas de moyen simple. Il y a beaucoup de solutions de rechange.
Il ne devrait vraiment pas y avoir de moyen simple, à cause de ce que sont les générateurs: un moyen de sortir une séquence de valeurs sans garder la séquence en mémoire . Il n'y a donc pas de traversée en arrière.
Vous pouvez écrire une fonction has_next ou peut-être même l'appliquer à un générateur comme méthode avec un décorateur sophistiqué si vous le souhaitez.
assez juste, cela a du sens. Je savais qu'il n'y avait aucun moyen de trouver la longueur d'un générateur, mais je pensais que j'aurais peut-être manqué un moyen de savoir s'il allait initialement générer quoi que ce soit.
Dan
1
Oh, et pour référence, j'ai essayé d'implémenter ma propre suggestion de "décorateur fantaisie". DUR. Apparemment, copy.deepcopy ne fonctionne pas sur les générateurs.
David Berger
47
Je ne suis pas sûr de pouvoir être d'accord avec "il ne devrait pas y avoir de moyen simple". Il y a beaucoup d'abstractions en informatique qui sont conçues pour sortir une séquence de valeurs sans garder la séquence en mémoire, mais qui permettent au programmeur de demander s'il y a une autre valeur sans la retirer de la «file d'attente» s'il y en a. Il y a une telle chose comme un simple coup d'oeil sans nécessiter de "traversée en arrière". Cela ne veut pas dire qu'une conception d'itérateur doit fournir une telle fonctionnalité, mais c'est certainement utile. Peut-être que vous vous opposez au fait que la première valeur pourrait changer après le coup d'oeil?
LarsH
9
Je m'oppose au motif qu'une implémentation typique ne calcule même pas une valeur tant qu'elle n'est pas nécessaire. On pourrait forcer l'interface à faire cela, mais cela pourrait être sous-optimal pour les implémentations légères.
David Berger
6
@ S.Lott vous n'avez pas besoin de générer la séquence entière pour savoir si la séquence est vide ou non. La valeur de stockage d'un élément est suffisante - voir ma réponse.
Mark Ransom
99
Suggestion:
def peek(iterable):try:
first = next(iterable)exceptStopIteration:returnNonereturn first, itertools.chain([first], iterable)
Usage:
res = peek(mysequence)if res isNone:# sequence is empty. Do stuff.else:
first, mysequence = res
# Do something with first, maybe?# Then iterate over the sequence:for element in mysequence:# etc.
Je ne comprends pas tout à fait l'intérêt de renvoyer le premier élément deux fois return first, itertools.chain([first], rest).
njzk2
6
@ njzk2 J'allais pour une opération "peek" (d'où le nom de la fonction). wiki "Peek est une opération qui renvoie la valeur du haut de la collection sans supprimer la valeur des données"
John Fouhy
Cela ne fonctionnera pas si le générateur est conçu pour ne produire aucun. def gen(): for pony in range(4): yield None if pony == 2 else pony
Paul
4
@Paul Regardez attentivement les valeurs de retour. Si le générateur est terminé - c'est-à-dire qu'il ne retourne pas Nonemais augmente StopIteration- le résultat de la fonction est None. Sinon, c'est un tuple, ce qui ne l'est pas None.
Fund Monica's Lawsuit
Cela m'a beaucoup aidé dans mon projet actuel. J'ai trouvé un exemple similaire dans le code du module de bibliothèque standard de python 'mailbox.py'. This method is for backward compatibility only. def next(self): """Return the next message in a one-time iteration.""" if not hasattr(self, '_onetime_keys'): self._onetime_keys = self.iterkeys() while True: try: return self[next(self._onetime_keys)] except StopIteration: return None except KeyError: continue
pair
29
Une manière simple est d'utiliser le paramètre optionnel pour next () qui est utilisé si le générateur est épuisé (ou vide). Par exemple:
Non. Ceci est incorrect pour tout générateur dont la première valeur fournie est fausse.
mehtunguh
7
Utilisez un object()lieu de classpour en faire une ligne plus courte: _exhausted = object(); if next(iterable, _exhausted) is _exhausted:
Messa
13
next(generator, None) is not None
Ou remplacez, Nonemais quelle que soit la valeur que vous connaissez, ce n'est pas dans votre générateur.
Edit : Oui, cela sautera 1 élément dans le générateur. Souvent, cependant, je vérifie si un générateur est vide uniquement à des fins de validation, alors je ne l'utilise pas vraiment. Ou sinon je fais quelque chose comme:
def foo(self):if next(self.my_generator(),None)isNone:raiseException("Not initiated")for x in self.my_generator():...
Autrement dit, cela fonctionne si votre générateur provient d'une fonction , comme dans generator().
Pourquoi ce n'est pas la meilleure réponse? Au cas où le générateur reviendrait None?
Sait le
8
Probablement parce que cela vous oblige à consommer réellement le générateur au lieu de simplement tester s'il est vide.
bfontaine
3
C'est mauvais car au moment où vous appelez le prochain (générateur, Aucun), vous sauterez 1 élément s'il est disponible
Nathan Do
Correct, vous allez manquer le 1er élément de votre génération et vous allez également consommer votre génération plutôt que de tester si elle est vide.
AJ
12
La meilleure approche, à mon humble 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. À ce stade, thingcontiendra la dernière valeur générée. Si rien n'a été généré, il sera indéfini - sauf si vous avez déjà défini la variable. Vous pouvez vérifier la valeur de thing, mais c'est un peu peu fiable. Au lieu de cela, définissez simplement un drapeau dans le bloc et vérifiez-le ensuite:
ifnot thing_generated:print"Avast, ye scurvy dog!"
Cette solution essaiera de consommer tout le générateur le rendant ainsi inutilisable pour des générateurs infinis.
Viktor Stískala
@ ViktorStískala: Je ne vois pas votre point. Il serait insensé de tester si un générateur infini produisait des résultats.
vezult
Je voulais souligner 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.
Viktor Stískala
1
@ ViktorStískala: Compris. Cependant, mon point est le suivant: généralement, vous voulez réellement fonctionner sur la sortie du générateur. Dans mon exemple, si rien n'est généré, vous le savez maintenant. Sinon, vous opérez sur la sortie générée comme prévu - "L'utilisation du générateur est le test". Pas besoin de tests spéciaux, ni de consommer inutilement la puissance du générateur. J'ai modifié ma réponse pour clarifier cela.
vezult
8
Je déteste offrir une deuxième solution, en particulier celui que je ne me utiliser, mais, si vous absolument deviez le faire 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()exceptStopIteration:print'The generator was empty'
first_item = empty_marker
if first_item isnot 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 pense que ce n'est pas ainsi que les générateurs doivent être utilisés.
Je me rends compte que ce post a 5 ans à ce stade, mais je l'ai trouvé en cherchant une manière idiomatique de le faire, et je n'ai pas vu ma solution postée. Donc pour la postérité:
import itertools
def get_generator():"""
Returns (bool, generator) where bool is true iff the generator is not empty.
"""
gen =(i for i in[0,1,2,3,4])
a, b = itertools.tee(gen)try:
a.next()exceptStopIteration:return(False, b)return(True, b)
Bien sûr, comme je suis sûr que de nombreux commentateurs le souligneront, c'est piraté et ne fonctionne que dans certaines situations limitées (où les générateurs sont sans effets secondaires, par exemple). YMMV.
Cela n'appellera le gengénérateur qu'une seule fois pour chaque élément, donc les effets secondaires ne sont pas un trop gros problème. Mais il stockera une copie de tout ce qui a été extrait du générateur via b, mais pas via a, donc les implications de mémoire sont similaires à une simple exécution list(gen)et à une vérification.
Matthias Fripp
Il a deux problèmes. 1. Cet outil informatique peut nécessiter un stockage auxiliaire important (en fonction de la quantité de données temporaires à stocker). En général, si un itérateur utilise la plupart ou toutes les données avant qu'un autre itérateur ne démarre, il est plus rapide d'utiliser list () au lieu de tee (). 2. Les itérateurs de tee ne sont pas threadsafe. Une RuntimeError peut être déclenchée lors de l'utilisation simultanée d'itérateurs retournés par le même appel tee (), même si l'itérable d'origine est threadsafe.
AJ
3
Désolé pour l'approche évidente, mais la meilleure façon serait de faire:
for item in my_generator:print item
Vous avez maintenant détecté que le générateur est vide pendant que vous l'utilisez. Bien sûr, l'élément ne sera jamais affiché si le générateur est vide.
Cela ne correspond peut-être pas exactement à votre code, mais c'est à cela que sert l'idiome du générateur: itérer, alors peut-être que vous pourriez changer légèrement votre approche, ou ne pas utiliser du tout de générateurs.
Ou ... l'interlocuteur pourrait donner une idée de pourquoi on essaierait de détecter un générateur vide?
S.Lott
vouliez-vous dire "rien ne sera affiché car le générateur est vide"?
SilentGhost
S.Lott. Je suis d'accord. Je ne vois pas pourquoi. Mais je pense que même s'il y avait une raison, le problème serait peut-être mieux tourné pour utiliser chaque élément à la place.
Ali Afshar
1
Cela n'indique pas au programme si le générateur était vide.
Ethan Furman
3
Tout ce que vous avez à faire pour voir si un générateur est vide est d'essayer d'obtenir le résultat suivant. Bien sûr, si vous n'êtes pas prêt à utiliser ce résultat, vous devez le stocker pour le renvoyer plus tard.
Voici une classe wrapper qui peut être ajoutée à un itérateur existant pour ajouter un __nonzero__test, afin que vous puissiez voir si le générateur est vide avec un simple if. Il peut probablement aussi être transformé en décorateur.
Cela va dans la bonne direction. Il doit être modifié pour permettre de jeter un œil aussi loin que vous le souhaitez, en stockant autant de résultats que nécessaire. Idéalement, cela permettrait de pousser des éléments arbitraires sur la tête du flux. Un itérateur poussable est une abstraction très utile que j'utilise souvent.
sfkleach
@sfkleach Je ne vois pas la nécessité de compliquer cela pour un aperçu multiple, c'est assez utile en l'état et répond à la question. Même s'il s'agit d'une vieille question, elle reçoit encore un coup d'œil occasionnel, donc si vous voulez laisser votre propre réponse, quelqu'un pourrait la trouver utile.
Mark Ransom
Mark a tout à fait raison de dire que sa solution répond à la question, qui est le point clé. J'aurais dû mieux le formuler. Ce que je voulais dire, c'est que les itérateurs poussables avec un pushback illimité sont un idiome que j'ai trouvé extrêmement utile et que la mise en œuvre est sans doute encore plus simple. Comme suggéré, je publierai le code de la variante.
sfkleach
2
À la demande de Mark Ransom, voici une classe que vous pouvez utiliser pour encapsuler n'importe quel itérateur afin que vous puissiez jeter un coup d'œil à l'avance, repousser les valeurs dans le flux et vérifier qu'elles sont vides. C'est une idée simple avec une implémentation simple que j'ai trouvée très pratique dans le passé.
classPushable:def __init__(self, iter):
self.source = iter
self.stored =[]def __iter__(self):return self
def __bool__(self):if self.stored:returnTruetry:
self.stored.append(next(self.source))exceptStopIteration:returnFalsereturnTruedef push(self, value):
self.stored.append(value)def peek(self):if self.stored:return self.stored[-1]
value = next(self.source)
self.stored.append(value)return value
def __next__(self):if self.stored:return self.stored.pop()return next(self.source)
>>> g=(i for i in[])>>> g,empty=is_empty_no_side_effects(g)>>> empty
True>>> g=(i for i in range(10))>>> g,empty=is_empty_no_side_effects(g)>>> empty
False>>> list(g)[0,1,2,3,4,5,6,7,8,9]
>>> gen =(i for i in[])>>> next(gen)Traceback(most recent call last):File"<pyshell#43>", line 1,in<module>
next(gen)StopIteration
À la fin du générateur StopIterationest déclenché, car dans votre cas, la fin est immédiatement atteinte, l'exception est levée. Mais normalement, vous ne devriez pas vérifier l'existence de la valeur suivante.
une autre chose que vous pouvez faire est:
>>> gen =(i for i in[])>>>ifnot list(gen):print('empty generator')
Ce qui consomme en fait tout le générateur. Malheureusement, il n'est pas clair d'après la question s'il s'agit d'un comportement souhaitable ou indésirable.
S.Lott
comme toute autre façon de "toucher" le générateur, je suppose.
SilentGhost
Je me rends compte que c'est vieux, mais utiliser 'list ()' ne peut pas être le meilleur moyen, si la liste générée n'est pas vide mais en fait grande, alors c'est inutilement inutile
Chris_Rands
1
Si vous avez besoin de savoir avant d'utiliser le générateur, alors non, il n'y a pas de moyen simple. Si vous pouvez attendre après avoir utilisé le générateur, il existe un moyen simple:
was_empty =Truefor some_item in some_generator:
was_empty =False
do_something_with(some_item)if was_empty:
handle_already_empty_generator_case()
Enveloppez simplement le générateur avec itertools.chain , mettez quelque chose qui représentera la fin de l'itérable comme le deuxième iterable, puis vérifiez simplement cela.
Utilisez eog = object()au lieu de supposer que float('-inf')cela ne se produira jamais dans l'itérable.
bfontaine
@bfontaine Good idea
smac89
1
Dans mon cas, j'avais besoin de savoir si une foule de générateurs était remplie avant de la transmettre à une fonction, qui fusionnait les éléments, c'est-à-dire zip(...). La solution est similaire, mais assez différente, de la réponse acceptée:
def filter_empty(iterables):for iterable in iterables:
itr_has_items, iterable = has_items(iterable)if itr_has_items:yield iterable
def merge_iterables(iterables):
populated_iterables = filter_empty(iterables)for items in zip(*populated_iterables):# Use items for each "slice"
Mon problème particulier a la propriété que les itérables sont vides ou ont exactement le même nombre d'entrées.
Je n'ai trouvé que cette solution fonctionnant également pour les itérations vides.
def is_generator_empty(generator):
a, b = itertools.tee(generator)try:
next(a)exceptStopIteration:returnTrue, b
returnFalse, b
is_empty, generator = is_generator_empty(generator)
Ou si vous ne souhaitez pas utiliser d'exception pour cela, essayez d'utiliser
def is_generator_empty(generator):
a, b = itertools.tee(generator)for item in a:returnFalse, b
returnTrue, b
is_empty, generator = is_generator_empty(generator)
Dans la solution marquée, vous ne pouvez pas l'utiliser pour des générateurs vides comme
Voici mon approche simple que j'utilise pour continuer à renvoyer un itérateur tout en vérifiant si quelque chose a été produit.Je vérifie simplement si la boucle s'exécute:
n =0for key, value in iterator:
n+=1yield key, value
if n ==0:print("nothing found in iterator)
break
Voici un décorateur simple qui enveloppe le générateur, il renvoie donc None s'il est vide. Cela peut être utile si votre code a besoin de savoir si le générateur produira quelque chose avant de le parcourir.
def generator_or_none(func):"""Wrap a generator function, returning None if it's empty. """def inner(*args,**kwargs):# peek at the first item; return None if it doesn't existtry:
next(func(*args,**kwargs))exceptStopIteration:returnNone# return original generator otherwise first item will be missingreturn func(*args,**kwargs)return inner
Usage:
import random
@generator_or_nonedef random_length_generator():for i in range(random.randint(0,10)):yield i
gen = random_length_generator()if gen isNone:print('Generator is empty')
Un exemple où cela est utile est dans la création de modèles de code - c'est-à-dire jinja2
Cela appelle la fonction du générateur deux fois, ce qui entraînera le coût de démarrage du générateur deux fois. Cela pourrait être substantiel si, par exemple, la fonction de générateur est une requête de base de données.
Ian Gold du
0
en utilisant islice, vous n'avez qu'à vérifier jusqu'à la première itération pour découvrir s'il est vide.
à partir d'itertools importer islice
def isempty (iterable):
return list (islice (iterable, 1)) == []
Nous ne pouvons pas utiliser "any ()" pour tout le générateur. J'ai juste essayé de l'utiliser avec un générateur contenant plusieurs dataframes. J'ai reçu ce message "La valeur de vérité d'un DataFrame est ambiguë." sur any (my_generator_of_df)
probitaille
any(generator)fonctionne lorsque vous savez que le générateur générera des valeurs qui peuvent être converties en bool- les types de données de base (par exemple, int, string) fonctionnent. any(generator)sera False lorsque le générateur est vide, ou lorsque le générateur n'a que de fausses valeurs - par exemple, si un générateur va générer 0, `` (chaîne vide) et False, alors il sera toujours False. Cela peut ou non être le comportement prévu, du moment que vous en êtes conscient :)
from cytoolz import peek
from typing importTuple,Iterabledef is_empty_iterator(g:Iterable)->Tuple[Iterable, bool]:try:
_, g = peek(g)return g,FalseexceptStopIteration:return g,True
L'itérateur retourné par cette fonction sera équivalent à l'itérateur d'origine passé en argument.
[]
est idéalement Falsey afin que vous puissiez faire une vérification si et faire un comportement spécial pour quelque chose ou rien. Les générateurs sont vrais même s'ils ne donnent aucun élément.glob.iglob("filepattern")
un modèle générique fourni par l'utilisateur et je souhaite avertir l'utilisateur si le modèle ne correspond à aucun fichier. Bien sûr, je peux contourner ce problème de différentes manières, mais il est utile de pouvoir tester proprement si l'itérateur est venu vide ou non.Réponses:
La réponse simple à votre question: non, il n'y a pas de moyen simple. Il y a beaucoup de solutions de rechange.
Il ne devrait vraiment pas y avoir de moyen simple, à cause de ce que sont les générateurs: un moyen de sortir une séquence de valeurs sans garder la séquence en mémoire . Il n'y a donc pas de traversée en arrière.
Vous pouvez écrire une fonction has_next ou peut-être même l'appliquer à un générateur comme méthode avec un décorateur sophistiqué si vous le souhaitez.
la source
Suggestion:
Usage:
la source
return first, itertools.chain([first], rest)
.def gen(): for pony in range(4): yield None if pony == 2 else pony
None
mais augmenteStopIteration
- le résultat de la fonction estNone
. Sinon, c'est un tuple, ce qui ne l'est pasNone
.This method is for backward compatibility only. def next(self): """Return the next message in a one-time iteration.""" if not hasattr(self, '_onetime_keys'): self._onetime_keys = self.iterkeys() while True: try: return self[next(self._onetime_keys)] except StopIteration: return None except KeyError: continue
Une manière simple est d'utiliser le paramètre optionnel pour next () qui est utilisé si le générateur est épuisé (ou vide). Par exemple:
Edit: Correction du problème signalé dans le commentaire de mehtunguh.
la source
object()
lieu declass
pour en faire une ligne plus courte:_exhausted = object()
;if next(iterable, _exhausted) is _exhausted:
next(generator, None) is not None
Ou remplacez,
None
mais quelle que soit la valeur que vous connaissez, ce n'est pas dans votre générateur.Edit : Oui, cela sautera 1 élément dans le générateur. Souvent, cependant, je vérifie si un générateur est vide uniquement à des fins de validation, alors je ne l'utilise pas vraiment. Ou sinon je fais quelque chose comme:
Autrement dit, cela fonctionne si votre générateur provient d'une fonction , comme dans
generator()
.la source
None
?La meilleure approche, à mon humble avis, serait d'éviter un test spécial. La plupart du temps, l'utilisation d'un générateur est le test:
Si cela ne suffit pas, vous pouvez toujours effectuer un test explicite. À ce stade,
thing
contiendra la dernière valeur générée. Si rien n'a été généré, il sera indéfini - sauf si vous avez déjà défini la variable. Vous pouvez vérifier la valeur dething
, mais c'est un peu peu fiable. Au lieu de cela, définissez simplement un drapeau dans le bloc et vérifiez-le ensuite:la source
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.Je déteste offrir une deuxième solution, en particulier celui que je ne me utiliser, mais, si vous absolument deviez le faire et de ne pas consommer le générateur, comme dans d' autres réponses:
Maintenant, je n'aime vraiment pas cette solution, car je pense que ce n'est pas ainsi que les générateurs doivent être utilisés.
la source
Je me rends compte que ce post a 5 ans à ce stade, mais je l'ai trouvé en cherchant une manière idiomatique de le faire, et je n'ai pas vu ma solution postée. Donc pour la postérité:
Bien sûr, comme je suis sûr que de nombreux commentateurs le souligneront, c'est piraté et ne fonctionne que dans certaines situations limitées (où les générateurs sont sans effets secondaires, par exemple). YMMV.
la source
gen
générateur qu'une seule fois pour chaque élément, donc les effets secondaires ne sont pas un trop gros problème. Mais il stockera une copie de tout ce qui a été extrait du générateur viab
, mais pas viaa
, donc les implications de mémoire sont similaires à une simple exécutionlist(gen)
et à une vérification.Désolé pour l'approche évidente, mais la meilleure façon serait de faire:
Vous avez maintenant détecté que le générateur est vide pendant que vous l'utilisez. Bien sûr, l'élément ne sera jamais affiché si le générateur est vide.
Cela ne correspond peut-être pas exactement à votre code, mais c'est à cela que sert l'idiome du générateur: itérer, alors peut-être que vous pourriez changer légèrement votre approche, ou ne pas utiliser du tout de générateurs.
la source
Tout ce que vous avez à faire pour voir si un générateur est vide est d'essayer d'obtenir le résultat suivant. Bien sûr, si vous n'êtes pas prêt à utiliser ce résultat, vous devez le stocker pour le renvoyer plus tard.
Voici une classe wrapper qui peut être ajoutée à un itérateur existant pour ajouter un
__nonzero__
test, afin que vous puissiez voir si le générateur est vide avec un simpleif
. Il peut probablement aussi être transformé en décorateur.Voici comment vous l'utiliseriez:
Notez que vous pouvez vérifier la vacuité à tout moment, pas seulement au début de l'itération.
la source
À la demande de Mark Ransom, voici une classe que vous pouvez utiliser pour encapsuler n'importe quel itérateur afin que vous puissiez jeter un coup d'œil à l'avance, repousser les valeurs dans le flux et vérifier qu'elles sont vides. C'est une idée simple avec une implémentation simple que j'ai trouvée très pratique dans le passé.
la source
Je suis juste tombé sur ce fil et j'ai réalisé qu'il manquait une réponse très simple et facile à lire:
Si nous ne sommes censés consommer aucun élément, nous devons réinjecter le premier élément dans le générateur:
Exemple:
la source
À la fin du générateur
StopIteration
est déclenché, car dans votre cas, la fin est immédiatement atteinte, l'exception est levée. Mais normalement, vous ne devriez pas vérifier l'existence de la valeur suivante.une autre chose que vous pouvez faire est:
la source
Si vous avez besoin de savoir avant d'utiliser le générateur, alors non, il n'y a pas de moyen simple. Si vous pouvez attendre après avoir utilisé le générateur, il existe un moyen simple:
la source
Enveloppez simplement le générateur avec itertools.chain , mettez quelque chose qui représentera la fin de l'itérable comme le deuxième iterable, puis vérifiez simplement cela.
Ex:
Il ne reste plus qu'à vérifier cette valeur que nous avons ajoutée à la fin de l'itérable, lorsque vous le lirez, cela signifiera la fin
la source
eog = object()
au lieu de supposer quefloat('-inf')
cela ne se produira jamais dans l'itérable.Dans mon cas, j'avais besoin de savoir si une foule de générateurs était remplie avant de la transmettre à une fonction, qui fusionnait les éléments, c'est-à-dire
zip(...)
. La solution est similaire, mais assez différente, de la réponse acceptée:Définition:
Usage:
Mon problème particulier a la propriété que les itérables sont vides ou ont exactement le même nombre d'entrées.
la source
Je n'ai trouvé que cette solution fonctionnant également pour les itérations vides.
Ou si vous ne souhaitez pas utiliser d'exception pour cela, essayez d'utiliser
Dans la solution marquée, vous ne pouvez pas l'utiliser pour des générateurs vides comme
la source
C'est une question ancienne et avec réponse, mais comme personne ne l'a montré auparavant, la voici:
Vous pouvez lire plus ici
la source
Voici mon approche simple que j'utilise pour continuer à renvoyer un itérateur tout en vérifiant si quelque chose a été produit.Je vérifie simplement si la boucle s'exécute:
la source
Voici un décorateur simple qui enveloppe le générateur, il renvoie donc None s'il est vide. Cela peut être utile si votre code a besoin de savoir si le générateur produira quelque chose avant de le parcourir.
Usage:
Un exemple où cela est utile est dans la création de modèles de code - c'est-à-dire jinja2
la source
en utilisant islice, vous n'avez qu'à vérifier jusqu'à la première itération pour découvrir s'il est vide.
la source
Qu'en est-il de l'utilisation de any ()? Je l'utilise avec des générateurs et ça marche très bien. Ici, il y a un gars qui explique un peu à ce sujet
la source
any(generator)
fonctionne lorsque vous savez que le générateur générera des valeurs qui peuvent être converties enbool
- les types de données de base (par exemple, int, string) fonctionnent.any(generator)
sera False lorsque le générateur est vide, ou lorsque le générateur n'a que de fausses valeurs - par exemple, si un générateur va générer 0, `` (chaîne vide) et False, alors il sera toujours False. Cela peut ou non être le comportement prévu, du moment que vous en êtes conscient :)Utilisez la fonction peek dans cytoolz.
L'itérateur retourné par cette fonction sera équivalent à l'itérateur d'origine passé en argument.
la source
Je l'ai résolu en utilisant la fonction somme. Voir ci-dessous un exemple que j'ai utilisé avec glob.iglob (qui renvoie un générateur).
* Cela ne fonctionnera probablement pas pour les générateurs ÉNORMES mais devrait bien fonctionner pour les petites listes
la source