Python: Продовження наступної ітерації у зовнішньому циклі


135

Мені хотілося знати, чи є якісь вбудовані способи продовжити наступну ітерацію у зовнішній цикл у python. Наприклад, розгляньте код:

for ii in range(200):
    for jj in range(200, 400):
        ...block0...
        if something:
            continue
    ...block1...

Я хочу, щоб це продовження заяви виходило з циклу jj і переходило до наступного елемента в циклі ii. Я можу реалізувати цю логіку якось іншим способом (встановивши змінну прапора), але чи є простий спосіб це зробити, або це як запит занадто багато?


11
Насправді існує робоча операція goto для Python: entrian.com/goto . Це було випущено як першотравневий жарт :-), але, мабуть, спрацює.
кодеп

3
О, будь ласка, не використовуйте цей готовий жарт! Це надзвичайно розумно, але пізніше вам буде сумно, якщо ви вкладете його у свій код.
Нед Батчелдер

Відповіді:


71
for i in ...:
    for j in ...:
        for k in ...:
            if something:
                # continue loop i

У загальному випадку, коли у вас є кілька рівнів циклічного циклу і breakце не працює для вас (тому що ви хочете продовжити одну з верхніх циклів, а не ту, що знаходиться над поточною), ви можете зробити одну з наступних

Рефакторинг циклів, з яких потрібно вийти з функції

def inner():
    for j in ...:
        for k in ...:
            if something:
                return


for i in ...:
    inner()

Недоліком є ​​те, що вам може знадобитися передати цій новій функції деякі змінні, які раніше були в області застосування. Ви можете просто передати їх як параметри, зробити їх екземплярами змінних на об’єкті (створити новий об’єкт саме для цієї функції, якщо це має сенс), або глобальні змінні, одинакові, як би там не було (ем, ем).

Або ви можете визначити inner як вкладену функцію і дозволити їй просто фіксувати те, що їй потрібно (може бути повільніше?)

for i in ...:
    def inner():
        for j in ...:
            for k in ...:
                if something:
                    return
    inner()

Використовуйте винятки

У філософському плані це винятки, що порушують потік програми через структуровані будівельні блоки програмування (якщо, за, поки), коли це необхідно.

Перевага полягає в тому, що вам не доведеться розбивати єдиний фрагмент коду на кілька частин. Це добре, якщо це якісь обчислення, які ви проектуєте, записуючи його в Python. Введення абстракцій в цей ранній момент може сповільнити вас.

Погано в такому підході те, що автори перекладача / упорядника зазвичай припускають, що винятки є винятковими та оптимізують їх відповідно.

class ContinueI(Exception):
    pass


continue_i = ContinueI()

for i in ...:
    try:
        for j in ...:
            for k in ...:
                if something:
                    raise continue_i
    except ContinueI:
        continue

Створіть для цього спеціальний клас винятків, щоб ви не ризикували випадково заглушити якийсь інший виняток.

Щось ще цілком

Я впевнений, що є ще інші рішення.


Не можу повірити, що я не думав перенести свій другий цикл на інший метод. Мені стає повільно
pmccallum

1
Для мене використання виключень - це хороший спосіб досягти цього. Я погоджуюся з @ user7610 - "по-філософськи це винятки".
Ренато Бирро

Стара хороша булева змінна та If заяви?
MrR

ОП шукає альтернативне рішення для цього: "Я можу реалізувати цю логіку інакше (встановивши змінну прапора) [...]"
user7610

149
for ii in range(200):
    for jj in range(200, 400):
        ...block0...
        if something:
            break
    else:
        ...block1...

Break розірве внутрішній цикл, і block1 не буде виконуватися (він запуститься, лише якщо внутрішній цикл вийде нормально).


1
Привіт, чи є такі варіанти, як це? Тому що я хочу зробити ще один цикл у block1, і мені подобається, що мій код заглибиться на 3 рівні. Дивна ситуація.
Сахас

3
Мені це здається, що ти намагаєшся зробити щось із циклів, до яких найкраще підійти по-іншому ...
Kimvais

Так. Тому я не використовував структуру for..else. Тепер мені все ще знадобиться цикл, але я буду використовувати змінні прапор для переадресації управління.
Сахас

3
for...elseчасто є корисною конструкцією, хоча це може заплутати. Просто пам’ятайте, що elseв цьому контексті означає «без перерви».
асмеурер

Здається, це обмежено лише двома шарами петель. Мені потрібно оновити код, який містить три вкладені петлі, і нова вимога клієнта означає, що за певних обставин найпотужніший цикл повинен продовжувати наступну ітерацію самого зовнішнього циклу. Я припускаю, що ваша пропозиція не стосується цього сценарію.
kasperd

42

Іншими мовами ви можете позначати цикл і відривати від міченого циклу. Пропозиція щодо покращення Python (PEP) 3136 запропонувала додати їх до Python, але Guido відхилив це :

Однак я відкидаю це, виходячи з того, що такий складний код вимагати цієї функції дуже рідко. У більшості випадків існують робочі кола, які створюють чистий код, наприклад, використовуючи "return". Хоча я впевнений, що існують деякі (рідкісні) реальні випадки, коли ясність коду буде страждати від рефакторингу, що дає можливість використовувати return, це компенсується двома проблемами:

  1. Складність додається мові, постійно. Це впливає не тільки на всі реалізації Python, але і на кожен інструмент аналізу джерела, а також, звичайно, всю документацію на мову.

  2. Моє сподівання, що функція буде зловживати більшою мірою, ніж вона буде використана правильно, що призведе до чистого зменшення чіткості коду (вимірюється всьому коді Python, написаному відтепер). Ледачі програмісти є скрізь, і перед тим, як ви це дізнаєтесь, у вас на руках неймовірний безлад з незрозумілим кодом.

