How to Split a Python List Into Evenly Sized Chunks

To split a Python list into evenly sized chunks, slice it in steps of the chunk size with a list comprehension. Each slice items[i:i + n] becomes one chunk, and the last chunk just holds whatever’s left over:

n = 3
chunks = [items[i:i + n] for i in range(0, len(items), n)]

I ran every example in a Windows Command Prompt on Python 3.12.5 with NumPy 2.5.3, and the newer itertools one on Python 3.14.8.

In this tutorial, we’ll split a Python list into evenly sized chunks with slicing, a generator, itertools.batched(), padding, and an even split into N parts.

Split a list into chunks of size N with slicing

Here are ten US orders to ship in batches of three. The range() step jumps three items at a time:

orders = ["NY-1001", "CA-1002", "TX-1003", "FL-1004", "WA-1005",
          "IL-1006", "OH-1007", "GA-1008", "NC-1009", "AZ-1010"]

size = 3
chunks = [orders[i:i + size] for i in range(0, len(orders), size)]

for n, chunk in enumerate(chunks, start=1):
    print(f"Batch {n}: {chunk}")

Output:

Batch 1: ['NY-1001', 'CA-1002', 'TX-1003']
Batch 2: ['FL-1004', 'WA-1005', 'IL-1006']
Batch 3: ['OH-1007', 'GA-1008', 'NC-1009']
Batch 4: ['AZ-1010']
Command Prompt output splitting a Python list of 10 orders into evenly sized chunks of 3
Ten orders split into three full batches of three and a final batch holding the one order left over.

Slices don’t fail past the end, so you get the short last chunk for free. New to [start:stop]? See how list slicing works in Python.

Chunk a large list lazily with a generator

The comprehension builds a list of lists with every chunk in memory. For a huge list, a generator’s yield hands back one chunk at a time instead:

def chunked(items, size):
    for start in range(0, len(items), size):
        yield items[start:start + size]

invoice_ids = list(range(5001, 5013))        # 12 invoices, Oct 2026
batches = chunked(invoice_ids, 5)
print(type(batches).__name__)
for batch in batches:
    print(len(batch), batch)

Output:

generator
5 [5001, 5002, 5003, 5004, 5005]
5 [5006, 5007, 5008, 5009, 5010]
2 [5011, 5012]

Nothing’s sliced until your loop asks. It’s the same Python for loop, just paused between chunks.

How do you use itertools.batched() in Python 3.12 and later?

Python 3.12 added itertools.batched(iterable, n), which chunks any iterable for you. Python 3.13 added strict=True to reject a short last batch:

import sys
from itertools import batched

print("Python", sys.version.split()[0])
emails = ["megan@contoso.com", "alex@contoso.com", "adele@contoso.com",
          "lee@contoso.com", "diego@contoso.com", "grady@contoso.com", "nestor@contoso.com"]

for group in batched(emails, 3):
    print(group)

try:
    print(list(batched(emails, 3, strict=True)))
except ValueError as err:
    print("ValueError:", err)

Output:

Python 3.14.8
('megan@contoso.com', 'alex@contoso.com', 'adele@contoso.com')
('lee@contoso.com', 'diego@contoso.com', 'grady@contoso.com')
('nestor@contoso.com',)
ValueError: batched(): incomplete batch
Command Prompt output of Python 3.14 itertools.batched splitting seven emails into tuples of three, with strict=True raising ValueError
batched() returns tuples, not lists, ending with a one-item tuple, and strict=True rejects that short final batch.

On Python 3.12.5, the same strict=True call failed with TypeError: batched() takes at most 2 arguments (3 given), so check your Python version first.

Pad the last chunk with itertools.zip_longest()

If you need every chunk the same length, like seating rows for a 2026 event, the grouper recipe fills the gap with a placeholder:

from itertools import zip_longest

seats = ["Ava", "Ben", "Cole", "Dana", "Eli", "Faye", "Gus"]

def grouper(items, size, fill=None):
    args = [iter(items)] * size              # the same iterator, repeated
    return list(zip_longest(*args, fillvalue=fill))

for row in grouper(seats, 3, fill="(empty)"):
    print(row)

Output:

('Ava', 'Ben', 'Cole')
('Dana', 'Eli', 'Faye')
('Gus', '(empty)', '(empty)')

All three arguments share one iterator, so each tuple pulls the next three names. The Python zip() function stops short instead of padding.

Split a list into N evenly sized parts

Splitting into a set number of parts is a different job. For eleven tasks and four workers, divmod() gives the base size and remainder, and the first parts get one extra item:

import numpy as np

def split_into(items, parts):
    q, r = divmod(len(items), parts)          # base size, leftover count
    out, start = [], 0
    for i in range(parts):
        end = start + q + (1 if i < r else 0)
        out.append(items[start:end])
        start = end
    return out

tasks = [f"T{n}" for n in range(1, 12)]       # 11 tasks for 4 workers
print("divmod  :", [len(p) for p in split_into(tasks, 4)], split_into(tasks, 4))
print("NumPy   :", [a.tolist() for a in np.array_split(tasks, 4)])

Output:

divmod  : [3, 3, 3, 2] [['T1', 'T2', 'T3'], ['T4', 'T5', 'T6'], ['T7', 'T8', 'T9'], ['T10', 'T11']]
NumPy   : [['T1', 'T2', 'T3'], ['T4', 'T5', 'T6'], ['T7', 'T8', 'T9'], ['T10', 'T11']]
Command Prompt output splitting 11 Python list items into 4 near-equal parts with divmod and NumPy array_split
Both methods split eleven tasks into parts of 3, 3, 3 and 2 items, in the same order.

np.array_split() gives you the same sizes as arrays; .tolist() turns them back into lists. See creating NumPy arrays in Python.

To split by a value or condition instead, see splitting a list in Python.

More splitting practice:

Frequently asked questions

How do I split a list into chunks of size N in Python?

Use [items[i:i + n] for i in range(0, len(items), n)].

What does itertools.batched() return?

An iterator of tuples, each holding up to n items.

Which Python version has itertools.batched()?

Python 3.12; its strict argument needs 3.13 or later.

How do I make every chunk the same length?

Pad it with zip_longest(..., fillvalue=...), or reject it with batched(..., strict=True).

How do I split a list into N equal parts?

Size the parts with divmod(), or call np.array_split(items, n).

Where is itertools.batched() documented?

See itertools.batched() in the Python docs.