Python - 循环内的上一个和下一个值

dir*_*r01 69 python loops

我怎么能在python中做这样的事情?

foo = somevalue
previous = next = 0

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

nos*_*klo 131

解决方案到目前为止只处理列表,大多数都在复制列表.根据我的经验,很多时候这是不可能的.

此外,他们没有处理您可以在列表中重复元素的事实.

你的问题的标题是" 循环中的上一个和下一个值 ",但是如果你在循环中运行大多数答案,你将最终在每个元素上再次遍历整个列表以找到它.

所以我刚刚创建了一个函数.使用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)
Run Code Online (Sandbox Code Playgroud)

然后在循环中使用它,你将有前一个和下一个项目:

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

结果:

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

它适用于任何大小的列表(因为它不复制列表),以及任何可迭代的(文件,集等).这样您就可以遍历序列,并在循环内部使用上一个和下一个项目.无需再次搜索序列中的项目.

代码的简短说明:

  • tee 用于在输入序列上有效地创建3个独立的迭代器
  • chain将两个序列连接成一个; 它在这里用于单元素序列追加[None]prevs
  • islice用于创建除第一个之外的所有元素的序列,然后chain用于将a附加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将忽略最后一个元素,这是正确的 - 没有这样的元素,最后一个元素将是它prev.我们可以尝试剥离最后的元素,prevs但是izip行为会使它变得多余

还要注意的是tee,izip,islicechain来自itertools模块; 它们在运行中(懒惰地)对它们的输入序列进行操作,这使得它们有效并且不需要在任何时间将整个序列同时存储在存储器中.

python 3,导入时会显示错误izip,您可以使用zip而不是izip.无需进口zip,这是预定义的python 3-

  • @LakshmanPrasad我有一个维基心情,所以我添加了一些解释:-). (6认同)
  • 值得一提的是,在Python 3中,`izip`可以替换为内置的`zip`功能;-) (6认同)
  • 该解决方案需要对tee,islice,chain和izip进行解释. (3认同)
  • @becomingGuru:没有必要将SO变成Python参考文档的镜像.所有这些功能都在官方文档中得到了很好的解释(带有示例) (3认同)
  • 是的,他们正在做大致相同的事情。我并不“真正习惯”itertools,所以当我困惑地通过你的解决方案来弄清楚它时,我仍然声称我的解决方案更简单,因为它甚至不需要导入itertools,或者那里使用的所有聪明的机制(你必须解释一下),也不是它创建的七个额外的迭代器。因此,那些不“真正”理解 itertools 的其他人也可以理解:)您的解决方案还假设 None 可用作标记,但这可以简单地修复。您的教程是关于使用 itertools 的精彩教程! (2认同)

Han*_*Gay 84

这应该可以解决问题.

foo = somevalue
previous = next_ = None
l = len(objects)
for index, obj in enumerate(objects):
    if obj == foo:
        if index > 0:
            previous = objects[index - 1]
        if index < (l - 1):
            next_ = objects[index + 1]
Run Code Online (Sandbox Code Playgroud)

这是关于该enumerate功能的文档.

  • 但最好不要使用'next'作为变量名,因为它是一个内置函数. (12认同)

小智 7

如果您只想迭代具有下一个和上一个元素的元素(例如,您想跳过第一个和最后一个元素)并且您的输入是一个列表,则您可以zip使用输入本身而无需第一个元素和第二个元素元素:

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

for prev, current, nxt in zip(words, words[1:], words[2:]):
    print(prev, current, nxt)
Run Code Online (Sandbox Code Playgroud)

输出:

one two three
two three four
three four five
Run Code Online (Sandbox Code Playgroud)

如果您不想跳过第一个和最后一个元素,并且希望在位于第一个元素时prev设置为(以及最后一个元素),请首先使用这些值填充列表:NonenxtNone

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

输出:

None one two
one two three
two three four
three four five
four five None
Run Code Online (Sandbox Code Playgroud)

你可以用任何你想要的东西来填充。如果您希望列表“环绕”(例如,第prev一个元素的 是最后一个元素,nxt最后一个元素的 是第一个元素),请用这些而不是填充您的输入None

# avoid IndexError if words is an empty list
padded_words = [words[-1], *words, words[0]] if words else []
Run Code Online (Sandbox Code Playgroud)

输出:

five one two
one two three
two three four
three four five
four five one
Run Code Online (Sandbox Code Playgroud)


RYS*_*RYS 6

使用列表推导,返回一个包含当前,上一个和下一个元素的三元组:

three_tuple = [(current, 
                my_list[idx - 1] if idx >= 1 else None, 
                my_list[idx + 1] if idx < len(my_list) - 1 else None) for idx, current in enumerate(my_list)]
Run Code Online (Sandbox Code Playgroud)


小智 6

Python 3.10 引入pairwiseitertools.

基于其实现的想法,用于获取迭代器的当前值和以下两个值:

import itertools
def triowise(iterable):
    b, c = itertools.tee(iterable[1:])
    next(c, None)
    return zip(iterable, b, c)    
Run Code Online (Sandbox Code Playgroud)

如果要访问索引,请小心,因为它不会是中间值的索引。对于我的情况,加 1 就足够了。

一个例子:

>>> for n, (a, b, c) in enumerate(triowise('ABCDEFGH')):
...    n += 1
...    print('index', n, 'previous', a, 'current', b, 'next', c)

'index 1 previous A current B next C'
'index 2 previous B current C next D'
'index 3 previous C current D next E'
'index 4 previous D current E next F'
'index 5 previous E current F next G'
'index 6 previous F current G next H'
Run Code Online (Sandbox Code Playgroud)


小智 5

我不知道这怎么还没出现,因为它只使用内置函数并且很容易扩展到其他偏移量:

values = [1, 2, 3, 4]
offsets = [None] + values[:-1], values, values[1:] + [None]
for value in list(zip(*offsets)):
    print(value) # (previous, current, next)

(None, 1, 2)
(1, 2, 3)
(2, 3, 4)
(3, 4, None)
Run Code Online (Sandbox Code Playgroud)