循环还访问前一个值和下一个值

2025-02-24 09:30:00
admin
原创
22
摘要:问题描述:如何迭代对象列表,访问上一个、当前和下一个项目?像这个 C/C++ 代码一样,在 Python 中?foo = somevalue; previous = next = 0; for (i=1; i<objects.length(); i++) { if (objects[i]==f...

问题描述:

如何迭代对象列表,访问上一个、当前和下一个项目?像这个 C/C++ 代码一样,在 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];
    }
}

解决方案 1:

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

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

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

所以我刚刚创建了一个函数。使用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

  • 最后izip用于将 3 个序列转换为一个三元组序列。

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

还要注意tee,、和来自模块;它们即时(懒惰地)对输入序列进行操作,这使得它们高效并且不需要随时将整个序列放在内存中izipislice`chain`itertools

在 Python 3 中,导入时会显示错误izip。您可以使用zip代替izip。无需导入zip,它是在 Python 3 中预定义的(来源)。

解决方案 2:

这应该可以解决问题。

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

解决方案 3:

如果您只想迭代具有下一个元素和上一个元素的元素(例如,您想跳过第一个元素和最后一个元素)并且您的输入是一个列表,那么您可以将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设置(并设置为最后一个元素),请先使用这些值填充列表: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

解决方案 4:

使用列表推导,返回包含当前、上一个和下一个元素的 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)]

解决方案 5:

Python 3.10 引入pairwiseitertools

根据它们的实现来获取迭代器的当前值和以下两个值的想法:

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

如果要访问索引,请小心,因为它不会是中间值的索引。对于我的情况,加 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'

解决方案 6:

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

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:

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

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”,这与您的代码不同。同样,边界语义很奇怪……并且很难从您的代码中理解 :)

解决方案 8:

也许不是很符合 Python 风格,但我没有在答案中看到更清晰的内容。

def iter_in_pairs(iterable):
    for i in range(1, len(iterable)):
        yield (iterable[i-1], iterable[i])

test = [0, 1, 2, 3, 4, 5]
for prev, cur in iter_in_pairs(test):
    print(prev, cur)

0 1
1 2
2 3
3 4
4 5

解决方案 9:

使用条件表达式来简化 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)

解决方案 10:

对于那些想要解决这个问题并且想要循环元素的人来说,下面的方法可能会有效 -

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)

解决方案 11:

两个简单的解决方案:

  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

解决方案 12:

一种更简单的方法是使用 Python 中的数组索引:

foo = 3
next = previous = 0
objects = [1, 2, 3, 4, 5, 6, 7, 8]

for object in range(1, len(objects) - 1):
  if objects[object] == foo:
    next = objects[object + 1]
    previous = objects[object - 1]

该代码本质上是 OP 提供的 C++ 代码的 Python 等效版本。

解决方案 13:

您只需使用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'


解决方案 14:

据我所知这应该相当快,但我没有测试过:

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

解决方案 15:

使用生成器非常简单:

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('
test 2:')
    for a, b, c in pniter([ 20, 30, 40, 50, 60, 70, 80 ]):
        print( a, b, c )
    print('
test 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('
test 4:')
    for a, b, c in pniter([ 20, 30, None, 50, 60, 70, 80 ]):
        print( a, b, c )
    print('
test 5:')
    for a, b, c in pniter([ 20, 30, None, 50, 60, 70, 80 ], ['sig']):
        print( a, b, c )
    print('
test 6:')
    for a, b, c in pniter([ 20, ['→Signal value←'], None, '→Signal value←', 60, 70, 80 ], signal ):
        print( a, b, c )

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

解决方案 16:

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

解决方案 17:

我认为这可行而且不复杂

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]

解决方案 18:

非常 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.")
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1314  
  随着数字化时代的飞速发展,企业数据成为了核心资产,其安全性至关重要。信创国产电脑作为保障数据安全的关键一环,正逐渐在企业中得到广泛应用。信创国产电脑基于自主研发的技术体系,从硬件到软件都致力于构建安全可靠的环境,为企业数据安全保驾护航。接下来,我们将深入探讨信创国产电脑如何在多个层面提升企业数据安全性。硬件层面的安全保...
什么是信创概念   16  
  随着信息技术的飞速发展,电脑作为重要的办公和生产工具,其性能对于用户的工作效率和体验有着至关重要的影响。在当前信创产业蓬勃发展的背景下,国产电脑逐渐崛起,与进口品牌电脑形成了激烈的竞争态势。了解信创国产电脑与进口品牌在性能方面的差异,对于企业和个人在选择电脑设备时具有重要的指导意义。处理器性能对比处理器是电脑的核心组件...
国产化信创什么意思   16  
  信创国产化适配方案在当前数字化时代具有极其重要的意义。随着全球政治经济形势的变化以及信息技术的飞速发展,实现信息技术的自主可控成为国家和企业的关键需求。信创国产化适配方案旨在将各类信息技术产品和服务进行国产化替代,降低对国外技术的依赖,保障信息安全,推动国内信息技术产业的发展。这不仅关乎国家的信息安全战略,也为国内相关...
信创电脑   17  
  信创国产化操作系统的兴起,是我国信息技术产业发展历程中的一个重要里程碑。随着全球科技竞争的日益激烈,实现信息技术的自主可控成为国家战略的关键部分。国产化操作系统作为信创产业的核心基础,其发展不仅关乎操作系统本身的技术突破,更对整个软件生态产生了深远影响。从底层架构的变革到应用开发模式的转变,从软件企业的发展策略调整到用...
信创国产软件有哪些   13  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用