7

Как прервать выполнение нескольких циклов?

1

У меня есть следующий код, который не работает:

while True:
    # Срез: вывод текущего состояния
    while True:
        ok = get_input("Все в порядке? (y/n)")
        if ok.lower() == "y": break 2 # Это не работает :(
        if ok.lower() == "n": break
# Продолжение обработки с меню и т.д.

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

5 ответ(ов)

7

Мой первый инстинкт — это рефакторинг вложенного цикла в отдельную функцию и использование return для выхода из неё.

1

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

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

class GetOutOfLoop(Exception):
    pass

try:
    done = False
    while not done:
        isok = False
        while not (done or isok):
            ok = get_input("Это нормально? (y/n)")
            if ok in ("y", "Y") or ok in ("n", "N"):
                done = True  # вероятно, лучше так
                raise GetOutOfLoop
        # другие действия
except GetOutOfLoop:
    pass

В этом конкретном примере исключение, возможно, не является необходимым.

С другой стороны, в прикладных программах в текстовом режиме часто есть опции "Y", "N" и "Q". Для опции "Q" мы хотим немедленного выхода. Это более исключительная ситуация.

0

Для выхода из вложенных циклов можно использовать переменную-прерывание. Сначала присвойте ей какое-либо значение (например, False или 0), а затем, внутри внешнего цикла, перед выходом измените значение на что-то другое (например, True или 1). После выхода из цикла проверьте значение переменной-прерывания в "родительском" цикле. Вот пример:

breaker = False  # наша могучая переменная для выхода из цикла!
while True:
    while True:
        if conditionMet:
            # вставьте код здесь...
            breaker = True
            break
    if breaker:  # интересная часть!
        break  # <--- !

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

0

Я склонен согласиться с тем, что рефакторинг в функцию обычно является лучшим подходом в подобных ситуациях, но когда вам действительно нужно выйти из вложенных циклов, вот интересный вариант подхода с выбрасыванием исключений, описанного @S.Lott. Он использует оператор with в Python, чтобы сделать выбрасывание исключений немного более аккуратным. Сначала определим новый менеджер контекста (это нужно сделать всего один раз):

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("текущее состояние")
        while True:
            ok = input("Это нормально? (y/n)")
            if ok == "y" or ok == "Y": raise mylabel
            if ok == "n" or ok == "N": break
        print("дальнейшая обработка")

Преимущества: (1) это немного чище (нет явного блока try-except), и (2) вы получаете кастомный подкласс Exception для каждого использования nested_break; нет необходимости объявлять свой собственный подкласс Exception каждый раз.

0

Ваш код реализует два вложенных цикла, которые продолжают выполнять действия до тех пор, пока переменная keeplooping равна True. Когда функция finisheddoingstuff() возвращает True, вы устанавливаете keeplooping в False, что завершает оба цикла.

Однако можно улучшить эту логику, используя флаг в внутреннем цикле и проверяя его в родительском цикле сразу после выхода из внутреннего. Пример кода может выглядеть так:

keeplooping = True
while keeplooping:
    # Выполняем какие-то действия
    finished_inner_loop = False
    while not finished_inner_loop:
        # Выполняем другие действия
        if finisheddoingstuff():
            finished_inner_loop = True
    # Проверка условия для завершения внешнего цикла
    keeplooping = not finished_inner_loop

Что касается использования конструкции GOTO — хоть это и не является "питоническим" стилем (Python не поддерживает GOTO), можно использовать различные модули-неудобства в шутливом ключе. Однако лучше придерживаться идиоматического стиля Python, чтобы сохранить чистоту и читаемость кода.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь