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

时间:2009-06-18 10:23:38

标签: 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]
    }
}

15 个答案:

答案 0 :(得分:120)

直到现在,解决方案只处理列表,而且大多数都在复制列表。根据我的经验,很多时候这是不可能的。

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

你问题的标题是“循环中的上一个和下一个值”,但是如果你在循环中运行大多数答案,你最终会在每个循环上再次迭代整个列表找到它的元素。

所以我刚刚创建了一个函数。使用itertools模块,拆分并切片iterable,并生成包含前一个和下一个元素的元组。不完全是你的代码所做的,但值得一看,因为它可能解决你的问题。

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用于制作除第一个元素之外的所有元素的序列,然后使用chainNone附加到其末尾
  • 现在有3个基于some_iterable的独立序列,如下所示:
    • prevsNone, A, B, C, D, E
    • itemsA, B, C, D, E
    • nextsB, C, D, E, None
  • 最后izip用于将3个序列更改为一个三元组序列。

注意izip在任何输入序列耗尽时停止,因此prevs的最后一个元素将被忽略,这是正确的 - 没有这样的元素,最后一个元素将是它的{{1 }}。我们可以尝试从prev剥离最后一个元素,但prevs的行为会使这个元素变得多余

另请注意,izipteeizipislice来自chain模块;它们在运行时(懒惰地)对它们的输入序列进行操作,这使它们变得高效,并且不需要在任何时间将整个序列同时存储在存储器中。

itertools中,导入python 3时会显示错误,您可以使用izip代替zip。无需导入izip,它已在zip - source

中预先定义

答案 1 :(得分:76)

这应该可以解决问题。

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]

以下是enumerate函数的文档。

答案 2 :(得分:5)

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

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)]

答案 3 :(得分:2)

这是使用没有边界错误的生成器的版本:

def trios(input):
    input = iter(input) # make sure input is an iterator
    try:
        prev, current = input.next(), input.next()
    except StopIteration:
        return
    for next in input:
        yield prev, current, next
        prev, current = current, next

def find_prev_next(objects, foo):
    prev, next = 0, 0
    for temp_prev, current, temp_next in trios(objects):
        if current == foo:
            prev, next = temp_prev, temp_next
    return prev, next

print find_prev_next(range(10), 1)
print find_prev_next(range(10), 0)
print find_prev_next(range(10), 10)
print find_prev_next(range(0), 10)
print find_prev_next(range(1), 10)
print find_prev_next(range(2), 10)

请注意,边界行为是我们从不在第一个或最后一个元素中查找“foo”,这与您的代码不同。同样,边界语义很奇怪......很难从你的代码中得到解决:)

答案 4 :(得分:1)

使用条件表达式来简化python&gt; = 2.5

def prenext(l,v) : 
   i=l.index(v)
   return l[i-1] if i>0 else None,l[i+1] if i<len(l)-1 else None


# example
x=range(10)
prenext(x,3)
>>> (2,4)
prenext(x,0)
>>> (None,2)
prenext(x,9)
>>> (8,None)

答案 5 :(得分:1)

对于任何想要解决此问题且又想循环元素的人,下面的方法都可以工作-

from collections import deque  

foo = ['A', 'B', 'C', 'D']

def prev_and_next(input_list):
    CURRENT = input_list
    PREV = deque(input_list)
    PREV.rotate(-1)
    PREV = list(PREV)
    NEXT = deque(input_list)
    NEXT.rotate(1)
    NEXT = list(NEXT)
    return zip(PREV, CURRENT, NEXT)

for previous_, current_, next_ in prev_and_next(foo):
    print(previous_, current_, next)

答案 6 :(得分:1)

我不知道怎么回事,因为它仅使用内置函数并且可以轻松扩展到其他偏移量:

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)

答案 7 :(得分:1)

两个简单的解决方案:

  1. 如果必须同时定义上一个和下一个值的变量:
alist = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five']

prev = alist[0]
curr = alist[1]

for nxt in alist[2:]:
    print(f'prev: {prev}, curr: {curr}, next: {nxt}')
    prev = curr
    curr = nxt

Output[1]:
prev: Zero, curr: One, next: Two
prev: One, curr: Two, next: Three
prev: Two, curr: Three, next: Four
prev: Three, curr: Four, next: Five
  1. 如果必须使用当前值变量遍历列表中的所有值:
alist = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five']

prev = None
curr = alist[0]

for nxt in alist[1:] + [None]:
    print(f'prev: {prev}, curr: {curr}, next: {nxt}')
    prev = curr
    curr = nxt

Output[2]:
prev: None, curr: Zero, next: One
prev: Zero, curr: One, next: Two
prev: One, curr: Two, next: Three
prev: Two, curr: Three, next: Four
prev: Three, curr: Four, next: Five
prev: Four, curr: Five, next: None

