检测“for”中最后一个元素的Pythonic方法是什么?环形?

发布于 2024-08-09 22:02:25 字数 311 浏览 1 评论 0原文

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

如何简化或改进它?

How can I treat the last element of the input specially, when iterating with a for loop? In particular, if there is code that should only occur "between" elements (and not "after" the last one), how can I structure the code?

Currently, I write code like so:

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

How can I simplify or improve this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

养猫人 2024-08-16 22:02:25

大多数时候,将第一个迭代作为特殊情况而不是最后一个迭代会更容易(而且更便宜):

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)
    try:
        last = next(it)
    except StopIteration:
        return
    # 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

Most of the times it is easier (and cheaper) to make the first iteration the special case instead of the last one:

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

    item()

This will work for any iterable, even for those that have no len():

file = open('/path/to/file')
for line in file:
    process_line(line)
    
    # No way of telling if this is the last line!

Apart from that, I don't think there is a generally superior solution as it depends on what you are trying to do. For example, if you are building a string from a list, it's naturally better to use str.join() than using a for loop “with special case”.


Using the same principle but more compact:

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

Looks familiar, doesn't it? :)


For @ofko, and others who really need to find out if the current value of an iterable without len() is the last one, you will need to look ahead:

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)
    try:
        last = next(it)
    except StopIteration:
        return
    # 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

Then you can use it like this:

>>> for i, has_more in lookahead(range(3)):
...     print(i, has_more)
0 True
1 True
2 False
冷了相思 2024-08-16 22:02:25

如果项目是唯一的:

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]:
        #process everything except the last element
    for x in list[-1:]:
        #process only last element

if the items are unique:

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

other options:

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]:
        #process everything except the last element
    for x in list[-1:]:
        #process only last element
老娘不死你永远是小三 2024-08-16 22:02:25

虽然这个问题很老了,但我通过谷歌来到这里,发现了一个非常简单的方法:列表切片。假设您想添加一个“&”所有列表条目之间。

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

这将返回 '1 & 2& 3'。

Although that question is pretty old, I came here via google and I found a quite simple way: List slicing. Let's say you want to put an '&' between all list entries.

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

This returns '1 & 2 & 3'.

风蛊 2024-08-16 22:02:25

“代码之间”是头尾模式的一个示例。

您有一个项目,后面跟着一系列 ( Between, item ) 对。您还可以将其视为一系列(项目,之间)对,后跟一个项目。通常,将第一个元素视为特殊元素并将所有其他元素视为“标准”情况会更简单。

此外,为了避免重复代码,您必须提供一个函数或其他对象来包含您不想重复的代码。在循环中嵌入一个 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 )

这更可靠,因为它更容易证明,它不会创建额外的数据结构(即列表的副本),并且不需要大量浪费的 if 条件执行除了一次之外,这总是错误的。

The 'code between' is an example of the Head-Tail pattern.

You have an item, which is followed by a sequence of ( between, item ) pairs. You can also view this as a sequence of (item, between) pairs followed by an item. It's generally simpler to take the first element as special and all the others as the "standard" case.

Further, to avoid repeating code, you have to provide a function or other object to contain the code you don't want to repeat. Embedding an if statement in a loop which is always false except one time is kind of silly.

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 )

This is more reliable because it's slightly easier to prove, It doesn't create an extra data structure (i.e., a copy of a list) and doesn't require a lot of wasted execution of an if condition which is always false except once.

仅冇旳回忆 2024-08-16 22:02:25

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

L[-1]

但是,看起来您所做的不止于此。你的方式并没有什么问题。我什至快速浏览了一些 Django 代码他们的模板标签,他们基本上做你正在做的事情。

If you're simply looking to modify the last element in data_list then you can simply use the notation:

L[-1]

However, it looks like you're doing more than that. There is nothing really wrong with your way. I even took a quick glance at some Django code for their template tags and they do basically what you're doing.

奶气 2024-08-16 22:02:25

您可以使用以下代码确定最后一个元素:

