hasNext w iteratorach Pythona?


Odpowiedzi:


106

Nie, nie ma takiej metody. Koniec iteracji jest oznaczony wyjątkiem. Zobacz dokumentację .


71
„Łatwiej jest prosić o przebaczenie niż o pozwolenie”.

118
„Łatwiej jest prosić o przebaczenie niż o pozwolenie.”: Sprawdzanie, czy iterator ma następny element, nie jest pytaniem o pozwolenie. Są sytuacje, w których chcesz przetestować istnienie kolejnego elementu bez jego zużywania. Przyjąłbym rozwiązanie try catch, gdyby istniała unnext()metoda umieszczania pierwszego elementu z powrotem po sprawdzeniu, czy istnieje przez wywołanie next().
Giorgio

15
@Giorgio, nie ma sposobu, aby dowiedzieć się, czy istnieje inny element bez wykonania kodu, który go generuje (nie wiesz, czy generator wykona, yieldczy nie). Oczywiście nie jest trudno napisać adapter, który przechowuje wyniki next()i zapewnia has_next()i move_next().
avakar

5
Ten sam pomysł można wykorzystać do zaimplementowania hasNext()metody (do tworzenia, buforowania i zwracania wartości true w przypadku sukcesu lub zwracania fałszu w przypadku niepowodzenia). Wtedy oba hasNext()i next()będą zależeć od wspólnej getNext()metody bazowej i elementu w pamięci podręcznej. Naprawdę nie rozumiem, dlaczego next()nie miałoby być w bibliotece standardowej, skoro tak łatwo jest zaimplementować adapter, który to zapewnia.
Giorgio

3
@LarsH: Masz na myśli np. Iterator, który czyta z pliku, który można zmienić podczas czytania z niego? Zgadzam się, że może to być problem (który dotyczy każdej biblioteki next()i hasNext()metody, a nie tylko hipotetycznej biblioteki Pythona). Więc tak, next()i hasNext()staje się trudne, jeśli zawartość skanowanego strumienia zależy od tego, kiedy elementy są odczytywane.
Giorgio,

239

Istnieje alternatywa dla StopIterationużywania next(iterator, default_value).

Na przykład:

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

Możesz więc wykryć dla Nonelub inną wstępnie określoną wartość na końcu iteratora, jeśli nie chcesz korzystać z wyjątku.


70
jeśli używasz None jako "wartownika", najlepiej upewnij się, że twój iterator nie ma żadnych Nonów. można też zrobić sentinel = object()i next(iterator, sentinel)oraz test z is.
sam boosalis

1
podążając za @samboosalis Wolałbym raczej użyć wbudowanego unittest.mock.sentinelobiektu, który pozwala na napisanie wyraźnego, next(a, sentinel.END_OF_ITERATION)a następnieif next(...) == sentinel.END_OF_ITERATION
ClementWalter

to jest ładniejsze niż wyjątek
datdinhquoc

Problem polega na tym, że w ten sposób KONSUMUJESZ również następną wartość z iteratora. hasNext w Javie nie zużywa następnej wartości.
Alan Franzoni

39

Jeśli naprawdę potrzebujesz do has-nextfunkcjonalności (bo jesteś po prostu wiernie transkrypcji algorytm od implementacji referencyjnej w Javie, powiedzmy, albo dlatego piszesz prototyp, że będzie potrzeba być łatwo przepisywane na Javie, kiedy jest gotowy), to łatwo zdobądź go z małą klasą wrapper. Na przykład:

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

teraz coś w stylu

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

emituje

c
i
a
o

jako wymagane.

Zwróć uwagę, że użycie programu next(sel.it)jako wbudowanego wymaga Pythona 2.6 lub nowszego; jeśli używasz starszej wersji Pythona, użyj self.it.next()zamiast tego (i podobnie next(x)w przykładzie użycia). [[Możesz rozsądnie pomyśleć, że ta uwaga jest zbędna, ponieważ Python 2.6 istnieje już od ponad roku - ale częściej niż nie, kiedy używam funkcji Pythona 2.6 w odpowiedzi, jakiś komentator lub inny czuje się zobowiązany do wskazania że cechami 2.6, więc chociaż raz staram się uprzedzić takie komentarze ;-)]]


