访问前后值的循环

121 投票
17 回答
316283 浏览
提问于 2025-04-15 12:20

我想知道怎么在Python中遍历一个对象列表,同时访问前一个、当前和下一个项目。就像下面这个C/C++的代码那样?

foo = somevalue;
previous = next = 0;

for (i=1; i<objects.length(); i++) {
    if (objects[i]==foo) {
        previous = objects[i-1];
        next = objects[i+1];
    }
}

17 个回答

8

如果你只想遍历那些有前一个和后一个元素的项(也就是说,你想跳过第一个和最后一个元素),而你的输入是一个列表,你可以用一个方法把这个列表和自己配对,但不包括第一个和最后一个元素:

words = "one two three four five".split()

for prev, current, nxt in zip(words, words[1:], words[2:]):
    print(prev, current, nxt)

输出结果:

one two three
two three four
three four five

如果你不想跳过第一个和最后一个元素,并且希望在第一个元素时,prev的值为None(而在最后一个元素时,nxt的值也为None),你可以先在列表的两头加上这些值:

words = "one two three four five".split()

padded_words = [None, *words, None]

for prev, current, nxt in zip(padded_words, padded_words[1:], padded_words[2:]):
    print(prev, current, nxt)

输出结果:

None one two
one two three
two three four
three four five
four five None

你可以用任何你想要的值来填充。如果你希望你的列表“循环”,也就是说,第一个元素的prev是最后一个元素,而最后一个元素的nxt是第一个元素,那么就用这些值来填充,而不是None

# avoid IndexError if words is an empty list
padded_words = [words[-1], *words, words[0]] if words else []

输出结果:

five one two
one two three
two three four
three four five
four five one
184

到目前为止,解决方案主要是处理列表,而且大多数都是在复制列表。根据我的经验,很多时候这并不可行。

另外,它们没有考虑到列表中可能会有重复的元素。

你问题的标题是“循环中的前一个和下一个值”,但如果你在循环中运行这里的大多数答案,你会发现每次都要遍历整个列表来找到它。

所以我创建了一个函数,利用itertools模块,来分割和切片可迭代对象,并生成包含前一个和下一个元素的元组。虽然这和你的代码不完全一样,但值得一看,因为它可能解决你的问题。

from itertools import tee, islice, chain, izip

def previous_and_next(some_iterable):
    prevs, items, nexts = tee(some_iterable, 3)
    prevs = chain([None], prevs)
    nexts = chain(islice(nexts, 1, None), [None])
    return izip(prevs, items, nexts)

然后在循环中使用它,你就能在里面得到前一个和下一个项目:

mylist = ['banana', 'orange', 'apple', 'kiwi', 'tomato']

for previous, item, nxt in previous_and_next(mylist):
    print "Item is now", item, "next is", nxt, "previous is", previous

结果:

Item is now banana next is orange previous is None
Item is now orange next is apple previous is banana
Item is now apple next is kiwi previous is orange
Item is now kiwi next is tomato previous is apple
Item is now tomato next is None previous is kiwi

这个方法适用于任何大小的列表(因为它不复制列表),也适用于任何可迭代对象(文件、集合等)。这样你只需遍历序列,就能在循环中获取前一个和下一个项目。无需再次在序列中查找项目。

代码的简要说明:

  • tee用于高效地创建3个独立的迭代器,针对输入序列
  • chain将两个序列链接成一个;这里用它将单元素序列[None]添加到prevs
  • islice用于生成一个不包含第一个元素的序列,然后用chain在末尾添加一个None
  • 现在有3个基于some_iterable的独立序列,分别是:
    • prevs: None, A, B, C, D, E
    • items: A, B, C, D, E
    • nexts: B, C, D, E, None
  • 最后,izip用于将这3个序列合并成一个三元组的序列。

注意,izip在任何输入序列耗尽时会停止,所以prevs的最后一个元素会被忽略,这是正确的——最后一个元素没有前一个元素。我们可以尝试从prevs中去掉最后一个元素,但由于izip的行为,这样做是多余的。

另外要注意,teeizipislicechain都来自itertools模块;它们在处理输入序列时是按需(懒惰)操作的,这使得它们高效,并且不需要在任何时候将整个序列都放在内存中。

在Python 3中,导入izip时会出现错误。你可以用zip代替izip。不需要导入zip,它在Python 3中是预定义的(来源)。

132

这样做就可以了。

foo = somevalue
previous_item = next_item = None
l = len(objects)
for index, obj in enumerate(objects):
    if obj == foo:
        if index > 0:
            previous_item = objects[index - 1]
        if index < (l - 1):
            next_item = objects[index + 1]

这里是关于enumerate函数的文档。

撰写回答