196

ジェネレーターに , , のようなアイテムがないかどうかをテストする簡単な方法はありpeekますhasNextisEmpty?

4

23 に答える 23

130

提案:

def peek(iterable):
    try:
        first = next(iterable)
    except StopIteration:
        return None
    return first, itertools.chain([first], iterable)

使用法:

res = peek(mysequence)
if res is None:
    # sequence is empty.  Do stuff.
else:
    first, mysequence = res
    # Do something with first, maybe?
    # Then iterate over the sequence:
    for element in mysequence:
        # etc.
于 2009-03-19T22:01:31.527 に答える
65

あなたの質問に対する簡単な答えは: いいえ、簡単な方法はありません。回避策はたくさんあります。

ジェネレーターが何であるかを考えると、単純な方法があるべきではありません:シーケンスをメモリに保持せずに値のシーケンスを出力する方法です。したがって、後方トラバーサルはありません。

has_next 関数を記述したり、必要に応じて派手なデコレータを使用したメソッドとしてジェネレータに平手打ちしたりすることもできます。

于 2009-03-19T16:25:12.830 に答える
22

next(generator, None) is not None

または、ジェネレーターにないNoneことがわかっている値を置き換えます。

編集:はい、これはジェネレーターで1つのアイテムをスキップします。ただし、多くの場合、ジェネレーターが空であるかどうかを検証目的でのみチェックし、実際には使用しません。または、そうでなければ私は次のようなことをします:

def foo(self):
    if next(self.my_generator(), None) is None:
        raise Exception("Not initiated")

    for x in self.my_generator():
        ...

つまり、これは、ジェネレーターが のように関数から派生している場合に機能generator()ます。

于 2016-05-22T14:58:54.963 に答える
16

最良のアプローチは、私見ですが、特別なテストを避けることです。ほとんどの場合、ジェネレーターの使用はテストです。

thing_generated = False

# Nothing is lost here. if nothing is generated, 
# the for block is not executed. Often, that's the only check
# you need to do. This can be done in the course of doing
# the work you wanted to do anyway on the generated output.
for thing in my_generator():
    thing_generated = True
    do_work(thing)

それでも十分でない場合でも、明示的なテストを実行できます。この時点でthing、最後に生成された値が含まれます。何も生成されなかった場合は、変数を定義していない限り、未定義になります。の値を確認できますがthing、それは少し信頼できません。代わりに、ブロック内にフラグを設定し、後で確認してください。

if not thing_generated:
    print "Avast, ye scurvy dog!"
于 2009-03-19T12:08:48.183 に答える
8

私は2番目の解決策、特に私自身は使用しない解決策を提供するのは嫌いですが、他の回答のように、これを絶対に実行し、ジェネレーターを消費しないようにする必要がある場合:

def do_something_with_item(item):
    print item

empty_marker = object()

try:
     first_item = my_generator.next()     
except StopIteration:
     print 'The generator was empty'
     first_item = empty_marker

if first_item is not empty_marker:
    do_something_with_item(first_item)
    for item in my_generator:
        do_something_with_item(item)

これはジェネレーターの使用方法ではないと信じているため、このソリューションは本当に好きではありません。

于 2009-03-19T10:23:37.500 に答える
5

ジェネレーターが空かどうかを確認するために必要なことは、次の結果を取得しようとすることだけです。もちろん、その結果を使用する準備ができていない場合は、後で再度返すために保存する必要があります。

テストを追加するために既存のイテレータに追加できるラッパー クラスを次に示します。__nonzero__これにより、単純なif. おそらく、デコレータにすることもできます。

class GenWrapper:
    def __init__(self, iter):
        self.source = iter
        self.stored = False

    def __iter__(self):
        return self

    def __nonzero__(self):
        if self.stored:
            return True
        try:
            self.value = next(self.source)
            self.stored = True
        except StopIteration:
            return False
        return True

    def __next__(self):  # use "next" (without underscores) for Python 2.x
        if self.stored:
            self.stored = False
            return self.value
        return next(self.source)

使用方法は次のとおりです。

with open(filename, 'r') as f:
    f = GenWrapper(f)
    if f:
        print 'Not empty'
    else:
        print 'Empty'

繰り返しの開始時だけでなく、いつでも空であることを確認できることに注意してください。

于 2014-06-13T06:30:58.543 に答える
3
>>> gen = (i for i in [])
>>> next(gen)
Traceback (most recent call last):
  File "<pyshell#43>", line 1, in <module>
    next(gen)
StopIteration

ジェネレーターの最後に発生StopIterationします。あなたの場合、すぐに終了に達するため、例外が発生します。しかし、通常、次の値の存在をチェックするべきではありません。

あなたができる別のことは次のとおりです。

>>> gen = (i for i in [])
>>> if not list(gen):
    print('empty generator')
于 2009-03-19T10:00:23.140 に答える
2

明らかなアプローチで申し訳ありませんが、最善の方法は次のとおりです。

for item in my_generator:
     print item

使用中にジェネレーターが空であることを検出しました。もちろん、ジェネレーターが空の場合、アイテムは表示されません。

これはあなたのコードに正確に適合しないかもしれませんが、これがジェネレーターのイディオムの目的です: 反復するため、アプローチを少し変更するか、ジェネレーターをまったく使用しない場合があります。

于 2009-03-19T10:14:14.853 に答える
1

ジェネレーターを使用する前に知る必要がある場合は、いいえ、簡単な方法はありません。ジェネレーターを使用したまで待つことができる場合は、簡単な方法があります。

was_empty = True

for some_item in some_generator:
    was_empty = False
    do_something_with(some_item)

if was_empty:
    handle_already_empty_generator_case()
于 2011-11-01T19:47:29.997 に答える
0

これは古くからある回答済みの質問ですが、これまで誰も示していないので、次のようになります。

for _ in generator:
    break
else:
    print('Empty')

詳細はこちら

于 2020-02-21T10:53:04.850 に答える
0

これはジェネレーターをラップする単純なデコレーターで、空の場合は None を返します。これは、ループする前にジェネレーターが何かを生成するかどうかをコードが知る必要がある場合に役立ちます。

def generator_or_none(func):
    """Wrap a generator function, returning None if it's empty. """

    def inner(*args, **kwargs):
        # peek at the first item; return None if it doesn't exist
        try:
            next(func(*args, **kwargs))
        except StopIteration:
            return None

        # return original generator otherwise first item will be missing
        return func(*args, **kwargs)

    return inner

使用法:

import random

@generator_or_none
def random_length_generator():
    for i in range(random.randint(0, 10)):
        yield i

gen = random_length_generator()
if gen is None:
    print('Generator is empty')

これが役立つ 1 つの例は、コードのテンプレート化 (例: jinja2) です。

{% if content_generator %}
  <section>
    <h4>Section title</h4>
    {% for item in content_generator %}
      {{ item }}
    {% endfor %
  </section>
{% endif %}
于 2016-07-29T03:43:42.677 に答える
-2

sum関数を使って解決しました。glob.iglob (ジェネレーターを返す) で使用した例については、以下を参照してください。

def isEmpty():
    files = glob.iglob(search)
    if sum(1 for _ in files):
        return True
    return False

*これはおそらく巨大なジェネレーターでは機能しませんが、小さなリストではうまく機能するはずです

于 2016-06-14T20:09:31.373 に答える