"Il est plus facile de demander pardon que la permission."
119
"Il est plus facile de demander pardon que la permission.": Vérifier si un itérateur a un élément suivant ne demande pas la permission. Il existe des situations dans lesquelles vous souhaitez tester l'existence d'un élément suivant sans le consommer. J'accepterais la solution try catch s'il y avait une unnext()méthode pour remettre le premier élément après avoir vérifié qu'il existe en appelant next().
Giorgio
15
@Giorgio, il n'y a aucun moyen de savoir si un autre élément existe sans exécuter le code qui le génère (vous ne savez pas si le générateur s'exécutera yieldou non). Il n'est bien sûr pas difficile d'écrire un adaptateur qui stocke le résultat de next()et fournit has_next()et move_next().
avakar
5
La même idée pourrait être utilisée pour implémenter la hasNext()méthode (pour produire, mettre en cache et renvoyer true en cas de succès, ou renvoyer false en cas d'échec). Ensuite , à la fois hasNext()et next()dépendrait d'un sous - jacent commun getNext()méthode et élément mis en cache. Je ne vois vraiment pas pourquoi next()ne devrait pas être dans la bibliothèque standard s'il est si facile d'implémenter un adaptateur qui le fournit.
Giorgio
3
@LarsH: Vous voulez dire par exemple un itérateur qui lit un fichier qui peut être modifié en lisant? Je suis d'accord que cela peut être un problème (qui affecte n'importe quelle bibliothèque next()et hasNext()méthode, pas seulement une bibliothèque Python hypothétique). Alors oui, next()et cela hasNext()devient délicat si le contenu du flux analysé dépend du moment où les éléments sont lus.
Giorgio
239
Il existe une alternative à l' StopIterationutilisation de next(iterator, default_value).
Par exemple:
>>> a = iter('hi')>>>print next(a,None)
h
>>>print next(a,None)
i
>>>print next(a,None)None
Ainsi, vous pouvez détecter pour Noneou toute autre valeur pré-spécifiée pour la fin de l'itérateur si vous ne voulez pas la manière d'exception.
si vous utilisez None comme "sentinelle", assurez-vous que votre itérateur n'a pas de Nones. vous pouvez également faire sentinel = object()et next(iterator, sentinel)et tester avec is.
sam boosalis
1
suivant @samboosalis, je préférerais utiliser un unittest.mock.sentinelobjet intégré qui vous permet d'écrire un explicite next(a, sentinel.END_OF_ITERATION), puisif next(...) == sentinel.END_OF_ITERATION
ClementWalter
c'est plus joli que l'exception
datdinhquoc
Le problème est que, de cette façon, vous CONSUMEZ également la valeur suivante de l'itérateur. hasNext en Java ne consomme pas la valeur suivante.
Alan Franzoni
39
Si vous avez vraiment besoin d' une has-nextfonctionnalité (parce que vous ne faites que transcrire fidèlement un algorithme à partir d'une implémentation de référence en Java, par exemple, ou parce que vous écrivez un prototype qui devra être facilement transcrit en Java une fois terminé), il est facile de obtenez-le avec une petite classe wrapper. Par exemple:
class hn_wrapper(object):def __init__(self, it):
self.it = iter(it)
self._hasnext =Nonedef __iter__(self):return self
def next(self):if self._hasnext:
result = self._thenext
else:
result = next(self.it)
self._hasnext =Nonereturn result
def hasnext(self):if self._hasnext isNone:try: self._thenext = next(self.it)exceptStopIteration: self._hasnext =Falseelse: self._hasnext =Truereturn self._hasnext
maintenant quelque chose comme
x = hn_wrapper('ciao')while x.hasnext():print next(x)
émet
c
i
a
o
comme demandé.
Notez que l'utilisation de next(sel.it)comme élément intégré nécessite Python 2.6 ou supérieur; si vous utilisez une ancienne version de Python, utilisez à la self.it.next()place (et de la même manière next(x)dans l'exemple d'utilisation). [[Vous pourriez raisonnablement penser que cette note est redondante, puisque Python 2.6 existe depuis plus d'un an maintenant - mais plus souvent qu'autrement lorsque j'utilise les fonctionnalités de Python 2.6 dans une réponse, certains commentateurs ou autres se sentent obligés de signaler qu'il s'agit de fonctionnalités 2.6, j'essaye donc de prévenir de tels commentaires pour une fois ;-)]]
"transcrire fidèlement un algorithme à partir d'une implémentation de référence en Java" est la pire raison d'avoir besoin d'une has_nextméthode. La conception de Python rend impossible, par exemple, d'utiliser filterpour vérifier si un tableau contient un élément correspondant à un prédicat donné. L'arrogance et la myopie de la communauté Python sont stupéfiantes.
Jonathan Cast
belle réponse, je copie ceci pour l'illustration d'un modèle de conception tiré du code Java
madtyn
Je suis avec Python3 et ce code me donneTypeError: iter() returned non-iterator
madtyn
1
@JonathanCast je ne suis pas sûr de suivre. En Python, vous utiliseriez généralement mapet anyau lieu de filter, mais vous pouvez utiliser SENTINEL = object(); next(filter(predicate, arr), SENTINEL) is not SENTINELou oublier a SENTINELet simplement utiliser try: exceptet attraper le StopIteration.
juanpa.arrivillaga
13
En plus de toutes les mentions de StopIteration, la boucle Python "for" fait simplement ce que vous voulez:
>>> it = iter("hello")>>>for i in it:...print i
...
h
e
l
l
o
Je me suis toujours demandé pourquoi diable python a toutes ces méthodes __ xxx __? Ils semblent si laids.
mP.
6
Question légitime! Habituellement, c'est la syntaxe des méthodes exposées par une fonction intégrée (par exemple, len, appelle en fait len ). Une telle fonction intégrée n'existe pas pour length_hint, mais il s'agit en fait d'une proposition en attente (PEP424).
fulmicoton
1
@mP. ces fonctions sont là, car elles sont parfois nécessaires. Ils sont intentionnellement laids, car ils sont considérés comme une méthode de dernier recours: si vous les utilisez, vous savez que vous faites quelque chose de non pythonique et potentiellement dangereux (qui peut également cesser de fonctionner à tout moment).
Arne Babenhauserheide
Comme __init__et __main__? Imho, c'est un peu le bordel, peu importe que vous essayez de le justifier.
user1363990
5
hasNextse traduit quelque peu par l' StopIterationexception, par exemple:
>>> it = iter("hello")>>> it.next()'h'>>> it.next()'e'>>> it.next()'l'>>> it.next()'l'>>> it.next()'o'>>> it.next()Traceback(most recent call last):File"<stdin>", line 1,in<module>StopIteration
Le cas d'utilisation qui m'a conduit à rechercher ceci est le suivant
def setfrom(self,f):"""Set from iterable f"""
fi = iter(f)for i in range(self.n):try:
x = next(fi)exceptStopIteration:
fi = iter(f)
x = next(fi)
self.a[i]= x
où hasnext () est disponible, on pourrait faire
def setfrom(self,f):"""Set from iterable f"""
fi = iter(f)for i in range(self.n):ifnot hasnext(fi):
fi = iter(f)# restart
self.a[i]= next(fi)
ce qui pour moi est plus propre. De toute évidence, vous pouvez contourner les problèmes en définissant des classes d'utilitaires, mais ce qui se passe alors, c'est que vous avez une prolifération de vingt solutions de contournement presque équivalentes différentes, chacune avec leurs bizarreries, et si vous souhaitez réutiliser du code qui utilise différentes solutions de contournement, vous devez soit avoir plusieurs quasi-équivalents dans votre application unique, ou parcourir et réécrire du code pour utiliser la même approche. La maxime «faites-le une fois et faites-le bien» échoue gravement.
De plus, l'itérateur lui-même a besoin d'une vérification interne 'hasnext' pour s'exécuter pour voir s'il a besoin de lever une exception. Cette vérification interne est ensuite masquée afin qu'elle doive être testée en essayant d'obtenir un élément, en interceptant l'exception et en exécutant le gestionnaire s'il est levé. Il s'agit de cacher l'OMI inutile.
Pour ce cas d'utilisation, vous pouvez utiliser itertools.cycle
eaglebrain
0
La méthode suggérée est StopIteration . S'il vous plaît voir l'exemple Fibonacci de tutorialspoint
#!usr/bin/python3import sys
def fibonacci(n):#generator function
a, b, counter =0,1,0whileTrue:if(counter > n):returnyield a
a, b = b, a + b
counter +=1
f = fibonacci(5)#f is iterator objectwhileTrue:try:print(next(f), end=" ")exceptStopIteration:
sys.exit()
La façon dont j'ai résolu mon problème est de garder le décompte du nombre d'objets itérés jusqu'à présent. Je voulais parcourir un ensemble en utilisant des appels à une méthode d'instance. Puisque je connaissais la longueur de l'ensemble et le nombre d'articles comptés jusqu'à présent, j'avais effectivement unhasNext méthode.
Bien sûr, l'exemple est un jouet, mais vous voyez l'idée. Cela ne fonctionnera pas dans les cas où il n'y a aucun moyen d'obtenir la longueur de l'itérable, comme un générateur, etc.
Réponses:
Non, une telle méthode n'existe pas. La fin de l'itération est indiquée par une exception. Consultez la documentation .
la source
unnext()
méthode pour remettre le premier élément après avoir vérifié qu'il existe en appelantnext()
.yield
ou non). Il n'est bien sûr pas difficile d'écrire un adaptateur qui stocke le résultat denext()
et fournithas_next()
etmove_next()
.hasNext()
méthode (pour produire, mettre en cache et renvoyer true en cas de succès, ou renvoyer false en cas d'échec). Ensuite , à la foishasNext()
etnext()
dépendrait d'un sous - jacent commungetNext()
méthode et élément mis en cache. Je ne vois vraiment pas pourquoinext()
ne devrait pas être dans la bibliothèque standard s'il est si facile d'implémenter un adaptateur qui le fournit.next()
ethasNext()
méthode, pas seulement une bibliothèque Python hypothétique). Alors oui,next()
et celahasNext()
devient délicat si le contenu du flux analysé dépend du moment où les éléments sont lus.Il existe une alternative à l'
StopIteration
utilisation denext(iterator, default_value)
.Par exemple:
Ainsi, vous pouvez détecter pour
None
ou toute autre valeur pré-spécifiée pour la fin de l'itérateur si vous ne voulez pas la manière d'exception.la source
sentinel = object()
etnext(iterator, sentinel)
et tester avecis
.unittest.mock.sentinel
objet intégré qui vous permet d'écrire un explicitenext(a, sentinel.END_OF_ITERATION)
, puisif next(...) == sentinel.END_OF_ITERATION
Si vous avez vraiment besoin d' une
has-next
fonctionnalité (parce que vous ne faites que transcrire fidèlement un algorithme à partir d'une implémentation de référence en Java, par exemple, ou parce que vous écrivez un prototype qui devra être facilement transcrit en Java une fois terminé), il est facile de obtenez-le avec une petite classe wrapper. Par exemple:maintenant quelque chose comme
émet
comme demandé.
Notez que l'utilisation de
next(sel.it)
comme élément intégré nécessite Python 2.6 ou supérieur; si vous utilisez une ancienne version de Python, utilisez à laself.it.next()
place (et de la même manièrenext(x)
dans l'exemple d'utilisation). [[Vous pourriez raisonnablement penser que cette note est redondante, puisque Python 2.6 existe depuis plus d'un an maintenant - mais plus souvent qu'autrement lorsque j'utilise les fonctionnalités de Python 2.6 dans une réponse, certains commentateurs ou autres se sentent obligés de signaler qu'il s'agit de fonctionnalités 2.6, j'essaye donc de prévenir de tels commentaires pour une fois ;-)]]la source
has_next
méthode. La conception de Python rend impossible, par exemple, d'utiliserfilter
pour vérifier si un tableau contient un élément correspondant à un prédicat donné. L'arrogance et la myopie de la communauté Python sont stupéfiantes.TypeError: iter() returned non-iterator
map
etany
au lieu defilter
, mais vous pouvez utiliserSENTINEL = object(); next(filter(predicate, arr), SENTINEL) is not SENTINEL
ou oublier aSENTINEL
et simplement utilisertry: except
et attraper leStopIteration
.En plus de toutes les mentions de StopIteration, la boucle Python "for" fait simplement ce que vous voulez:
la source
Essayez la méthode __length_hint __ () à partir de n'importe quel objet itérateur:
la source
__init__
et__main__
? Imho, c'est un peu le bordel, peu importe que vous essayez de le justifier.hasNext
se traduit quelque peu par l'StopIteration
exception, par exemple:StopIteration
docs: http://docs.python.org/library/exceptions.html#exceptions.StopIterationla source
Vous pouvez
tee
l'itérateur à l'aide de,itertools.tee
et vérifierStopIteration
sur l'itérateur teed.la source
Non. Le concept le plus similaire est probablement une exception StopIteration.
la source
Je crois que python a juste next () et selon la doc, il lève une exception est qu'il n'y a plus d'éléments.
http://docs.python.org/library/stdtypes.html#iterator-types
la source
Le cas d'utilisation qui m'a conduit à rechercher ceci est le suivant
où hasnext () est disponible, on pourrait faire
ce qui pour moi est plus propre. De toute évidence, vous pouvez contourner les problèmes en définissant des classes d'utilitaires, mais ce qui se passe alors, c'est que vous avez une prolifération de vingt solutions de contournement presque équivalentes différentes, chacune avec leurs bizarreries, et si vous souhaitez réutiliser du code qui utilise différentes solutions de contournement, vous devez soit avoir plusieurs quasi-équivalents dans votre application unique, ou parcourir et réécrire du code pour utiliser la même approche. La maxime «faites-le une fois et faites-le bien» échoue gravement.
De plus, l'itérateur lui-même a besoin d'une vérification interne 'hasnext' pour s'exécuter pour voir s'il a besoin de lever une exception. Cette vérification interne est ensuite masquée afin qu'elle doive être testée en essayant d'obtenir un élément, en interceptant l'exception et en exécutant le gestionnaire s'il est levé. Il s'agit de cacher l'OMI inutile.
la source
La méthode suggérée est StopIteration . S'il vous plaît voir l'exemple Fibonacci de tutorialspoint
la source
La façon dont j'ai résolu mon problème est de garder le décompte du nombre d'objets itérés jusqu'à présent. Je voulais parcourir un ensemble en utilisant des appels à une méthode d'instance. Puisque je connaissais la longueur de l'ensemble et le nombre d'articles comptés jusqu'à présent, j'avais effectivement un
hasNext
méthode.Une version simple de mon code:
Bien sûr, l'exemple est un jouet, mais vous voyez l'idée. Cela ne fonctionnera pas dans les cas où il n'y a aucun moyen d'obtenir la longueur de l'itérable, comme un générateur, etc.
la source