이전 및 다음 값에도 액세스하는 Python 루프


90

이전, 현재 및 다음 항목에 액세스하여 객체 목록을 반복하려면 어떻게해야합니까? 파이썬에서이 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];
    }
}

foo가 목록의 시작 또는 끝에 있으면 어떻게됩니까? 현재 이것은 배열 범위를 벗어납니다.
Brian

2
"foo"의 첫 번째 발생이 필요한 경우 일치 할 때 "for"블록에서 "break"를 수행합니다.
van

첫 번째 (0이 아닌) 요소에서 반복을 시작하고 마지막이지만 하나의 요소에서 반복을 종료 하시겠습니까?
smci

foo목록에서 정확히 한 번 발생하는 것이 보장 됩니까? 곱셈이 발생하면 여기에서 일부 접근 방식이 실패하거나 첫 번째 접근 방식 만 찾습니다. 그리고 이것이 발생하지 않으면 다른 접근 방식이 실패하거나 ValueError와 같은 예외가 발생합니다. 몇 가지 테스트 케이스를 제공하면 도움이 될 것입니다.
smci

또한 여기에있는 예제는 둘 다 알려진 길이를 가지며 색인이 가능한 일련의 객체입니다. 여기에있는 답변 중 일부는 항상 인덱싱 할 수있는 것은 아니며 항상 길이가있는 것도 아니며 항상 유한하지 않은 반복자에 대한 일반화입니다.
smci

답변:


107

이것은 트릭을 할 것입니다.

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기능 에 대한 문서입니다 .


17
그러나 내장 함수이기 때문에 'next'를 변수 이름으로 사용하지 않는 것이 가장 좋습니다.
mkosmala

1
루프의 끝에서 : 이것의 편집 된 버전은 논리적으로 소리를 아직하지 않습니다 objnext_의도하지 않은 부작용이있을 수 있습니다 마지막 반복에 대한 동일한 개체가 될 것입니다.
TemporalWolf

질문 문은 OP가 첫 번째 (0이 아닌) 요소에서 반복을 시작하고 마지막이지만 하나의 요소에서 반복을 종료한다고 명시 적으로 말합니다. 그래서 index에서 실행되지해야 1 ... (l-1)하지, 0 ... l당신이 여기 가지고, 그리고 필요하면-절을 특수 맡았다. Btw, 매개 변수가 enumerate(..., start=1)있지만 end. 그래서 우리는 실제로 enumerate().
smci

147

지금까지의 솔루션은 목록 만 다루며 대부분은 목록을 복사하고 있습니다. 내 경험상 불가능한 일이 많다.

또한 목록에서 반복되는 요소를 가질 수 있다는 사실을 다루지 않습니다.

질문 제목은 " 루프 내부의 이전 및 다음 값 "이지만 루프 내부에서 대부분의 답변을 실행하는 경우 각 요소에서 전체 목록을 다시 반복하여 검색하게됩니다.

그래서 방금 그 함수를 만들었습니다. 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끝에 a 를 추가하는 데 사용됩니다.
  • 이제 some_iterable다음과 같은 3 개의 독립적 인 시퀀스가 ​​있습니다 .
    • prevs: None, A, B, C, D, E
    • items: A, B, C, D, E
    • nexts: B, C, D, E, None
  • 마지막으로 izip3 개의 시퀀스를 3 개의 시퀀스로 변경하는 데 사용됩니다.

참고 izip모든 입력 순서가 소진 될때의 마지막 요소는, 그래서 중지 prevs마지막 요소가 될 것이라고 이러한 요소가 없습니다 - 올바른 무시됩니다 prev. 우리는 마지막 요소를 제거하려고 할 수 prevs있지만 izip의 동작은

또한 점에 유의 tee, izip, islicechain으로부터 온 itertools모듈; 즉석에서 (게으른) 입력 시퀀스에 대해 작동하므로 효율적으로 만들고 전체 시퀀스를 한 번에 메모리에 저장할 필요가 없습니다.

에서는 python 3가져 오는 동안 오류가 표시 됩니다 . 대신 izip사용할 수 있습니다 . 수입 할 필요는 , 그것은에 미리 정의되지 않는다 - 소스zipizipzippython 3


3
@becomingGuru : SO를 파이썬 참조 문서의 거울로 바꿀 필요는 없습니다. 이 모든 기능은 공식 문서 (예제 포함)에 매우 잘 설명되어 있습니다
Eli Bendersky

1
@becomingGuru : 문서에 대한 링크를 추가했습니다.
nosklo

6
@LakshmanPrasad 나는 위키 분위기에 있으므로 몇 가지 설명을 추가했습니다 :-).
Kos

7
파이썬 3 izip에서 내장 zip함수 로 대체 될 수 있다는 것을 언급 할 가치가있을 것입니다 ;-)
Tomasito665

1
이것은 좋은 해결책이지만 지나치게 복잡해 보입니다. 이것에서 영감을 얻은 stackoverflow.com/a/54995234/1265955 를 참조하십시오 .
빅토리아

6

목록 이해력을 사용하여 현재, 이전 및 다음 요소가있는 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)]

4

