イテレータが少なくとも1つの要素を生成するかどうかを確認するワンライナー?


100

現在私はこれをやっています:

try:
    something = iterator.next()
    # ...
except StopIteration:
    # ...

しかし、簡単なifステートメントの中に配置できる式が欲しいのですが。このコードを扱いにくくする組み込みの機能はありますか?

any()Falseイテラブルが空の場合はを返しますが、空でない場合はすべてのアイテムを反復処理する可能性があります。最初のアイテムをチェックするためだけに必要です。


誰かが私が何をしようとしているのかと尋ねます。SQLクエリを実行してその結果を生成する関数を作成しました。この関数を呼び出すとき、クエリが何かを返したかどうかを知り、それに基づいて決定をしたい場合があります。


2
また、そのコードの問題は、最初の要素を使用するため、関数にパッケージ化できないことです。良い質問。
andrewrk

2
私の場合、要素はまったく必要ありません。少なくとも1つの要素があることを知りたいだけです。
BastienLéonard2010年

2
ハァッ!同じ解決策を見つけようとする私のユースケース!
Daniel


回答:


133

anyTrueの場合、最初の要素を超えることはありません。イテレータが何か偽のような結果を出す場合、あなたは書くことができますany(True for _ in iterator)


これはre.finditerで私にはうまくいくようです。あなたは最初の成功ですべての停止が簡単にテストできます:実行any((x > 100 for x in xrange(10000000)))してから実行します-2 any((x > 10000000 for x in xrange(100000000)))番目ははるかに長くかかるはずです。
chbrown

1
これは、「少なくともx」の場合にsum(1 for _ in itertools.islice(iterator, max_len)) >= max_len
デイブバトラー

11
同様に、イテレータが空かどうかを確認する必要がある場合、イテレータが空かall(False for _ in iterator)どうかを確認できます。(イテレータが空の場合はすべてTrueを返し、それ以外の場合は最初のFalse要素を検出すると停止します)
KGardevoir

22
このソリューションの大きな問題は、イテレータからの戻り値が空でない場合、実際には使用できないことです。
ケンウィリアムズ

42

Python 2.6以降sentinelでは、イテレータが生成できない値に名前がバインドされている場合、

if next(iterator, sentinel) is sentinel:
    print('iterator was empty')

イテレータが何を生成する可能性があるのか​​わからない場合は、独自のセンチネルを(たとえば、モジュールの上部に)作成します。

sentinel = object()

それ以外の場合は、センチネルロールで、イテレータが生成できない可能性がある(アプリケーションの考慮事項に基づいて)「知っている」任意の値を使用できます。


1
いいね!私の使用例でif not next(iterator, None):は、Noneはアイテムの一部にならないと確信していたので十分でした。私を正しい方向に向けてくれてありがとう!
wasabigeek

1
@wasabi not空のリスト、False、ゼロなどの偽のオブジェクトに対してTrueが返されることを忘れないでください。is not Noneより安全で、私の意見ではより明確です。
Caagr98

21

これはあまりきれいではありませんが、ロスレスで関数にパッケージ化する方法を示しています。

def has_elements(iter):
  from itertools import tee
  iter, any_check = tee(iter)
  try:
    any_check.next()
    return True, iter
  except StopIteration:
    return False, iter

has_el, iter = has_elements(iter)
if has_el:
  # not empty

これは実際にはpythonicではなく、特定のケースでは、次のデフォルトのように、より良い(しかし一般的ではない)ソリューションがおそらくあります。

first = next(iter, None)
if first:
  # Do something

Noneは多くのイテラブルで有効な要素になる可能性があるため、これは一般的ではありません。


これはおそらくこれを行う最良の方法です。しかし、それはOPが何をしようとしているのかを知るのに役立ちますか?おそらくもっとエレガントな解決策があります(結局のところ、これはPythonです)。
rossipedia 2010年

よろしくお願いしますnext()
BastienLéonard2010年

1
@Bastien、結構ですが、適切な歩哨を使用してください(私の回答を参照)。
Alex Martelli、

