PythonのイテレータのhasNext?


回答:


106

いいえ、そのような方法はありません。反復の終了は例外によって示されます。ドキュメントを参照してください。


71
「許可よりも許しを求める方が簡単です。」

118
「許可よりも許しを求める方が簡単です。」:イテレータに次の要素があるかどうかを確認しても、許可は求められません。消費せずに次の要素の存在をテストしたい場合があります。をunnext()呼び出して最初の要素が存在することを確認した後、最初の要素を元に戻すメソッドがあった場合、try catchソリューションを受け入れますnext()
ジョルジョ

15
@Giorgio、それを生成するコードを実行せずに別の要素が存在するかどうかを知る方法はありません(ジェネレーターが実行されるかどうかはわかりyieldません)。の結果を格納するアダプター書くために、もちろん、難しいことではないnext()と提供has_next()とをmove_next()
avakar 2012

5
同じアイデアを使用してhasNext()メソッドを実装することもできます(成功した場合はtrueを生成、キャッシュしてtrueを返し、失敗した場合はfalseを返します)。そして、両方hasNext()next()共通基盤となるに依存するgetNext()方法と、キャッシュされたアイテム。next()それを提供するアダプターを実装するのがとても簡単なのに、なぜ標準ライブラリに入れるべきではないのか本当にわかりません。
ジョルジョ

3
@LarsH:たとえば、ファイルから読み取るときに変更可能なファイルから読み取るイテレータですか?これは問題になる可能性があることに同意します(これは、架空のPythonライブラリだけでなく、ライブラリの提供next()hasNext()メソッドに影響します)。そうです、next()そしてhasNext()スキャンされたストリームの内容がに依存する場合、トリッキーになったときに要素が読み込まれます。
Giorgio

238

StopIterationを使用することで、に代わるものがありnext(iterator, default_value)ます。

例:

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

そのためNone、例外の方法が必要ない場合は、イテレータの終了を検出するか、イテレータの最後に他の事前に指定された値を指定できます。


69
「センチネル」としてNoneを使用する場合は、イテレータにNoneがないことを確認してください。またsentinel = object()next(iterator, sentinel)を使用してテストすることもできますis
sam boosalis 2013年

1
次@samboosalis私はむしろビルトイン使用することになりunittest.mock.sentinelますが、明示的に記述することができますオブジェクトnext(a, sentinel.END_OF_ITERATION)、その後if next(...) == sentinel.END_OF_ITERATION
ClementWalter

これは例外よりもきれいです
datdinhquoc

問題は、この方法で、イテレータから次の値も消費することです。JavaのhasNextは次の値を消費しません。
アランフランゾーニ

39

本当に機能が必要has-next場合(たとえば、Javaの参照実装からアルゴリズムを忠実に転記しているため、または、完成したときにJavaに簡単に転記する必要があるプロトタイプを作成しているため)、それは簡単です小さなラッパークラスで取得します。例えば:

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

今のようなもの

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

発する

c
i
a
o

要求に応じ。

next(sel.it)組み込みとして使用するには、Python 2.6以降が必要です。古いバージョンのPythonを使用している場合は、self.it.next()代わりに使用します(next(x)使用例でも同様です)。[[Python 2.6が1年以上前から存在しているため、このメモは冗長であると合理的に考えるかもしれませんが、Python 2.6の機能を応答で使用する場合は、多くの場合、一部のコメンターまたは他の人が指摘する義務があると感じていますそれら 2.6の機能であるため、そのようなコメントを1度は未然に防ぐようにしています;-)]]


9
「Javaの参照実装からアルゴリズムを忠実に転記する」ことが、has_nextメソッドを必要とする最悪の理由です。Pythonの設計では、たとえば、filter指定された述語に一致する要素が配列に含まれているかどうかをチェックすることはできません。Pythonコミュニティの傲慢さと近視眼は驚異的です。
ジョナサンキャスト

いい答えです。Javaコードから取ったいくつかのデザインパターンの図解のためにこれをコピーしています
madtyn

私はPython3を使用しており、このコードが私に与えるTypeError: iter() returned non-iterator
madtyn

1
@JonathanCast私がフォローしているのかわかりません。Pythonでは、通常map、のany代わりにおよびを使用しfilterますが、を使用SENTINEL = object(); next(filter(predicate, arr), SENTINEL) is not SENTINELまたは忘れてSENTINEL、を使用try: exceptしてキャッチすることもできますStopIteration
juanpa.arrivillaga

13

StopIterationに関するすべての言及に加えて、Pythonの「for」ループは単純にあなたが望むことをします:

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

7

イテレータオブジェクトから__length_hint __()メソッドを試してください。

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

5
なぜPythonにこれらすべての__ xxx __メソッドがあるのか​​、いつも疑問に思いました。彼らはとても醜いようです。
mP。

6
正当な質問です!通常、これは組み込み関数によって公開されるメソッドの構文です(たとえば、lenは実際にはlenを呼び出しています)。そのような組み込み関数はlength_hintには存在しませんが、実際には保留中の提案(PEP424)です。
フルミコトン2013

1
@mP。これらの関数は時々必要になるため、そこにあります。それらは最後の手段として見なされているため、意図的に醜くなっています。それらを使用すると、Python以外の、潜在的に危険な何かを実行していることがわかります(これは、いつかは動作を停止する可能性もあります)。
Arne Babenhauserheide

同様に__init__して __main__?Imho、それを正当化しようとするかどうかに関係なく、少し混乱しています。
user1363990 2017

5

hasNextややStopIteration例外に変換されます。例:

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

あなたはできるteeイテレータを使用してitertools.tee、とのチェックStopIterationイテレータをteedに。




1

これを検索するようになったユースケースは次のとおりです

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()が利用可能な場合、

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)

私にはどちらがきれいですか。明らかに、ユーティリティクラスを定義することで問題を回避できますが、その後発生するのは、それぞれに癖がある20種類のほぼ同等の回避策が急増していることです。異なる回避策を使用するコードを再利用する場合は、次のいずれかを行う必要があります。単一のアプリケーションに同等のものが複数ある場合、または同じアプローチを使用するようにコードを選択して書き直す場合。「一度やればうまくいく」という格言はひどく失敗する。

さらに、イテレーター自体は、例外を発生させる必要があるかどうかを確認するために実行する内部の「hasnext」チェックが必要です。その後、この内部チェックは非表示になるため、アイテムを取得して例外をキャッチし、スローされた場合はハンドラーを実行してテストする必要があります。これはIMOを非表示にする必要はありません。


1
このユースケースについては、使用することができますitertools.cycleを
eaglebrain

0

推奨される方法はStopIterationです。チュートリアルポイントからフィボナッチの例をご覧ください

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

私が問題を解決した方法は、これまでに繰り返されたオブジェクトの数を数えることです。インスタンスメソッドの呼び出しを使用してセットを反復処理したかったのです。セットの長さと、これまでにカウントされたアイテムの数がわかっているので、効果的なhasNext方法がありました。

私のコードの簡単なバージョン:

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

もちろん、この例はおもちゃですが、あなたはそのアイデアを理解しました。これは、ジェネレータなど、イテラブルの長さを取得する方法がない場合には機能しません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.