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']

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

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']]

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:
- Split a tuple in Python
- Split a string into equal halves
- Split a string at every nth character
- Split a string into an array in Python
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.

Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn