2025, Nov 06 09:01
Как избежать сплющивания списков в Python и сохранить группировку
Разбираем типичную ошибку в Python: сплющивание при объединении списков. Покажем, как сохранить список списков для вложенных циклов, с примерами и пояснениями.
Если объединять несколько списков в Python неправильным способом, можно ненароком «сплющить» данные и потерять структуру, от которой зависят ваши циклы. Типичный симптом — вложенный цикл печатает метку группы только один раз, после чего выводит все элементы одним сплошным блоком. Исправление касается не самих циклов — оно про форму данных, по которым вы итерируетесь.
Проблема
Вы хотите объединить несколько проходов, а затем пройтись по ним двумя вложенными циклами for, печатая номер прохода перед каждой группой товаров. Пример ниже показывает проблему: печатается только один заголовок прохода, а затем — все элементы одной длинной последовательностью.
# В каждом проходе лежат разные товары
lane_a = ["Tomatoes", "Carrots", "Lettuce"]
lane_b = ["Eggs", "Butter", "Cream"]
lane_c = ["Pasta", "Rice", "Cereal"]
# объединяем все списки
lanes = [lane_a + lane_b + lane_c]
section_no = 1
# итерируемся по спискам в lanes
for section in lanes:
print("Aisle: ", section_no) # печатаем номер прохода перед каждым подсписком
# выводим каждый элемент каждого подсписка
for element in section:
print(f"Contains {element}")
section_no += 1
Что здесь не так
Суть проблемы — в способе объединения списков. Оператор + внутри квадратных скобок создает один общий, склеенный список, завернутый в еще один список. Иными словами, получается единственный внешний элемент, в котором лежат все позиции. Внешний цикл выполняется ровно один раз, поэтому вы видите лишь один заголовок прохода. Внутренний цикл затем проходит по всем товарам без границ между проходами.
Решение
Вместо конкатенации сохраните настоящую вложенную структуру: поместите отдельные списки в контейнер как самостоятельные элементы. Так группировка сохраняется, и вложенные циклы смогут печатать заголовок для каждого подсписка.
# В каждом проходе лежат разные товары
lane_a = ["Tomatoes", "Carrots", "Lettuce"]
lane_b = ["Eggs", "Butter", "Cream"]
lane_c = ["Pasta", "Rice", "Cereal"]
# формируем список списков (сохраняем группировку)
lanes = [lane_a, lane_b, lane_c]
section_no = 1
# для каждого подсписка печатаем заголовок, затем его элементы
for section in lanes:
print("Aisle: ", section_no)
for element in section:
print(f"Contains {element}")
section_no += 1
Почему это важно
Стратегия итерации следует за формой данных. Если вам нужен сгруппированный вывод с вложенными циклам, данные должны оставаться вложенными. Конкатенация «сплющивает» списки и стирает границы, что напрямую влияет на логику выполнения и результат. Сохраняя список списков, вы оставляете группировку явной, делаете внешний цикл осмысленным и избегаете тонких ошибок, когда метка появляется лишь один раз.
Выводы
Прежде чем писать или отлаживать циклы, убедитесь в структуре, по которой вы итерируетесь. Если ожидается несколько групп, проверьте, что у вас список списков, а не один «сплющенный» список. Когда группировка верна, вложенные циклы естественно дают нужный результат. Если хотите обойтись без ручных счетчиков, enumerate аккуратно предоставит индексы, но ключевое — сохранить данные вложенными.