Как распрямить вложенный список в Python

TL;DR — коротко
Рассмотрены практичные способы превращения вложенного списка в «плоский» в Python: простой одинарный concat через sum(), явный цикл или list comprehension, а также более надёжные способы — itertools.chain и рекурсивная функция для глубокой вложенности. Важно учитывать типы элементов (строки, словари) и производительность.
Задача
Вложенный список — это список, содержащий другие итерируемые объекты (списки, кортежи, множества, словари и т. п.). Цель — получить единый список с элементами верхнего уровня, то есть «распрямить» структуру.
Ключевые варианты решения: суммирование списков, цикл / list comprehension, itertools.chain, рекурсивное разворачивание для произвольной глубины.
Подход 1 — sum() для списка списков
Подходит, когда у вас ровно список списков (list of list). Метод прост и читаем:
nested_list = [[1, 3, 4], [2, 4], [7, 9, 0]]
flat_list = sum(nested_list, [])
print(flat_list)
# Вывод: [1, 3, 4, 2, 4, 7, 9, 0]Важно: sum() выполняет конкатенацию списков и при большом объёме данных может быть медленнее из‑за многократных копирований. Он не работает для списков с кортежами, множествами или словарями, если вы ожидаете конкатенации разных типов.
Подход 2 — цикл for или list comprehension
Работает для последовательностей разного типа: list, tuple, set, а также для итерации по ключам словаря (обратите внимание, что при итерации по словарю по умолчанию возвращаются ключи).
Явный цикл:
nested_list = [[1, 3, 4], (1, 5, 6), {1, 2, 4}, {"e": 3, "o": 9, "t": 7}]
flat_list = []
for i in nested_list:
for k in i:
flat_list.append(k)
print(flat_list)
# Возможный вывод: [1, 3, 4, 1, 5, 6, 1, 2, 4, 'e', 'o', 't']И тот же результат через list comprehension:
flat_list = [k for i in nested_list for k in i]
print(flat_list)Совет: чтобы получить значения словаря, итерируйтесь по .values() или .items() в зависимости от нужд.
Подход 3 — itertools.chain для эффективной конкатенации
Если требуется объединить последовательности ровно на один уровень вложенности и вы заботитесь о производительности, используйте itertools.chain.from_iterable:
import itertools
nested_list = [[1, 3, 4], [2, 4], [7, 9, 0]]
flat_list = list(itertools.chain.from_iterable(nested_list))
print(flat_list)chain.from_iterable выполняет конкатенацию без лишних копирований и обычно быстрее, чем sum при больших входных данных.
Подход 4 — рекурсивная распаковка для произвольной глубины
Если вложенность может быть произвольной (списки внутри списков внутри списков и т. д.), используйте рекурсию или стек. При этом осторожно обходите строки и байты — их лучше не рассматривать как коллекции символов.
Простой рекурсивный генератор, который не распадается на символы строк:
from collections.abc import Iterable
def flatten(iterable):
for item in iterable:
if isinstance(item, (list, tuple, set)):
yield from flatten(item)
else:
yield item
nested = [1, [2, [3, 4], 'abc'], (5, 6)]
print(list(flatten(nested)))
# Вывод: [1, 2, 3, 4, 'abc', 5, 6]Если нужно безопасно обходить любые итерируемые, кроме строк и байтов:
def flatten_safe(iterable):
for item in iterable:
if isinstance(item, Iterable) and not isinstance(item, (str, bytes, bytearray)):
yield from flatten_safe(item)
else:
yield itemКогда методы дают неожиданный результат
- sum() поддерживает только конкатенацию списков; для других типов он приведёт к TypeError.
- Итерирование по словарю возвращает ключи; чтобы получить значения — используйте dict.values().
- Строки и байты являются итерируемыми: рекурсивная распаковка может разбить строку по символам, если этого не учитывать.
- Для очень больших структур рекурсивный подход может вызвать переполнение стека; используйте итеративный алгоритм или увеличьте лимит рекурсии с осторожностью.
Критерии приёмки (тесты)
- Вход: [[1,2],[3]] -> ожидаемый выход: [1,2,3]
- Вход: [1,(2,3),{4,5}] -> выход содержит все элементы из кортежа и множества (порядок множества не гарантирован)
- Вход с словарём: [{‘a’:1},{‘b’:2}] -> ожидаемое при итерации по ключам: [‘a’,’b’]; при использовании .values(): [1,2]
- Глубокая вложенность: [1,[2,[3,[4]]]] -> корректный плоский список [1,2,3,4]
Личные рекомендации и эвристики
- Если у вас всё — списки одного уровня: используйте itertools.chain.from_iterable для скорости и читаемости.
- Если данные смешанные или вложенность неизвестна — пишите явный рекурсивный генератор с исключением строк.
- Избегайте sum() для больших объёмов или смешанных типов.
Краткий глоссарий
- Плоский список — список без вложенных итерируемых контейнеров.
- chain.from_iterable — функция из itertools для эффективного «склеивания» последовательностей.
- Рекурсия — функция, вызывающая сама себя для обхода любой глубины структуры.
Итог
Распрямление вложенных списков в Python можно решить простыми приёмами (sum, цикл, list comprehension), а можно выбрать более универсальные и производительные варианты (itertools.chain, рекурсивный генератор). Выбор зависит от формата входа, объёма данных и требований к порядку элементов.
Важно: всегда тестируйте на граничных случаях — словари, строки, очень глубокая вложенность.
Похожие материалы
Как устроить идеальную вечеринку для просмотра ТВ
Как распаковать несколько RAR‑файлов сразу
Приватный просмотр в Linux: как и зачем
Windows 11 не видит iPod — способы исправить
PS5: как настроить игровые пресеты