Модуль 3 · Списки и циклы

Справочник по Python · модуль 3 · списки и циклы

Глава 3.7

3.7

Вложенные циклы
(nested loops)

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

Категория
Циклы и двумерные данные
Уровень
Начальный
Связанные темы
for, range(), вложенные списки
Зачем это знать
Обрабатывать строки таблиц и сочетания значений

§1Общая схема

Pythonдва уровня повторения
for outer in outer_values:
    for inner in inner_values:
        action

Внутренний for находится в теле внешнего. Поэтому каждый новый outer запускает весь перебор inner сначала.

§2Пошаговое выполнение

main.pyвсе пары от 0 до 2
for row in range(3):
    for column in range(3):
        print(row, column)
Выводдевять сочетаний
0 0
0 1
0 2
1 0
1 1
1 2
2 0
2 1
2 2

При row = 0 внутренний цикл получает 0, 1, 2. Затем row становится равен 1, и внутренний диапазон снова начинается с нуля.

§3Количество повторений

Если внешний цикл делает N повторений, а внутренний — M для каждого внешнего, самое глубокое действие выполняется N × M раз.

Схемапроизведение размеров
3 внешних × 3 внутренних = 9 вызовов print()
4 внешних × 5 внутренних = 20 действий

§4Таблица умножения

main.pyтаблица 5 × 5
for row in range(1, 6):
    for column in range(1, 6):
        print(row * column, end=" ")
    print()
Выводновая строка после внутреннего цикла
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
5 10 15 20 25

print() без аргументов выровнен с внутренним циклом, поэтому выполняется после каждой готовой строки.

§5Матрица

Матрицу можно представить списком строк. Внешний цикл получает строку, внутренний — элементы этой строки.

main.pyобход двумерного списка
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for row in matrix:
    for element in row:
        print(element)

§6Поиск совпадений

Вложенные циклы позволяют сравнить каждый элемент первого списка с каждым элементом второго.

main.pyобщие значения
first = [1, 2, 3]
second = [2, 3, 4]
for left in first:
    for right in second:
        if left == right:
            print("Совпадение:", left)
Выводдва совпадения
Совпадение: 2
Совпадение: 3

§7break во вложенном цикле

break завершает только ближайший цикл — тот, внутри которого написан.

main.pyвнутренний цикл останавливается на 2
for outer in range(3):
    for inner in range(5):
        if inner == 2:
            break
        print(outer, inner)
Выводвнешний цикл продолжился
0 0
0 1
1 0
1 1
2 0
2 1

§8Как выйти сразу из обоих

Раз break завершает только ближайший цикл, возникает вопрос: как остановить перебор целиком, если нужное найдено? Готовой конструкции для этого в Python нет — есть два приёма.

Первый — флаг. Результат сохраняют в переменную и после внутреннего цикла проверяют её.

main.pyпоиск значения в таблице
matrix = [[1, 2], [3, 4], [5, 6]]
found = None

for row in matrix:
    for value in row:
        if value == 4:
            found = value
            break
    if found is not None:
        break

print(found)
Выводперебор остановлен на третьей строке
4

Второй break стоит уже в теле внешнего цикла и потому завершает его. Обратите внимание на отступ: он на два уровня меньше первого.

Второй приём — вынести перебор в функцию и выйти через return. Он завершает функцию целиком, а значит и оба цикла сразу.

main.pyта же задача без флага
def find(matrix, target):
    for row in matrix:
        for value in row:
            if value == target:
                return value
    return None

print(find([[1, 2], [3, 4]], 4))
print(find([[1, 2], [3, 4]], 99))
Выводнайдено и не найдено
4
None

Второй вариант читается лучше: нет ни флага, ни повторной проверки, а последний return None явно описывает случай «ничего не нашлось». Функции разбираются в главе 6.2.

§9continue во вложенном цикле

continue пропускает текущий шаг ближайшего цикла. Внешнее значение при этом не меняется.

main.pyвнутреннее значение 1 пропущено
for outer in range(3):
    for inner in range(3):
        if inner == 1:
            continue
        print(outer, inner)

§10Сложность

Два полных цикла по n элементов выполняют около n² сравнений. Для 100 элементов это 10 000, а для 10 000 — уже 100 000 000.

Сначала правильность, затем масштаб

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

§11Частые ошибки

Что написаноЧто произойдётКак правильно
внутренний for без отступаполучатся два независимых циклапоместить внутренний цикл в тело внешнего
ожидать, что внутренний цикл продолжится с прошлого значениярасчёт шагов будет невернымпомнить: он начинается заново для каждого внешнего значения
break внутри внутреннего циклавнешний цикл продолжит работуотдельно управлять выходом из обоих уровней
перепутать отступ итогового print()строки таблицы объединятся или будут разбиты неверновыровнять его после внутреннего цикла
полный двойной перебор больших списковчисло операций быстро растётоценить N × M до запуска

§12Проверьте себя

Сколько раз выполнится действие при диапазонах 4 и 5?

4 × 5 = 20 раз.

Какой цикл завершит break внутри внутреннего тела?

Только ближайший внутренний цикл.

Почему внутренний цикл запускается повторно?

Потому что его полный перебор является телом каждой итерации внешнего цикла.

§13Шпаргалка

Два уровняfor row in rows: for item in row:
Число действийN × M
breakзавершает ближайший цикл
continueпропускает шаг ближайшего

Итог главы

Для каждого значения внешнего цикла внутренний цикл проходит все свои значения. Глубокое действие выполняется N × M раз. break и continue управляют только ближайшим циклом.

Глава 3.7 · Вложенные циклы · Справочник по PythonАвтор: Макаров Максим Николаевич