3
このソリューションには、巨大なメモリリークがあります。teeitertoolsでは、ケースには、元のイテレータから一つ一つの要素を維持する必要がありますany_check、これまで事前に必要です。これは、元のイテレータをリストに変換するよりも悪いです。
ラファウDowgird

1
@RafałDowgird これは、元のイテレータを単にリストに変換するよりも悪いです。本当にそうではありません–無限シーケンスについて考えてください。
Piotr Dobrogost 2016年

6

あなたは使うことができます:

if zip([None], iterator):
    # ...
else:
    # ...

しかし、コードリーダーにとっては説明にはなりません


2
..([なし]の代わりに任意の1項目の
イテラブル

5

これを行う最善の方法は、peekablefromを使用することmore_itertoolsです。

from more_itertools import peekable
iterator = peekable(iterator)
if iterator:
    # Iterator is non-empty.
else:
    # Iterator is empty.

古いイテレータへの参照を保持している場合は、そのイテレータが高度になることに注意してください。それ以降は、新しいピーク可能なイテレータを使用する必要があります。ただし、実際には、peekableはその古いイテレーターを変更する唯一のコードであると想定しているため、とにかく古いイテレーターへの参照を保持してはなりません。


3

何について:

In [1]: i=iter([])

In [2]: bool(next(i,False))
Out[2]: False

In [3]: i=iter([1])

In [4]: bool(next(i,False))
Out[4]: True

4
面白い!しかし、next()が実際に生成されたものであるため、Falseが返された場合はどうなりますか?
BastienLéonard18年

@BastienLéonardクラスを作成しclass NotSet: pass、次を確認しますif next(i, NotSet) is NotSet: print("Iterator is empty")
Elijas

-1

__length_hint__ の長さを推定しますlist(it)-それはプライベートメソッドですが:

x = iter( (1, 2, 3) )
help(x.__length_hint__)
      1 Help on built-in function __length_hint__:
      2 
      3 __length_hint__(...)
      4     Private method returning an estimate of len(list(it)).

4
すべての反復子について保証されているわけではありません。>>> def it():...収率1 ...収率2 ...収率3 ... >>> i = it()>>> i .__ length_hint__トレースバック(最後の最新の呼び出し):ファイル " <stdin> "、1行目<モジュール> AttributeError: 'generator' object has no attribute ' length_hint '
andrewrk

3
単なるヒントなので、エントリが0を超えるイテレータに対して0を返すこともおそらく合法です。
Glenn Maynard

-1

これは、次のアイテムがあるかどうかを(ブール値に変換して)チェックできるオーバーキルイテレータラッパーです。もちろん、かなり非効率的です。

class LookaheadIterator ():

    def __init__(self, iterator):
        self.__iterator = iterator
        try:
            self.__next      = next (iterator)
            self.__have_next = True
        except StopIteration:
            self.__have_next = False

    def __iter__(self):
        return self

    def next (self):
        if self.__have_next:
            result = self.__next
            try:
                self.__next      = next (self.__iterator)
                self.__have_next = True
            except StopIteration:
                self.__have_next = False

            return result

        else:
            raise StopIteration

    def __nonzero__(self):
        return self.__have_next

x = LookaheadIterator (iter ([]))
print bool (x)
print list (x)

x = LookaheadIterator (iter ([1, 2, 3]))
print bool (x)
print list (x)

出力:

False
[]
True
[1, 2, 3]

-2

少し遅れますが...イテレータをリストにして、そのリストを操作することができます。

# Create a list of objects but runs out the iterator.
l = [_ for _ in iterator]

# If the list is not empty then the iterator had elements; else it was empty.
if l :
    pass # Use the elements of the list (i.e. from the iterator)
else :
    pass # Iterator was empty, thus list is empty.

4
リスト全体を列挙するため、これは非効率的です。無限のジェネレーターでは機能しません。
becko

@becko:そうですね。しかし、元の質問の場合はそうではないようです。
Jens

3
もう一つの問題は、イテレータがメモリオーバーフローを引き起こす可能性がオブジェクトの無限の量、およびプログラムは次のステートメントに到達することはありませんという事実生成することができることである
ウィレム・ヴァンOnsem
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.