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

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

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

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

Почему в Python просто нет break (n), где n - количество уровней, из которых вы хотите выйти?

Nathan 02.05.2010 20:49

C++ хорош здесь с goto, если вы глубоко запутались в большом количестве циклов.

Drake Johnson 12.12.2019 17:50

@Nathan См. Почему python не позволяет использовать ключевое слово вроде goto для выхода из n циклов, очень хорошее объяснение Натана.

Shivam Jha 09.10.2020 10:34

да, это хорошо.

KingWitherBrine 26.11.2020 22:11

У Неда Батчелдера есть интересный доклад, посвященный "Как мне вырваться из двух петель?". Предупреждение о спойлере: сделайте двойной контур одиночным.

Tiago Martins Peres 李大仁 03.12.2020 12:28
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
557
5
487 079
32
Перейти к ответу Данный вопрос помечен как решенный

Ответы 32

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

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

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» нам нужен немедленный выход. Это более необычно.

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

Gregg Lind 22.10.2008 00:56

Интересная идея. Я разрываюсь, любить это или ненавидеть.

Craig McQueen 25.01.2010 05:54

Это решение было бы более полезным, если бы оно показывало две вариации по отдельности. (1) с помощью флага (done). (2) создание исключения. Объединение их в единое решение только усложняет задачу. Для будущих читателей: ЛИБО используйте все строки, содержащие done, ИЛИ определите GetOutOfLoop(Exception) и поднимите / кроме этого.

ToolmakerSteve 18.12.2013 04:25

В общем, использование блоков try для чего-либо, кроме исключений, очень не одобряется. Блоки try специально разработаны для обработки ошибок, и использовать их для какого-то странного потока управления не очень хорошо стилистически.

nobillygreen 26.12.2013 21:28

Интересно, что думает BDFL о подобном использовании исключений ... не то чтобы его слово было законом, но мне было бы интересно узнать.

mike rodent 14.02.2016 15:36

В Python3 это будут raise Exception('GetOutOfLoop') и except Exception:.

tommy.carstensen 08.01.2017 17:37

@ tommy.carstensen Это чепуха; как определение нового подкласса исключения и его повышение (как показано в ответе), так и передача настраиваемого сообщения конструктору Exception (например, raise Exception('bla bla bla')) действительны как в Python 2, так и в Python 3. Первое предпочтительнее в этом случае, потому что мы этого не делаем. Я хочу, чтобы наш блок except перехватил исключения все, но только специальное исключение, которое мы используем для выхода из цикла. Если мы сделаем что-то так, как вы предлагаете, а затем ошибка в нашем коде приведет к возникновению неожиданного исключения, это будет неправильно обработано так же, как и преднамеренный выход из цикла.

Mark Amery 22.04.2019 14:08

@GreggLind: даже если они дешевы в Python (они дорогие во многих других языках), хотите ли вы, чтобы отладчик прерывал работу каждую секунду при возникновении исключения?

Thomas Weller 24.06.2019 10:19

логика .. Думаю, у меня это сработает.

KingWitherBrine 26.11.2020 22:16

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

Вы также можете использовать перейти к следующим образом (используя первоапрельский модуль из здесь):

#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 отсутствует в индексе модуля), для чего пришел?

Matthew Scharley 10.10.2008 04:20

Если это что-то вроде команды COME FROM в INTERCAL, то ничего

1800 INFORMATION 10.10.2008 04:28

comefrom в Python позволяет перенаправить запущенную программу в другое место всякий раз, когда она достигает определенной метки. Здесь больше информации (entrian.com/goto)

Matt J 10.10.2008 04:30

Другими словами, это действительно плохая идея, если вы не любите свой спагетти-код.

Matthew Scharley 10.10.2008 04:36

Да, заявление пришло! fortran.com/come_from.html Я не ожидал, что кто-то действительно его реализует.

Federico A. Ramponi 10.10.2008 04:41

Как упоминалось на странице, на которую я ссылался, это быстрый и грязный способ изменить поток кода во время отладки / запуска, но это определенно не производственная вещь.

Matt J 10.10.2008 04:45

@Matt J: Мне нравится, как вы используете первоапрельский фарс (даже если он работает!), Но давайте будем серьезнее. Вы действительно, честно предлагаете использовать модуль goto?

tzot 10.10.2008 05:55

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

