空の反復性をループできるようにするための行動を起こす慣用的な方法

StackOverflow https://stackoverflow.com/questions/3486257

  •  28-09-2019
  •  | 
  •  

質問

反復性をループしており、イテレーターが空である場合は何らかのアクションを実行したいとします。これを行うために考えることができる2つの最良の方法は、次のとおりです。

for i in iterable:
     # do_something
if not iterable:
    # do_something_else

empty = True
for i in iterable:
    empty = False
    # do_something
if empty:
    # do_something_else

1つ目は、コレクションであることに依存します(ループがある関数/メソッドに反復可能に渡された場合には役に立たない)および2番目のセット empty 醜いように見えるループを通過するたびに。

私が欠けている別の方法はありますか、それとも2番目の代替品が最高ですか?私がこれを私のために処理するループステートメントに追加できる条項がある場合、それは本当にクールでしょう else 作る not_found 旗が消えます。


私は賢いハックを探していません。

私は多くのコードを含むソリューションを探していません

シンプルな言語機能を探しています。私は探しています クリアPythonic 反復可能な反復を繰り返し、経験豊富なPythonプログラマーが理解できるという反復性が空である場合に何らかのアクションを実行します。すべての反復にフラグを設定せずにそれを行うことができれば、それは素晴らしいでしょう。これを行う単純なイディオムがない場合は、それを忘れてください。

役に立ちましたか?

解決

これはこれを行う最もきれいな方法だと思います:

# first try with exceptions
def nonempty( iter ):
    """ returns `iter` if iter is not empty, else raises TypeError """
    try:
        first = next(iter)
    except StopIteration:
        raise TypeError("Emtpy Iterator")
    yield first
    for item in iter:
        yield item


# a version without exceptions. Seems nicer:
def isempty( iter ):
    """ returns `(True, ())` if `iter` if is empty else `(False, iter)`
         Don't use the original iterator! """
    try:
        first = next(iter)
    except StopIteration:
        return True, ()
    else:
        def iterator():
            yield first
            for item in iter:
                yield item
        return False, iterator()



for x in ([],[1]):
    # first version
    try:
        list(nonempty(iter(x))) # trying to consume a empty iterator raises
    except TypeError:
        print x, "is empty"
    else:
        print x, "is not empty"

    # with isempty
    empty, it = isempty(iter(x))
    print x,  "is", ("empty" if empty else "not empty")

他のヒント

これは非常にハッキッシュですが、削除できます i そして、ループの後に存在するかどうかを確認します(そうでない場合、ループは決して起こりませんでした):

try:
    del i
except NameException: pass

for i in iterable:
    do_something(i)

try:
    del i
except NameException:
    do_something_else()

旗を使うよりもそれはおそらく醜いと思います

更新2

好きだった Odomontoisの答え. 。 IMHOこれは、私が以下に書いたものよりもこの問題により適しています。

アップデート

(OPのコメントと編集された質問を読んだ後)あなたもそれを行うことができます。下記参照:

def with_divisible(n, a, b, f):
 it = (i for i in xrange(a, b) if not i % n)
 for i in wrapper(it):
  f(i)

>>> with_divisible(1, 1, 1, lambda x: x)
Traceback (most recent call last):
  File "<pyshell#55>", line 1, in <module>
    with_divisible(1, 1, 1, lambda x: x)
  File "<pyshell#54>", line 3, in with_divisible
    for i in wrapper(it):
  File "<pyshell#46>", line 4, in wrapper
    raise EmptyIterableException("Empty")
EmptyIterableException: Empty

>>> with_divisible(7, 1, 21, lambda x: x)
7
14
...Snipped...
    raise EmptyIterableException("Empty")
EmptyIterableException: Empty

元の答え

興味深い問題。私はいくつかの実験をして、次のことを思いつきました:

class EmptyIterableException(Exception):
    pass

def wrapper(iterable):
    for each in iterable:
        yield each
    raise EmptyIterableException("Empty")

try:
    for each in wrapper(iterable):
        do_something(each)
except EmptyIterableException, e:
    do_something_else()
if not map(do_something_callable,iterable) : 
    # do something else

消費される前にイテレーターを部分的にチェックする場合の一般的な方法は使用することです itertools.tee. 。このようにして、最初から他のコピーを消費しながら、イテレーターの2つのコピーを繰り返して、1つを空虚に確認できます。

from itertools import tee
it1, it2 = tee(iterable)
try:
    it1.next()
    for i in it2:
        do_some_action(i) #iterator is not empty
except StopIteration:
    do_empty_action() #iterator is empty

StopIteration 例外は、 it1.next(), 、他のように StopIteration ループ内でフルームを提起した例外は、そのループを終了します。

編集: :そのような例外が嫌いな人のために、 islice 単一のステップループをセットアップするために使用できます。

from itertools import tee, islice
it1, it2 = tee(iterable)
for _ in islice(it1, 1):
    #loop entered if iterator is not empty
    for i in it2:
        do_some_action(i)
    break #if loop entered don't execute the else section
else:
    do_empty_action()

私は個人的に最初のスタイルを好みます。 ymmv。

「if」と「for」を逆転させるのはどうですか:

if iterable:
    for i in iterable:
        do_something(i)
else:
    do_something_else()

わかりました、これはうまくいきません!

他の解決策は次のとおりです。 http://code.activestate.com/recipes/413614-testing-for-an-empt--iterator/

これはの組み合わせです マイケル・ムロゼク'砂 FM答え:

def with_divisible(n, a, b, f):
    '''apply f to every integer x such that n divides x and a <= x < b'''
    it = (i for i in xrange(a, b) if not i % n)
    for i in it:
        f(i)
    try: i            # test if `it` was empty
    except NameError: print('do something else')

def g(i):
    print i,

with_divisible( 3, 1, 10, g)   # Prints 3 6 9.
with_divisible(33, 1, 10, g)   # Prints "do something else"

ジェネレーターには、ジェネレーターが使い果たされるとはいませんが、停止が発生した後にのみ「gi_frame」プロパティがあります。それが受け入れられるなら、ここにあなたが試すことができるものがあります:

import types

def do(x, f, f_empty):
    if type(x) == types.GeneratorType:
        # generators have a 'gi_frame' property,
        # which is None once the generator is exhausted
        if x.gi_frame:
            # not empty
            return f(x)
        return f_empty(x)
    if x:
        return f(x)
    return f_empty(x)

def nempty(lst):
    print lst, 'not empty'

def empty(lst):
    print 'Twas empty!'

# lists
do([2,3,4], nempty, empty)
do([], nempty, empty)

# generators
do((i for i in range(5)), nempty, empty)
gen = (i for i in range(1))
gen.next()
try:
    gen.next()
except StopIteration:
    pass
do(gen, nempty, empty)
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top