hasNext in iteratori Python?


Risposte:


107

No, non esiste tale metodo. La fine dell'iterazione è indicata da un'eccezione. Vedere la documentazione di .


71
"È più facile chiedere perdono che permesso."

119
"È più facile chiedere perdono che autorizzazione.": Controllare se un iteratore ha un elemento successivo non richiede l'autorizzazione. Esistono situazioni in cui si desidera verificare l'esistenza di un elemento successivo senza consumarlo. Accetterei la soluzione try catch se esistesse un unnext()metodo per ripristinare il primo elemento dopo aver verificato che esiste chiamando next().
Giorgio,

15
@Giorgio, non c'è modo di sapere se esiste un altro elemento senza eseguire il codice che lo genera (non si sa se il generatore verrà eseguito yieldo meno). Naturalmente, non è difficile scrivere un adattatore che memorizzi il risultato next()e fornisca has_next()e move_next().
avakar,

5
La stessa idea potrebbe essere utilizzata per implementare il hasNext()metodo (per produrre, memorizzare nella cache e restituire true in caso di successo o restituire false in caso di errore). Quindi entrambi hasNext()e next()dipenderebbero da un getNext()metodo sottostante comune e dall'elemento memorizzato nella cache. Davvero non vedo perché next()non dovrebbe essere nella libreria standard se è così facile implementare un adattatore che lo fornisce.
Giorgio,

