101

如何遍历对象列表,访问上一个、当前和下一个项目?像 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];
    }
}
4

15 回答 15

164

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

此外,它们不处理您可以在列表中包含重复元素的事实。

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

所以我刚刚创建了一个函数。使用该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用于将 a 附加None到其末尾
  • 现在有 3 个基于some_iterable该外观的独立序列:
    • prevsNone, A, B, C, D, E
    • itemsA, B, C, D, E
    • nextsB, C, D, E, None
  • finallyizip用于将 3 个序列变为一个三元组序列。

请注意,izip当任何输入序列用尽时停止,因此prevs将忽略 的最后一个元素,这是正确的 - 没有这样的元素,最后一个元素将是它的prev. 我们可以尝试从 中剥离最后的元素,prevsizip

还要注意tee, izip,islicechain来自itertools模块;它们在运行中(懒惰地)对其输入序列进行操作,这使它们变得高效,并且不需要随时将整个序列存储在内存中。

python 3,导入时会报错izip,可以zipizip. 无需导入zippython 3内 预定义

于 2009-06-18T11:12:58.690 回答
121

这应该可以解决问题。

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功能的文档。

于 2009-06-18T10:28:51.383 回答
7

使用列表推导,返回一个包含当前、前一个和下一个元素的 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)]
于 2015-09-22T10:40:53.257 回答
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)
于 2019-06-18T17:19:02.913 回答
4

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

def trios(iterable):
    it = iter(iterable)
    try:
        prev, current = next(it), next(it)
    except StopIteration:
        return
    for next in it:
        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”,这与您的代码不同。同样,边界语义很奇怪......并且很难从您的代码中理解:)

于 2009-06-18T11:29:11.137 回答
4

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

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设置为(并且是最后一个元素),请先用这些值填充您的列表: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)

输出:

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
于 2021-03-04T05:24:20.290 回答
2

使用条件表达式来简化 python >= 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)
于 2009-06-18T11:02:48.973 回答
2

对于正在寻找解决方案并希望循环元素的任何人,下面可能会起作用 -

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)
于 2019-05-08T15:56:08.583 回答
2

两个简单的解决方案:

  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
于 2020-09-10T17:21:24.573 回答
1

使用生成器,非常简单:

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 不实习的值。但是,任何单例标记值都可以用作信号,这在某些情况下可能会简化用户代码。

于 2019-03-05T04:08:02.670 回答
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'


于 2009-06-18T10:49:16.413 回答
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
于 2014-03-24T11:40:31.690 回答
-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
于 2015-10-09T11:06:07.620 回答
-1

我认为这行得通,并不复杂

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]
于 2020-05-01T23:05:57.743 回答
-2

非常 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.")
于 2020-08-21T14:50:05.853 回答