当使用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

我如何简化或改进它?


当前回答

如果你乐意用这个清单来破坏,那么下面就是。 为了加快从O(n²)到O(n)的过程,我们将反转列表,因为pop(0)每次迭代都会移动列表——参见下面Nicholas Pipitone的评论

data_list.reverse()
while data_list:
   value = data_list.pop()
   code_that_is_done_for_every_element(value)
   if data_list:
       code_that_is_done_between_elements(value)
   else:
       code_that_is_done_for_last_element(value)


这适用于空列表和非唯一项的列表。 因为列表通常是暂时的,所以这种方法非常有效……以销毁列表为代价。

其他回答

如果你乐意用这个清单来破坏,那么下面就是。 为了加快从O(n²)到O(n)的过程,我们将反转列表,因为pop(0)每次迭代都会移动列表——参见下面Nicholas Pipitone的评论

data_list.reverse()
while data_list:
   value = data_list.pop()
   code_that_is_done_for_every_element(value)
   if data_list:
       code_that_is_done_between_elements(value)
   else:
       code_that_is_done_for_last_element(value)


这适用于空列表和非唯一项的列表。 因为列表通常是暂时的,所以这种方法非常有效……以销毁列表为代价。

这类似于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

大多数情况下,让第一次迭代成为特殊情况比最后一次迭代更容易(也更便宜):

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,以及其他真正需要找出不带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

如果你正在浏览这个列表,这对我来说也是有效的:

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

因此,这绝对不是“更短”的版本——如果“最短”和“Pythonic”实际上是兼容的,人们可能会离题。

但如果经常需要这种模式,就把逻辑放到a中 10行生成器-并获得与元素相关的任何元数据 在for调用中直接定位。这里的另一个优势是它会 适用于任意可迭代对象,而不仅仅是序列。

_sentinel = object()

def iter_check_last(iterable):
    iterable = iter(iterable)
    current_element = next(iterable, _sentinel)
    while current_element is not _sentinel:
        next_element = next(iterable, _sentinel)
        yield (next_element is _sentinel, current_element)
        current_element = next_element
In [107]: for is_last, el in iter_check_last(range(3)):
     ...:     print(is_last, el)
     ...: 
     ...: 
False 0
False 1
True 2