Как прервать выполнение нескольких циклов?
У меня есть следующий код, который не работает:
while True:
# Срез: вывод текущего состояния
while True:
ok = get_input("Все в порядке? (y/n)")
if ok.lower() == "y": break 2 # Это не работает :(
if ok.lower() == "n": break
# Продолжение обработки с меню и т.д.
Есть ли способ сделать так, чтобы это работало? Или мне нужно первым делом сделать одну проверку для выхода из цикла ввода, а затем другую, более ограниченную, проверку в внешнем цикле, чтобы выйти полностью, если пользователь удовлетворен?
5 ответ(ов)
Мой первый инстинкт — это рефакторинг вложенного цикла в отдельную функцию и использование return
для выхода из неё.
Прежде всего, обычная логика может оказаться полезной.
Если по какой-то причине условия завершения не удается корректно обработать, исключения могут служить запасным планом.
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" мы хотим немедленного выхода. Это более исключительная ситуация.
Для выхода из вложенных циклов можно использовать переменную-прерывание. Сначала присвойте ей какое-либо значение (например, False или 0), а затем, внутри внешнего цикла, перед выходом измените значение на что-то другое (например, True или 1). После выхода из цикла проверьте значение переменной-прерывания в "родительском" цикле. Вот пример:
breaker = False # наша могучая переменная для выхода из цикла!
while True:
while True:
if conditionMet:
# вставьте код здесь...
breaker = True
break
if breaker: # интересная часть!
break # <--- !
Если у вас есть бесконечный цикл, это единственный способ выйти из него. Для других циклов выполнение будет гораздо быстрее. Этот подход также работает, если у вас много вложенных циклов. Вы можете выйти из всех или только из нескольких. Безграничные возможности! Надеюсь, это поможет!
Я склонен согласиться с тем, что рефакторинг в функцию обычно является лучшим подходом в подобных ситуациях, но когда вам действительно нужно выйти из вложенных циклов, вот интересный вариант подхода с выбрасыванием исключений, описанного @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
каждый раз.
Ваш код реализует два вложенных цикла, которые продолжают выполнять действия до тех пор, пока переменная 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, чтобы сохранить чистоту и читаемость кода.
Как клонировать список, чтобы он не изменялся неожиданно после присваивания?
Сохранить график в файл изображения вместо его отображения
Преобразование списка словарей в DataFrame pandas
Как отсортировать список/кортеж списков/кортежей по элементу на заданном индексе
Что такое Python egg?