Skip to content

How to Initialize List with Default Values in Python

How to initialize Python lists with default values using list multiplication and comprehensions, with examples that avoid common pitfalls when creating nested lists.

Initializing lists with default values is a fundamental operation in Python, allowing developers to pre-allocate memory or set up data structures for later use. Whether you need a simple list of zeros, a sequence of numbers, or a complex grid (matrix), choosing the right initialization method is crucial for performance and bug prevention.

This guide explores the standard multiplication technique, list comprehensions, and how to avoid the common "mutable reference" trap when creating multi-dimensional lists.

Method 1: Using the Multiplication Operator *

The most concise way to initialize a list with immutable values (like integers, floats, strings, or None) is using the multiplication operator.

Syntax:

[value] * count

Example:

zeros = [0] * 5
print(f"Zeros: {zeros}")

strings = ["empty"] * 3
print(f"Strings: {strings}")

placeholders = [None] * 4
print(f"Placeholders: {placeholders}")

Output:

Zeros: [0, 0, 0, 0, 0]
Strings: ['empty', 'empty', 'empty']
Placeholders: [None, None, None, None]

List comprehensions provide a flexible way to initialize lists. They are particularly useful when the initial values need to be calculated dynamically or when creating lists of mutable objects to ensure each element is unique in memory.

Syntax:

[expression for _ in range(count)]

Example:

squares = [x**2 for x in range(5)]
print(f"Squares: {squares}")

dicts = [{"id": i} for i in range(3)]
print(f"Dictionaries: {dicts}")

Output:

Squares: [0, 1, 4, 9, 16]
Dictionaries: [{'id': 0}, {'id': 1}, {'id': 2}]

Method 3: The Common Pitfall (Multi-dimensional Lists)

A classic bug in Python occurs when initializing a list of lists (a matrix) using the multiplication operator. This creates shallow copies, meaning all sub-lists point to the exact same object in memory.

Example of Problem

grid_bad = [[0] * 3] * 3

print("Initial Bad Grid:")
print(grid_bad)

grid_bad[0][0] = 99
print("\nAfter Modifying [0][0]:")
print(grid_bad)

Output:

Initial Bad Grid:
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]

After Modifying [0][0]:
[[99, 0, 0], [99, 0, 0], [99, 0, 0]]

Solution

Use a list comprehension. This forces Python to execute the inner list creation logic [0] * 3 freshly for every row, creating independent objects.

grid_good = [[0] * 3 for _ in range(3)]

grid_good[0][0] = 99
print("Correct Grid Modification:")
print(grid_good)

Output:

Correct Grid Modification:
[[99, 0, 0], [0, 0, 0], [0, 0, 0]]

Method 4: Using itertools and Generators

For very large lists where memory efficiency is a concern, or for functional programming styles, you can use iterators.

Using itertools.repeat

This creates an iterator that yields the same value repeatedly. It is memory efficient because it doesn't store the list in memory until you explicitly convert it.

import itertools

iterator = itertools.repeat("A", 3)

repeat_list = list(iterator)
print(f"Repeat List: {repeat_list}")

Output:

Repeat List: ['A', 'A', 'A']

Using Generator Expressions

If you only need to iterate over the default values once (e.g., passing them to a function), use a generator expression instead of a list comprehension to save memory.

gen = (x * 10 for x in range(5))

print("Iterating generator:")
for val in gen:
    print(val, end=" ")

Output:

Iterating generator:
0 10 20 30 40 

Conclusion

To initialize lists efficiently in Python:

  1. Use [value] * n for immutable types like integers, floats, strings, and None.
  2. Use [... for _ in range(n)] for mutable types (lists, dicts) to avoid shared reference bugs.
  3. Use Generators if you are dealing with massive sequences and don't need to store everything in memory at once.
Navigation