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

以下是我目前的工作方式:

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!

除此之外,我认为没有一个普遍优越的解决方案,因为它取决于你想要做什么。例如,如果要从列表构建字符串,那么使用 str.join()比使用 for 循环“自然更好用特殊情况&# 8221;


使用相同的原则,但更紧凑:

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

其他提示

'code between'是 Head-Tail 模式的一个例子。

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

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

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 除了一次以外总是假的条件。

如果您只是想修改 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 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

是否有可能迭代所有 - 但最后一个元素,并将最后一个元素视为循环外部?毕竟,创建一个循环来执行类似于循环的所有元素的操作;如果一个元素需要特殊的东西,它就不应该在循环中。

(另请参阅此问题: -the-最后元件-IN-A - 环 - 值得-A-单独处理

编辑:由于问题更多的是关于“介于两者之间”,第一个元素是特殊的,因为它没有前任,或最后元素的特殊之处在于它没有继承者。

你的方式没有任何问题,除非你有10万个循环,并希望节省100 000“如果”声明。在这种情况下,你可以这样:

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

OUPUTS:

<*>

如果我觉得我会这样做,对我来说似乎很简单。

使用切片和 来检查最后一个元素:

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循环控件的正确处理,但是如果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()。

我喜欢@ ethan-t的方法,但而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))

因此,我们总是通过延迟处理一次迭代来向前看一个项目。为了在第一次迭代中跳过某些事情,我只是抓住错误。

当然,您需要稍微考虑一下,以便在需要时引发 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 iffirst: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

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

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

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]
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top