Python yineleyicilerde hasNext?


Yanıtlar:


106

Hayır, böyle bir yöntem yok. Yinelemenin sonu bir istisna ile gösterilir. Belgelere bakın .


71
"Affetmek, izin vermekten daha kolaydır."

119
"Affetmek, izin vermekten daha kolaydır.": Yineleyicinin bir sonraki öğeye sahip olup olmadığını kontrol etmek, izin istememektedir. Bir sonraki öğenin varlığını tüketmeden test etmek istediğiniz durumlar vardır. Ben unnext()arayarak var olduğunu kontrol ettikten sonra ilk eleman geri koymak için bir yöntem olsaydı ben try catch çözüm kabul ediyorum next().
Giorgio

15
@Giorgio, onu oluşturan kodu çalıştırmadan başka bir öğenin var olup olmadığını bilmenin bir yolu yoktur (jeneratörün yürütülüp yürütülmeyeceğini yieldbilmiyorsunuz). Depolar sonucu olduğunu O, elbette, zor değil bir adaptör yazmaya edilir next()ve sağlar has_next()ve move_next().
avakar

5
Aynı fikir hasNext()yöntemi uygulamak için de kullanılabilir (üretmek için önbelleğe almak ve başarı üzerine true döndürmek veya başarısız olduğunda false döndürmek). Sonra iki hasNext()ve next()altta yatan bir ortak bağlı olacağını getNext()yöntem ve önbelleğe öğeyi. Bunu next()sağlayan bir bağdaştırıcıyı uygulamak çok kolaysa, neden standart kitaplıkta olmamalı gerçekten görmüyorum .
Giorgio

3
@LarsH: Örneğin, bir dosyadan okurken değiştirilebilen bir yineleyici mi demek istiyorsunuz? Bunun bir sorun olabileceğini kabul ediyorum ( sadece varsayımsal bir Python kütüphanesini değil, herhangi bir kütüphane sağlama next()ve hasNext()yöntemini de etkiler ). Bu yüzden evet next()ve hasNext()taranan akışın içeriği öğelerin ne zaman okunduğuna bağlıysa zorlaşır.
Giorgio

239

StopIterationKullanarak bir alternatif var next(iterator, default_value).

Exapmle için:

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

Böylece None, istisna yolunu istemiyorsanız, yineleyicinin sonu için önceden belirlenmiş başka bir değer tespit edebilirsiniz .


70
"sentinel" olarak None kullanırsanız, yineleyicinizin herhangi bir Nones olmadığından emin olmalısınız. Ayrıca yapabileceğini sentinel = object()ve next(iterator, sentinel)birlikte ve testi is.
sam boosalis

1
Daha doğrusu dahili kullanayım @samboosalis aşağıdaki unittest.mock.sentinelEğer açık bir yazmasına olanak tanır nesne next(a, sentinel.END_OF_ITERATION)sonra veif next(...) == sentinel.END_OF_ITERATION
ClementWalter

Bu istisnadan daha güzel
datdinhquoc

Sorun, bu şekilde, yineleyiciden sonraki değeri de tüketmenizdir. Java'da hasNext bir sonraki değeri tüketmez.
Alan Franzoni

39

Eğer gerçekten varsa gerek bir has-next(sadece sadakatle diyelim ki, Java referans uygulanmasından bir algoritma transkripsiyonu çünkü, yoksa bir prototip yazıyoruz çünkü işlevselliği olacak o bitince kolayca Java transkripsiyonu gerekir), bu kolaydır küçük bir sargı sınıfıyla elde edin. Örneğin:

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

şimdi böyle bir şey

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

yayar

c
i
a
o

gereğince, gerektiği gibi.

Yerleşik next(sel.it)olarak kullanımının Python 2.6 veya daha üstünü gerektirdiğini unutmayın; Python'un daha eski bir sürümünü kullanıyorsanız, self.it.next()bunun yerine kullanın (ve next(x)örnek kullanımda benzer şekilde ). [[Bu notun gereksiz olduğunu düşünebilirsiniz, çünkü Python 2.6 bir yıldan uzun bir süredir var - ancak Python 2.6 özelliklerini bir yanıtta kullandığımdan daha sık değil, bazı yorumcular veya başkaları işaret etmek zorunda kaldı onlar vardır , böylece bir kez ;-)]] için böyle bir yorum önlemek çalışıyorum, 2.6 özellikleri


9
"Java'daki bir başvuru uygulamasından bir algoritmayı sadakatle kopyalamak" bir has_nextyönteme ihtiyaç duymanın en kötü nedenidir . Python'un tasarımı, örneğin, filterbir dizinin belirli bir yüklemle eşleşen bir öğe içerip içermediğini kontrol etmeyi imkansız hale getirir . Python topluluğunun kibir ve dar görüşlülüğü şaşırtıcı.
Jonathan Cast