for i,element in enumerate(list):
    if (i==len(list)-1):
        print("last element is" + element)

you can determine the last element with this code :

for i,element in enumerate(list):
    if (i==len(list)-1):
        print("last element is" + element)
染年凉城似染瑾 2024-08-16 22:02:25

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

This is similar to Ants Aasma's approach but without using the itertools module. It's also a lagging iterator which looks-ahead a single element in the iterator stream:

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
离不开的别离 2024-08-16 22:02:25

我们可以使用 for-else

cities = [
  'Jakarta',
  'Surabaya',
  'Semarang'
]

for city in cities[:-1]:
  print(city)
else:
  print(' '.join(cities[-1].upper()))

输出来实现这一点:

Jakarta
Surabaya
S E M A R A N G

这个想法是我们只使用 for-else 循环直到 n-1 索引,然后在for 已耗尽,我们使用 [-1] 直接访问最后一个索引。

We can achieve that using for-else

cities = [
  'Jakarta',
  'Surabaya',
  'Semarang'
]

for city in cities[:-1]:
  print(city)
else:
  print(' '.join(cities[-1].upper()))

output:

Jakarta
Surabaya
S E M A R A N G

The idea is we only using for-else loops until n-1 index, then after the for is exhausted, we access directly the last index using [-1].

不乱于心 2024-08-16 22:02:25

您可以在输入数据上使用滑动窗口来查看下一个值,并使用哨兵来检测最后一个值。这适用于任何可迭代对象,因此您无需事先知道长度。成对实现来自 itertools Recipe

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

You can use a sliding window over the input data to get a peek at the next value and use a sentinel to detect the last value. This works on any iterable, so you don't need to know the length beforehand. The pairwise implementation is from itertools recipes.

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
荒岛晴空 2024-08-16 22:02:25

我将提供一种更优雅、更健壮的方式,如下,使用拆包:

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 错误

I will provide with a more elegant and robust way as follows, using 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

Test:

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

The result is:

1 True
2 True
3 False

傲影 2024-08-16 22:02:25

特殊情况:输入末尾有一个已知的“哨兵”值,或者已知值是不同的

在这种情况下,我们可以简单地检查最后一个输入的值。

特殊情况:仅对最后一个元素执行一些额外的操作

Python 的 for 循环不会创建单独的作用域,这是设计的。因此,只需在循环之后编写“额外”代码:

for element in iterable:
    do_normal_thing(element)
do_extra_thing(element)

特殊情况:在每个元素“之间”做一些额外的事情

通常,人们将此问题设想为:在每次迭代中做一些正常的事情,然后做一些额外的事情,除了在最后一次迭代中跳过额外的部分。正如 S.Lott 的回答中所述,这使得问题变得过于复杂。检测 for 循环的第一次迭代很简单 - 例如,通过使用一个简单的标志变量 - 这也可以解决问题:做一些事情在每次迭代中都会有额外的部分,然后是正常的部分,除了在第一次迭代中跳过额外的部分。因此:(

first = True
for element in iterable:
    if first:
        first = False
    else:
        do_extra_thing(element)
    do_normal_thing(element)

当然,有些情况甚至更特殊。例如,如果“做一些正常的事情”意味着“将元素附加到输出字符串”,而“做一些额外的事情”意味着“将分隔符字符串附加到输出” ”,那么我们真正要做的是用分隔符连接元素。)

特殊情况:处理重叠的元素

对有时需要循环输入中重叠的元素对的人会将其设想为:循环除最后一个元素之外的每个元素;对于每个元素,对该元素及其后面的元素执行一些操作。

这实际上是描述问题的一种非常尴尬的方式,因为从元素获取“下一个元素”不起作用。我们需要输入的迭代器或索引值(对于序列)。

这个问题最好被视为一个完全不同的问题,这里详细介绍:如何迭代重叠(当前,下一个)列表中的值对?

一般情况:对最后一个元素做一些特殊的事情

如果问题与上述任何一种特殊情况都不匹配,则有必要采取更严格的方法。以下是一些选项:

通过索引验证最后一个元素