答案 8 :(得分:1)

如果您只想迭代具有下一个和上一个元素的元素(例如,您想跳过第一个和最后一个元素)并且您的输入是一个列表,您可以{{ 3}} 输入本身没有第一个元素和第二个元素:

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(并且 nxtNone 表示最后一个元素),首先用这些值填充您的列表:

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

答案 9 :(得分:0)

您可以在列表中使用index查找somevalue的位置,然后根据需要获取上一个和下一个:


def find_prev_next(elem, elements):
    previous, next = None, None
    index = elements.index(elem)
    if index > 0:
        previous = elements[index -1]
    if index < (len(elements)-1):
        next = elements[index +1]
    return previous, next


foo = 'three'
list = ['one','two','three', 'four', 'five']

previous, next = find_prev_next(foo, list)

print previous # should print 'two'
print next # should print 'four'


答案 10 :(得分:0)

AFAIK这应该很快,但我没有测试它:

def iterate_prv_nxt(my_list):
    prv, cur, nxt = None, iter(my_list), iter(my_list)
    next(nxt, None)

    while True:
        try:
            if prv:
                yield next(prv), next(cur), next(nxt, None)
            else:
                yield None, next(cur), next(nxt, None)
                prv = iter(my_list)
        except StopIteration:
            break

使用示例:

>>> my_list = ['a', 'b', 'c']
>>> for prv, cur, nxt in iterate_prv_nxt(my_list):
...    print prv, cur, nxt
... 
None a b
a b c
b c None

答案 11 :(得分:0)

使用生成器,这很简单:

signal = ['→Signal value←']
def pniter( iter, signal=signal ):
    iA = iB = signal
    for iC in iter:
        if iB is signal:
            iB = iC
            continue
        else:
            yield iA, iB, iC
        iA = iB
        iB = iC
    iC = signal
    yield iA, iB, iC

if __name__ == '__main__':
    print('test 1:')
    for a, b, c in pniter( range( 10 )):
        print( a, b, c )
    print('\ntest 2:')
    for a, b, c in pniter([ 20, 30, 40, 50, 60, 70, 80 ]):
        print( a, b, c )
    print('\ntest 3:')
    cam = { 1: 30, 2: 40, 10: 9, -5: 36 }
    for a, b, c in pniter( cam ):
        print( a, b, c )
    for a, b, c in pniter( cam ):
        print( a, a if a is signal else cam[ a ], b, b if b is signal else cam[ b ], c, c if c is signal else cam[ c ])
    print('\ntest 4:')
    for a, b, c in pniter([ 20, 30, None, 50, 60, 70, 80 ]):
        print( a, b, c )
    print('\ntest 5:')
    for a, b, c in pniter([ 20, 30, None, 50, 60, 70, 80 ], ['sig']):
        print( a, b, c )
    print('\ntest 6:')
    for a, b, c in pniter([ 20, ['→Signal value←'], None, '→Signal value←', 60, 70, 80 ], signal ):
        print( a, b, c )

请注意,包含None且与信号值相同的值的测试仍然有效,因为对信号值的检查使用“ is”,并且信号是Python不进行内插的值。不过,任何单例标记值都可以用作信号,这在某些情况下可以简化用户代码。

答案 12 :(得分:0)

我认为这可行且不复杂

array= [1,5,6,6,3,2]
for i in range(0,len(array)):
    Current = array[i]
    Next = array[i+1]
    Prev = array[i-1]

答案 13 :(得分:0)

非常C / C ++风格的解决方案:

    foo = 5
    objectsList = [3, 6, 5, 9, 10]
    prev = nex = 0
    
    currentIndex = 0
    indexHigher = len(objectsList)-1 #control the higher limit of list
    
    found = False
    prevFound = False
    nexFound = False
    
    #main logic:
    for currentValue in objectsList: #getting each value of list
        if currentValue == foo:
            found = True
            if currentIndex > 0: #check if target value is in the first position   
                prevFound = True
                prev = objectsList[currentIndex-1]
            if currentIndex < indexHigher: #check if target value is in the last position
                nexFound = True
                nex = objectsList[currentIndex+1]
            break #I am considering that target value only exist 1 time in the list
        currentIndex+=1
    
    if found:
        print("Value %s found" % foo)
        if prevFound:
            print("Previous Value: ", prev)
        else:
            print("Previous Value: Target value is in the first position of list.")
        if nexFound:
            print("Next Value: ", nex)
        else:
            print("Next Value: Target value is in the last position of list.")
    else:
        print("Target value does not exist in the list.")

答案 14 :(得分:-1)

Pythonic和优雅的方式:

objects = [1, 2, 3, 4, 5]
value = 3
if value in objects:
   index = objects.index(value)
   previous_value = objects[index-1]
   next_value = objects[index+1] if index + 1 < len(objects) else None