是否有一些内置标记序列的最后一个?

Is there some built-in which marks the last of a sequence?

我需要这样的东西:

>>> for i in mark_last([1, 2, 3]):
...  print(i)
... 
(1, False)
(2, False)
(3, True)

我是这样实现的,但是...

def mark_last(li):
    items = iter(items)
    try:
        prev = next(items)
    except StopIteration:
        return
    for item in items:
        yield prev, False
        prev = item
    yield prev, True

有内置的吗?或者更短的方法来做到这一点?也许与 itertools.groupby() 相结合? – 不接受带有 len() 的技巧,因为它们不适用于生成器。

您可以根据 iwindow 定义 mark_last,其中 returns 滑动 window 在可迭代对象上。

import itertools as IT

def iwindow(iterable, n=2):
    """
    Returns a sliding window (of width n) over data from the sequence.
    s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ...
    """
    iterables = IT.tee(iterable, n)
    iterables = (IT.islice(it, pos, None) for pos, it in enumerate(iterables))
    for result in IT.izip(*iterables):
        yield result

def mark_last(iterable):
    for i, j in iwindow(iterable):
        yield i, False
    yield j, True

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

产量

(1, False)
(2, False)
(3, True)

请注意,您可以直接使用 iwindow 解决您的问题,而无需 mark_last

在Python3+

这对于大型列表的性能可能有点困难...

>>> def mark_last(iterable):
...     *others, last = iterable
...     for element in others:
...             yield (element, False)
...     yield (last, True)
...
>>> for i in mark_last([1, 2, 3]):
...     print(i)
...
(1, False)
(2, False)
(3, True)

From the docs:

If the target list contains one target prefixed with an asterisk, called a “starred” target: The object must be a sequence with at least as many items as there are targets in the target list, minus one. The first items of the sequence are assigned, from left to right, to the targets before the starred target. The final items of the sequence are assigned to the targets after the starred target. A list of the remaining items in the sequence is then assigned to the starred target (the list can be empty).

这就是 *others, last = iterable 行正在做的事情,但反过来。

Is there some built-in which marks the last of a sequence?

没有,没有。

除了两点,你的功能还不错:

  1. 它如何处理空序列?
  2. 你应该 break 而不是 raise StopIteration;最终 raise StopIteration 将导致 RunTimeError (PEP 479).

你给出的更简洁的版本是

def mark_last(items):
    items = iter(items)

    prev = next(items)
    for item in items:
        yield prev, False
        prev = item

    yield item, True

请注意,不推荐使用裸 next 来引发 StopIteration,因此您可能希望使用显式 try...except.