[python] 여러 루프에서 벗어나는 방법?

다음 코드가 주어지면 작동하지 않습니다.

while True:
    #snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok.lower() == "y": break 2 #this doesn't work :(
        if ok.lower() == "n": break
    #do more processing with menus and stuff

이 작업을 수행하는 방법이 있습니까? 또는 입력 루프에서 벗어나기 위해 한 가지 검사를 한 다음 외부 루프를 검사하여 사용자가 만족하는 경우 모두 함께 나눕니 까?



답변

내 첫 번째 본능은 중첩 루프를 함수로 리팩터링하고 사용 return하는 것입니다.


답변

짧은 또 다른 접근법이 있습니다. 단점은 외부 루프 만 끊을 수 있지만 때로는 정확히 원하는 루프입니다.

for a in xrange(10):
    for b in xrange(20):
        if something(a, b):
            # Break the inner loop...
            break
    else:
        # Continue if the inner loop wasn't broken.
        continue
    # Inner loop was broken, break the outer.
    break

이것은 for / else 구문에 설명되어 있습니다 : 왜 파이썬은 for 및 while 루프 후에 ‘else’를 사용합니까?

주요 통찰력 : 외부 루프가 항상 끊어지는 것처럼 보입니다 . 그러나 내부 루프가 끊어지지 않으면 외부 루프도 끊어지지 않습니다.

continue문은 여기 마법이다. for-else 절에 있습니다. 내부 휴식이없는 경우 발생하는 정의의해 . 이 상황 continue에서 외피가 깔끔하게 우회됩니다.


답변

PEP 3136 은 분류 된 중단 / 계속을 제안합니다. Guido “이 기능을 요구하기에 너무 복잡한 코드는 매우 드물기 때문에” 거부했습니다 . PEP는 (예외 기술과 같은) 몇 가지 해결 방법을 언급하지만 Guido는 반환을 사용하기 위해 리팩토링하는 것이 대부분의 경우 더 간단하다고 생각합니다.


답변

첫째, 일반적인 논리가 도움이됩니다.

어떤 이유로 종료 조건을 해결할 수없는 경우 예외는 대체 계획입니다.

class GetOutOfLoop( Exception ):
    pass

try:
    done= False
    while not done:
        isok= False
        while not (done or isok):
            ok = get_input("Is this ok? (y/n)")
            if ok in ("y", "Y") or ok in ("n", "N") :
                done= True # probably better
                raise GetOutOfLoop
        # other stuff
except GetOutOfLoop:
    pass

이 특정 예에서는 예외가 필요하지 않을 수 있습니다.

반면, 문자 모드 응용 프로그램에는 종종 “Y”, “N”및 “Q”옵션이 있습니다. “Q”옵션의 경우 즉시 종료해야합니다. 더 예외적입니다.


답변

함수로 리팩토링하는 것이 일반적으로 이런 상황에 가장 적합한 방법이라는 데 동의하는 경향이 있지만, 중첩 루프에서 실제로 벗어날 필요가있는 경우 @ S.Lott에서 설명한 예외 제기 접근법의 흥미로운 변형이 있습니다. 파이썬의 with문장을 사용하여 예외 발생을 조금 더 멋지게 만듭니다. 다음을 사용하여 새 컨텍스트 관리자를 정의하십시오 (한 번만 수행).

from contextlib import contextmanager
@contextmanager
def nested_break():
    class NestedBreakException(Exception):
        pass
    try:
        yield NestedBreakException
    except NestedBreakException:
        pass

이제이 컨텍스트 관리자를 다음과 같이 사용할 수 있습니다.

with nested_break() as mylabel:
    while True:
        print "current state"
        while True:
            ok = raw_input("Is this ok? (y/n)")
            if ok == "y" or ok == "Y": raise mylabel
            if ok == "n" or ok == "N": break
        print "more processing"

장점 : (1) 약간 더 깔끔하고 (명시적인 시도 제외 블록은 없음) (2) Exception사용할 때마다 사용자 정의 된 서브 클래스를 얻습니다 nested_break. Exception매번 자신의 서브 클래스 를 선언 할 필요가 없습니다 .


답변

먼저 입력을 가져오고 유효성을 검사하는 프로세스를 만드는 것도 고려할 수 있습니다. 해당 함수 내에서 올바른 경우 값을 반환하고 그렇지 않은 경우 while 루프 에서 계속 회전 할 수 있습니다 . 이것은 본질적으로 해결 한 문제를 방지하며 일반적으로보다 일반적인 경우 (여러 루프에서 발생)에 적용 할 수 있습니다. 코드 에이 구조를 유지해야하고 부기 부울을 다루고 싶지 않은 경우 …

다음과 같은 방식으로 goto 를 사용할 수도 있습니다 ( 여기서 April Fools 모듈 사용 ).

#import the stuff
from goto import goto, label

while True:
    #snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y": goto .breakall
        if ok == "n" or ok == "N": break
    #do more processing with menus and stuff
label .breakall

나는 “고토를 사용하지 말아야한다”는 것을 알고 있지만, 이런 경우에는 이상하게 작동합니다.


답변

‘루프 브레이커’로 사용할 새 변수를 소개하십시오. 먼저 무언가 (False, 0 등)를 할당 한 다음 외부 루프 내부에서 벗어나기 전에 값을 다른 것으로 변경하십시오 (True, 1, …). 루프가 종료되면 ‘부모’루프에서 해당 값을 확인하십시오. 보여 드리겠습니다 :

breaker = False #our mighty loop exiter!
while True:
    while True:
        if conditionMet:
            #insert code here...
            breaker = True
            break
    if breaker: # the interesting part!
        break   # <--- !

무한 루프가 있다면 이것이 유일한 방법입니다. 다른 루프의 경우 실행 속도가 훨씬 빠릅니다. 중첩 루프가 많은 경우에도 작동합니다. 모두 또는 몇 개만 종료 할 수 있습니다. 끝없는 가능성! 이것이 도움이 되었기를 바랍니다!