„Ł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.
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 =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
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 są cechami 2.6, więc chociaż raz staram się uprzedzić takie komentarze ;-)]]
„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
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
5
hasNextco nieco przekłada się na StopIterationwyjątek, np .:
>>> 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
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)exceptStopIteration:
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):ifnot 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.
Sugerowanym sposobem jest zatrzymanie . Zobacz przykład Fibonacciego z 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()
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ę.
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.
Odpowiedzi:
Nie, nie ma takiej metody. Koniec iteracji jest oznaczony wyjątkiem. Zobacz dokumentację .
źródło
unnext()
metoda umieszczania pierwszego elementu z powrotem po sprawdzeniu, czy istnieje przez wywołanienext()
.yield
czy nie). Oczywiście nie jest trudno napisać adapter, który przechowuje wynikinext()
i zapewniahas_next()
imove_next()
.hasNext()
metody (do tworzenia, buforowania i zwracania wartości true w przypadku sukcesu lub zwracania fałszu w przypadku niepowodzenia). Wtedy obahasNext()
inext()
będą zależeć od wspólnejgetNext()
metody bazowej i elementu w pamięci podręcznej. Naprawdę nie rozumiem, dlaczegonext()
nie miałoby być w bibliotece standardowej, skoro tak łatwo jest zaimplementować adapter, który to zapewnia.next()
ihasNext()
metody, a nie tylko hipotetycznej biblioteki Pythona). Więc tak,next()
ihasNext()
staje się trudne, jeśli zawartość skanowanego strumienia zależy od tego, kiedy elementy są odczytywane.Istnieje alternatywa dla
StopIteration
używanianext(iterator, default_value)
.Na przykład:
Możesz więc wykryć dla
None
lub inną wstępnie określoną wartość na końcu iteratora, jeśli nie chcesz korzystać z wyjątku.źródło
sentinel = object()
inext(iterator, sentinel)
oraz test zis
.unittest.mock.sentinel
obiektu, który pozwala na napisanie wyraźnego,next(a, sentinel.END_OF_ITERATION)
a następnieif next(...) == sentinel.END_OF_ITERATION
Jeśli naprawdę potrzebujesz do
has-next
funkcjonalnoś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:teraz coś w stylu
emituje
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żyjself.it.next()
zamiast tego (i podobnienext(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 są cechami 2.6, więc chociaż raz staram się uprzedzić takie komentarze ;-)]]źródło
has_next
metodę. Konstrukcja Pythona uniemożliwia, powiedzmy, użyciefilter
do sprawdzenia, czy tablica zawiera element pasujący do danego predykatu. Arogancja i krótkowzroczność społeczności Pythona jest oszałamiająca.TypeError: iter() returned non-iterator
map
iany
zamiastfilter
, ale możesz użyćSENTINEL = object(); next(filter(predicate, arr), SENTINEL) is not SENTINEL
lub zapomnieć aSENTINEL
i po prostu użyćtry: except
i złapaćStopIteration
.Oprócz wszystkich wzmianek o StopIteration, pętla „for” w Pythonie po prostu robi to, co chcesz:
źródło
Wypróbuj metodę __length_hint __ () z dowolnego obiektu iteratora:
źródło
__init__
i__main__
? Imho, to trochę bałaganu, bez względu na to, czy próbujesz to usprawiedliwić.hasNext
co nieco przekłada się naStopIteration
wyjątek, np .:StopIteration
dokumenty: http://docs.python.org/library/exceptions.html#exceptions.StopIterationźródło
Możesz użyć
tee
iteratora za pomocąitertools.tee
i sprawdzićStopIteration
w iteratorze teed.źródło
Nie. Najbardziej podobną koncepcją jest najprawdopodobniej wyjątek StopIteration.
źródło
Uważam, że python ma po prostu next () i zgodnie z dokumentem zgłasza wyjątek, że nie ma więcej elementów.
http://docs.python.org/library/stdtypes.html#iterator-types
źródło
Przykład użycia, który doprowadził mnie do wyszukiwania tego, jest następujący
gdzie jest dostępna funkcja hasnext (), można to zrobić
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.
źródło
Sugerowanym sposobem jest zatrzymanie . Zobacz przykład Fibonacciego z tutorialspoint
źródło
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
hasNext
metodę.Prosta wersja mojego kodu:
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.
źródło