Тож якщо це те, на що ви сподівалися, вам не пощастить, але подивіться на одну з інших відповідей, як там є хороші варіанти.


4
Цікаво. Я згоден з Гвідо тут. Хоча в деяких випадках було б добре, це було б зловживанням. Ще одна причина його нереалізації - це те, що зараз досить просто вперед переносити код порту між C і Python. Як тільки Python починає підбирати функції, яких відсутні інші мови, це стає складніше. Візьмемо для прикладу той факт, що у вас може бути інша заява на циклі for для Python ... це робить код менш портативним для інших мов.
eric.frederich

2
Всі вітайте Гуйдо наш BDFL
JnBrymn

4
Це скоріше червоне наслідування, ніж хороший контр-аргумент, але мені здається, що поведінка for-elseскладніша, складніше читати і, ймовірно, більше зловживає (якщо не відвертою помилкою), ніж іменовані петлі. Думаю, я використав би інше ключове слово, ніж else- можливо, щось подібне resumeбуло б добре? Ви breakв циклі, і resumeце відразу після нього?
ArtOfWarfare

5
Це мене сумує. Я не можу повірити, як я люблю і ненавиджу Python одночасно. Так красиво, але все-таки wtf.
jlh

5
@jlh Переважно wtf для мене. Іноді я думаю, що хочеться бути різним не для законних цілей, а просто бути іншим. Це хороший приклад тому. Мені трапляється потреба обривати зовнішні петлі досить часто.
Рікаел

14

Я думаю, ви могли б зробити щось подібне:

for ii in range(200):
    restart = False
    for jj in range(200, 400):
        ...block0...
        if something:
            restart = True
            break
    if restart:
        continue
    ...block1...

4
-1: ОП чітко заявляла, що вони знають, що можуть зробити щось подібне, і це просто схоже на грандіозну версію прийнятої відповіді (яка передувала вашим 8 місяців, тому не могло бути, що ви просто пропустили прийняте відповідь).
ArtOfWarfare

10
Прийнятий відповідь не зрозуміліше , якщо ви ніколи не бачили for, elseраніше (і я думаю , що навіть більшість людей , які не може згадати з верхньою частиною голови , як це працює).
asmeurer

3

Я думаю, що один з найпростіших способів досягти цього - це замінити "продовжити" на "перерву" на заяву, тобто

for ii in range(200):
 for jj in range(200, 400):
    ...block0...
    if something:
        break
 ...block1...       

Наприклад, ось простий код, щоб побачити, як саме він триває:

for i in range(10):
    print("doing outer loop")
    print("i=",i)
    for p in range(10):
        print("doing inner loop")
        print("p=",p)
        if p==3:
            print("breaking from inner loop")
            break
    print("doing some code in outer loop")

2

Ще один спосіб вирішення подібної проблеми - використання Exception ().

for ii in range(200):
    try:
        for jj in range(200, 400):
            ...block0...
            if something:
                raise Exception()
    except Exception:
        continue
    ...block1...

Наприклад:

for n in range(1,4):
    for m in range(1,4):
        print n,'-',m

результат:

    1-1
    1-2
    1-3
    2-1
    2-2
    2-3
    3-1
    3-2
    3-3

Припускаючи, що ми хочемо перейти до зовнішньої n петлі з m циклу, якщо m = 3:

for n in range(1,4):
    try:
        for m in range(1,4):
            if m == 3:
                raise Exception()            
            print n,'-',m
    except Exception:
        continue

результат:

    1-1
    1-2
    2-1
    2-2
    3-1
    3-2

Посилання: http://www.programming-idioms.org/idiom/42/continue-outer-loop/1264/python


1

Ми хочемо щось знайти, а потім зупинити внутрішню ітерацію. Я використовую систему прапорів.

for l in f:
    flag = True
    for e in r:
        if flag==False:continue
        if somecondition:
            do_something()
            flag=False

Я не знаю, чому було скасовано ваше рішення; хтось опублікував в основному те саме, що було отримано 10 разів
Локан

Мені не дуже пощастило з stackoverflow.
Естер

1
Можливо тому, що тут вже розміщено абсолютно те саме, що, мабуть, ... І False:continueсправа в ... незвичайному форматуванні. Як це часто трапляється в «природних» системах, де експоненція є нормою, вам доведеться лише пощастити кілька разів на SO, щоб набрати значну кількість балів репутації. У будь-якому випадку мої "найкращі" відповіді, як правило, найменш популярні.
користувач7610

0

Я просто щось подібне зробив. Моє рішення для цього полягала в тому, щоб замінити інтер'єр для циклу на розуміння списку.

for ii in range(200):
    done = any([op(ii, jj) for jj in range(200, 400)])
    ...block0...
    if done:
        continue
    ...block1...

де op - деякий логічний оператор, що діє на комбінації ii та jj. У моєму випадку, якщо будь-яка з операцій повернулася правдою, я був зроблений.

Це насправді не так вже й відрізняється від розбиття коду на функцію, але я подумав, що використання оператора "будь-який", щоб зробити логічний АБО у списку булевих і виконувати логіку все в одному рядку, було цікавим. Це також уникає виклику функції.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.