Matt J 10.10.2008 06:05

Мне нравится шутка, но суть переполнения стека заключается в продвижении хорошего кода, поэтому я должен проголосовать за вас :(

Christian Oudard 22.12.2008 16:40

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

J.T. Hurley 09.01.2009 23:28

использование goto опасно, потому что большинство из вас не понимает, как работает стек, это сжимаемо для тех, кто имел опыт работы с любым языком ассемблера, в моем случае с микрочипами PIC. Каждый вложенный цикл обычно занимает один слот памяти, а break и return освобождают этот слот. Goto не освобождает память, поэтому со временем вы можете перегрузить стековую память. Хотя я действительно не уверен, заботятся ли об этом современные компиляторы.

Daniel N. 19.03.2015 04:38

@ J.T.Hurley нет, это не чисто и не читается. Я имею в виду, может показаться, что это чистый и читаемый в этом примере, но в любом реальном жизненном сценарии goto создает святой беспорядок. (Также это ооочень антипифонический ...)

Alois Mahdal 18.08.2015 03:46

"Странные случаи, подобные этому" - действительно, этот случай не странный и очень хорошо решается без дерьмового оператора goto.

Thomas Weller 24.06.2019 10:23

goto имеет плохую репутацию, на мой взгляд, любой профессиональный программист должен уметь справиться с этим должным образом.

Albert Renshaw 12.08.2019 00:53
Ответ принят как подходящий

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

Это еще одна мысль, которая у меня возникла, поскольку, я уверен, функция get_input_yn () была бы полезна и в другом месте.

Matthew Scharley 10.10.2008 04:28

согласовано в этом конкретном случае, но в общем случае «У меня есть вложенные циклы, что мне делать» рефакторинг может не иметь смысла.

quick_dry 10.10.2008 05:11

использование исключения может быть проще, если вы должны уступить вместо использования return, однако в таком случае вам, вероятно, следует использовать itertools.islice ().

robert king 13.02.2012 07:53

Какое значение имеет islice? И почему вы должны быть вынуждены использовать yield во внутренней функции, которая ничего не дает (или не создает и не возвращает)? И даже если вы имеете в виду другой случай, когда цикл является дает результат, вы все равно можете преобразовать его в функцию; просто используйте yield from (3.3+), цикл for вокруг yield (3.2-) или return в качестве итератора генератора вместо yield, использующего его (либо), в зависимости от ситуации.

abarnert 22.03.2013 03:08

Обычно возможно преобразовать внутренний цикл в собственный метод, который возвращает true для продолжения, false для разрыва внешнего цикла. while condition1: / если не MyLoop2 (params): break. Альтернативой является установка логического флага, который проверяется на обоих уровнях. more = True / while condition1 и более: / while condition2 и более: / if stopCondition: more = False / break / ...

ToolmakerSteve 22.11.2013 23:44

Я согласен с тем, что стремление использовать return - правильный подход. Причина в том, что, согласно Дзен Python, «плоский лучше, чем вложенный». У нас есть три уровня вложенности, и если это начинает мешать, пора уменьшить вложенность или, по крайней мере, выделить всю вложенность в отдельную функцию.

Lutz Prechelt 03.04.2014 14:14

В целом использование return - это хорошая идея, но я обнаружил, что понимание списка тоже может помочь.

VladFr 24.05.2015 13:33

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

Jamie Marshall 22.07.2018 03:22

@robertking, ты когда-нибудь видел ответ абарнерта тебе? Я согласен с ним в том, что ваш комментарий кажется бессмысленным.

Mark Amery 23.04.2019 16:52

@JamieMarshall За исключением того, что Python прекрасно обходится без него. Если ваш код настолько сложен, что вам нужен «помеченный перерыв», то вы, вероятно, все равно делаете что-то не так. В любом случае вы всегда можете использовать raise для имитации маркированного break. Создайте новый экземпляр Exception для каждого другого label, используйте raise LabelException() вместо break label и оберните в try: ... except LabelException: код, который должен перейти к этой метке. Если ваш код настолько сложен, что вам нужны метки, добавляющие некоторые классы исключений, raise и except не будут такими уж плохими, код в любом случае уродлив.

Giacomo Alzetta 05.11.2019 20:28

но дело в том, что бы вы вернули, я имею в виду, что вы не можете просто вернуть случайную вещь?

KingWitherBrine 26.11.2020 22:10

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

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

это своего рода установка флага!

SIslam 23.10.2015 09:36

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

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.

Brian Peterson 08.05.2018 20:13

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

Хотя рефакторинг / return обычно является правильным решением, я видел немало случаев, когда простое краткое выражение «break 2» имело бы такой смысл. Кроме того, рефакторинг / return не работает так же для continue. В этих случаях за числовым прерыванием и продолжением будет легче следить и меньше загромождать, чем рефакторинг до крошечной функции, создание исключений или запутанная логика, включающая установку флага прерывания на каждом уровне вложения. Жаль, что Гвидо отверг это.

James Haigh 11.07.2013 22:29

break; break было бы неплохо.

PyRulez 18.07.2013 01:13

Я согласен с Гвидо. Дзен Python говорит: «Плоский лучше, чем вложенный». Редко бывают ситуации, когда невозможно избежать трех и более вложенных циклов.

Jeyekomon 05.04.2018 18:13

@Jeyekomon Проблема в том, что вам не нужно 3 или более вложенных циклов, чтобы это было проблемой. 2 вложенных цикла довольно распространены

Jon 07.04.2018 00:20

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

Jeyekomon 07.04.2018 21:06

«Код, настолько сложный, чтобы требовать эту функцию, встречается очень редко». Но если вы когда-нибудь будете использовать такой сложный код, отсутствие помеченных циклов сделает его еще более сложным, поскольку вам придется вручную пересылать break через все циклы. Глупый.

BallpointBen 14.04.2018 01:57

@ Джеймс Хей Я не думаю, что это позор. Python и без него уже достаточно загроможден. Надеюсь, он никогда не станет таким загроможденным, как C++. Все, для чего вы могли бы использовать многоуровневый разрыв, может быть достигнуто с помощью конструкций for; else или if; else.

Bachsau 18.04.2018 21:54

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

Mark Amery 22.04.2019 13:31

@MarkAmery, да, в этом есть смысл. Я стал на 6 лет мудрее. ;)

