当使用for循环迭代时,我如何处理输入的最后一个元素?特别是,如果有代码应该只出现在元素之间(而不是在最后一个元素之后),我该如何构造代码?

目前,我写的代码是这样的:

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

我如何简化或改进它?


当前回答

数一次项目,并跟上剩余项目的数量:

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

这种方法只计算一次列表的长度。本页上的许多解决方案似乎都假定长度是预先不可用的,但这不是您的问题的一部分。如果你有长度,就用它。

其他回答

是否不可能遍历除最后一个元素以外的所有元素,并在循环之外处理最后一个元素?毕竟,创建循环的目的与循环遍历的所有元素类似;如果一个元素需要一些特殊的东西,它就不应该在循环中。

(另请参阅这个问题:循环中的最后一个元素是否值得单独处理)

编辑:因为这个问题更多的是关于“中间”,所以要么第一个元素是特殊的,因为它没有前一个元素,要么最后一个元素是特殊的,因为它没有后继元素。

我将提供一种更优雅和健壮的方式,如下所示,使用unpacking:

def mark_last(iterable):
    try:
        *init, last = iterable
    except ValueError:  # if iterable is empty
        return

    for e in init:
        yield e, True
    yield last, False

测试:

for a, b in mark_last([1, 2, 3]):
    print(a, b)

结果是:

1真正的 2真 3错误

将最后一项的特殊处理延迟到循环之后。

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

如果你只是想修改data_list中的最后一个元素,那么你可以简单地使用符号:

L[-1]

然而,看起来你做的还不止这些。你的方式并没有什么问题。我甚至快速浏览了一些Django的模板标签代码,它们做的基本和你做的一样。

假设input是一个迭代器,下面是使用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
>>>