50

Is there a way to access a list's (or tuple's, or other iterable's) next or previous element while looping through it with a for loop?

l = [1, 2, 3]
for item in l:
    if item == 2:
        get_previous(l, item)
4

15 回答 15

77

表示为生成器函数:

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
于 2008-11-27T14:28:23.527 回答
34
l = [1, 2, 3]

for i, j in zip(l, l[1:]):
    print(i, j)
于 2014-05-08T01:10:28.057 回答
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

于 2008-11-27T17:15:38.353 回答
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()

它将一次生成一个序列 N 项的视图,并移动步骤。例如。

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

当在前瞻/后向情况下使用时,您还需要处理没有下一个或前一个值的数字,您可能希望用适当的值填充序列,例如无。

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)
于 2008-11-27T14:29:16.303 回答
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

如果你运行它,你会看到它抓取了上一个和下一个项目,而不关心列表中的重复项目。

于 2014-02-26T01:40:44.207 回答
5

查看Tempita 项目中的 looper 实用程序。它为您提供了一个围绕循环项目的包装对象,该对象提供诸如上一个、下一个、第一个、最后一个等属性。

看一下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
于 2008-11-28T14:21:20.083 回答
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)
于 2016-12-08T19:07:10.310 回答
1

我不认为有一个简单的方法,特别是因为一个可迭代的可以是一个生成器(没有回头路)。您可以通过将元素的索引传递到循环体中来使用序列:

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

enumerate()函数是内置函数。

于 2008-11-27T13:55:31.177 回答
1

直接上一个?

你的意思是下面的,对吧?

previous = None
for item in someList:
    if item == target: break
    previous = item
# previous is the item before the target

如果你想要n 个先前的项目,你可以使用一种大小为n的循环队列来做到这一点。

queue = []
for item in someList:
    if item == target: break
    queue .append( item )
    if len(queue ) > n: queue .pop(0)
if len(queue ) < n: previous = None
previous = previous[0]
# previous is *n* before the target
于 2008-11-27T15:46:17.753 回答
1

如果您不想导入任何内容,这里是使用 for 循环访问生成器的前一项的示例。它使用类变量在下一次调用之前存储每个下一个结果。如果您想要的不仅仅是前一个项目,这个变量可能是一个小列表。类内部是一个方法生成器,它有效地扩展了 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(),默认参数。

于 2021-10-26T20:36:45.500 回答
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
于 2021-10-15T12:12:36.207 回答
1

我知道这是一个老问题,但我发现展示一个简单的解决方案很重要,它也适用于生成器和其他类型的可迭代对象,而不像大多数只适用于列表类对象的答案。这有点类似于布赖恩的回答和这里的解决方案: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)
于 2020-04-01T05:34:11.057 回答
0

迭代器只有 next() 方法,所以你不能向前或向后看,你只能得到下一项。

如果您正在迭代列表或元组,则 enumerate(iterable) 可能很有用。

于 2008-11-27T13:39:28.650 回答
-2

不是很pythonic,但可以完成并且很简单:

l=[1,2,3]
for index in range(len(l)):
    if l[index]==2:
        l[index-1]

TO DO:保护边缘

于 2011-01-12T18:50:54.037 回答
-7

最简单的方法是在列表中搜索项目:

def get_previous(l, item):
    idx = l.find(item)
    return None if idx == 0 else l[idx-1]

当然,这仅适用于列表仅包含唯一项目的情况。另一个解决方案是:

for idx in range(len(l)):
    item = l[idx]
    if item == 2:
        l[idx-1]
于 2008-11-27T13:41:17.007 回答