如果输入是一个序列(即,它具有已知的长度),则确定最后一个元素的索引很简单。通过使用enumerate访问索引,那么解决问题就变得很简单了。例如:

for index, element in enumerate(sequence):
    if index == len(sequence) - 1:
        do_something_special(element)
    else:
        do_something_normal(element)

对输入进行切片

同样,如果输入是一个序列,我们可以采用一个 切片,其中包含除最后一个元素之外的所有元素,迭代在此之上,然后处理最后一个元素:

for element in sequence[:-1]:
    do_something_normal(element)
do_something_special(sequence[-1])

这假设至少有一个元素(否则, element[-1] 将引发 IndexError)。这可以通过通常的方式处理(通过提前显式检查,或使用标准异常处理);或者我们可以迭代两个单独的切片以避免错误:

for element in sequence[:-1]:
    do_something_normal(element)
for element in sequence[-1:]:
    do_something_special(element)

这是有效的,因为切片不关心“丢失”元素;如果 sequence 为空,则 sequence[-1:] 将只是一个空列表。

无法对没有确定长度的迭代进行切片,因为只能通过一次查看一个元素来处理它们,并且仅查看一个元素并不能确定它是否是最后一个元素。 itertools.islice 无法解决此问题(因此明确不允许切片的停止点为负值)。

使用lookahead

请参阅Ferdinand Beyer 的回答中的lookahead 迭代器实现;这允许我们编写如下代码:

for element, is_last in lookahead(sequence):
    if is_last:
        do_something_special(element)
    else:
        do_something_normal(element)

这个想法的另一个实现是Ants Aasma的答案。这基本上将问题重新构建为“迭代重叠对”问题,除了添加了一个哨兵值,以便最后一个值输入有一个“对”,然后循环可以简单地检查该哨兵。

Special case: input has a known "sentinel" value at the end, or values are known to be distinct

In this case, we can simply check for the value of the last input.

Special case: do something extra with the last element only

Python's for loop does not create a separate scope, which is by design. Therefore, just write the "extra" code after the loop:

for element in iterable:
    do_normal_thing(element)
do_extra_thing(element)

Special case: do something extra "between" each element

Often, people conceive of this problem as: do something normal and then something extra on each iteration, except skipping the extra part on the last iteration. As described in S.Lott's answer, this makes the problem too complex. It is trivial to detect the first iteration of a for loop - for example, by using a simple flag variable - which also allows for solving the problem: do something extra and then something normal on each iteration, except skipping the extra part on the first iteration. Thus:

first = True
for element in iterable:
    if first:
        first = False
    else:
        do_extra_thing(element)
    do_normal_thing(element)

(Of course, there are cases that are even more special. For example, if "do something normal" means "append the element to an output string", and "do something extra" means "append a separator string to the output", then what we are really doing is joining up the elements with separators in between.)

Special case: handle overlapping pairs of elements

Sometimes people who need to loop over overlapping pairs of elements in the input, will conceive of this as: loop over every element except the last; for each of those elements, do something with that element and the one after it.

This is actually a very awkward way to describe the problem, because getting the "next element" from an element does not work. We need either an iterator over the input, or index values (for a sequence).

This problem is better considered as a completely different problem, which is covered in depth here: How can I iterate over overlapping (current, next) pairs of values from a list?.

General case: do something special with the last element

If the problem doesn't match either of the above special cases, it will be necessary to take a more heavy-duty approach. Here are some options:

Verify the last element by its index

If the input is a sequence (i.e., it has a known length), then it is trivial to determine the index of the last element. By accessing the index using enumerate, then, it becomes straightforward to solve the problem. For example:

for index, element in enumerate(sequence):
    if index == len(sequence) - 1:
        do_something_special(element)
    else:
        do_something_normal(element)

Slice the input

Again if the input is a sequence, we can take a slice that contains every element but the last, iterate over that, and then handle the last element:

for element in sequence[:-1]:
    do_something_normal(element)
do_something_special(sequence[-1])

