254

forループの最後の要素を特別に処理するための最良の方法(よりコンパクトで「pythonic」な方法)を知りたいです。要素間でのみ呼び出す必要があり、最後のコードでは抑制されているコードがあります。

これが私が現在行っている方法です:

for i, data in enumerate(data_list):
    code_that_is_done_for_every_element
    if i != len(data_list) - 1:
        code_that_is_done_between_elements

もっと良い方法はありますか?

注: を使用するなどのハックで作成したくありませんreduce;)

4

31 に答える 31

185

ほとんどの場合、最後の反復ではなく最初の反復を特殊なケースにする方が簡単 (かつ安価)です。

first = True
for data in data_list:
    if first:
        first = False
    else:
        between_items()

    item()

これは、ないものであっても、あらゆるイテラブルに対して機能しますlen()

file = open('/path/to/file')
for line in file:
    process_line(line)

    # No way of telling if this is the last line!

それとは別に、何をしようとしているのかにもよるので、一般的に優れた解決策はないと思います。たとえば、リストから文字列を作成している場合、 「特殊なケースで」ループを使用するstr.join()よりも、使用する方が当然優れています。for


同じ原理を使用して、よりコンパクトにします。

for i, line in enumerate(data_list):
    if i > 0:
        between_items()
    item()

おなじみですね。:)


@ofko や iterable の現在の値がlen()最後の値かどうかを本当に確認する必要がある人は、先を見据える必要があります。

def lookahead(iterable):
    """Pass through all values from the given iterable, augmented by the
    information if there are more values to come after the current one
    (True), or if it is the last value (False).
    """
    # Get an iterator and pull the first value.
    it = iter(iterable)
    last = next(it)
    # Run the iterator to exhaustion (starting from the second value).
    for val in it:
        # Report the *previous* value (more to come).
        yield last, True
        last = val
    # Report the last value.
    yield last, False

次に、次のように使用できます。

>>> for i, has_more in lookahead(range(3)):
...     print(i, has_more)
0 True
1 True
2 False
于 2009-10-27T12:02:28.947 に答える
21

「code between」は、Head-Tailパターンの例です。

item があり、その後に一連の ( between, item ) ペアが続きます。これは、一連の (アイテム、間) ペアとそれに続くアイテムとして表示することもできます。一般に、最初の要素を特別なものとして、その他すべてを「標準」のケースとして扱う方が簡単です。

さらに、コードの繰り返しを避けるために、繰り返したくないコードを含む関数またはその他のオブジェクトを提供する必要があります。1 回を除いて常に false になるループにifステートメントを埋め込むのは、ばかげています。

def item_processing( item ):
    # *the common processing*

head_tail_iter = iter( someSequence )
head = next(head_tail_iter)
item_processing( head )
for item in head_tail_iter:
    # *the between processing*
    item_processing( item )

これは、証明するのが少し簡単なので、より信頼性が高くなります。余分なデータ構造 (つまり、リストのコピー) を作成せず、1 回を除いて常に false であるif条件の多くの無駄な実行を必要としません。

于 2009-10-27T13:38:36.737 に答える
18

最後の要素を変更するdata_listだけの場合は、次の表記を使用できます。

L[-1]

しかし、あなたはそれ以上のことをしているようです。あなたのやり方に本当に問題はありません。テンプレート タグのDjango コードをざっと見ただけでも、基本的にはあなたがしていることを実行します。

于 2009-10-27T12:05:19.563 に答える
12

これは Ants Aasma のアプローチに似ていますが、 itertools モジュールを使用していません。これは、イテレータ ストリーム内の 1 つの要素を先読みする遅延イテレータでもあります。

def last_iter(it):
    # Ensure it's an iterator and get the first field
    it = iter(it)
    prev = next(it)
    for item in it:
        # Lag by one item so I know I'm not at the end
        yield 0, prev
        prev = item
    # Last item
    yield 1, prev

def test(data):
    result = list(last_iter(data))
    if not result:
        return
    if len(result) > 1:
        assert set(x[0] for x in result[:-1]) == set([0]), result
    assert result[-1][0] == 1

test([])
test([1])
test([1, 2])
test(range(5))
test(xrange(4))

for is_last, item in last_iter("Hi!"):
    print is_last, item
于 2009-10-27T20:32:51.687 に答える
5

入力データに対してスライディング ウィンドウを使用して次の値を確認し、センチネルを使用して最後の値を検出できます。これはあらゆる iterable で機能するため、事前に長さを知る必要はありません。ペアワイズ実装はitertools レシピからのものです。

from itertools import tee, izip, chain

def pairwise(seq):
    a,b = tee(seq)
    next(b, None)
    return izip(a,b)

def annotated_last(seq):
    """Returns an iterable of pairs of input item and a boolean that show if
    the current item is the last item in the sequence."""
    MISSING = object()
    for current_item, next_item in pairwise(chain(seq, [MISSING])):
        yield current_item, next_item is MISSING:

for item, is_last_item in annotated_last(data_list):
    if is_last_item:
        # current item is the last item
于 2009-10-27T13:17:21.813 に答える
4

