Wrong Way to Create and Initialize a List of Lists in Python
A standard way to initialize a flat list in Python is the multiplication operator to create a new list by repeatedly concatenating an existing list:
numbers = [42] * 5 print(numbers) # [42, 42, 42, 42, 42]
π Related Tutorial: How to create a list in Python?
Consequently, many beginner coders try to use the multiplication operator to create a list of lists like so:
lists = [[42]] * 5 print(lists) # [[42], [42], [42], [42], [42]]
While the resulting list of lists looks just fine, there’s a problem:
π Attention: Using any form of concatenation to create a list of lists results in a list of copies rather than a list of unique list objects. If you change one inner list, all copies change as well. This is a variant of the aliasing problem where the same memory location can be accessed from multiple symbolic names in the code.
For example, let’s change the first inner list of the nested list we just created and see what happens to the remaining lists:
lists = [[42]] * 5 print(lists) # [[42], [42], [42], [42], [42]] lists[0].append('alice') print(lists) # [[42, 'alice'], [42, 'alice'], [42, 'alice'], [42, 'alice'], [42, 'alice']]
You can see in the highlighted lines that changing a single element of a single inner list changes all the inner lists—because they all refer to the same object in memory!
We can confirm this using the id()
function that returns an integer representation of the memory location of a given object:
for lst in lists: print(id(lst))
Output:
2457467746176
2457467746176
2457467746176
2457467746176
2457467746176
All inner lists point to the same memory location!
π‘ Discussion: While this approach is a proper way to create a list of lists, it is only suitable if you really want to have copies—which is almost never the case in practice.
To fix this, we’ll dive into multiple ways to create and initialize a nested list in Python next!
Method 1: Simple for Loop
The most straightforward way to create and initialize a 2D nested list (list of lists) is to use a for
loop in combination with the list.append()
method to add one inner list in each loop iteration.
Here’s an example:
numbers = [42] * 5 print(numbers) # [42, 42, 42, 42, 42] # initialize empty list lists = [] # iterate for initialization for i in range(5): # create a new list lst = [42] # append new list to nested list lists.append(lst)
Output:
print(lists) # [[42], [42], [42], [42], [42]]
The resulting list of lists now consists of different list objects, not the same ones:
for lst in lists: print(id(lst))
Output:
2760572738112
2760603925568
2760572736000
2760572737216
2760572736960
π‘ Discussion: This method is best if you prefer super simple and straightforward Python code. The loop body to initialize each inner list is also flexible and easily extendable. So, if you need a complicated code snippet to initialize each inner list differently, this is the way to go!
Method 2: List Comprehension
The most concise way to initialize a list of lists is to use list comprehension.
- For example, the expression
[[42] for _ in range(10)]
creates a list of 10 inner lists each containing only the element42
. - The expression
[[1, 2, 3] for _ in range(10)]
creates a list of 10 inner lists each containing three elements.
Here’s a code snippet for copy&pasting:
# Initialize nested list using list comprehension: lists = [[i] for i in range(10)] print(lists) # [[0], [1], [2], [3], [4], [5], [6], [7], [8], [9]]
List of empty lists: And if you need empty inner lists for initialization:
# Initialize list of empty lists using list comprehension: lists = [[] for _ in range(10)] print(lists) # [[], [], [], [], [], [], [], [], [], []]
π‘ Discussion: This is the most concise and pythonic method to create and initialize a list of lists. Each inner list points to a new list object so there’s no aliasing problem as pointed out in the beginning of this article.
Let’s dive into a more expressive variant of this approach next. π
Check out my new Python book Python One-Liners (Amazon Link).
If you like one-liners, you’ll LOVE the book. It’ll teach you everything there is to know about a single line of Python code. But it’s also an introduction to computer science, data science, machine learning, and algorithms. The universe in a single line of Python!
The book was released in 2020 with the world-class programming book publisher NoStarch Press (San Francisco).
Publisher Link: https://nostarch.com/pythononeliners
Method 3: Nested List Comprehension
You can use a list comprehension statement to create each inner list and a list comprehension statement to create the output list from the inner lists. This creates completely unique inner list objects and combines them, programmatically, into the outer list.
Here’s a simple example that creates and initializes a 2D list of lists:
lists = [[j for j in range(5)] for _ in range(10)] print(lists) ''' Prettified output: [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]] '''
This variant also looks pretty:
lists = [[j for j in range(i)] for i in range(10)] ''' [[], [0], [0, 1], [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7, 8]] '''
You can also use nested list comprehension to create a 3D list of lists by using three independent list comprension expressions in a single line of code:
lists = [[[k for k in range(5)] for j in range(i)] for i in range(10)]
Result:
[[], [[0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]]
π‘ Discussion: Nested list comprehension is great to create multi-dimensional nested lists. Now, this quickly becomes pretty big as the number of elements in the neste list grows exponentially. But you got the point—nested list comprehension is a very concise and powerful approach to create n-dimensional nested lists!
Method 4: NumPy empty() and tolist()
If your list of lists should be initialized with numerical values, a great way is to use the NumPy library. You can use the function np.empty(shape)
to create a new array with the given shape tuple and the array.tolist()
function to convert the result to a normal Python list.
Here’s an example with 10 empty inner lists:
shape = (10, 0)
import numpy as np lists = np.empty((10, 0)).tolist() print(lists) # [[], [], [], [], [], [], [], [], [], []]
Here’s an example with three inner lists, each with two elements:
shape = (3, 2)
import numpy as np lists = np.empty((3, 2)).tolist() print(lists) # [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0]]
Here’s an example with two inner lists, each with 10 elements. You can see that the elements are initialized with random values (close to zero):
shape = (2, 10)
import numpy as np lists = np.empty((2, 10)).tolist() print(lists) # [[6.230420704259778e-307, 4.672967463650385e-307, 1.6912109553714024e-306, 9.345982466430538e-307, 1.891468961626159e-307, 7.56571287681344e-307, 3.11525958331296e-307, 1.2461072285391713e-306, 1.3796232035098384e-306, 1.2906087058647337e-306], [2.2251825065982158e-306, 1.3351196894724102e-306, 1.7802234158176518e-306, 1.0570034520433751e-307, 1.1126102663813858e-306, 1.112615019651964e-306, 1.4241083850164095e-306, 7.565977700541159e-307, 6.230597256278243e-307, 1.4241953022955674e-306]]
You can even pass more-dimensional shape tuples into it:
shape = (2, 3, 4)
import numpy as np lists = np.empty((2, 3, 4)).tolist() print(lists) # [[[6.230420704259778e-307, 4.672967463650385e-307, 1.6912109553714024e-306, 9.345982466430538e-307], [1.891468961626159e-307, 7.56571287681344e-307, 3.11525958331296e-307, 1.2461072285391713e-306], [1.3796232035098384e-306, 1.2906087058647337e-306, 2.2251825065982158e-306, 1.3351196894724102e-306]], [[1.7802234158176518e-306, 1.0570034520433751e-307, 1.1126102663813858e-306, 1.112615019651964e-306], [1.4241083850164095e-306, 7.565977700541159e-307, 6.230597256278243e-307, 1.4241953022955674e-306], [9.791017606390451e-307, 6.898051509498844e-307, 1.7802016872024348e-306, 1.4241097429693734e-306]]]
The result is a list of lists of lists of numerical elements (three-dimensional).
π‘ Discussion: NumPy np.empty()
is the best approach if you need an arbitrarily dimensional nested list of numerical values. It is efficient and super easy to use.
Method 5: Nested For Loop and List append()
For comprehensibility, I’d also like to provide you this simple approach to create a list of lists in a simple, straightforward, and easy-to-follow step-by-step approach using the list.append()
method to append both an element to the inner list and an inner list to the outer list:
lst = [] for i in range(10): inner = [] for j in range(2): inner.append(j) lst.append(inner) print(lst) # [[0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1]]
This approach is not really used very often because at least the inner list could be constructed much more simply using list comprehension or a direct list concatenation/multiplication approach as shown next:
Method 6: For Loop and List Concatenation/Multiplication
You can repeatedly add a newly-created list to an initially empty list by using a for
loop in combination with the list multiplication approach. Note that this indeed creates a new list object in each iteration so aliasing is not a problem.
Here’s an example:
lst = [] for i in range(10): inner = [0] * 5 lst.append(inner) print(lst) # [[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, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
Here you can see that each inner list has a unique identifier and, thus, points to a new object in memory:
for x in lst: print(id(x)) ''' 2602238605120 2602207222848 2602238606528 2602207220736 2602207221952 2602207221696 2602207221312 2602207219264 2602207220288 2602207220352 '''
Related Articles:
Summary
This article has presented the most Pythonic ways to create and initialize a list of lists in Python:
- Method 1: Simple for Loop
- Method 2: List Comprehension
- Method 3: Nested List Comprehension
- Method 4: NumPy empty() and tolist()
- Method 5: Nested For Loop and List append()
- Method 6: For Loop and List Concatenation/Multiplication