This assumes that there is at least one element (otherwise, element[-1] will raise an IndexError). This can be handled in the usual ways (by an explicit check ahead of time, or with standard exception handling); or we can iterate over two separate slices to avoid the error:

for element in sequence[:-1]:
    do_something_normal(element)
for element in sequence[-1:]:
    do_something_special(element)

This works because slicing doesn't care about "missing" elements; if sequence is empty, then sequence[-1:] will simply be an empty list.

It's not possible to slice iterables without a definite length, because they can only be processed by looking at the elements one at a time, and simply looking at an element doesn't determine whether it's the last one. itertools.islice cannot fix this (and therefore explicitly disallows negative values for the stop point of the slice).

Use lookahead

See the lookahead iterator implementation in Ferdinand Beyer's answer; this allows us to write code like:

for element, is_last in lookahead(sequence):
    if is_last:
        do_something_special(element)
    else:
        do_something_normal(element)

Another implementation of this idea is Ants Aasma's answer. This basically reframes the problem as a "iterate over overlapping pairs" problem, except that a sentinel value is added so that the last value in the input has a "pair", and the loop can then simply check for that sentinel.

鱼窥荷 2024-08-16 22:02:25

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

(另请参阅这个问题:does-the- last-element-in-a-loop-deserve-a-separate-treatment

编辑:由于问题更多是关于“之间”,所以第一个元素是特殊的其中之一是它没有前任元素,或者 last 元素是特殊的,因为它没有后继元素。

Is there no possibility to iterate over all-but the last element, and treat the last one outside of the loop? After all, a loop is created to do something similar to all elements you loop over; if one element needs something special, it shouldn't be in the loop.

(see also this question: does-the-last-element-in-a-loop-deserve-a-separate-treatment)

EDIT: since the question is more about the "in between", either the first element is the special one in that it has no predecessor, or the last element is special in that it has no successor.

差↓一点笑了 2024-08-16 22:02:25

使用切片和 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 可以检测相同的元素并为它们重用相同的对象。例如,对于相同值的字符串和公共整数。

Use slicing and is to check for the last element:

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>

Caveat emptor: This only works if all elements in the list are actually different (have different locations in memory). Under the hood, Python may detect equal elements and reuse the same objects for them. For instance, for strings of the same value and common integers.

请别遗忘我 2024-08-16 22:02:25

对我来说,处理列表末尾的特殊情况的最简单和Python式的方法是:

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

当然,这也可以用于以特殊方式处理第一个元素。

For me the most simple and pythonic way to handle a special case at the end of a list is:

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

Of course this can also be used to treat the first element in a special way .

梦在夏天 2024-08-16 22:02:25

除了向上计数之外,您还可以向下计数:

  nrToProcess = len(list)
  for s in list:
    s.doStuff()
    nrToProcess -= 1
    if nrToProcess==0:  # this is the last one
      s.doSpecialStuff()

Instead of counting up, you can also count down:

  nrToProcess = len(list)
  for s in list:
    s.doStuff()
    nrToProcess -= 1
    if nrToProcess==0:  # this is the last one
      s.doSpecialStuff()
绅士风度i 2024-08-16 22:02:25

我喜欢 @ethan-t 的方法,但从我的角度来看,while True 是危险的。

data_list = [1, 2, 3, 2, 1]  # sample data
L = list(data_list)  # destroy L instead of data_list
while L:
    e = L.pop(0)
    if L:
        print(f'process element {e}')
    else:
        print(f'process last element {e}')
del L

这里,data_list 使得最后一个元素的值等于列表的第一个元素。 L 可以与 data_list 交换,但在这种情况下,循环后结果为空。如果您在处理之前检查列表不为空或者不需要检查(哎呀!),也可以使用 while True

data_list = [1, 2, 3, 2, 1]
if data_list:
    while True:
        e = data_list.pop(0)
        if data_list:
            print(f'process element {e}')
        else:
            print(f'process last element {e}')
            break
else:
    print('list is empty')

好的部分是它很快。坏处 - 它是可破坏的(data_list 变为空)。

最直观的解决方案:

data_list = [1, 2, 3, 2, 1]  # sample data
for i, e in enumerate(data_list):
    if i != len(data_list) - 1:
        print(f'process element {e}')
    else:
        print(f'process last element {e}')

哦,是的,你已经提出了!

I like the approach of @ethan-t, but while True is dangerous from my point of view.

data_list = [1, 2, 3, 2, 1]  # sample data
L = list(data_list)  # destroy L instead of data_list
while L:
    e = L.pop(0)
    if L:
        print(f'process element {e}')
    else:
        print(f'process last element {e}')
del L

Here, data_list is so that last element is equal by value to the first one of the list. L can be exchanged with data_list but in this case it results empty after the loop. while True is also possible to use if you check that list is not empty before the processing or the check is not needed (ouch!).

data_list = [1, 2, 3, 2, 1]
if data_list:
    while True:
        e = data_list.pop(0)
        if data_list:
            print(f'process element {e}')
        else:
            print(f'process last element {e}')
            break
else:
    print('list is empty')

The good part is that it is fast. The bad - it is destructible (data_list becomes empty).

Most intuitive solution:

data_list = [1, 2, 3, 2, 1]  # sample data
for i, e in enumerate(data_list):
    if i != len(data_list) - 1:
        print(f'process element {e}')
    else:
        print(f'process last element {e}')

Oh yes, you have already proposed it!

已下线请稍等 2024-08-16 22:02:25

如果您正在循环 List
使用 enumerate 函数是最好的尝试之一。

for index, element in enumerate(ListObj):
    # print(index, ListObj[index], len(ListObj) )

    if (index != len(ListObj)-1 ):
        # Do things to the element which is not the last one
    else:
        # Do things to the element which is the last one

If you are looping the List,
Using enumerate function is one of the best try.

for index, element in enumerate(ListObj):
    # print(index, ListObj[index], len(ListObj) )

    if (index != len(ListObj)-1 ):
        # Do things to the element which is not the last one
    else:
        # Do things to the element which is the last one
埖埖迣鎅 2024-08-16 22:02:25

你的方法没有任何问题,除非你有 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

输出:

1
2
3
3

如果感觉就像我会那样做,对我来说似乎很简单。

There is nothing wrong with your way, unless you will have 100 000 loops and wants save 100 000 "if" statements. In that case, you can go that way :

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

Outputs :

1
2
3
3

But really, in your case I feel like it's overkill.

In any case, you will probably be luckier with slicing :

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

#outputs
1
2
last : 3

or just :

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

#outputs
1
2
3
last : 3

Eventually, a KISS way to do you stuff, and that would work with any iterable, including the ones without __len__ :

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

Ouputs:

1
2
3
3

If feel like I would do it that way, seems simple to me.

薆情海 2024-08-16 22:02:25

如果您正在浏览该列表,对我来说这也有效:

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

if you are going through the list, for me this worked too:

for j in range(0, len(Array)):
    if len(Array) - j > 1:
        notLast()
泪眸﹌ 2024-08-16 22:02:25

谷歌让我想到了这个老问题,我想我可以为这个问题添加不同的方法。

这里的大多数答案都会按照要求处理 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() 更优雅。

Google brought me to this old question and I think I could add a different approach to this problem.

Most of the answers here would deal with a proper treatment of a for loop control as it was asked, but if the data_list is destructible, I would suggest that you pop the items from the list until you end up with an empty 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()

you could even use while len(element_list) if you don't need to do anything with the last element. I find this solution more elegant then dealing with next().

看轻我的陪伴 2024-08-16 22:02:25

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

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

Delay the special handling of the last item until after the loop.

>>> for i in (1, 2, 3):
...     pass
...
>>> i
3
风轻花落早 2024-08-16 22:02:25

如果您乐于破坏该列表,那么可以选择以下内容。
我们将反转列表以加快从 O(n^2) 到 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)


