7 Best Ways to Initialize a List of Lists in Python

4.5/5 - (2 votes)

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 element 42.
  • 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:


Programming Humor – Python

“I wrote 20 short programs in Python yesterday. It was wonderful. Perl, I’m leaving you.”xkcd