Учитывая следующий код (который не работает):
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
Есть ли способ заставить это работать? Или мне нужно сделать одну проверку, чтобы выйти из цикла ввода, а затем другую, более ограниченную, проверить внешний цикл, чтобы полностью выйти из цикла, если пользователь удовлетворен?
C++ хорош здесь с goto, если вы глубоко запутались в большом количестве циклов.
@Nathan См. Почему python не позволяет использовать ключевое слово вроде goto для выхода из n циклов, очень хорошее объяснение Натана.
да, это хорошо.
У Неда Батчелдера есть интересный доклад, посвященный "Как мне вырваться из двух петель?". Предупреждение о спойлере: сделайте двойной контур одиночным.






Во-первых, полезна обычная логика.
Если по какой-либо причине условия прекращения не могут быть разработаны, исключения представляют собой запасной план.
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» нам нужен немедленный выход. Это более необычно.
Серьезно, исключения - это дешевый очень сильно, и идиоматический питон использует их очень много. Также очень легко определить и выбросить собственные.
Интересная идея. Я разрываюсь, любить это или ненавидеть.
Это решение было бы более полезным, если бы оно показывало две вариации по отдельности. (1) с помощью флага (done). (2) создание исключения. Объединение их в единое решение только усложняет задачу. Для будущих читателей: ЛИБО используйте все строки, содержащие done, ИЛИ определите GetOutOfLoop(Exception) и поднимите / кроме этого.
В общем, использование блоков try для чего-либо, кроме исключений, очень не одобряется. Блоки try специально разработаны для обработки ошибок, и использовать их для какого-то странного потока управления не очень хорошо стилистически.
Интересно, что думает BDFL о подобном использовании исключений ... не то чтобы его слово было законом, но мне было бы интересно узнать.
В Python3 это будут raise Exception('GetOutOfLoop') и except Exception:.
@ tommy.carstensen Это чепуха; как определение нового подкласса исключения и его повышение (как показано в ответе), так и передача настраиваемого сообщения конструктору Exception (например, raise Exception('bla bla bla')) действительны как в Python 2, так и в Python 3. Первое предпочтительнее в этом случае, потому что мы этого не делаем. Я хочу, чтобы наш блок except перехватил исключения все, но только специальное исключение, которое мы используем для выхода из цикла. Если мы сделаем что-то так, как вы предлагаете, а затем ошибка в нашем коде приведет к возникновению неожиданного исключения, это будет неправильно обработано так же, как и преднамеренный выход из цикла.
@GreggLind: даже если они дешевы в Python (они дорогие во многих других языках), хотите ли вы, чтобы отладчик прерывал работу каждую секунду при возникновении исключения?
логика .. Думаю, у меня это сработает.
Во-первых, вы можете также подумать о том, чтобы сделать процесс получения и проверки ввода функцией; внутри этой функции вы можете просто вернуть значение, если оно верное, и продолжать вращаться в цикле пока, если нет. Это по существу устраняет проблему, которую вы решили, и обычно может применяться в более общем случае (выход из нескольких циклов). Если вы абсолютно должны сохранить эту структуру в своем коде и действительно не хотите иметь дело с булевыми значениями бухгалтерского учета ...
Вы также можете использовать перейти к следующим образом (используя первоапрельский модуль из здесь):
#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" и все такое, но это хорошо работает в таких странных случаях, как этот.
Я сейчас учусь в колледже, быстро (поскольку goto отсутствует в индексе модуля), для чего пришел?
Если это что-то вроде команды COME FROM в INTERCAL, то ничего
comefrom в Python позволяет перенаправить запущенную программу в другое место всякий раз, когда она достигает определенной метки. Здесь больше информации (entrian.com/goto)
Другими словами, это действительно плохая идея, если вы не любите свой спагетти-код.
Да, заявление пришло! fortran.com/come_from.html Я не ожидал, что кто-то действительно его реализует.
Как упоминалось на странице, на которую я ссылался, это быстрый и грязный способ изменить поток кода во время отладки / запуска, но это определенно не производственная вещь.
@Matt J: Мне нравится, как вы используете первоапрельский фарс (даже если он работает!), Но давайте будем серьезнее. Вы действительно, честно предлагаете использовать модуль goto?
Я не претендую на то, чтобы это было питоническое или «чистое» решение, но это является парадигма, которая используется в других языках когда необходимо. Я также предложил инкапсулировать внутренний цикл в функцию, которую считаю «лучше», но не требует пояснений.
Мне нравится шутка, но суть переполнения стека заключается в продвижении хорошего кода, поэтому я должен проголосовать за вас :(
Я думаю, что это чистое и достаточно удобочитаемое решение, чтобы его можно было назвать хорошим кодом, поэтому я голосую за него. :)
использование goto опасно, потому что большинство из вас не понимает, как работает стек, это сжимаемо для тех, кто имел опыт работы с любым языком ассемблера, в моем случае с микрочипами PIC. Каждый вложенный цикл обычно занимает один слот памяти, а break и return освобождают этот слот. Goto не освобождает память, поэтому со временем вы можете перегрузить стековую память. Хотя я действительно не уверен, заботятся ли об этом современные компиляторы.
@ J.T.Hurley нет, это не чисто и не читается. Я имею в виду, может показаться, что это чистый и читаемый в этом примере, но в любом реальном жизненном сценарии goto создает святой беспорядок. (Также это ооочень антипифонический ...)
"Странные случаи, подобные этому" - действительно, этот случай не странный и очень хорошо решается без дерьмового оператора goto.
goto имеет плохую репутацию, на мой взгляд, любой профессиональный программист должен уметь справиться с этим должным образом.
Моим первым побуждением было бы преобразовать вложенный цикл в функцию и использовать return для выхода.
Это еще одна мысль, которая у меня возникла, поскольку, я уверен, функция get_input_yn () была бы полезна и в другом месте.
согласовано в этом конкретном случае, но в общем случае «У меня есть вложенные циклы, что мне делать» рефакторинг может не иметь смысла.
использование исключения может быть проще, если вы должны уступить вместо использования return, однако в таком случае вам, вероятно, следует использовать itertools.islice ().
Какое значение имеет islice? И почему вы должны быть вынуждены использовать yield во внутренней функции, которая ничего не дает (или не создает и не возвращает)? И даже если вы имеете в виду другой случай, когда цикл является дает результат, вы все равно можете преобразовать его в функцию; просто используйте yield from (3.3+), цикл for вокруг yield (3.2-) или return в качестве итератора генератора вместо yield, использующего его (либо), в зависимости от ситуации.
Обычно возможно преобразовать внутренний цикл в собственный метод, который возвращает true для продолжения, false для разрыва внешнего цикла. while condition1: / если не MyLoop2 (params): break. Альтернативой является установка логического флага, который проверяется на обоих уровнях. more = True / while condition1 и более: / while condition2 и более: / if stopCondition: more = False / break / ...
Я согласен с тем, что стремление использовать return - правильный подход. Причина в том, что, согласно Дзен Python, «плоский лучше, чем вложенный». У нас есть три уровня вложенности, и если это начинает мешать, пора уменьшить вложенность или, по крайней мере, выделить всю вложенность в отдельную функцию.
В целом использование return - это хорошая идея, но я обнаружил, что понимание списка тоже может помочь.
Просто нужны вложенные циклы. Вы не должны вкладывать больше трех, но именно вложенные циклы заставляют работать поиск и сканирование, поэтому, даже если вы сами их не кодируете, вы их используете. Должен быть способ разорвать несколько циклов, поскольку это основная необходимость для всех других языков программирования, о которых я знаю.
@robertking, ты когда-нибудь видел ответ абарнерта тебе? Я согласен с ним в том, что ваш комментарий кажется бессмысленным.
@JamieMarshall За исключением того, что Python прекрасно обходится без него. Если ваш код настолько сложен, что вам нужен «помеченный перерыв», то вы, вероятно, все равно делаете что-то не так. В любом случае вы всегда можете использовать raise для имитации маркированного break. Создайте новый экземпляр Exception для каждого другого label, используйте raise LabelException() вместо break label и оберните в try: ... except LabelException: код, который должен перейти к этой метке. Если ваш код настолько сложен, что вам нужны метки, добавляющие некоторые классы исключений, raise и except не будут такими уж плохими, код в любом случае уродлив.
но дело в том, что бы вы вернули, я имею в виду, что вы не можете просто вернуть случайную вещь?
keeplooping=True
while keeplooping:
#Do Stuff
while keeplooping:
#do some other stuff
if finisheddoingstuff():
keeplooping=False
или что-то вроде того. Вы можете установить переменную во внутреннем цикле и проверять ее во внешнем цикле сразу после выхода из внутреннего цикла, при необходимости прерываясь. Мне вроде как нравится метод GOTO, при условии, что вы не против использования модуля первоапрельской шутки - это не Pythonic, но имеет смысл.
это своего рода установка флага!
Это не самый красивый способ сделать это, но, на мой взгляд, это лучший способ.
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
Я почти уверен, что здесь можно что-нибудь придумать, используя рекурсию, но не знаю, подходит ли это вам.
Это было правильное решение для меня. Мой вариант использования сильно отличался от OP. Я дважды перебирал одни и те же данные, чтобы найти перестановки, поэтому я не хотел разделять два цикла while.
PEP 3136 предлагает пометку break / continue. Guido отклонил это, потому что «код, настолько сложный, чтобы требовать эту функцию, встречается очень редко». Однако в PEP упоминаются некоторые обходные пути (например, техника исключения), в то время как Гвидо считает, что рефакторинг для использования return будет проще в большинстве случаев.
Хотя рефакторинг / return обычно является правильным решением, я видел немало случаев, когда простое краткое выражение «break 2» имело бы такой смысл. Кроме того, рефакторинг / return не работает так же для continue. В этих случаях за числовым прерыванием и продолжением будет легче следить и меньше загромождать, чем рефакторинг до крошечной функции, создание исключений или запутанная логика, включающая установку флага прерывания на каждом уровне вложения. Жаль, что Гвидо отверг это.
break; break было бы неплохо.
Я согласен с Гвидо. Дзен Python говорит: «Плоский лучше, чем вложенный». Редко бывают ситуации, когда невозможно избежать трех и более вложенных циклов.
@Jeyekomon Проблема в том, что вам не нужно 3 или более вложенных циклов, чтобы это было проблемой. 2 вложенных цикла довольно распространены
@ Джон, я знаю. Достаточно ли большая проблема для двух циклов, чтобы создать для этого синтаксис в Python, особенно когда есть несколько существующих обходных путей? Сложный вопрос. И в конце концов, кто-то окажется недовольным, как бы вы ни решили. Python нацелен на то, чтобы быть мощным языком, но все еще с нераздуваемым синтаксисом.
«Код, настолько сложный, чтобы требовать эту функцию, встречается очень редко». Но если вы когда-нибудь будете использовать такой сложный код, отсутствие помеченных циклов сделает его еще более сложным, поскольку вам придется вручную пересылать break через все циклы. Глупый.
@ Джеймс Хей Я не думаю, что это позор. Python и без него уже достаточно загроможден. Надеюсь, он никогда не станет таким загроможденным, как C++. Все, для чего вы могли бы использовать многоуровневый разрыв, может быть достигнуто с помощью конструкций for; else или if; else.
@PyRulez break; break уже имеет допустимый синтаксис; это просто два последовательных оператора break, как если бы они были написаны в последовательных строках. Поэтому он не имеет смысла в качестве синтаксиса-кандидата для этого. Вместо того, чтобы вводить какой-то странный синтаксический особый случай, когда код, который в противном случае анализировал бы как два оператора, получает альтернативное значение, было бы чище и менее запутанным поддерживать нумерованный разрыв (например, break 2), как это делают другие языки и предлагает PEP.
@MarkAmery, да, в этом есть смысл. Я стал на 6 лет мудрее. ;)
Судя по всему, я могу редактировать пост только 5 минут (было 6). Итак, вот мой отредактированный пост: Мои 2 цента: Perl помечен как break (но называет его «последним») и «следующим», чтобы перейти непосредственно к следующей итерации. Совсем не редкость - постоянно пользуюсь. Я новичок в Python и уже нуждаюсь в нем. Кроме того, пронумерованные разрывы были бы ужасны для рефакторинга - лучше пометить цикл, из которого вы хотите выйти, а затем использовать break <label>, чтобы явно указать, из какого цикла вы хотите выйти.
Преобразуйте логику цикла в итератор, который выдает переменные цикла и возвращает по завершении - вот простой пример, который размещает изображения в строках / столбцах до тех пор, пока у нас не закончатся изображения или места для их размещения:
def it(rows, cols, images):
i = 0
for r in xrange(rows):
for c in xrange(cols):
if i >= len(images):
return
yield r, c, images[i]
i += 1
for r, c, image in it(rows=4, cols=4, images=['a.jpg', 'b.jpg', 'c.jpg']):
... do something with r, c, image ...
Это дает преимущество разделения сложной логики цикла и обработки ...
Вот еще один короткий подход. Недостатком является то, что вы можете разорвать только внешний цикл, но иногда это именно то, что вам нужно.
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 - это волшебство. Это в предложении for-else. По определению, которое происходит, если нет внутреннего разрыва. В этой ситуации continue аккуратно обходит внешний разрыв.
@eugeney Почему бы и нет? Первый разрыв выйдет из внутреннего цикла.
@eugeney Мне кажется, что я что-то здесь упускаю. Можете опубликовать пример?
@Mingliang, который можно уйти, прежде чем продолжить.
@RishitBansal Хотя это глубокий разрез: внешний цикл имеет значение, потому что условие внутреннего разрыва something(a, b) также зависит от a. Внешний цикл может работать, пока something(a, b) не является True.
Получил это из видео Раймонда Хеттингера, youtu.be/OSGv2VnC0go?t=971, прочтите операторы else, прикрепленные к циклам for как no_break, тогда это станет легче понять.
@HongCheng Да, это так.
Это умно. :-) Однако не однозначно. Честно говоря, меня не убеждают аргументы в пользу того, чтобы в Python помечены как break или break (n). Обходные пути добавляют сложности.
Это очень эффективно и действенно. Решает мой вопрос без недочетов!
Это не сработает в следующей ситуации. Если во внутреннем цикле есть два разрыва, один из которых предназначен для разрыва только внутреннего цикла, а другой предназначен для разрыва обоих циклов
Я склонен согласиться с тем, что рефакторинг в функцию обычно является лучшим подходом для такого рода ситуаций, но когда вам В самом деле нужно вырваться из вложенных циклов, вот интересный вариант подхода к созданию исключений, который описал @ 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-except) и (2) вы получаете специально созданный подкласс Exception для каждого использования nested_break; нет необходимости каждый раз объявлять собственный подкласс Exception.
Представьте новую переменную, которую вы будете использовать как «прерыватель цикла». Сначала присвойте ему что-нибудь (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 # <--- !
Если у вас бесконечный цикл, это единственный выход; для других циклов выполнение действительно намного быстрее. Это также работает, если у вас много вложенных циклов. Вы можете выйти из всех или только из нескольких. Безграничные возможности! Надеюсь, это помогло!
На мой взгляд, самое простое и легкое для чтения решение. Спасибо, что поделился!
Просто, но блестяще, спасибо!
Похож на предыдущий, но более компактный. (Логические значения - это просто числа)
breaker = False #our mighty loop exiter!
while True:
while True:
ok = get_input("Is this ok? (y/n)")
breaker+= (ok.lower() == "y")
break
if breaker: # the interesting part!
break # <--- !
Это выглядит довольно некрасиво и затрудняет понимание кода по сравнению с предыдущим. Кроме того, это неправильно. Он пропускает фактическую проверку того, является ли ввод приемлемым, и прерывается после 1 цикла.
Я приехал сюда, потому что у меня был внешний цикл и внутренний цикл:
for x in array:
for y in dont_use_these_values:
if x.value==y:
array.remove(x) # fixed, was array.pop(x) in my original answer
continue
do some other stuff with x
Как видите, на самом деле он не перейдет к следующему x, а вместо этого перейдет к следующему y.
то, что я нашел для решения этой проблемы, было просто дважды прогнать массив:
for x in array:
for y in dont_use_these_values:
if x.value==y:
array.remove(x) # fixed, was array.pop(x) in my original answer
continue
for x in array:
do some other stuff with x
Я знаю, что это был конкретный случай вопроса OP, но я публикую его в надежде, что это поможет кому-то по-другому взглянуть на свою проблему, сохраняя при этом простоту.
Вероятно, это не Python. Какой тип массива? Наверное, список, но что в нем? Даже если он содержит целые числа, array.pop (x), вероятно, не будет делать то, что вы хотите.
Неплохо подмечено. Я не могу найти код, на который ссылался. Для всех, кто читает это, array.pop (i) «Удаляет элемент с индексом i из массива и возвращает его». согласно документации Python. Поэтому нужно получить индекс элемента x в массиве, чтобы этот код работал должным образом. Также существует функция array.remove (x), которая будет делать то, что ожидается. Я изменю свой ответ выше, чтобы исправить эту ошибку. Это предполагает, что второй массив не содержит дубликатов, поскольку array.remove (x) удалит только первый найденный экземпляр x.
Хорошо, тогда я понял. В этом случае простое использование break вместо continue будет делать то, что вы хотите, не так ли? :-)
Да, для эффективности и ясности вы, вероятно, захотите использовать в этих примерах break вместо continue. :)
И почему бы не продолжить цикл, если выполняются два условия? Я думаю, что это более питонический способ:
dejaVu = True
while dejaVu:
while True:
ok = raw_input("Is this ok? (y/n)")
if ok == "y" or ok == "Y" or ok == "n" or ok == "N":
dejaVu = False
break
Не так ли?
Всего наилучшего.
почему не только while dejaVu:? Вы все равно устанавливаете значение true.
эй, это работает! Я думал в двух условиях True пропустить две петли, но достаточно одного.
@MatthewScharley Я думаю, это показывает, что это работает во вложенных циклах.
@ MauroAspé, это не совсем то, что запрашивает OP. он по-прежнему будет выполнять весь внешний цикл, но цель состоит в том, что, если вы сломаете остальную часть кода, не будет выполняться
@yamm Может ли это быть решено с помощью if not dejaVu: break внизу и, таким образом, выхода из основного цикла? Думаю, решение наиболее близко к тому, о чем просили. +1
Попробуйте использовать бесконечный генератор.
from itertools import repeat
inputs = (get_input("Is this ok? (y/n)") for _ in repeat(None))
response = (i.lower()= = "y" for i in inputs if i.lower() in ("y", "n"))
while True:
#snip: print out current state
if next(response):
break
#do more processing with menus and stuff
# this version uses a level counter to choose how far to break out
break_levels = 0
while True:
# snip: print out current state
while True:
ok = get_input("Is this ok? (y/n)")
if ok == "y" or ok == "Y":
break_levels = 1 # how far nested, excluding this break
break
if ok == "n" or ok == "N":
break # normal break
if break_levels:
break_levels -= 1
break # pop another level
if break_levels:
break_levels -= 1
break
# ...and so on
# this version breaks up to a certain label
break_label = None
while True:
# snip: print out current state
while True:
ok = get_input("Is this ok? (y/n)")
if ok == "y" or ok == "Y":
break_label = "outer" # specify label to break to
break
if ok == "n" or ok == "N":
break
if break_label:
if break_label != "inner":
break # propagate up
break_label = None # we have arrived!
if break_label:
if break_label != "outer":
break # propagate up
break_label = None # we have arrived!
#do more processing with menus and stuff
В этом случае, как указывали и другие, следует использовать функциональную декомпозицию. Код в Python 3:
def user_confirms():
while True:
answer = input("Is this OK? (y/n) ").strip().lower()
if answer in "yn":
return answer == "y"
def main():
while True:
# do stuff
if user_confirms():
break
В структуре Python while ... else есть скрытый трюк, который можно использовать для имитации двойного разрыва без особых изменений / дополнений кода. По сути, если условие while ложно, срабатывает блок else. Ни исключения, continue или break не запускают блок else. Для получения дополнительной информации см. Ответы на «Предложение Else в инструкции Python while» или Документ Python о while (v2.7).
while True:
#snip: print out current state
ok = ""
while ok != "y" and ok != "n":
ok = get_input("Is this ok? (y/n)")
if ok == "n" or ok == "N":
break # Breaks out of inner loop, skipping else
else:
break # Breaks out of outer loop
#do more processing with menus and stuff
Единственным недостатком является то, что вам нужно переместить условие двойного нарушения в условие while (или добавить переменную-флаг). Варианты этого существуют также для цикла for, где блок else запускается после завершения цикла.
Похоже, это не соответствует требованию о двойных перерывах. Работает для конкретной проблемы, но не для актуального вопроса.
@Dakkaron Вы уверены, что правильно поняли код? Код действительно решает вопрос OP и ломается так же, как request. Однако он не прерывает несколько циклов, а использует предложение else, чтобы заменить необходимость удвоения разрыва.
Насколько я понимаю, вопрос был How to break out of multiple loops in Python? и должен был быть ответ: «Не работает, попробуйте что-нибудь еще». Я знаю, что он точно исправляет данный пример OP, но не отвечает на их вопрос.
@Dakkaron, см. Формулировку проблемы под кодом, и, на мой взгляд, она действительно отвечает на вопрос ОП.
возможно, небольшой трюк, как показано ниже, подойдет, если вы не предпочтете рефакторинг в функцию
добавлена 1 переменная break_level для управления условием цикла while
break_level = 0
# while break_level < 3: # if we have another level of nested loop here
while break_level < 2:
#snip: print out current state
while break_level < 1:
ok = get_input("Is this ok? (y/n)")
if ok == "y" or ok == "Y": break_level = 2 # break 2 level
if ok == "n" or ok == "N": break_level = 1 # break 1 level
Другой способ сократить вашу итерацию до одноуровневого цикла - использовать генераторы, которые также указаны в ссылка на Python
for i, j in ((i, j) for i in A for j in B):
print(i , j)
if (some_condition):
break
Вы можете масштабировать его до любого количества уровней для цикла
Обратной стороной является то, что вы больше не можете сломать только один уровень. Все или ничего.
Еще одним недостатком является то, что он не работает с циклом while. Изначально я хотел опубликовать этот ответ на Python - выход из всех циклов, но, к сожалению, он закрыт как дубликат этого
Он также работает для циклов while, вам нужно только написать свой генератор как def (с yield), а не как понимание.
Да, спикер на PyCon утверждает здесь, что даже принятый ответ @ RobertRossney не является истинно Pythonic, но генератор - это правильный способ разорвать несколько циклов. (Рекомендую посмотреть видео целиком!)
Вы можете определить переменную (например, break_statement), затем изменить ее на другое значение при возникновении условия двух прерываний и использовать ее в операторе if, чтобы также выйти из второго цикла.
while True:
break_statement=0
while True:
ok = raw_input("Is this ok? (y/n)")
if ok == "n" or ok == "N":
break
if ok == "y" or ok == "Y":
break_statement=1
break
if break_statement==1:
break
Хороший момент, однако на каждом из уровней выше нашего внутреннего уровня интереса нам нужно будет сканировать эту переменную. Очень плохо, что в языке нет инструкции GoTo с точки зрения производительности.
Я хотел бы напомнить вам, что функции в Python могут быть созданы прямо в середине кода и могут получать доступ к окружающим переменным прозрачно для чтения и с декларацией nonlocal или global для записи.
Таким образом, вы можете использовать функцию как «разрушаемую структуру управления», определяя место, куда вы хотите вернуться:
def is_prime(number):
foo = bar = number
def return_here():
nonlocal foo, bar
init_bar = bar
while foo > 0:
bar = init_bar
while bar >= foo:
if foo*bar == number:
return
bar -= 1
foo -= 1
return_here()
if foo == 1:
print(number, 'is prime')
else:
print(number, '=', bar, '*', foo)
>>> is_prime(67)
67 is prime
>>> is_prime(117)
117 = 13 * 9
>>> is_prime(16)
16 = 4 * 4
Надеюсь, это поможет:
x = True
y = True
while x == True:
while y == True:
ok = get_input("Is this ok? (y/n)")
if ok == "y" or ok == "Y":
x,y = False,False #breaks from both loops
if ok == "n" or ok == "N":
break #breaks from just one
Поскольку этот вопрос стал стандартным для взлома определенного цикла, я хотел бы дать свой ответ на примере использования Exception.
Хотя в конструкции с несколькими циклами не существует метки с именем break of loop, мы можем использовать Пользовательские исключения, чтобы разбить конкретный цикл по нашему выбору. Рассмотрим следующий пример, в котором мы напечатаем все числа до 4 цифр в системе счисления по основанию 6:
class BreakLoop(Exception):
def __init__(self, counter):
Exception.__init__(self, 'Exception 1')
self.counter = counter
for counter1 in range(6): # Make it 1000
try:
thousand = counter1 * 1000
for counter2 in range(6): # Make it 100
try:
hundred = counter2 * 100
for counter3 in range(6): # Make it 10
try:
ten = counter3 * 10
for counter4 in range(6):
try:
unit = counter4
value = thousand + hundred + ten + unit
if unit == 4 :
raise BreakLoop(4) # Don't break from loop
if ten == 30:
raise BreakLoop(3) # Break into loop 3
if hundred == 500:
raise BreakLoop(2) # Break into loop 2
if thousand == 2000:
raise BreakLoop(1) # Break into loop 1
print('{:04d}'.format(value))
except BreakLoop as bl:
if bl.counter != 4:
raise bl
except BreakLoop as bl:
if bl.counter != 3:
raise bl
except BreakLoop as bl:
if bl.counter != 2:
raise bl
except BreakLoop as bl:
pass
Когда мы печатаем вывод, мы никогда не получим никакого значения, где единица измерения равна 4. В этом случае мы не прерываем ни один цикл, поскольку BreakLoop(4) поднимается и попадает в тот же цикл. Точно так же, когда десятое место имеет 3, мы переходим к третьему циклу, используя BreakLoop(3). Каждый раз, когда у 100 разряда 5, мы переходим ко второму циклу, используя BreakLoop(2), а когда у разряда тысячи 2, мы переходим к первому циклу, используя BreakLoop(1).
Короче говоря, поднимите свое исключение (встроенное или определяемое пользователем) во внутренних циклах и перехватите его в цикле, откуда вы хотите возобновить управление. Если вы хотите прервать выполнение всех циклов, перехватите исключение вне всех циклов. (Я не показывал этот случай на примере).
Чтобы выйти из нескольких вложенных циклов без преобразования в функцию, используйте «симулированный оператор goto» со встроенным StopIteration исключение:
try:
for outer in range(100):
for inner in range(100):
if break_early():
raise StopIteration
except StopIteration: pass
См. это обсуждение об использовании операторов goto для выхода из вложенных циклов.
Это выглядит намного лучше, чем создание собственного класса для обработки исключения, и выглядит очень аккуратно. Есть ли причина, по которой я не должен этого делать?
Фактически StopIteration используется для генераторов, но я думаю, что обычно у вас нет неотловленных исключений StopIteration. Так что это кажется хорошим решением, но в любом случае нет ошибки при создании нового исключения.
Лучшее и простое решение для меня
Я решаю эту проблему, определяя переменную, на которую ссылаются, чтобы определить, перейдете ли вы на следующий уровень или нет. В этом примере эта переменная называется shouldbreak.
Variable_That_Counts_To_Three=1
while 1==1:
shouldbreak='no'
Variable_That_Counts_To_Five=0
while 2==2:
Variable_That_Counts_To_Five+=1
print(Variable_That_Counts_To_Five)
if Variable_That_Counts_To_Five == 5:
if Variable_That_Counts_To_Three == 3:
shouldbreak='yes'
break
print('Three Counter = ' + str(Variable_That_Counts_To_Three))
Variable_That_Counts_To_Three+=1
if shouldbreak == 'yes':
break
print('''
This breaks out of two loops!''')
Это дает большой контроль над тем, как именно вы хотите, чтобы программа прервалась, позволяя вам выбирать, когда вы хотите прервать и на сколько уровней опуститься.
Использование правильных логических значений True или False, по крайней мере, превратит это из ужасного в просто непривлекательное.
Используя функцию:
def myloop():
for i in range(1,6,1): # 1st loop
print('i:',i)
for j in range(1,11,2): # 2nd loop
print(' i, j:' ,i, j)
for k in range(1,21,4): # 3rd loop
print(' i,j,k:', i,j,k)
if i%3==0 and j%3==0 and k%3==0:
return # getting out of all loops
myloop()
Попробуйте запустить приведенные выше коды, закомментировав также return.
Без использования какой-либо функции:
done = False
for i in range(1,6,1): # 1st loop
print('i:', i)
for j in range(1,11,2): # 2nd loop
print(' i, j:' ,i, j)
for k in range(1,21,4): # 3rd loop
print(' i,j,k:', i,j,k)
if i%3==0 and j%3==0 and k%3==0:
done = True
break # breaking from 3rd loop
if done: break # breaking from 2nd loop
if done: break # breaking from 1st loop
Теперь запустите указанные выше коды как есть, а затем попробуйте запустить, закомментировав каждую строку, содержащую break, по одному снизу.
Простой способ превратить несколько циклов в один разрывной цикл - использовать numpy.ndindex.
for i in range(n):
for j in range(n):
val = x[i, j]
break # still inside the outer loop!
for i, j in np.ndindex(n, n):
val = x[i, j]
break # you left the only loop there was!
Вы должны индексировать свои объекты, в отличие от возможности явно перебирать значения, но, по крайней мере, в простых случаях это кажется примерно в 2-20 раз проще, чем большинство предложенных ответов.
На примере: эти две матрицы равны / одинаковы?
matrix1 и matrix2 имеют одинаковый размер, n, двумерные матрицы.
Первое решение, без функции
same_matrices = True
inner_loop_broken_once = False
n = len(matrix1)
for i in range(n):
for j in range(n):
if matrix1[i][j] != matrix2[i][j]:
same_matrices = False
inner_loop_broken_once = True
break
if inner_loop_broken_once:
break
Второе решение, с функцией
Это окончательное решение для моего случая
def are_two_matrices_the_same (matrix1, matrix2):
n = len(matrix1)
for i in range(n):
for j in range(n):
if matrix1[i][j] != matrix2[i][j]:
return False
return True
Хорошего дня!
Вот реализация, которая, кажется, работает:
break_ = False
for i in range(10):
if break_:
break
for j in range(10):
if j == 3:
break_ = True
break
else:
print(i, j)
Единственный недостаток заключается в том, что вам нужно определить break_ до циклов.
There is no way to do this from a language level. Some languages have a goto others have a break that takes an argument, python does not.
The best options are:
Set a flag which is checked by the outer loop, or set the outer loops condition.
Put the loop in a function and use return to break out of all the loops at once.
Reformulate your logic.
Благодарим Вивека Нагараджана, программиста с 1987 года.
Использование функции
def doMywork(data):
for i in data:
for e in i:
return
Использование флага
is_break = False
for i in data:
if is_break:
break # outer loop break
for e in i:
is_break = True
break # inner loop break
Лично я бы использовал логическое значение, которое переключается, когда я готов разорвать внешний цикл. Например
while True:
#snip: print out current state
quit = False
while True:
ok = input("Is this ok? (y/n)")
if ok.lower() == "y":
quit = True
break # this should work now :-)
if ok.lower() == "n":
quit = True
break # This should work too :-)
if quit:
break
#do more processing with menus and stuff
Почему в Python просто нет break (n), где n - количество уровней, из которых вы хотите выйти?