내장 함수 만 사용하고 다른 오프셋으로 쉽게 확장 할 수 있기 때문에 이것이 어떻게 아직 나타나지 않았는지 모르겠습니다.

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)

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"를 찾지 않는다는 것입니다. 다시 말하지만, 경계 의미론은 이상하며 코드에서 파악하기 어렵습니다. :)


2

파이썬> = 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)

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)

마지막 next_에 밑줄? 편집 할 수 없습니다 - "... 적어도 6해야"
Xpector

왜 이것이 간단한 for-loop 및 액세스보다 선호 objects[i-1], objects[i], objects[i+1]됩니까? 아니면 발전기? 그것은 나에게 완전히 모호한 것처럼 보입니다. 또한 PREV와 NEXT가 데이터를 복사하기 때문에 3x 메모리를 불필요하게 사용합니다.
smci

@smci i+1목록의 마지막 요소에 대한 접근 방식을 어떻게 얻 습니까? 다음 요소는 첫 번째 요소 여야합니다. 나는 경계를 벗어난다.
ElectRocnic

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이 인턴하지 않는 값이기 때문입니다. 그러나 모든 싱글 톤 마커 값을 신호로 사용할 수 있으므로 일부 상황에서 사용자 코드를 단순화 할 수 있습니다.


8
"그것은 매우 간단합니다"
미구엘 스티븐스

'sentinel'을 의미 할 때 'signal'이라고 말하지 마세요. 또한 if iB is signalsignal = None이 아니라면 객체를 비교 하는 데 절대 사용하지 마십시오 None. iterbuiltin을 숨기므로 인수 이름으로 사용하지 마십시오 iter(). 동일합니다 next. 어쨌든 발전기 접근 방식은 간단 할 수 있습니다yield prev, curr, next_
smci

@smci 아마도 당신의 사전은 신호와 센티넬에 관한 다른 정의를 가지고있을 것입니다. 동일한 값을 가진 다른 항목이 아닌 특정 항목에 대해 테스트하고 싶었 기 때문에 "is"를 사용했습니다. "is"는 해당 테스트에 대한 올바른 연산자입니다. iter와 next shadow의 사용은 달리 참조되지 않은 것들만 사용하므로 문제는 아니지만 동의하지만 모범 사례는 아닙니다. 마지막 청구에 대한 컨텍스트를 제공하려면 더 많은 코드를 표시해야합니다.
Victoria

@Victoria : 'sentinel [value]'는 잘 정의 된 소프트웨어 용어이고, 'signal'은 아닙니다 (신호 처리 또는 커널 신호에 대해 이야기하지 않음). [파이썬으로 사물을 비교하는 것처럼 is대신 ==당신이 문자열을 인턴으로 CPython에 의존하고 있기 때문에, 문자열을 함께 멀리 얻을 수 있지만, 그렇다하더라도 왜, 그것은 잘 알려진 함정이야, 여기에 여러 이유가 있습니다 v1 = 'monkey'; v2 = 'mon'; v3 = 'key, 다음 v1 is (v2 + v3)제공 False. 코드가 정수 / 문자열 대신 객체를 사용하도록 전환하면 사용 is이 중단됩니다. 따라서 일반적으로 ==평등을 비교 하는 데 사용해야 합니다.
smci

@smci 컴퓨터 소프트웨어에서 가장 어려운 문제는 서로 다른 용어를 사용하는 다른 그룹의 사람들로 인해 통신, 네트워킹이 작동하지 않는 것입니다. 속담처럼 표준은 훌륭하고 모든 사람은 하나가 있습니다. 나는 파이썬 == 연산자와 is 연산자의 차이점을 완전히 이해하고 있으며, 이것이 바로 제가 사용하기로 선택한 이유입니다. 선입견 인 "용어 및 규칙"을 지나쳐 보면 ==는 비교하는 모든 항목이 시퀀스를 종료하는 것을 허용하지만 is를 사용하면 신호로 사용되는 특정 객체에서만 종료된다는 것을 알 수 있습니다. (또는 원하는 경우 보초).
Victoria

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

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'



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

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]

파이썬이 배열의 음수 인덱스를 지원한다는 사실은 거의 몰랐습니다. 감사합니다!
ElectRocnic

1
하지만 결국 실패 할 것입니다 :(
ElectRocnic

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.")

-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

1
value끝에 있으면 실패합니다 . 또한, 같은 마지막 요소를 반환 previous_value하는 경우 value첫 번째입니다.
Trang Oul

귀하의 요구 사항에 따라 다릅니다. 에서 음의 지수는 previous_value 목록에서 마지막 요소를 반환하고 next_value올릴 것이다 IndexError그리고 그게 오류
ImportError를

나는이 방법을 꽤 가끔 찾고 있었다. 이제 나는 그것을 얻는다 .. 감사 @ImportError. 이제 스크립트를 멋지게 확장 할 수 있습니다.
Azam

제한 사항 : value더에 한 번 이상 발생할 수 objects있지만, 사용 .index()(이 발생하지 않는 경우 또는 ValueError를)에만 첫 번째 항목을 찾을 수 있습니다.
smci
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.