How to create a nested list in Python
Learn how to create nested lists in Python. Explore different methods, tips, real-world applications, and common error debugging.

In Python, a nested list is simply a list inside another list. This powerful structure is essential when you need to represent complex, multi-dimensional data like matrices or grids.
In this article, you'll explore several techniques to create and manage these data structures. You'll also find practical tips, real-world applications, and clear advice to help you debug common issues.
Basic creation of a nested list
nested_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(nested_list)--OUTPUT--[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
The example shows the most direct way to create a nested list. You're defining a list where each element is itself a list. In this case, nested_list contains three inner lists, which you can visualize as a 3x3 grid or matrix.
Each inner list represents a row in this structure:
[1, 2, 3]is the first row.[4, 5, 6]is the second row.[7, 8, 9]is the third row.
This method of manually defining the list is ideal when you already know the data's size and content upfront.
Common methods for creating nested lists
When you need to build lists dynamically, Python offers powerful tools such as list comprehension, for loops, and the .append() method.
Using list comprehension for nested lists
nested_list = [[i+j*3 for i in range(1, 4)] for j in range(3)]
print(nested_list)--OUTPUT--[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
List comprehension provides a compact syntax for creating lists. This example uses a nested comprehension, where one comprehension is inside another. Think of it as an outer loop that builds the rows and an inner loop that fills them with data.
- The outer comprehension,
for j in range(3), runs three times to create each of the three inner lists. - The inner comprehension,
for i in range(1, 4), generates the values for each row. - The expression
i+j*3calculates each element's value, resulting in the familiar 3x3 grid.
Creating nested lists with the .append() method
outer_list = []
for i in range(3):
inner_list = [i*3+j for j in range(1, 4)]
outer_list.append(inner_list)
print(outer_list)--OUTPUT--[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
This approach builds the list more explicitly. You start with an empty outer_list and use a for loop to construct each row one by one.
- Inside the loop, a new
inner_listis created during each pass. - The
.append()method then adds this newly created list to theouter_list.
This step-by-step process can be easier to read and debug than a more compact list comprehension, especially when the logic for creating rows is complex.
Building nested lists with for loops
nested_list = []
for i in range(3):
row = []
for j in range(3):
row.append(i*3 + j + 1)
nested_list.append(row)
print(nested_list)--OUTPUT--[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
This approach uses nested for loops, giving you explicit control over building the list. You start with an empty nested_list and construct it step by step.
- The outer loop iterates to create each row. Crucially, it initializes a fresh, empty
rowlist on each pass. - The inner loop then populates this new
rowwith elements. - Finally, the completed
rowis appended to thenested_list, ensuring each row is a distinct object.
Advanced techniques for nested lists
With the fundamentals covered, you can tackle more complex scenarios using advanced techniques for dynamic structures, safe duplication with copy.deepcopy(), and patterned list generation.
Using recursion to create dynamic nested lists
def create_nested_list(depth, value=0):
if depth == 0:
return value
return [create_nested_list(depth-1, value) for _ in range(3)]
result = create_nested_list(3)
print(result)--OUTPUT--[[[0, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]]]
Recursion allows a function to call itself, which is perfect for building structures with a variable number of levels. The create_nested_list function uses this approach to construct lists based on the depth you provide.
- The base case,
if depth == 0, is the crucial stopping point. It ends the recursion and returns the finalvalue. - Otherwise, the function calls itself with a reduced
depth, wrapping each result in a new list. This process repeats, adding another layer of nesting each time.
Calling create_nested_list(3) generates a 3D list because the function recurses three levels deep.
Deep copying nested lists with copy.deepcopy()
import copy
original = [[1, 2], [3, 4]]
shallow_copy = list(original)
deep_copy = copy.deepcopy(original)
original[0][0] = 99
print(f"Original: {original}, Shallow: {shallow_copy}, Deep: {deep_copy}")--OUTPUT--Original: [[99, 2], [3, 4]], Shallow: [[99, 2], [3, 4]], Deep: [[1, 2], [3, 4]]
When you copy a nested list, a simple copy using list() or slicing creates a shallow copy. This means the new list contains references to the original inner lists, not new ones. As the code demonstrates, modifying the original list also changes the shallow_copy because they share the same nested data.
To create a truly independent duplicate, you need copy.deepcopy(). This function recursively copies every element, ensuring your new list is a completely separate object.
- A shallow copy shares inner objects with the original.
- A deep copy creates entirely new inner objects.
Creating nested lists with specific patterns
triangle = [[j for j in range(i+1)] for i in range(5)]
for row in triangle:
print(row)--OUTPUT--[0]
[0, 1]
[0, 1, 2]
[0, 1, 2, 3]
[0, 1, 2, 3, 4]
List comprehensions are also perfect for creating "jagged" lists, where inner lists have different lengths. In this example, the length of each new row is determined by the outer loop's current state.
- The outer loop,
for i in range(5), iterates five times to create five rows. - The inner list's size is set by
range(i+1), making each row one element longer than the previous one.
This dynamic sizing is what produces the triangular pattern, a technique you can adapt for many other patterned structures.
Move faster with Replit
Replit is an AI-powered development platform that transforms natural language into working applications. Describe what you want to build, and Replit Agent creates it—complete with databases, APIs, and deployment.
For the nested list techniques covered in this article, Replit Agent can turn these concepts into production applications:
- Build a matrix calculator that performs mathematical operations on 2D grids.
- Create a data processing utility that imports tabular data for row and column manipulation.
- Deploy a game board generator for puzzles like Sudoku or mazes.
Describe your project to Replit Agent, and it will write the code, test it, and handle deployment automatically.
Common errors and challenges
While nested lists are powerful, they come with a few common pitfalls that can trip you up if you're not careful.
- Avoiding the list multiplication trap: A frequent mistake is using the multiplication operator (
*) to initialize a nested list, such as[[]] * 3. This doesn't create three unique inner lists; it creates three references pointing to the exact same list. Consequently, modifying one inner list affects all of them. Always use a list comprehension or aforloop to ensure each inner list is a distinct object. - Correctly accessing and modifying elements: You can access any element using two indices:
list[row][column]. For example,my_list[1][2]gets the third element from the second inner list. Modifying an element follows the same logic. Just be mindful of the list's dimensions to avoid anIndexError, which occurs when you try to access an index that doesn't exist. - Avoiding shared references when copying: As covered earlier, making a shallow copy of a nested list means the new list still shares the original's inner lists. This can cause confusing bugs when you modify what you believe is an independent copy. To create a truly separate duplicate, always use
copy.deepcopy().
Avoiding the list multiplication trap with * operator
Using the multiplication operator (*) to build a nested list seems like a convenient shortcut, but it often leads to unexpected results. This method creates multiple references to the same inner list, not unique ones. The following code demonstrates the problem.
# Trying to create a 3x3 grid filled with zeros
grid = [[0] * 3] * 3
grid[0][0] = 1 # Trying to modify just the first element
print(grid) # Will print [[1, 0, 0], [1, 0, 0], [1, 0, 0]]
The outer * 3 operation duplicates the reference to the single inner list, not the list itself. Since all three rows point to the same object, modifying one changes them all. The following code demonstrates the correct approach.
# Correctly create a 3x3 grid filled with zeros
grid = [[0 for _ in range(3)] for _ in range(3)]
grid[0][0] = 1 # Now only modifies the first element
print(grid) # Will print [[1, 0, 0], [0, 0, 0], [0, 0, 0]]
The correct approach uses a list comprehension because it ensures each inner list is a unique object. The outer loop runs three times, and on each pass, the inner comprehension [0 for _ in range(3)] is re-evaluated to create a brand new list.
This guarantees that modifying an element like grid[0][0] only affects its own row. You'll want to watch for this issue whenever you're initializing a grid or matrix with default values.
Correctly accessing and modifying nested list elements
Accessing elements in a nested list requires two indices, one for the row and one for the column. A common mistake is to go out of bounds, which triggers an IndexError because the requested index doesn't exist. The following code demonstrates this common mistake.
nested_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# This will cause an IndexError
element = nested_list[3][3]
print(element)
The code tries to access nested_list[3][3], but Python uses zero-based indexing. The valid indices for this 3x3 list are 0, 1, and 2. Accessing index 3 is out of bounds, causing the error. The corrected code below shows how to access elements safely.
nested_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# Remember: indexing starts at 0, so valid indices are 0-2
element = nested_list[2][2] # Access the bottom-right element
print(element) # Outputs: 9
The fix works by using the correct indices for Python's zero-based system. In a 3x3 list, valid indices range from 0 to 2. Accessing nested_list[2][2] successfully retrieves the element in the last row and column. Always double-check your indices when iterating or accessing elements dynamically. This simple check helps you avoid an IndexError, which happens when you try to use an index that is outside the valid range for the list's size.
Avoiding shared references when copying nested lists
Using .copy() or list() on a nested list can lead to surprising results. You're not creating a true duplicate but a shallow copy, where the inner lists are shared. The code below shows how modifying the copy also changes the original.
original = [[1, 2], [3, 4]]
copy = original.copy() # or list(original)
copy[0][0] = 99
print(f"Original: {original}") # Original is modified too!
Modifying copy[0][0] to 99 also changes the original list because original.copy() only duplicates the outer list, not the inner ones. Both variables end up pointing to the same nested list. The code below shows the fix.
import copy
original = [[1, 2], [3, 4]]
deep_copy = copy.deepcopy(original)
deep_copy[0][0] = 99
print(f"Original: {original}") # Original stays intact
The fix is to use copy.deepcopy(), which creates a completely independent copy. This function recursively duplicates every element, ensuring the new list and its inner lists are entirely separate objects. As a result, modifying the deep_copy leaves the original list untouched. You'll want to use this method whenever you need to alter a duplicated nested list without causing side effects in the original data, preventing hard-to-find bugs from shared references.
Real-world applications
With the common pitfalls out of the way, you can confidently use nested lists for real-world tasks like managing student grades or analyzing weather data.
Working with nested lists for student grades
A nested list is an excellent tool for managing a gradebook, allowing you to group each student's scores and easily calculate their average.
# Student grades for different subjects [math, science, history]
grades = [[85, 90, 78], [92, 88, 95], [79, 85, 80]]
# Calculate average grade for each student
for i, student_grades in enumerate(grades):
avg = sum(student_grades) / len(student_grades)
print(f"Student {i+1} average: {avg:.1f}")
In this example, the grades list neatly organizes scores, with each inner list holding the grades for a single student. The code then iterates through this structure to process each student's data individually.
- The
enumerate()function is used in theforloop to get both the index and the list of scores for each student. - Inside the loop,
sum()andlen()work together on the inner list to efficiently calculate the average score. - An f-string formats the output, using the index to create a clean, numbered list of results.
Creating a weather data analysis system
You can use a nested list to organize weather data from multiple cities, which lets you quickly find metrics like the highest temperature for each location.
# Daily temperatures for a week in three cities [city1, city2, city3]
weather_data = [
[72, 75, 68, 79, 82, 81, 78], # City 1
[88, 91, 93, 90, 89, 85, 87], # City 2
[62, 64, 68, 70, 69, 63, 62] # City 3
]
# Find highest temperature and its day for each city
for city_idx, temps in enumerate(weather_data):
max_temp = max(temps)
max_day = temps.index(max_temp) + 1
print(f"City {city_idx+1}: Highest temp {max_temp}°F on day {max_day}")
The weather_data list groups weekly temperatures by city, with each inner list representing one location. The for loop then processes each city's data individually.
- It identifies the peak temperature within each seven-day list.
- It then finds the position of that peak value. Since list positions start at zero, the code adds 1 to give you a natural day number, such as Day 1 instead of 0.
This pattern is effective for analyzing any dataset organized in rows, letting you extract key insights from each one independently.
Get started with Replit
Turn what you've learned into a real tool. Tell Replit Agent to “build a matrix calculator” or “create a utility to analyze tabular data from a CSV file.”
The agent writes the code, tests for errors, and deploys your app automatically. Start building with Replit.
Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.
Create & deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.


.png)
.png)