güzel cevap, bunu Java kodundan alınan bazı tasarım deseninin ilustrationu için kopyalarım
madtyn

Python3 ile beraberim ve bu kod bana verirTypeError: iter() returned non-iterator
madtyn

1
@JonathanCast takip ettiğimden emin değilim. Python'da, tipik olarak mapve anyyerine kullanırsınız filter, ancak a'yı kullanabilir SENTINEL = object(); next(filter(predicate, arr), SENTINEL) is not SENTINELveya unutabilir SENTINELve sadece kullanın try: exceptve yakalayın StopIteration.
juanpa.arrivillaga

13

StopIteration'ın tüm sözlerine ek olarak, Python "for" döngüsü istediğinizi yapar:

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

7

Herhangi bir yineleyici nesnesinden __length_hint __ () yöntemini deneyin:

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

5
Hep neden python tüm bu __ xxx __ yöntemleri var merak ettim? Çok çirkin görünüyorlar.
mP.

6
Meşru soru! Genellikle yerleşik bir işlevle ortaya çıkan yöntemlerin sözdizimidir (örneğin len, aslında len olarak adlandırılır ). Böyle bir yerleşik işlev length_hint için mevcut değildir, ancak aslında bekleyen bir tekliftir (PEP424).
fulmicoton

1
@mP. bu işlevler oradadır, çünkü bazen ihtiyaç duyulur. Kasıtlı olarak çirkindirler, çünkü son çare yöntemi olarak kabul edilirler: Onları kullanırsanız, pitonik olmayan ve potansiyel olarak tehlikeli bir şey yaptığınızı bilirsiniz (bu da herhangi bir noktada çalışmayı durdurabilir).
Arne Babenhauserheide

Beğen __init__ve __main__? Imho, haklı çıkarmaya çalışsanız da biraz karışıklık.
user1363990

5

hasNextbir şekilde StopIterationistisna anlamına gelir, örneğin:

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

Sen edebilirsiniz teeyineleyici kullanarak itertools.teeve kontrol StopIterationyineleyici durması üzerine.


3

Hayır. En benzer kavram büyük olasılıkla bir StopIteration istisnasıdır.


10
Hangi Python kontrol akışı için istisnalar kullanır? Kulağa hoş geliyor.
mP.

5
Sağ: normal kontrol akışını tanımlamak için değil, hataları işlemek için istisnalar kullanılmalıdır.
Giorgio


1

Beni aramaya yönlendiren kullanım durumu şudur:

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 

hasnext () kullanılabilir olduğunda,

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)

ki bu bana daha temiz. Açıkçası, yardımcı sınıfları tanımlayarak sorunların üstesinden gelebilirsiniz, ancak o zaman olan şey, her biri tuhaflıkları olan yirmi tek farklı neredeyse eşdeğer geçici çözümün çoğalmasına sahip olmanız ve farklı geçici çözümler kullanan kodu yeniden kullanmak istiyorsanız, tek bir uygulamanızda birden fazla eşdeğeri varsa veya aynı yaklaşımı kullanmak için kodu alıp yeniden yazarak dolaşın. 'Bir kez yapın ve iyi yapın' maxim kötü bir şekilde başarısız olur.

Ayrıca, yineleyicinin bir istisna oluşturması gerekip gerekmediğini görmek için dahili bir 'hasnext' kontrolüne sahip olması gerekir. Bu iç kontrol daha sonra bir öğeyi almaya çalışarak, istisnayı yakalayarak ve atıldığında işleyiciyi çalıştırarak test edilmesi gereken şekilde gizlenir. Bu, IMO'yu gizlemek için gereksizdir.


1
Bu kullanım durumunda, itertools.cycle
eaglebrain

0

Önerilen yol StopIteration . Lütfen tutorialspoint'ten Fibonacci örneğine bakın

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

Sorunumu çözme şeklim, şimdiye kadar yinelenen nesnelerin sayısını korumaktır. Bir örnek yöntemi çağrıları kullanarak bir küme üzerinde yineleme yapmak istedim. Setin uzunluğunu ve şimdiye kadar sayılan öğelerin sayısını bildiğim için etkili bir hasNextyöntemim vardı .

Kodumun basit bir sürümü:

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

Tabii ki, örnek bir oyuncak, ama fikri anladınız. Bu, bir jeneratör vb. Gibi tekrarlanabilir uzunluğun elde edilmesinin bir yolu olmadığı durumlarda işe yaramaz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.