Вложенные циклы в Python: цикл в цикле
Цикл внутри цикла: дни и дела, план зала кинотеатра, таблица умножения и пары «каждый с каждым» — как читается вложенность и во сколько раз она умножает число шагов.
Редакция Питоники
Один цикл повторяет действие для каждого элемента списка. А если само это действие — тоже перебор? Достаём из расписания день и на каждый день перечисляем все дела; обходим зал кинотеатра и в каждом ряду проверяем каждое место. Так рождается вложенный цикл: цикл внутри цикла. Разбираемся, как он читается, где полезен и во сколько раз успевает подрасти число шагов.
Первый вложенный цикл: дни и дела
Внешний цикл берёт дни по одному, внутренний — дела. Читается слева направо: для каждого дня — для каждого дела — сделай печать:
days = ['сб', 'вс']
chores = ['пыль', 'полы']
for day in days:
for chore in chores:
print(day, '-', chore)
сб - пыль сб - полы вс - пыль вс - полы
Разбираем по шагам. Первый день сб: внутренний цикл пробегает список chores целиком — печатаются сб - пыль и сб - полы. Затем внешний цикл делает второй шаг, и внутренний проходит свой список заново с самого начала: вс - пыль, вс - полы. Внутренний цикл на каждой итерации внешнего пробегает все свои шаги целиком — как минуты внутри часа.
Обход вложенного списка: план зала
Списки бывают вложенными: зал кинотеатра — это список рядов, а каждый ряд — список мест. Обойти такую структуру без вложенности не получится: внешний цикл достаёт ряды, внутренний — места в каждом ряду.
hall = [
[1, 0, 1],
[0, 0, 1],
[1, 1, 0],
]
for row in hall:
for seat in row:
if seat == 1:
print('X', end='')
else:
print('O', end='')
print()
XOX OOX XXO
Разбираем. for row in hall достаёт ряды — и каждый ряд сам список. Внутренний for seat in row проходит места этого ряда. Печать настроена хитро: end='' печатает символ без пробела и без перевода строки, а пустой print() после внутреннего цикла переводит строку — и план зала рисуется ряд за рядом.
Таблица умножения: два range
Классика вложенных циклов — таблица умножения. Оба счётчика управляются через range: внешний перебирает первый множитель, внутренний — второй.
for i in range(1, 4):
for j in range(1, 4):
print(i, 'x', j, '=', i * j)
1 x 1 = 1 1 x 2 = 2 1 x 3 = 3 2 x 1 = 2 2 x 2 = 4 2 x 3 = 6 3 x 1 = 3 3 x 2 = 6 3 x 3 = 9
Девять строк — три на три. Поменяйте range(1, 4) на range(1, 11) — получите полную таблицу на сто строк, а сам код не вырастет ни на строчку. Полная таблица с ровными колонками и форматированием ждёт в уроке про FizzBuzz и таблицу умножения.
Вложенность с аккумулятором
Вложенные циклы чаще всего нужны не ради печати, а ради подсчёта. Разберём на таблице с продажами: внешний цикл идёт по магазинам, внутренний — по дням, а аккумулятор копит общую сумму. Внутренний цикл при этом каждый раз начинается заново — так и должно быть.
data = [[1, 2], [3, 4], [5, 6]]
total = 0
for row in data:
for x in row:
total = total + x
print(total)
21
21 — это сумма всех шести чисел. Обратите внимание, где стоит накопление: внутри внутреннего цикла, потому что считать нужно каждый элемент. Если счётчик поставить во внешний цикл, он посчитает не элементы, а строки.
Пары элементов: кто с кем
Второе классическое применение — пары. Гости на фотографии: каждый снимается с каждым. Здесь оба цикла идут по одному и тому же списку.
guests = ['Аня', 'Борис', 'Вера']
for a in guests:
for b in guests:
if a != b:
print('Пара:', a, 'и', b)
Пара: Аня и Борис Пара: Аня и Вера Пара: Борис и Аня Пара: Борис и Вера Пара: Вера и Аня Пара: Вера и Борис
Условие if a != b отсекает пары с самим собой. Три гостя дали шесть пар: каждый с каждым, в обе стороны.
Вложенность склеивает и разные списки: логины и пароли, размеры и цвета — все комбинации двух множеств. Так тестируют пары «логин — пароль»:
logins = ['аня', 'boris']
passwords = ['123', 'qwerty']
for login in logins:
for password in passwords:
print(login, '/', password)
аня / 123 аня / qwerty boris / 123 boris / qwerty
Четыре строки — снова произведение длин: 2 × 2. Внешний список даёт первое слово пары, внутренний — второе.
А если порядок не важен — Аня с Борисом и Борис с Аней это одна и та же пара? Внутренний цикл тогда запускают не с начала списка, а с позиции после внешнего счётчика. Помогает range с началом: урок второй показывал, что range умеет стартовать не с нуля.
numbers = [3, 7, 4, 6, 1]
n = len(numbers)
for a in range(n):
for b in range(a + 1, n):
if numbers[a] + numbers[b] == 10:
print(numbers[a], '+', numbers[b])
3 + 7 4 + 6
Внутренний range(a + 1, n) начинается на позицию после внешнего счётчика — пары не дублируются и элементы не сравниваются сами с собой. Из пяти чисел нашлись две пары с суммой десять: три с семью и четыре с шестью.
Считаем комбинации
Вложенный цикл удобен там, где нужно перебрать все сочетания двух наборов. Каталог одежды с двумя размерами и двумя цветами даёт четыре варианта — и посчитать их вручную скучно, а вложенный цикл делает это за пару строк.
sizes = ['S', 'M']
colors = ['red', 'blue']
count = 0
for s in sizes:
for c in colors:
count = count + 1
print('вариантов:', count)
вариантов: 4
Счётчик стоит во внутреннем цикле — он растёт на каждой паре. Если бы он стоял во внешнем, получилось бы два: по одному на размер. Число комбинаций равно произведению длин наборов, и это же число — количество шагов вложенного цикла.
Сколько шагов стоит вложенность
У вложенности есть цена. Посчитаем шаги внутреннего цикла для разных размеров списка:
for n in [3, 5, 10]:
steps = 0
for i in range(n):
for j in range(n):
steps += 1
print(n, '->', steps)
3 -> 9 5 -> 25 10 -> 100
Шагов ровно n × n: при трёх — девять, при десяти — сто. Вложенные циклы умножают работу: список стал вдвое длиннее — работы стало вчетверо больше. На маленьких списках это незаметно, на тысячах элементов — уже повод искать инструмент попроще. А вот как выглядит три уровня вложенности:
for hour in range(24):
for minute in range(60):
for second in range(60):
print(hour, minute, second)
Сутки — это 24 × 60 × 60 итераций. Глубина в три и больше уровней — сигнал пересмотреть решение: часто внутренний перебор заменяется готовым инструментом — словарём, срезом или встроенной функцией.
Как выйти из вложенного цикла целиком
Оператор break выходит только из ближайшего цикла — из вложенного это следует автоматически, но не всегда получается то, что нужно. Задача «найти число в таблице и остановиться» требует выйти сразу из обоих уровней, а один break выйдет только из внутреннего.
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
target = 5
found = False
for row in matrix:
for value in row:
if value == target:
found = True
break
if found:
break
print('нашли' if found else 'не нашли')
нашли
Схема простая: внутренний цикл находит значение и выходит своим break, а внешний после каждого полного прохода проверяет флаг found и, если он поднят, выходит тоже своим break. Без второй проверки внешний цикл дошёл бы до конца таблицы. Если вложенных уровней много, такой флаг быстро становится громоздким — тогда удобнее вынести поиск в отдельную функцию и выйти из неё через return.
Таблица внутри списка
Соберём вложенный цикл в список — так получают таблицы. Внешний цикл создаёт строку, внутренний наполняет её, а готовые строки складываются в общий список.
table = []
for i in range(1, 4):
row = []
for j in range(1, 4):
row.append(i * j)
table.append(row)
for row in table:
print(row)
[1, 2, 3] [2, 4, 6] [3, 6, 9]
Каждая строка — отдельный список, и в конце они собираются в таблицу. Такой код полезен, когда результат вложенного цикла нужен потом: с ним можно работать как с обычным списком списков, индексировать и печатать. Кстати, ровно так устроены вложенные списки из урока о переборе — просто там они заданы вручную, а здесь собраны циклом.
| Задача | Как выглядит вложенный цикл |
|---|---|
| Таблица: строки и колонки | внешний — по строкам, внутренний — по колонкам |
| Пары «каждый с каждым» | оба цикла по одному списку, if отсекает совпадения |
| Вложенные списки | внешний достаёт подсписки, внутренний — их элементы |
| Комбинации без повторов | внутренний range стартует после внешнего: range(a + 1, n) |
Сначала предскажи ответ в голове — это главный навык программиста.
letters = ['а', 'б']
for a in letters:
print(a.upper())
for n in [1, 2]:
print(a, n)
data = [[1, 2], [3, 4]]
total = 0
for row in data:
for x in row:
total += x
print(total)
words = ['кот', 'пёс']
for w1 in words:
for w2 in words:
if w1 != w2:
print(w1, w2)
1. Сколько строк напечатает цикл: for day in ['сб', 'вс']: for chore in ['пыль', 'полы']: print(day, chore)?
2. Как читается вложенный цикл?
3. Сколько шагов сделает внутренний цикл, если внешний идёт по range(3), а внутренний по range(4)?
4. Какой внутренний range нужен, чтобы перебрать пары без повторов при внешнем for a in range(n)?
5. Что делает пустой print() после внутреннего цикла при обходе зала кинотеатра?
6. Сколько пар даст вложенный цикл по двум спискам длиной 3 и 5?
7. Почему для выхода из вложенного цикла нужен флаг?
8. Где стоит счётчик, если нужно посчитать все пары двух списков?
По спискам days = ['сб', 'вс'] и chores = ['зарядка', 'прогулка'] выведите вложенным циклом все пары строкой вида «сб - зарядка» (через ' - '), затем выведите количество пар: счётчик count обнулите до циклов и увеличивайте на 1 внутри внутреннего.
Что такое вложенный цикл в Python простыми словами?
Это цикл, написанный внутри тела другого цикла. Внешний делает один шаг, внутренний за это время проходит весь свой путь. Так печатают таблицы, обходят списки списков и перебирают пары элементов.
Урок 1: первый цикл forСколько раз выполнится тело внутреннего цикла?
Ровно произведение длин: внешний список из n элементов умножается на внутренний из m — получится n × m итераций. Три дня и четыре дела — двенадцать строк вывода. Отсюда и цена вложенности: рост любого списка умножает общую работу.
Как не запутаться в отступах во вложенных циклах?
Каждый уровень цикла — четыре пробела: тело внешнего с одним отступом, тело внутреннего с двумя. Если второй for стоит на том же уровне, что первый, вложенности нет — это два соседних цикла. Проверяйте структуру глазами: заголовок for и его тело всегда сдвинуты на один уровень.
Чем заменить глубокую вложенность в три цикла и больше?
Часто помогают встроенные проверки any и all вместо перебора, генераторы списков, словари для поиска по ключу и срезы. Правило: два уровня — рабочий инструмент, три и больше — повод упростить задачу готовым средством.
Урок 15: any и allКак найти элемент в двумерном списке и сразу прекратить перебор?
Внутренний цикл находит элемент и выходит через break, внешний — проверяет флаг и тоже выходит: if found: break. Второй break обязателен, иначе внешний цикл продолжит перебор до конца таблицы.
Понравился урок? Сошлитесь на него
«Внутренний цикл на каждой итерации внешнего пробегает все свои шаги целиком — как минуты внутри часа.»
Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.
Что дальше
Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.
Что читать дальше
Циклы · Урок 2
range в Python: цикл со счётчиком
range делает цикл for счётчиком: n повторов от нуля, диапазон от начала до конца, шаг и обратный отсчёт. Разбираем все три формы range на примерах с запуском.
Циклы · Урок 6
Оператор break в Python: выход из цикла
break выходит из цикла немедленно: нашли нужное — перебирать остальное незачем. Разбираем break в for и while, поиск первого совпадения и мёртвый код после выхода.
Циклы · Урок 19
Классика на циклах: FizzBuzz и таблица умножения
FizzBuzz и таблица умножения — два эталонных упражнения: на них проверяют циклы, остаток от деления, порядок условий и форматирование вывода.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
json · Урок 7
Вложенные структуры: списки в словарях и наоборот
Настоящие данные не плоские: список товаров внутри заказа, массив объектов в ответе API. Учимся читать вложенный JSON сверху вниз и не теряться в глубине.
вложенный json pythonвложенные словари python
json · Урок 10
Обход данных: циклы по JSON-спискам
for по списку словарей, извлечение полей, суммирование выручки и list comprehension — превращаем выгрузку заказов в отчёт.
python цикл по jsonподсчёт элементов json python
Циклы · Урок 14
Ошибки в циклах Python: от отступа до зависания
Ошибка в цикле повторяется на каждой итерации. Три полки цикловых ошибок — падение, молчаливая логика, зависание — и быстрый способ найти каждую.
ошибки циклов pythonindentationerror python цикл