PyRulez 22.04.2019 16:56

Судя по всему, я могу редактировать пост только 5 минут (было 6). Итак, вот мой отредактированный пост: Мои 2 цента: Perl помечен как break (но называет его «последним») и «следующим», чтобы перейти непосредственно к следующей итерации. Совсем не редкость - постоянно пользуюсь. Я новичок в Python и уже нуждаюсь в нем. Кроме того, пронумерованные разрывы были бы ужасны для рефакторинга - лучше пометить цикл, из которого вы хотите выйти, а затем использовать break <label>, чтобы явно указать, из какого цикла вы хотите выйти.

John Deighan 26.05.2019 22:00

Преобразуйте логику цикла в итератор, который выдает переменные цикла и возвращает по завершении - вот простой пример, который размещает изображения в строках / столбцах до тех пор, пока у нас не закончатся изображения или места для их размещения:

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 Почему бы и нет? Первый разрыв выйдет из внутреннего цикла.

Navin 26.11.2014 20:42

@eugeney Мне кажется, что я что-то здесь упускаю. Можете опубликовать пример?

Navin 26.11.2014 23:35

@Mingliang, который можно уйти, прежде чем продолжить.

Baldrickk 06.07.2017 11:55

@RishitBansal Хотя это глубокий разрез: внешний цикл имеет значение, потому что условие внутреннего разрыва something(a, b) также зависит от a. Внешний цикл может работать, пока something(a, b) не является True.

clemisch 18.05.2018 11:05

Получил это из видео Раймонда Хеттингера, youtu.be/OSGv2VnC0go?t=971, прочтите операторы else, прикрепленные к циклам for как no_break, тогда это станет легче понять.

Ambareesh 29.10.2019 07:45

@HongCheng Да, это так.

AniMir 26.02.2020 15:36

Это умно. :-) Однако не однозначно. Честно говоря, меня не убеждают аргументы в пользу того, чтобы в Python помечены как break или break (n). Обходные пути добавляют сложности.

rfportilla 02.04.2020 00:11

Это очень эффективно и действенно. Решает мой вопрос без недочетов!

Owen Wijaya 21.09.2020 11:28

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

Abdelmonem Mahmoud Amer 09.01.2021 14:30