最後の要素を除くすべてを反復処理し、最後の要素をループ外で処理する可能性はありませんか? 結局のところ、ループするすべての要素と同様のことを行うためにループが作成されます。1 つの要素が何か特別なものを必要とする場合、その要素はループ内にあるべきではありません。

(この質問も参照してください: does-the-last-element-in-a-loop-deserve-a-separate-treat )

編集:質問は「中間」に関するものであるため、最初の要素は先行要素がないという点で特別な要素であるか、最後の要素は後続要素がないという点で特別です。

于 2009-10-27T12:11:17.207 に答える
3

スライスとを使用isして、最後の要素を確認します。

for data in data_list:
    <code_that_is_done_for_every_element>
    if not data is data_list[-1]:
        <code_that_is_done_between_elements>

警告 emptor : これは、リスト内のすべての要素が実際に異なる (メモリ内の場所が異なる) 場合にのみ機能します。内部では、Python は等しい要素を検出し、同じオブジェクトを再利用する場合があります。たとえば、同じ値と共通の整数の文字列の場合です。

于 2015-06-23T03:27:22.160 に答える
3

100 000 のループがあり、100 000 の「if」ステートメントを保存したい場合を除き、あなたのやり方に問題はありません。その場合、そのように行くことができます:

iterable = [1,2,3] # Your date
iterator = iter(iterable) # get the data iterator

try :   # wrap all in a try / except
    while 1 : 
        item = iterator.next() 
        print item # put the "for loop" code here
except StopIteration, e : # make the process on the last element here
    print item

出力:

1
2
3
3

しかし、本当に、あなたの場合、それはやり過ぎのように感じます。

いずれにせよ、あなたはおそらく slicing でより幸運になるでしょう:

for item in iterable[:-1] :
    print item
print "last :", iterable[-1]

#outputs
1
2
last : 3

あるいは単に :

for item in iterable :
    print item
print iterable[-1]

#outputs
1
2
3
last : 3

最終的に、あなたのことを行うためのKISSの方法であり、それは、ないものを含め、あらゆるイテラブルで機能します__len__:

item = ''
for item in iterable :
    print item
print item

出力:

1
2
3
3

そうする気がするなら、私には簡単に思えます。

于 2009-10-27T12:15:19.570 に答える
3

Google はこの古い質問に私を連れてきました。この問題に別のアプローチを追加できると思います。

ここでの回答のほとんどは、求められたとおりに for ループ コントロールを適切に処理するものですが、data_list が破壊可能である場合は、空のリストになるまでリストから項目をポップすることをお勧めします。

while True:
    element = element_list.pop(0)
    do_this_for_all_elements()
    if not element:
        do_this_only_for_last_element()
        break
    do_this_for_all_elements_but_last()

最後の要素で何もする必要がない場合は、while len(element_list) を使用することもできます。このソリューションは、next() を扱うよりもエレガントだと思います。

于 2016-07-14T14:09:10.217 に答える
2

あなたがリストを調べているなら、私にとってこれもうまくいきました:

for j in range(0, len(Array)):
    if len(Array) - j > 1:
        notLast()
于 2016-01-08T21:51:37.140 に答える
0

入力をイテレータと仮定すると、 itertools の tee と izip を使用する方法は次のとおりです。

from itertools import tee, izip
items, between = tee(input_iterator, 2)  # Input must be an iterator.
first = items.next()
do_to_every_item(first)  # All "do to every" operations done to first item go here.
for i, b in izip(items, between):
    do_between_items(b)  # All "between" operations go here.
    do_to_every_item(i)  # All "do to every" operations go here.

デモ:

>>> def do_every(x): print "E", x
...
>>> def do_between(x): print "B", x
...
>>> test_input = iter(range(5))
>>>
>>> from itertools import tee, izip
>>>
>>> items, between = tee(test_input, 2)
>>> first = items.next()
>>> do_every(first)
E 0
>>> for i,b in izip(items, between):
...     do_between(b)
...     do_every(i)
...
B 0
E 1
B 1
E 2
B 2
E 3
B 3
E 4
>>>
于 2009-10-27T13:39:40.647 に答える
0

私の頭に浮かぶ最も簡単な解決策は次のとおりです。

for item in data_list:
    try:
        print(new)
    except NameError: pass
    new = item
print('The last item: ' + str(new))

そのため、処理の 1 回の繰り返しを遅らせることで、常に 1 つの項目を先読みします。最初の反復中に何かをスキップするには、単にエラーをキャッチします。

もちろん、必要なNameErrorときに を引き上げるためには、少し考える必要があります。

また、`構造体を保持します

try:
    new
except NameError: pass
else:
    # continue here if no error was raised

これは、 new という名前が以前に定義されていなかったことに依存しています。あなたが偏執的である場合は、以下をnew使用して存在しないことを確認できます。

try:
    del new
except NameError:
    pass

または、もちろん if ステートメント ( if notfirst: print(new) else: notfirst = True) を使用することもできます。しかし、私が知る限り、オーバーヘッドはより大きくなります。


Using `timeit` yields:

    ...: try: new = 'test' 
    ...: except NameError: pass
    ...: 
100000000 loops, best of 3: 16.2 ns per loop

そのため、オーバーヘッドは選択できないと予想しています。

于 2017-04-03T13:45:23.053 に答える