Как вырваться из нескольких циклов в Python? - PullRequest
390 голосов
/ 10 октября 2008

Учитывая следующий код (это не работает):

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

Есть ли способ заставить эту работу? Или мне нужно сделать одну проверку, чтобы разорвать входной цикл, затем другую, более ограниченную, проверку во внешнем цикле, чтобы разорвать все вместе, если пользователь удовлетворен?

Ответы [ 29 ]

417 голосов
/ 10 октября 2008

Моим первым инстинктом было бы преобразовать вложенный цикл в функцию и использовать return для разрыва.

160 голосов
/ 30 июня 2010

Вот еще один короткий подход. Недостатком является то, что вы можете разорвать только внешний цикл, но иногда это именно то, что вы хотите.

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, объясненная в: Почему python использует 'else' после циклов for и while?

Основные сведения: только кажется , как будто внешний цикл всегда прерывается. Но если внутренний цикл не прерывается, внешний цикл тоже не будет.

Заявление continue - это магия здесь. Это в пункте для другого. По определению это происходит, если нет внутреннего разрыва. В этой ситуации continue аккуратно обходит внешний разрыв.

140 голосов
/ 10 октября 2008

PEP 3136 предлагает помечать разрыв / продолжение. Гвидо отклонил его , потому что «код, настолько сложный, чтобы требовать эту функцию, очень редок». Однако в PEP упоминаются некоторые обходные пути (например, метод исключения), в то время как Гвидо считает, что рефакторинг для использования return в большинстве случаев будет проще.

101 голосов
/ 10 октября 2008

Во-первых, обычная логика полезна.

Если по какой-либо причине условия прекращения не могут быть разработаны, исключения являются запасным планом.

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» мы хотим немедленный выход. Это более исключительно.

48 голосов
/ 03 июля 2010

Я склонен согласиться с тем, что рефакторинг в функцию, как правило, является лучшим подходом для такой ситуации, но когда вам действительно нужно вырваться из вложенных циклов, вот интересный вариант исключения: подход повышения, который описал @ S.Lott. Он использует оператор Python 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) он немного чище (без явного блока try-кроме), и (2) вы получаете пользовательский подкласс Exception для каждого использования nested_break; нет необходимости каждый раз объявлять свой собственный Exception подкласс.

37 голосов
/ 10 октября 2008

Во-первых, вы также можете подумать о том, чтобы сделать процесс получения и проверки входных данных функцией; внутри этой функции вы можете просто вернуть значение, если оно правильное, и продолжать вращаться в цикле , тогда как , если нет. По сути, это устраняет проблему, которую вы решили, и обычно может применяться в более общем случае (разрыв нескольких циклов). Если вам абсолютно необходимо сохранить эту структуру в своем коде и действительно не хотите иметь дело с булевыми значениями бухгалтерии ...

Вы также можете использовать 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

Я знаю, я знаю, «ты не должен использовать goto» и все такое, но это хорошо работает в странных случаях, подобных этому.

26 голосов
/ 03 июля 2011

Введите новую переменную, которую вы будете использовать как «прерыватель цикла». Сначала присвойте ему что-нибудь (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   # <--- !

Если у вас бесконечный цикл, это единственный выход; для других циклов выполнение действительно намного быстрее. Это также работает, если у вас много вложенных циклов. Вы можете выйти из всех, или только несколько. Безграничные возможности! Надеюсь, это помогло!

14 голосов
/ 10 октября 2008

keeplooping=True
while keeplooping:
    #Do Stuff
    while keeplooping:
          #do some other stuff
          if finisheddoingstuff(): keeplooping=False

или что-то в этом роде. Вы можете установить переменную во внутреннем цикле и проверить ее во внешнем цикле сразу после выхода из внутреннего цикла, прервав при необходимости. Мне нравится метод GOTO, при условии, что вы не возражаете против использования модуля первоапрельской шутки - он не Pythonic, но он имеет смысл.

11 голосов
/ 28 октября 2017

Чтобы вырваться из нескольких вложенных циклов, без рефакторинга в функцию, используйте «симулированный оператор goto» со встроенным StopIортивным исключением :

try:
    for outer in range(100):
        for inner in range(100):
            if break_early():
                raise StopIteration

except StopIteration: pass

См. в этом обсуждении об использовании операторов goto для выхода из вложенных циклов.

11 голосов
/ 10 октября 2008

Это не самый красивый способ сделать это, но, на мой взгляд, это лучший способ.

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

Я почти уверен, что и здесь вы можете что-то придумать, используя рекурсию, но я не знаю, подходит ли вам этот вариант.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...