Я склонен согласиться с тем, что рефакторинг в функцию обычно является лучшим подходом для такого рода ситуаций, но когда вам В самом деле нужно вырваться из вложенных циклов, вот интересный вариант подхода к созданию исключений, который описал @ 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   # <--- !

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

На мой взгляд, самое простое и легкое для чтения решение. Спасибо, что поделился!

sampleuser 02.09.2020 11:54

Просто, но блестяще, спасибо!

Denny Seccon 17.02.2021 01:17

Похож на предыдущий, но более компактный. (Логические значения - это просто числа)

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 цикла.

Eric 30.12.2012 20:01

Я приехал сюда, потому что у меня был внешний цикл и внутренний цикл:

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), вероятно, не будет делать то, что вы хотите.

Veky 10.08.2016 20:07

Неплохо подмечено. Я не могу найти код, на который ссылался. Для всех, кто читает это, array.pop (i) «Удаляет элемент с индексом i из массива и возвращает его». согласно документации Python. Поэтому нужно получить индекс элемента x в массиве, чтобы этот код работал должным образом. Также существует функция array.remove (x), которая будет делать то, что ожидается. Я изменю свой ответ выше, чтобы исправить эту ошибку. Это предполагает, что второй массив не содержит дубликатов, поскольку array.remove (x) удалит только первый найденный экземпляр x.

Nathan Garabedian 09.04.2017 19:38

Хорошо, тогда я понял. В этом случае простое использование break вместо continue будет делать то, что вы хотите, не так ли? :-)

Veky 10.04.2017 13:22

Да, для эффективности и ясности вы, вероятно, захотите использовать в этих примерах break вместо continue. :)

Nathan Garabedian 03.07.2017 18:35

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

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.

Matthew Scharley 07.11.2012 03:23

эй, это работает! Я думал в двух условиях True пропустить две петли, но достаточно одного.

Mauro 07.11.2012 13:59

@MatthewScharley Я думаю, это показывает, что это работает во вложенных циклах.

handle 20.11.2014 19:07

@ MauroAspé, это не совсем то, что запрашивает OP. он по-прежнему будет выполнять весь внешний цикл, но цель состоит в том, что, если вы сломаете остальную часть кода, не будет выполняться

yamm 27.05.2015 10:55

@yamm Может ли это быть решено с помощью if not dejaVu: break внизу и, таким образом, выхода из основного цикла? Думаю, решение наиболее близко к тому, о чем просили. +1

milcak 18.09.2015 19:31

Попробуйте использовать бесконечный генератор.

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 05.10.2016 16:39

@Dakkaron Вы уверены, что правильно поняли код? Код действительно решает вопрос OP и ломается так же, как request. Однако он не прерывает несколько циклов, а использует предложение else, чтобы заменить необходимость удвоения разрыва.

holroy 05.10.2016 16:46

Насколько я понимаю, вопрос был How to break out of multiple loops in Python? и должен был быть ответ: «Не работает, попробуйте что-нибудь еще». Я знаю, что он точно исправляет данный пример OP, но не отвечает на их вопрос.

Dakkaron 05.10.2016 17:02

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

holroy 05.10.2016 17:13

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

добавлена ​​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), а не как понимание.

Veky 10.04.2017 13:23

Да, спикер на PyCon утверждает здесь, что даже принятый ответ @ RobertRossney не является истинно Pythonic, но генератор - это правильный способ разорвать несколько циклов. (Рекомендую посмотреть видео целиком!)

Post169 20.03.2018 00:55

Вы можете определить переменную (например, 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 с точки зрения производительности.

Anatoly Alekseev 12.12.2018 17:36

Я хотел бы напомнить вам, что функции в 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 для выхода из вложенных циклов.

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

mgjk 17.09.2019 00:11

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

Ekrem Dinçel 23.01.2020 21:52

Лучшее и простое решение для меня

Alexandre Huat 21.02.2020 14:00

Я решаю эту проблему, определяя переменную, на которую ссылаются, чтобы определить, перейдете ли вы на следующий уровень или нет. В этом примере эта переменная называется 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, по крайней мере, превратит это из ужасного в просто непривлекательное.

tripleee 19.02.2019 08:28

Используя функцию:

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:

  1. Set a flag which is checked by the outer loop, or set the outer loops condition.

  2. Put the loop in a function and use return to break out of all the loops at once.

  3. 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

Другие вопросы по теме