Эффективное деление списков на сегменты в языке Python
Разделение списка на более мелкие части - частая операция при обработке данных, создании батчей для машинного обучения или распределении работы между потоками. В Python существует несколько способов выполнить такое разбиение. Рассмотрим самые распространенные и эффективные подходы.
Основной способ: разбиение на части фиксированного размера с помощью list comprehension
Самый простой и производительный метод - использование генератора списка и срезов. Функция принимает исходный список и размер части.
def split_list(lst, chunk_size):
return [lst[i:i+chunk_size] for i in range(0, len(lst), chunk_size)]
my_list = [1,2,3,4,5,6,7,8,9]
print(split_list(my_list, 3))Split list python (разделение списка в python)
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Пояснение
- range(0, len(lst), chunk_size) генерирует начальные индексы каждого блока: 0, 3, 6.
- Срез
lst[i:i+chunk_size]берет до 3 элементов, начиная с i. Последний срез может быть короче, если длина не кратна chunk_size.
Возможные проблемы:
- Если
chunk_sizeравен 0, возникнет ошибка деления на ноль и пустой range. Необходима проверка. - При очень большом списке результат занимает много памяти (создается новый список списков). Для потоковой обработки лучше использовать генератор.
Цели использования: подготовка данных для пакетной обработки, разбиение на страницы, распределение задач.
Как разбить список на N примерно равных частей?
Для равномерного распределения элементов по заданному числу частей используется расчет базового размера и остатка.
def split_into_n_parts(lst, n):
avg = len(lst) // n
remainder = len(lst) % n
result = []
start = 0
for i in range(n):
end = start + avg + (1 if i < remainder else 0)
result.append(lst[start:end])
start = end
return result
print(split_into_n_parts([1,2,3,4,5,6,7], 3))
[[1, 2, 3], [4, 5], [6, 7]]
Цель: разбить данные на группы для параллельной обработки, когда количество групп фиксировано.
Как использовать itertools.islice для создания генератора частей?
Для экономии памяти и работы с большими списками удобно применять итератор, который возвращает части по мере необходимости.
from itertools import islice
def chunked_iter(lst, chunk_size):
it = iter(lst)
return iter(lambda: list(islice(it, chunk_size)), [])
for chunk in chunked_iter(range(10), 3):
print(chunk)
[0, 1, 2] [3, 4, 5] [6, 7, 8] [9]
Этот метод не создает промежуточный список всех частей, что уменьшает потребление памяти.
Как разбить список с помощью zip и итераторов?
Классический трюк - объединение нескольких итераторов в один с помощью zip, но с предварительным созданием копий итератора.
def chunked_zip(lst, chunk_size):
return [list(t) for t in zip(*[iter(lst)]*chunk_size)]
print(chunked_zip([1,2,3,4,5,6,7], 3))
[[1, 2, 3], [4, 5, 6]]
Важно: последний неполный блок отбрасывается. Это может быть нежелательно.
Цель: краткая запись, когда отбрасывание хвоста приемлемо.
Как использовать numpy для разбиения списка на части?
Если в проекте уже используется библиотека NumPy, функция numpy.array_split обеспечивает равномерное разбиение.
import numpy as np
my_list = [1,2,3,4,5,6,7,8,9,10]
parts = np.array_split(my_list, 3)
print(parts)
[array([1, 2, 3, 4]), array([5, 6, 7]), array([8, 9, 10])]
Результат - массив массивов. Для преобразования в списки: [list(p) for p in parts].
Как разбить список с помощью библиотеки more_itertools?
Библиотека more_itertools содержит готовую функцию chunked, которая возвращает список частей.
from more_itertools import chunked
my_list = [1,2,3,4,5,6,7,8]
print(list(chunked(my_list, 3)))
[[1, 2, 3], [4, 5, 6], [7, 8]]
Также есть ichunked для ленивого вычисления.
Как разбить список с помощью простого цикла for?
Иногда требуется более гибкое управление, например, с дополнительной обработкой каждого блока.
def split_manual(lst, chunk_size):
chunks = []
for i in range(0, len(lst), chunk_size):
chunk = lst[i:i+chunk_size]
# дополнительная обработка
chunks.append(chunk)
return chunks
print(split_manual([1,2,3,4,5], 2))
[[1, 2], [3, 4], [5]]
Цель: ясность кода, возможность вставить логику между блоками.
Расширенные примеры разделения списков
Скользящее окно (разделение с перекрытием)
def sliding_window(lst, window_size, step=1):
return [lst[i:i+window_size] for i in range(0, len(lst)-window_size+1, step)]
print(sliding_window([1,2,3,4,5], 3))
[[1, 2, 3], [2, 3, 4], [3, 4, 5]]
Цель: анализ временных рядов, поиск паттернов.
Разделение списка по условию
def split_by_condition(lst, condition):
true_list = [x for x in lst if condition(x)]
false_list = [x for x in lst if not condition(x)]
return true_list, false_list
even, odd = split_by_condition([1,2,3,4,5], lambda x: x%2==0)
print(even, odd)
[2, 4] [1, 3, 5]
Цель: фильтрация и группировка по произвольному правилу.
Разделение по списку длин
def split_by_sizes(lst, sizes):
result = []
start = 0
for size in sizes:
result.append(lst[start:start+size])
start += size
return result
print(split_by_sizes([1,2,3,4,5,6,7], [2,3,1]))
[[1, 2], [3, 4, 5], [6, 7]]
Цель: структурирование данных по заранее известным размерам блоков.
Разделение по произвольным индексам
from itertools import accumulate
def split_at_indices(lst, indices):
indices = [0] + sorted(indices) + [len(lst)]
return [lst[i:j] for i, j in zip(indices, indices[1:])]
print(split_at_indices([10,20,30,40,50,60], [2,5]))
[[10, 20], [30, 40, 50], [60]]
Цель: разбиение по заданным точкам разреза (например, разделы документа).
Генератор для экономии памяти
def chunked_generator(lst, chunk_size):
for i in range(0, len(lst), chunk_size):
yield lst[i:i+chunk_size]
for ch in chunked_generator(range(10), 3):
print(ch)
[0, 1, 2] [3, 4, 5] [6, 7, 8] [9]
Используется для обработки больших списков без загрузки всех частей в память одновременно.
Использование zip_longest для сохранения хвоста
from itertools import zip_longest
def chunked_zip_longest(lst, chunk_size, fillvalue=None):
args = [iter(lst)] * chunk_size
return [list(filter(lambda x: x is not fillvalue, t)) for t in zip_longest(*args, fillvalue=fillvalue)]
print(chunked_zip_longest([1,2,3,4,5,6,7], 3))
[[1, 2, 3], [4, 5, 6], [7]]
Цель: сохранить последний неполный блок при использовании трюка с zip.