什么是pythonic方法来检测python'for'循环中的最后一个元素?

e.t*_*deu 166 python for-loop idioms fencepost

我想知道对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
Run Code Online (Sandbox Code Playgroud)

有没有更好的方法?

注意:我不想使用hacks,例如使用reduce;)

Fer*_*yer 133

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

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

    item()
Run Code Online (Sandbox Code Playgroud)

这适用于任何迭代,即使对于那些没有len():

file = open('/path/to/file')
for line in file:
    process_line(line)

    # No way of telling if this is the last line!
Run Code Online (Sandbox Code Playgroud)

除此之外,我认为没有一个普遍优越的解决方案,因为它取决于你想要做什么.例如,如果您从列表中建立一个字符串,它是自然最好使用str.join()比使用for循环"特殊情况".


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

for i, line in enumerate(data_list):
    if i > 0:
        between_items()
    item()
Run Code Online (Sandbox Code Playgroud)

看起来很熟悉,不是吗?:)


对于@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
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样使用它:

>>> for i, has_more in lookahead(range(3)):
...     print(i, has_more)
0 True
1 True
2 False
Run Code Online (Sandbox Code Playgroud)

  • 等一下,这究竟是如何回答这个问题的? (42认同)
  • @ofko不用担心.如果你*真的*需要知道没有`len()`的*iterable*的最后一个元素,你需要向前看 - 看一下我的更新答案的例子. (6认同)
  • 这并不能回答问题 (4认同)

S.L*_*ott 18

'代码之间'是头尾模式的一个例子.

你有一个项目,后面是一系列(之间,项目)对.您还可以将其视为一系列(项目,之间)对,后跟项目.将第一个元素作为特殊元素并将所有其他元素作为"标准"案例通常更简单.

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

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

  • 函数调用比`if`语句慢,所以"浪费执行"参数不成立. (4认同)
  • 我将你的陈述“...并且不需要大量浪费地执行 if 条件,除了一次之外,该条件总是为假””作为“...并且速度更快,因为它节省了几个“if””。显然你只是指“代码清洁度”? (2认同)

Bar*_*tek 15

如果您只是想修改最后一个元素,data_list那么您只需使用符号:

L[-1]
Run Code Online (Sandbox Code Playgroud)

但是,看起来你做的不止于此.你的方式没有什么不妥.我甚至快速浏览了一些Django代码来获取模板标签,它们基本上就是你正在做的事情.

  • @spacetyper当最后一个值非唯一时,它会中断. (5认同)
  • @ e.tadeu如果你要修改它甚至不重要.将if语句更改为:`if data!= datalist [-1]:`并保持其他所有内容相同,这是我认为编写代码的最佳方法. (4认同)

Nur*_*uda 14

我们可以使用for-else

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

for city in cities[:-1]:
  print(city)
else:
  print(' '.join(cities[-1].upper()))
Run Code Online (Sandbox Code Playgroud)

输出:

Jakarta
Surabaya
S E M A R A N G
Run Code Online (Sandbox Code Playgroud)

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

  • `else:` 语句在这里没有用。您可以将 `print(' '.join(cities[-1].upper()))` 作为第三行(不缩进)并删除 `else:` 行。### 仅当“for”循环内有“break”时,“for-else”循环才有用。那么只有在“break”没有被触发的情况下,“else”才会执行。/sf/ask/698597931/ (4认同)

小智 13

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

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

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

  • 你刚刚重新实现了连接函数:`"&".join([str(x)for x in l]) (5认同)
  • 切片并不是“for”可以迭代的所有内容的选项。 (3认同)

And*_*lke 10

这类似于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
Run Code Online (Sandbox Code Playgroud)


Pal*_*lza 9

如果项目是唯一的:

for x in list:
    #code
    if x == list[-1]:
        #code
Run Code Online (Sandbox Code Playgroud)

其他选择:

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
Run Code Online (Sandbox Code Playgroud)


has*_*.sd 8

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

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

  • 我们应该计算循环之前的长度。这样每次循环都会计算它。 (9认同)

Ant*_*sma 5

您可以在输入数据上使用滑动窗口来查看下一个值,并使用标记来检测最后一个值。这适用于任何可迭代对象,因此您无需事先知道长度。成对实现来自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
Run Code Online (Sandbox Code Playgroud)


man*_*suo 5

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

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
Run Code Online (Sandbox Code Playgroud)

测试:

for a, b in mark_last([1, 2, 3]):
    print(a, b)
Run Code Online (Sandbox Code Playgroud)

结果是:

1 对
2 对
3 错


归档时间:

查看次数:

148291 次

最近记录:

5 年,11 月 前