9
„wierne przepisanie algorytmu z referencyjnej implementacji w Javie” to najgorszy powód, dla którego warto zastosować has_nextmetodę. Konstrukcja Pythona uniemożliwia, powiedzmy, użycie filterdo sprawdzenia, czy tablica zawiera element pasujący do danego predykatu. Arogancja i krótkowzroczność społeczności Pythona jest oszałamiająca.
Jonathan Cast

dobra odpowiedź,
kopiuję

Jestem z Python3 i ten kod mi dajeTypeError: iter() returned non-iterator
madtyn

1
@JonathanCast nie jestem pewien, czy śledzę. W Pythonie zwykle używałbyś mapi anyzamiast filter, ale możesz użyć SENTINEL = object(); next(filter(predicate, arr), SENTINEL) is not SENTINELlub zapomnieć a SENTINELi po prostu użyć try: excepti złapać StopIteration.
juanpa.arrivillaga

13

Oprócz wszystkich wzmianek o StopIteration, pętla „for” w Pythonie po prostu robi to, co chcesz:

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

7

Wypróbuj metodę __length_hint __ () z dowolnego obiektu iteratora:

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

5
Zawsze się zastanawiałem, dlaczego, u licha, Python ma wszystkie te metody __ xxx __? Wydają się takie brzydkie.
mP.

6
Uzasadnione pytanie! Zwykle jest to składnia metod, które są udostępniane przez wbudowaną funkcję (np. Len, w rzeczywistości wywołuje len ). Taka wbudowana funkcja nie istnieje dla length_hint, ale w rzeczywistości jest to oczekująca propozycja (PEP424).
fulmicoton

1
@poseł. te funkcje istnieją, ponieważ czasami są potrzebne. Są celowo brzydkie, ponieważ są uważane za metodę ostateczności: jeśli ich używasz, wiesz, że robisz coś nie-pytonicznego i potencjalnie niebezpiecznego (co również może przestać działać w dowolnym momencie).
Arne Babenhauserheide

Jak __init__i __main__? Imho, to trochę bałaganu, bez względu na to, czy próbujesz to usprawiedliwić.
user1363990


4

Możesz użyć teeiteratora za pomocą itertools.teei sprawdzić StopIterationw iteratorze teed.


3

Nie. Najbardziej podobną koncepcją jest najprawdopodobniej wyjątek StopIteration.


10
Który Python używa wyjątków do kontroli przepływu? Brzmi dość naiwnie.
mP.

5
Po prawej: wyjątki powinny być używane do obsługi błędów, a nie do definiowania normalnego przepływu kontroli.
Giorgio


1

Przykład użycia, który doprowadził mnie do wyszukiwania tego, jest następujący

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 

gdzie jest dostępna funkcja hasnext (), można to zrobić

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)

który dla mnie jest czystszy. Oczywiście możesz obejść problemy, definiując klasy narzędzi, ale co się wtedy dzieje, to mnożenie się dwudziestu różnych, prawie równoważnych obejść, z których każdy ma ich dziwactwa, a jeśli chcesz ponownie użyć kodu, który używa innych obejść, musisz albo mieć wiele zbliżonych do odpowiedników w jednej aplikacji lub przejść dookoła, wybierając i przepisując kod, aby zastosować to samo podejście. Zasada „zrób to raz i zrób to dobrze” kończy się niepowodzeniem.

Ponadto sam iterator musi mieć wewnętrzną kontrolę „hasnext”, aby sprawdzić, czy musi zgłosić wyjątek. To wewnętrzne sprawdzenie jest następnie ukrywane, więc należy je przetestować, próbując pobrać element, przechwytując wyjątek i uruchamiając procedurę obsługi, jeśli zostanie rzucona. To niepotrzebne ukrywanie IMO.


1
W tym przypadku możesz użyć itertools.cycle
eaglebrain

0

Sugerowanym sposobem jest zatrzymanie . Zobacz przykład Fibonacciego z 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

Sposób, w jaki rozwiązałem ten problem, to jak dotąd zliczanie iterowanych obiektów. Chciałem iterować zestaw przy użyciu wywołań metody instancji. Ponieważ znałem długość zestawu i liczbę policzonych dotychczas elementów, skutecznie miałem hasNextmetodę.

Prosta wersja mojego kodu:

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

Oczywiście przykład jest zabawkowy, ale masz pomysł. To nie zadziała w przypadkach, gdy nie ma możliwości uzyskania długości iterowalnej, jak generator itp.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.