11.1 Creating Lists: Literals, list(), and Comprehensions
In Python, lists are mutable, ordered sequences of elements, and their creation is a fundamental operation with several nuanced approaches. The method chosen impacts not only readability but also performance and clarity of intent.
Literal Syntax: The Square Bracket []
The most common and Pythonic method for creating a list is using the literal syntax with square brackets. Elements are placed inside the brackets, separated by commas. This syntax is direct, highly readable, and efficient because it is a built-in operation the Python interpreter handles without an explicit function call.
# Creating an empty list
empty_list = []
# Creating a list with heterogeneous elements
my_list = [1, "hello", 3.14, True, [1, 2, 3]]
# Creating a list with duplicate elements
fruits = ["apple", "banana", "apple", "cherry"]
print(fruits) # Output: ['apple', 'banana', 'apple', 'cherry']
This approach is preferred for its simplicity. It is important to remember that the elements within a list literal are evaluated at the time of creation. If a variable is used, its current value is captured, not its name.
x = 10
list_a = [x, 20] # list_a becomes [10, 20]
x = 99 # Changing x does NOT affect the list
print(list_a) # Output: [10, 20]
The list() Constructor
The list() built-in function is a versatile constructor that creates a list from an iterable object. Its primary use is to convert other iterable types (like tuples, strings, sets, or dictionaries) into a list. When called with no arguments, it creates an empty list, functionally equivalent to [].
# Creating a list from a string (iterates over characters)
list_from_string = list("hello")
print(list_from_string) # Output: ['h', 'e', 'l', 'l', 'o']
# Creating a list from a tuple
list_from_tuple = list((1, 2, 3))
print(list_from_tuple) # Output: [1, 2, 3]
# Creating a list from a range object
list_from_range = list(range(5))
print(list_from_range) # Output: [0, 1, 2, 3, 4]
# Creating a list from a dictionary (iterates over keys)
list_from_dict = list({'a': 1, 'b': 2})
print(list_from_dict) # Output: ['a', 'b']
A common pitfall arises when trying to create a list of a predefined size with a default value. Using the * operator on a literal list with a mutable element (like another list) creates multiple references to the same object, not distinct objects.
# INCORRECT: Creates three references to the same inner list.
list_of_lists = [[]] * 3
list_of_lists[0].append(99)
print(list_of_lists) # Output: [[99], [99], [99]] (Not what was intended!)
# CORRECT: Use a list comprehension to create distinct objects.
list_of_lists = [[] for _ in range(3)]
list_of_lists[0].append(99)
print(list_of_lists) # Output: [[99], [], []]
List Comprehensions: Concise and Expressive
List comprehensions provide a compact, readable, and often more efficient way to create lists based on existing iterables or sequences. They combine a for loop, an optional conditional filter, and an expression into a single, declarative line inside square brackets. They are not just syntactic sugar; they are optimized for performance, as the iteration and appending happen in underlying C code, making them faster than an equivalent for loop with .append().
The basic syntax is: [expression for item in iterable if condition]
# Create a list of squares for numbers 0 to 9
squares = [x**2 for x in range(10)]
print(squares) # Output: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# Create a list of even squares only
even_squares = [x**2 for x in range(10) if x % 2 == 0]
print(even_squares) # Output: [0, 4, 16, 36, 64]
# Nested loops in a comprehension: flattening a 2D list
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened = [num for row in matrix for num in row]
print(flattened) # Output: [1, 2, 3, 4, 5, 6, 7, 8, 9]
While powerful, comprehensions can become less readable if overly complex. A best practice is to avoid excessive nesting or complicated expressions. If the logic for generating the list element is multi-step or requires significant control flow, a traditional for loop with .append() is often more maintainable, even if slightly less performant. The goal is to prioritize clarity unless performance is a proven bottleneck.