这适用于空列表和非唯一项目列表。
由于列表通常是暂时的,因此这种方法效果很好......但代价是破坏列表。

If you are happy to be destructive with the list, then there's the following.
We are going to reverse the list in order to speed up the process from O(n^2) to O(n), because pop(0) moves the list each iteration - cf. Nicholas Pipitone's comment below

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)


This works well with empty lists, and lists of non-unique items.
Since it's often the case that lists are transitory, this works pretty well ... at the cost of destructing the list.

记忆里有你的影子 2024-08-16 22:02:25

我刚刚遇到这个问题,我的通用解决方案使用迭代器:

from typing import TypeVar, Iterable
E = TypeVar('E')

def metait(i: Iterable[E]) -> Iterable[tuple[E, bool, bool]]:

    first = True
    previous = None
    for elem in i:
        if previous:
            yield previous, first, False
            first = False
        previous = elem

    if previous:
        yield previous, first, True

您将收到一个元组,其中包含第一个和最后一个项目的原始元素和标志。它可以与每个迭代一起使用:

d = {'a': (1,2,3), 'b': (4,5,6), 'c': (7,8,9)}

for (k,v), is_first, is_last in metait(d.items()):
    print(f'{k}: {v}  {is_first} {is_last}')

这将为您提供:

a: (1, 2, 3)  True False
b: (4, 5, 6)  False False
c: (7, 8, 9)  False True

