python用什么方法来检测'for'循环?

我想知道对for循环中的最后一个元素进行特殊处理的最佳方式(更紧凑和“python”的方式)。有一段代码应该只被称为之间的元素,在最后一个中被抑制。

以下是我目前的做法:

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之类的hack来创建它。;)

317799 次浏览

大多数情况下,使第一个迭代成为特殊情况而不是最后一次迭代更容易(也更便宜):

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

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

L[-1]

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

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

(另见此问题:does-the-last-element-in-a-loop-deserve-a-separate-treatment)

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

你的方法没有错,除非你有10万个循环,想要保存10万个“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

如果我这样做的话,对我来说很简单。

您可以在输入数据上使用滑动窗口来查看下一个值,并使用哨兵来检测最后一个值。这适用于任何可迭代对象,所以你不需要事先知道它的长度。成对实现来自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

“code between”是首尾相接模式的一个例子。

你有一个项目,后面是一系列(项目之间)对。您还可以将其视为(item, between)对的序列,后跟一个item。通常更简单的做法是把第一个元素视为特殊元素,而把所有其他元素视为“标准”。的情况。

此外,为了避免重复代码,必须提供一个函数或其他对象来包含不想重复的代码。在循环中嵌入如果语句,除非有一次总是假的,这有点傻。

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 )

这更可靠,因为它更容易证明,它不会创建额外的数据结构(即列表的副本),并且不需要大量浪费如果条件的执行,该条件总是假的,只有一次例外。

假设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
>>>

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

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

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

返回'1 &2,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>

购者自慎:只有当列表中的所有元素实际上是不同的(在内存中有不同的位置)时才有效。实际上,Python可以检测相同的元素并为它们重用相同的对象。例如,对于具有相同值和普通整数的字符串。

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

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

谷歌让我想到了这个老问题,我想我可以用不同的方法来解决这个问题。

这里的大多数答案将处理一个正确的处理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()

如果你不需要对最后一个元素做任何事情,你甚至可以使用而len (element_list)。我发现这个解决方案比处理next()更优雅。

如果项目是唯一的:

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

我想到的最简单的解决办法是:

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

所以我们总是通过延迟处理一个迭代来提前一个项目。为了在第一次迭代中跳过某些操作,我只需捕获错误。

当然你需要思考一下,以便在你想要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

所以我认为开销是不可能当选的。

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

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

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

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

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

我喜欢@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}')

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

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

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

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

有多种方法。切片是最快的。再添加一个使用.index()方法的:

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

除了向上数,你也可以向下数:

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

我想到的一个简单的解决办法是:

for i in MyList:
# Check if 'i' is the last element in the list
if i == MyList[-1]:
# Do something different for the last
else:
# Do something for all other elements

第二个同样简单的解决方法是使用计数器:

# Count the no. of elements in the list
ListLength = len(MyList)
# Initialize a counter
count = 0


for i in MyList:
# increment counter
count += 1
# Check if 'i' is the last element in the list
# by using the counter
if count == ListLength:
# Do something different for the last
else:
# Do something for all other elements

迟到总比不到好。你的原始代码使用了enumerate(),但是你只使用了i索引来检查它是否是列表中的最后一项。下面是一个使用负索引的更简单的替代方法(如果你不需要enumerate()):

for data in data_list:
code_that_is_done_for_every_element
if data != data_list[-1]:
code_that_is_done_between_elements

if data != data_list[-1]检查迭代中的当前项是否不是列表中的最后一项。

希望这能有所帮助,即使是在近11年后。

你可以用下面的代码确定最后一个元素:

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

检查data是否与data_list (data_list[-1])中的最后一个数据不相同。

for data in data_list:
code_that_is_done_for_every_element
if data != data_list[- 1]:
code_that_is_done_between_elements

所以,这绝对不是“更短”的。版本-如果"shortest"和“;Pythonic"实际上是兼容的。

但如果一个人经常需要这种模式,就把逻辑放在a中 10行生成器-并获得与元素相关的任何元数据 位置直接在for调用。这里的另一个优势是它会

.使用任意可迭代对象,而不仅仅是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

我将提供一种更优雅和健壮的方式,如下所示,使用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 True
2 True
3假< / p >

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

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的多功能性和熟悉性使其最python化。

注意,与enumerate()不同,rev_enumerate()要求输入实现__len__,但这包括列表、元组、字典和集。

我们可以使用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]直接访问最后一个索引。

如果你正在循环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

如果你很乐意对这个列表进行破坏,那么还有下面的事情。 为了加快从O(n²)到O(n)的过程,我们将反转列表,因为pop(0)每次迭代都会移动列表- cf. 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)




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

我发现在循环表达式之前定义循环值很方便。对于这个方框示例,匹配循环中的值,或者其他可能需要它的地方。

numberofboxes = 1411


for j in range(1,numberofboxes):
if j != numberofboxes - 1:
print ("},")
else:
print("}")

我在下面分享了两个简单的方法来查找循环的结束。

方法1:

num_list = [1, 2, 3, 4]


for n in num_list:
if num_list[-1] == n:
print('this is the last iteration of the loop')

方法2:

num_list = [1, 2, 3, 4]


loop_count = len(num_list) - 1  # 3
for index, num in enumerate(num_list):
if index == loop_count:
print('this is the last iteration of the loop')

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

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