Pythonの「for」ループの最後の要素を検出するためのpythonの方法は何ですか?

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

質問

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

現在、私はそれをどのように行っていますか:

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 を使用するなどのハックで作成したくない;)

役に立ちましたか?

解決

ほとんどの場合、最初の反復を最後の反復ではなく特殊なケースにする方が簡単です(そしてより安価です):

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!

それとは別に、あなたが何をしようとしているかに依存するので、一般的に優れたソリューションがあるとは思いません。たとえば、リストから文字列を作成する場合、 for ループを使用するよりも str.join()を使用する方が自然です“特別な場合&# 8221;。


同じ原則を使用してよりコンパクトに:

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

おなじみのようですね。 :)


@ofko、および len()なしのiterableの現在の値が最後の値であるかどうかを本当に知る必要がある他の人のために、先読みする必要があります:

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

他のヒント

「コード間」は、ヘッドテールパターンの例です。

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

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

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

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

これは、証明するのがわずかに簡単であるため、より信頼性が高く、余分なデータ構造(リストのコピー)を作成せず、 if 条件。1回を除いて常にfalseです。

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

L[-1]

ただし、それ以上のことをしているようです。あなたのやり方には本当に悪いことは何もありません。私はいくつかの Djangoコードを一目見ましたテンプレートタグと基本的にあなたがやっていることをします。

その質問はかなり古いものですが、私はグーグルでここに来て、リストスライスという非常に簡単な方法を見つけました。 「&」を付けたいとしましょうすべてのリストエントリ間。

s = ""
l = [1, 2, 3]
for i in l[:-1]:
    s = s + str(i) + ' & '
s = s + str(l[-1])

これは '1&を返します2& 3 '。

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

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

アイテムが一意の場合:

for x in list:
    #code
    if x == list[-1]:
        #code

その他のオプション:

pos = -1
for x in list:
    pos += 1
    #code
    if pos == len(list) - 1:
        #code


for x in list:
    #code
#code - e.g. print x


if len(list) > 0:
    for x in list[:-1]
        #code
    for x in list[-1]:
        #code

入力データ上でスライディングウィンドウを使用して次の値を確認し、センチネルを使用して最後の値を検出できます。これはあらゆる反復可能オブジェクトで機能するため、事前に長さを知る必要はありません。ペアワイズ実装は、 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

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

(この質問もご覧ください: does -the-last-element-in-a-loop-deserve-a-separate-treatment

編集:質問は「間の」についての詳細であるため、 first 要素は先行要素がないという特別な要素であるか、または last 要素は、後続要素がないという点で特別です。

ループの数が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

しかし、本当に、あなたの場合、それは行き過ぎだと思います。

いずれにせよ、あなたはおそらくスライシングで幸運になるでしょう:

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

出力:

<*>

そのようにしたいと思うなら、私には簡単に思えます。

スライシングと 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>

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

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()を扱うよりもエレガントだと思います。

@ ethan-tのアプローチは好きですが、 while True は私の観点からは危険です。

while L:
    e = L.pop(0)
    # process element
    if not L:
        print('Last element has been detected.')

ループの後まで最後のアイテムの特別な処理を遅らせます。

>>> for i in (1, 2, 3):
...     pass
...
>>> i
3

イテレータとして入力を想定し、以下は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
>>>

リストを調べている場合、私にとってもこれはうまくいきました:

for j in range(0, len(Array)):
    if len(Array) - j > 1:
        notLast()

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

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

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

もちろん、必要なときに NameError を発生させるには、少し考える必要があります。

「counstruct」も保持

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

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

アイテムを1回カウントし、残りのアイテムの数を把握します。

remaining = len(data_list)
for data in data_list:
    code_that_is_done_for_every_element

    remaining -= 1
    if remaining:
        code_that_is_done_between_elements

この方法では、リストの長さを一度だけ評価します。このページのソリューションの多くは、長さが事前に利用できないことを前提としているようですが、それはあなたの質問の一部ではありません。長さがある場合は、それを使用します。

私にとって、リストの最後で特別なケースを処理するための最も簡単でPython的な方法は次のとおりです。

for data in data_list[:-1]:
    handle_element(data)
handle_special_element(data_list[-1])

もちろん、これを使用して最初の要素を特別な方法で処理することもできます。

複数の方法があります。スライスが最速になります。 .index()メソッドを使用するもう1つの追加:

>>> l1 = [1,5,2,3,5,1,7,43]                                                 
>>> [i for i in l1 if l1.index(i)+1==len(l1)]                               
[43]
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top