3
@LarsH: Intendi ad esempio un iteratore che legge da un file che può essere modificato durante la lettura da esso? Concordo sul fatto che questo può essere un problema (che influisce su qualsiasi metodo di fornitura next()e hasNext()metodo della libreria , non solo su un'ipotetica libreria Python). Quindi sì, next()e hasNext()diventa difficile se il contenuto del torrente che si deve acquisire dipende da quando gli elementi vengono letti.
Giorgio,

239

C'è un'alternativa al StopIterationusando next(iterator, default_value).

Per esempio:

>>> a = iter('hi')
>>> print next(a, None)
h
>>> print next(a, None)
i
>>> print next(a, None)
None

Quindi è possibile rilevare per Noneo altro valore predefinito per la fine dell'iteratore se non si desidera il modo dell'eccezione.


70
se usi Nessuno come "sentinella", assicurati che il tuo iteratore non abbia Nones. si potrebbe anche fare sentinel = object()e next(iterator, sentinel)e test con is.
Sam Boosalis,

1
seguendo @samboosalis preferirei usare l' unittest.mock.sentineloggetto incorporato che ti permette di scrivere un esplicito next(a, sentinel.END_OF_ITERATION)e poiif next(...) == sentinel.END_OF_ITERATION
ClementWalter

questo è più bello dell'eccezione
datdinhquoc

Il problema è che, in questo modo, CONSUMI anche il valore successivo dall'iteratore. hasNext in Java non consuma il valore successivo.
Alan Franzoni,

39

Se davvero bisogno di una has-nextfunzionalità (perché si sta appena fedelmente trascrivere un algoritmo da un'implementazione di riferimento in Java, per esempio, o perché si sta scrivendo un prototipo che volontà essere facilmente trascritto su Java al termine), è facile ottenerlo con una piccola classe wrapper. Per esempio:

class hn_wrapper(object):
  def __init__(self, it):
    self.it = iter(it)
    self._hasnext = None
  def __iter__(self): return self
  def next(self):
    if self._hasnext:
      result = self._thenext
    else:
      result = next(self.it)
    self._hasnext = None
    return result
  def hasnext(self):
    if self._hasnext is None:
      try: self._thenext = next(self.it)
      except StopIteration: self._hasnext = False
      else: self._hasnext = True
    return self._hasnext

ora qualcosa del genere

x = hn_wrapper('ciao')
while x.hasnext(): print next(x)

emette

c
i
a
o

come richiesto.

Si noti che l'uso di next(sel.it)come built-in richiede Python 2.6 o superiore; se stai usando una versione precedente di Python, usa self.it.next()invece (e in modo simile per next(x)l'utilizzo nell'esempio). [[Potresti ragionevolmente pensare che questa nota sia ridondante, poiché Python 2.6 è in circolazione da oltre un anno ormai - ma il più delle volte quando uso le funzionalità di Python 2.6 in una risposta, alcuni commentatori o altri si sentono obbligati a sottolineare che sono caratteristiche 2.6, quindi sto provando a prevenire tali commenti per una volta ;-)]]


9
"Trascrivere fedelmente un algoritmo da un'implementazione di riferimento in Java" è la ragione peggiore per aver bisogno di un has_nextmetodo. Il design di Python rende impossibile, diciamo, usare filterper verificare se un array contiene un elemento che corrisponde a un dato predicato. L'arroganza e la miopia della comunità Python è sbalorditiva.
Jonathan Cast il

bella risposta, lo sto copiando per l'illustrazione di alcuni pattern di progettazione presi dal codice Java
madtyn,

Sono con Python3 e questo codice mi dàTypeError: iter() returned non-iterator
madtyn

1
@JonathanCast non sono sicuro di seguire. In Python, in genere si usa mape anyinvece di filter, ma si può usare SENTINEL = object(); next(filter(predicate, arr), SENTINEL) is not SENTINELo dimenticare un SENTINELe semplicemente usare try: excepte catturare il StopIteration.
juanpa.arrivillaga,

13

Oltre a tutte le menzioni di StopIteration, il ciclo "for" di Python fa semplicemente quello che vuoi:

>>> it = iter("hello")
>>> for i in it:
...     print i
...
h
e
l
l
o

7

Prova il metodo __length_hint __ () da qualsiasi oggetto iteratore:

iter(...).__length_hint__() > 0

5
Mi sono sempre chiesto perché diavolo ha tutti quei metodi __ xxx __? Sembrano così brutti.
mP.

6
Domanda legittima! Di solito è la sintassi per i metodi che sono esposti da una funzione incorporata (ad esempio len, in realtà sta chiamando len ). Tale funzione incorporata non esiste per length_hint, ma in realtà è una proposta in sospeso (PEP424).
Fulmicoton,

1
@mP. queste funzioni ci sono, perché a volte sono necessarie. Sono intenzionalmente brutti, perché sono considerati come metodo di ultima istanza: se li usi, sai che fai qualcosa di non pitonico e potenzialmente pericoloso (che potrebbe anche smettere di funzionare in qualsiasi momento).
Arne Babenhauserheide,

Come __init__e __main__? Imho, è un po 'un casino, non importa che tu provi a giustificarlo.
user1363990

5

hasNextsi traduce in qualche modo StopIterationnell'eccezione, ad esempio:

>>> 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

4

È possibile teeutilizzare l'iteratore usando, itertools.teee verificare StopIterationl'iteratore teed.


3

No. Il concetto più simile è probabilmente un'eccezione StopIteration.


10
Quali Python utilizza le eccezioni per il flusso di controllo? Sembra piuttosto naft.
mP.

5
A destra: le eccezioni dovrebbero essere utilizzate per gestire gli errori, non per definire il normale flusso di controllo.
Giorgio,


1

Il caso d'uso che mi ha portato a cercare questo è il seguente

def setfrom(self,f):
    """Set from iterable f"""
    fi = iter(f)
    for i in range(self.n):
        try:
            x = next(fi)
        except StopIteration:
            fi = iter(f)
            x = next(fi)
        self.a[i] = x 

dove hasnext () è disponibile, si potrebbe fare

def setfrom(self,f):
    """Set from iterable f"""
    fi = iter(f)
    for i in range(self.n):
        if not hasnext(fi):
            fi = iter(f) # restart
        self.a[i] = next(fi)

che per me è più pulito. Ovviamente è possibile aggirare i problemi definendo le classi di utilità, ma ciò che accade è una proliferazione di venti soluzioni dispari quasi equivalenti diverse ognuna con le proprie stranezze e se si desidera riutilizzare il codice che utilizza soluzioni alternative diverse, è necessario avere più equivalenti quasi nella tua singola applicazione, o andare in giro a cercare e riscrivere il codice per usare lo stesso approccio. La massima "fallo una volta e fallo bene" fallisce male.

Inoltre, lo stesso iteratore deve avere un controllo 'hasnext' interno per essere eseguito per vedere se deve sollevare un'eccezione. Questo controllo interno viene quindi nascosto in modo che debba essere testato tentando di ottenere un elemento, rilevando l'eccezione ed eseguendo il gestore se lanciato. Questo non è necessario nascondere l'IMO.


1
Per questo caso d'uso, puoi usare itertools.cycle
eaglebrain il

0

Il modo suggerito è StopIteration . Vedi l'esempio di Fibonacci da tutorialspoint

#!usr/bin/python3

import sys
def fibonacci(n): #generator function
   a, b, counter = 0, 1, 0
   while True:
      if (counter > n): 
         return
      yield a
      a, b = b, a + b
      counter += 1
f = fibonacci(5) #f is iterator object

while True:
   try:
      print (next(f), end=" ")
   except StopIteration:
      sys.exit()

-2

Il modo in cui ho risolto il mio problema è quello di mantenere il conteggio del numero di oggetti ripetuti fino ad ora. Volevo scorrere su un set usando le chiamate a un metodo di istanza. Dato che conoscevo la lunghezza del set e il numero di elementi contati finora, ne ho effettivamente avuto unohasNext metodo.

Una versione semplice del mio codice:

class Iterator:
    # s is a string, say
    def __init__(self, s):
        self.s = set(list(s))
        self.done = False
        self.iter = iter(s)
        self.charCount = 0

    def next(self):
        if self.done:
            return None
        self.char = next(self.iter)
        self.charCount += 1
        self.done = (self.charCount < len(self.s))
        return self.char

    def hasMore(self):
        return not self.done

Ovviamente, l'esempio è un giocattolo, ma hai l'idea. Questo non funzionerà nei casi in cui non c'è modo di ottenere la lunghezza dell'iterabile, come un generatore ecc.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.