如何在for循环中访问前/后一个元素?

51

在使用 for 循环遍历一个 list(或者 tuple 或其他可迭代对象)的时候,有没有一种方法可以访问它的下一个或上一个元素?

l = [1, 2, 3]
for item in l:
    if item == 2:
        get_previous(l, item)
15个回答

78

作为生成器函数表达:

def neighborhood(iterable):
    iterator = iter(iterable)
    prev_item = None
    current_item = next(iterator)  # throws StopIteration if empty.
    for next_item in iterator:
        yield (prev_item, current_item, next_item)
        prev_item = current_item
        current_item = next_item
    yield (prev_item, current_item, None)

用法:

for prev,item,next in neighborhood(l):
    print prev, item, next

1
在这种情况下,我可能会执行“prev,item = item,next”。 - Paul Fisher
1
要使此循环无限(没有StopIteration),请执行from itertools import cycle并更改第二行为:iterator = cycle(iterable) - Dennis Williamson
在这种情况下,使用enumerate是否不太符合Python的风格? - batbrat
你要找的答案是Vicky Liau的,就在这个下面。或者,如果你想让它适用于任何可迭代对象而不仅仅是列表/元组/字符串等,可以使用使用itertools的那个 - user3064538
1
对于 Python 3.10 用户,现有一个可用的函数 itertools.pairwise,请参见 https://dev59.com/2XRC5IYBdhLWcg3wW_pk#69584524。 - kosciej16
显示剩余6条评论

35
l = [1, 2, 3]

for i, j in zip(l, l[1:]):
    print(i, j)

14
我曾使用这个代码,但进行了扩展以避免丢失开头和结尾的元素: for prev,cur,next in zip([None]+l[:-1], l, l[1:]+[None]): - Maximus
你可以通过这种方式避免删除起始/结束项:l = [None, *l, None],然后使用 for prev, cur, nxt in zip(l, l[1:], l[2:]): - user3064538

12
l = [1, 2, 3]
for i, item in enumerate(l):
    if item == 2:
        previous = l[i - 1]
        print(previous)

输出:

1

如果你要查找的项是列表中的第一项,那么它将环绕并返回列表中的最后一项。换句话说,将上面的代码中的第三行更改为if item == 1:将导致它打印3


如果你的列表只有一个元素 l = [2],那么这将返回与前一个元素相同的元素。 - user3064538

10

在处理需要上下文的生成器时,我经常使用以下实用函数来在迭代器上提供一个滑动窗口视图:

import collections, itertools

def window(it, winsize, step=1):
    """Sliding window iterator."""
    it=iter(it)  # Ensure we have an iterator
    l=collections.deque(itertools.islice(it, winsize))
    while 1:  # Continue till StopIteration gets raised.
        yield tuple(l)
        for i in range(step):
            l.append(it.next())
            l.popleft()

它将会生成一个每次移动step个位置,每次显示N个项目的序列视图。例如:

>>> list(window([1,2,3,4,5],3))
[(1, 2, 3), (2, 3, 4), (3, 4, 5)]

应用于前瞻/后顾情形,同时需要处理没有下一个或上一个值的数字时,您可能希望使用适当的值(例如None)填充序列。

l= range(10)
# Print adjacent numbers
for cur, next in window(l + [None] ,2):
    if next is None: print "%d is the last number." % cur
    else: print "%d is followed by %d" % (cur,next)

如果您正在使用 itertools,请改用Francisco Couzo的答案,并使用 itertools.tee:https://dev59.com/2XRC5IYBdhLWcg3wW_pk#41047005 - user3064538

8

我知道这个很老了,但为什么不直接使用 enumerate 呢?

l = ['adam', 'rick', 'morty', 'adam', 'billy', 'bob', 'wally', 'bob', 'jerry']

for i, item in enumerate(l):
    if i == 0:
        previous_item = None
    else:
        previous_item = l[i - 1]

    if i == len(l) - 1:
        next_item = None
    else:
        next_item = l[i + 1]

    print('Previous Item:', previous_item)
    print('Item:', item)
    print('Next Item:', next_item)
    print('')

    pass

如果您运行此代码,您将看到它会获取前一个和后一个项目,并不关心列表中重复的项目。

1
为什么会被踩?这是一段很好的代码,并且不涉及外部库或特殊函数。 - ronan_mac
问题还要求返回前一个项目。如果一个项目重复了,这个方法不会正确工作,对吗? [1,2,1,3] - Teepeemm
@Teepeemm,好的,我会更新一个符合要求的版本。 - RattleyCooper

5

请查看Tempita项目的looper实用工具。它为您提供了一个包装器对象,围绕循环项提供诸如previous、next、first、last等属性。

看一下looper类的源代码,它非常简单。还有其他类似的循环辅助工具,但我现在记不起来其他的了。

示例:

> easy_install Tempita
> python
>>> from tempita import looper
>>> for loop, i in looper([1, 2, 3]):
...     print loop.previous, loop.item, loop.index, loop.next, loop.first, loop.last, loop.length, loop.odd, loop.even
... 
None 1 0 2 True False 3 True 0
1 2 1 3 False False 3 False 1
2 3 2 None False True 3 True 0

1
所有链接都已损坏。 - Jean-Francois T.

3
如果您希望解决方案适用于可迭代对象,itertools文档提供了一个使用itertools.tee()的示例,可以完全满足您的需求。
import itertools

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = itertools.tee(iterable)
    next(b, None)
    return zip(a, b)

2

如果您不想导入任何东西,这里是使用for循环访问生成器的上一个项目的示例。它使用类变量在以下next调用之前存储每个下一个结果。如果您想要比即时前一个项目更多的项目,则此变量可以是一个小列表。在类内部是一个方法生成器,它有效地扩展了next()内置函数以包括前一个项目分配。

代码(Python 3.10):

def previous():
    class Plusprev():
        def __init__(pp, gen=None):
            pp.g = gen
            pp.nxt = ''
            pp.prev = 'start'

        def ppnext(pp):
            while pp.nxt != 'done':
                pp.nxt = next(pp.g,'done')
                yield pp.nxt
                pp.prev = pp.nxt

    sqgen = (n*n for n in range(13))
    ppcl = Plusprev(sqgen)
    nxtg = ppcl.ppnext()
    nxt = next(nxtg,'done')
    while nxt != 'done':
        print('\nprevious ',ppcl.prev)
        print('current ',nxt)
        nxt = next(nxtg,'done')

previous()

这里使用了内置函数next()和默认参数。


1

对于升级到Python 3.10的任何人,这样的函数已直接添加到itertools中。

import itertools

l = [1,2,3]
for x, y in itertools.pairwise(l):
    print(x, y)
# 1 2
# 2 3

1
我知道这是一个老问题,但我认为展示一个简单的解决方案很重要,它可以与生成器和其他类型的可迭代对象一起使用,而不像大多数答案只能用于类似列表的对象。它有点类似于Brian的答案和这里的解决方案:https://www.programcreek.com/python/example/1754/itertools.tee
import itertools

iter0, iter1 = itertools.tee(iterable)

for item, next_item in itertools.zip_longest(
    iter0,
    itertools.islice(iter1, 1, None)
):

    do_something(item, next_item)

或者,如果您确定第二个可迭代对象至少有一个元素,可以调用next

import itertools

iter0, iter1 = itertools.tee(iterable)
_ = next(iter1)

for item, next_item in itertools.zip_longest(iter0, iter1):

    do_something(item, next_item)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接