如何遍历对象列表,访问上一个,当前和下一个项目?像这样的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];
}
}
如何遍历对象列表,访问上一个,当前和下一个项目?像这样的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];
}
}
foo
在列表中仅发生一次?如果发生乘法,则此处的某些方法将失败,或仅找到第一个。而且,如果它永远不会发生,其他方法将失败,或者抛出诸如ValueError之类的异常。提供一些测试用例会有所帮助。
Answers:
这应该可以解决问题。
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
函数的文档。
obj
和next_
将是最后一次迭代,这可能会产生意想不到的副作用同一个对象。
index
应从开始运行1 ... (l-1)
,而不是从0 ... l
这里开始运行,并且不需要特殊情况的if子句。顺便说一句,有一个参数,enumerate(..., start=1)
但没有for end
。所以我们真的不想使用enumerate()
。
到目前为止,解决方案仅处理列表,并且大多数都在复制列表。以我的经验,很多时候这是不可能的。
同样,它们也不处理列表中可以包含重复元素的事实。
问题的标题为“循环中的上一个和下一个值”,但是如果您在循环中在此处运行大多数答案,则最终将在每个元素上再次遍历整个列表以找到它。
所以我刚刚创建了一个函数。使用该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
在其末尾附加asome_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
,islice
和chain
来自itertools
模块; 它们动态地(懒惰地)对输入序列进行操作,这使它们高效,并且不需要引入整个序列一次存储在内存中的需求。
在中python 3
,导入时会显示错误izip
,您可以使用zip
代替izip
。无需进口zip
,这是预定义的python 3
- 源
izip
可以用内置zip
函数;-)
这是使用生成器的版本,没有边界错误:
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”,这与您的代码不同。同样,边界语义很奇怪...并且很难从您的代码中理解:)
对于寻求解决方案且又想循环元素的任何人,下面的方法可能有用-
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)
objects[i-1], objects[i], objects[i+1]
?还是发电机?在我看来,这完全是晦涩的。此外,由于PREV和NEXT会复制数据,因此它不必要地使用3倍内存。
i+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不会内插的值。但是,任何单例标记值都可以用作信号,这在某些情况下可以简化用户代码。
if iB is signal
除非信号=无,否则永远不要使用比较对象的相等性,在这种情况下,请直接写入None
。请勿将其iter
用作参数名称,因为这会掩盖内置函数iter()
。同上next
。无论如何,生成器方法可能只是yield prev, curr, next_
is
代替而不是==
]进行比较,这是一个众所周知的陷阱,原因有以下几种:您可以不使用它来处理字符串,因为您依赖于cPython内部字符串,但是即使这样v1 = 'monkey'; v2 = 'mon'; v3 = 'key
,v1 is (v2 + v3)
也可以False
。而且,如果您的代码曾经切换到使用对象而不是整数/字符串,则使用is
会中断。因此,通常您应该使用它==
来比较相等性。
两种简单的解决方案:
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
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
您可以仅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'
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
我认为这有效并且并不复杂
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]
非常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.")
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
value
最后将失败。此外,返回最后一个元素是previous_value
,如果value
是第一位的。
previous_value
将返回列表中的最后一个元素并next_value
引发IndexError
错误,即错误
value
可能在中发生多次objects
,但使用.index()
只会发现其首次出现(如果未发生,则为ValueError)。