I just came across this question and my generic solution uses an Iterator:

from typing import TypeVar, Iterable
E = TypeVar('E')

def metait(i: Iterable[E]) -> Iterable[tuple[E, bool, bool]]:

    first = True
    previous = None
    for elem in i:
        if previous:
            yield previous, first, False
            first = False
        previous = elem

    if previous:
        yield previous, first, True

you will receive a tuple with the original elements and flags for the first and last item. It can be used with every iterable:

d = {'a': (1,2,3), 'b': (4,5,6), 'c': (7,8,9)}

for (k,v), is_first, is_last in metait(d.items()):
    print(f'{k}: {v}  {is_first} {is_last}')

This will give you:

a: (1, 2, 3)  True False
b: (4, 5, 6)  False False
c: (7, 8, 9)  False True
只为守护你 2024-08-16 22:02:25

假设输入作为迭代器,以下是使用 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
>>>

Assuming input as an iterator, here's a way using tee and izip from itertools:

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.

Demo:

>>> 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
>>>
绿光 2024-08-16 22:02:25

我想到的最简单的解决方案是:

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

因此,我们总是通过延迟处理一次迭代来向前看一项。为了在第一次迭代期间跳过某些操作,我只需捕获错误即可。

当然,您需要考虑一下,以便在您需要时引发 NameError

还要保留 `construct

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

所以我预计开销是无法选择的。

The most simple solution coming to my mind is:

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

So we always look ahead one item by delaying the the processing one iteration. To skip doing something during the first iteration I simply catch the error.

Of course you need to think a bit, in order for the NameError to be raised when you want it.

Also keep the `counstruct

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

This relies that the name new wasn't previously defined. If you are paranoid you can ensure that new doesn't exist using:

try:
    del new
except NameError:
    pass

Alternatively you can of course also use an if statement (if notfirst: print(new) else: notfirst = True). But as far as I know the overhead is bigger.


Using `timeit` yields:

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

so I expect the overhead to be unelectable.

转身以后 2024-08-16 22:02:25

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

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

这样您只需评估一次列表的长度。此页面上的许多解决方案似乎都假设长度无法提前获得,但这不是您问题的一部分。如果有长度,请使用它。

Count the items once and keep up with the number of items remaining:

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

This way you only evaluate the length of the list once. Many of the solutions on this page seem to assume the length is unavailable in advance, but that is not part of your question. If you have the length, use it.

羅雙樹 2024-08-16 22:02:25

可以有多种方式。切片将是最快的。添加一个使用 .index() 方法的函数:

>>> l1 = [1,5,2,3,5,1,7,43]                                                 
>>> [i for i in l1 if l1.index(i)+1==len(l1)]                               
[43]

