Все мы знаем, что Python — довольно элегантный язык программирования, но, увы, у всех есть свои слабые стороны. Иногда Python выглядит не так аккуратно, как ему следовало бы. К примеру, нам нужно выйти из вложенного цикла как на примере ниже:
for a in list_a:
for b in list_b:
if condition(a,b):
break
Ключевое слово break
позволяет выйти только из внутреннего цикла. Но можно ли напрямую сразу выйти из двух вложенных циклов? Есть ли для этого какие-нибудь ключевые слова или уловки? К сожалению, в Python нет встроенных средств для выполнения нужной операции.
Конечно, сравнивать языки друг с другом не стоит, но вот в PHP такое действие довольно просто можно провернуть:
foreach ($a_list as $a)
{
foreach ($b_list as $b)
{
if (condition($a, $b))
{
break 2; //break out of 2 loops
}
}
}
В PHP ключевое слово break
принимает число в качестве аргумента, которое определяет, из скольки вложенных циклов нужно выйти. Значением по умолчанию установлено 1, что как раз позволяет выйти из крайнего цикла. Это довольно лаконичное и разумное решение, PHP выглядит привлекательнее с точки зрения написания кода.
Правда, это не значит, что всем пора бежать изучать PHP. Python все-таки очень гибкий язык, так что того же результата можно достичь многими другими способами.
В статье представлено 5 способов выхода из вложенного цикла в Python. В конце статьи также указано, как можно избежать проблемы вложенности в циклах.
1. Переменная-флаг
Первое решение довольно простое и эффективное. Чтобы выйти из цикла, надо объявить переменную и использовать ее как flag
. Рассмотрим решение на примере ниже:
# добавьте переменную-флаг
break_out_flag = False
for i in range(5):
for j in range(5):
if j == 2 and i == 0:
break_out_flag = True
break
if break_out_flag:
break
Переменная break_out_flag
выступает в качестве пометки, чтобы показать программе, когда следует выходить из внешнего цикла. Работает, но выглядит не очень красиво, так как приходится создавать новую переменную только ради решения такой маленькой проблемы. Можно обойтись и без этого. Рассмотрим другие варианты.
2. Вызов исключения
Если мы не можем использовать ключевое слово break
как положено, почему бы тогда не выразить его действие другим способом? В Python существует возможность обработки исключений, поэтому из вложенного цикла можно выйти таким образом:
# вызовите исключение
try:
for i in range(5):
for j in range(5):
if j == 2 and i == 0:
raise StopIteration
except StopIteration:
pass
Код выше показывает, что break
можно выразить как исключение и таким образом выйти из цикла.
3. Повторная проверка условия
Поскольку выполнение одного условия приводит к прерыванию цикла, проверка того же условия также кажется приемлемым решением. Вот как это выглядит:
# проверьте то же условие второй раз
for i in range(5):
for j in range(5):
if j == 2 and i == 0:
break
if j == 2 and i == 0:
break
Этот способ выполняет свою задачу, но выглядит он не очень эффективно, ведь проверять одно и то же условие много раз займет много времени и ресурсов.
4. Конструкция for-else
В Python есть особая конструкция for-else
. Она не очень популярна, а некоторые совсем о ней не знают. Обычно, если пишут else
, то только после if
. А вот когда дело доходит до выхода из вложенных циклов, то такой необычный синтаксис может помочь в решении задачи:
# используйте конструкцию For-Else
for i in range(5):
for j in range(5):
if j == 2 and i == 0:
break
else: # можно вызывать, только если во внутреннем цикле нет break
continue
break
Этот метод берет все лучшее от синтаксиса for-else
, так как код в блоке else
сработает только в том случае, если внутренний цикл закончится без выхода. Если вы еще не знакомы с конструкцией for-else
, то ознакомьтесь с кодом ниже — это конструкция, которая объясняет поведение конструкции:
# то же самое, что и конструкция for-else
for i in range(5):
for j in range(5):
if j == 2 and i == 0:
break
if not (j == 2 and i == 0):
continue
break
Если вкратце, то этот способ работает, но требует использования необычного синтаксиса.
5. Дополнительная функция
Если поместить вложенный цикл в функцию, то проблема выхода из цикла решается довольно просто, ведь вместо break
можно использовать уже return
.
# напишите для этого функцию
def check_sth():
for i in range(5):
for j in range(5):
if j == 2 and i == 0:
return
check_sth() # запустите функцию, где нужно
Это решение выглядит более понятно, тут нет переменных-флагов, нет try-except
, for-else
и ненужных проверок условий. Функции в Python настраиваются очень гибко, так что если вложенный цикл будет присутствовать по одному разу в каждой функции, то можно вызвать ее сразу во внешней функции:
def out_func():
# какой-то код
def check_sth():
for i in range(5):
for j in range(5):
if j == 2 and i == 0:
return
# какой-то код
check_sth() # запуск функции
# какой-то код
Правда, создание вложенной функции для двух циклов тоже выглядит не особо разумно.
Cовет: избегайте вложенных циклов
Но на деле, если не существует действительно удобных решений для выхода из вложенных циклов, тогда почему бы не постараться избегать их? Применяя вспомогательную функцию, можно избежать вложенных циклов:
# как избежать вложенных циклов
import itertools
for i, j in itertools.product(range(5), range(5)):
if j == 2 and i == 0:
break
Этот пример показывает, как с помощью функции itertools.product()
можно обойтись без вложенности. Это простой способ вычисления декартова произведения на основе входных итераций. К несчастью, этим методом не получится избежать любых вложенных циклов. К примеру, он не сработает, если вам необходимо будет обработать бесконечные потоки данных в циклах.
Перед тем как функция itertools.product()
начнет свою работу, ей необходимо получить на вход все переменные, сохраняя в памяти массивы значений для генерации результата. Таким образом, функция полезна только при конечном количестве входных данных. И всё же лучше следовать совету не прибегать к вложенным циклам, ведь это улучшает читаемость кода.
Заключение
У нас есть как минимум 5 возможных путей выхода из вложенных циклов в Python. Ни один из них не является настолько же удобным, как в PHP, но, по крайней мере, они все же существуют. К счастью, нам не обязательно использовать вложенные циклы, так как мы можем преобразовать их в простые циклы благодаря функции itertools.product()
.
Читайте также:
- Python для начинающих: all, any, zip, enumerate, filter, map
- Жажда скорости: Python с расширениями С
- Асинхронная многопоточность в Python
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Yang Zhou: 5 Ways To Break Out of Nested Loops in Python