There can be multiple ways. slicing will be fastest. Adding one more which uses .index() method:

>>> l1 = [1,5,2,3,5,1,7,43]                                                 
>>> [i for i in l1 if l1.index(i)+1==len(l1)]                               
[43]
趁微风不噪 2024-08-16 22:02:25

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

但如果经常需要这种模式,只需将逻辑放入
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

So, this is definitely not the "shorter" version - and one might digress if "shortest" and "Pythonic" are actually compatible.

But if one needs this pattern often, just put the logic in to a
10-liner generator - and get any meta-data related to an element's
position directly on the for call. Another advantage here is that it will
work wit an arbitrary iterable, not only Sequences.

_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
呆橘 2024-08-16 22:02:25

这是一个老问题,已经有很多很好的回应,但我觉得这很Pythonic:

def rev_enumerate(lst):
    """
    Similar to enumerate(), but counts DOWN to the last element being the
    zeroth, rather than counting UP from the first element being the zeroth.

    Since the length has to be determined up-front, this is not suitable for
    open-ended iterators.

    Parameters
    ----------
    lst : Iterable
        An iterable with a length (list, tuple, dict, set).

    Yields
    ------
    tuple
        A tuple with the reverse cardinal number of the element, followed by
        the element of the iterable.
    """
    length = len(lst) - 1
    for i, element in enumerate(lst):
        yield length - i, element

像这样使用:

for num_remaining, item in rev_enumerate(['a', 'b', 'c']):
    if not num_remaining:
        print(f'This is the last item in the list: {item}')

或者也许你想做相反的事情:

for num_remaining, item in rev_enumerate(['a', 'b', 'c']):
    if num_remaining:
        print(f'This is NOT the last item in the list: {item}')

或者,只是想知道你走的时候还剩下多少。 ..

for num_remaining, item in rev_enumerate(['a', 'b', 'c']):
    print(f'After {item}, there are {num_remaining} items.')

我认为现有 enumerate 的多功能性和熟悉性使其最具 Pythonic 风格。

需要注意的是,与 enumerate() 不同,rev_enumerate() 要求输入实现 __len__,但这包括列表、元组、字典和集合就可以了。

This is an old question, and there's already lots of great responses, but I felt like this was pretty Pythonic:

def rev_enumerate(lst):
    """
    Similar to enumerate(), but counts DOWN to the last element being the
    zeroth, rather than counting UP from the first element being the zeroth.

    Since the length has to be determined up-front, this is not suitable for
    open-ended iterators.

    Parameters
    ----------
    lst : Iterable
        An iterable with a length (list, tuple, dict, set).

    Yields
    ------
    tuple
        A tuple with the reverse cardinal number of the element, followed by
        the element of the iterable.
    """
    length = len(lst) - 1
    for i, element in enumerate(lst):
        yield length - i, element

Used like this:

for num_remaining, item in rev_enumerate(['a', 'b', 'c']):
    if not num_remaining:
        print(f'This is the last item in the list: {item}')

Or perhaps you'd like to do the opposite:

for num_remaining, item in rev_enumerate(['a', 'b', 'c']):
    if num_remaining:
        print(f'This is NOT the last item in the list: {item}')

Or, just to know how many remain as you go...

for num_remaining, item in rev_enumerate(['a', 'b', 'c']):
    print(f'After {item}, there are {num_remaining} items.')

I think the versatility and familiarity with the existing enumerate makes it most Pythonic.

Caveat, unlike enumerate(), rev_enumerate() requires that the input implement __len__, but this includes lists, tuples, dicts and sets just fine.

梦屿孤独相伴 2024-08-16 22:02:25

最简单的解决方案可能是:

from more_itertools import mark_ends

for _, is_last, data in mark_ends(data_list):
    code_that_is_done_for_every_element
    if not is_last:
        code_that_is_done_between_elements

The simplest solution is probably:

from more_itertools import mark_ends

for _, is_last, data in mark_ends(data_list):
    code_that_is_done_for_every_element
    if not is_last:
        code_that_is_done_between_elements
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文