Python - Create List with Examples
• Python offers multiple methods to create lists: literal notation, the `list()` constructor, list comprehensions, and generator expressions—each optimized for different use cases
Key Insights
• Python offers multiple methods to create lists: literal notation, the list() constructor, list comprehensions, and generator expressions—each optimized for different use cases
• List comprehensions provide up to 3x performance improvement over traditional loops for list creation, making them the preferred choice for transforming iterables
• Understanding when to use mutable lists versus immutable tuples prevents unintended side effects in function arguments and shared references
Creating Lists with Literal Notation
The most straightforward way to create a list uses square brackets. This literal syntax is both readable and performant.
# Empty list
empty = []
# List with initial values
numbers = [1, 2, 3, 4, 5]
mixed_types = [1, "hello", 3.14, True, None]
# Nested lists
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
# Access nested elements
print(matrix[0][1]) # Output: 2
Lists accept any object type and maintain insertion order. Python doesn’t enforce type homogeneity, though maintaining consistent types improves code maintainability.
Using the list() Constructor
The list() constructor converts iterables into lists. This proves essential when working with ranges, strings, tuples, sets, or custom iterables.
# From range
numbers = list(range(10)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
evens = list(range(0, 20, 2)) # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
# From string
chars = list("Python") # ['P', 'y', 't', 'h', 'o', 'n']
# From tuple
tuple_data = (1, 2, 3)
list_data = list(tuple_data) # [1, 2, 3]
# From set (unordered)
unique_nums = list({3, 1, 2, 1, 3}) # [1, 2, 3] (order may vary)
# From dictionary (keys only)
config = {"host": "localhost", "port": 8080}
keys = list(config) # ['host', 'port']
values = list(config.values()) # ['localhost', 8080]
The constructor creates a shallow copy of the original iterable. Modifying the new list doesn’t affect the source, but nested objects remain shared references.
List Comprehensions for Transformation
List comprehensions provide concise syntax for creating lists from existing iterables with optional filtering and transformation.
# Basic transformation
squares = [x**2 for x in range(10)]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# With conditional filtering
evens = [x for x in range(20) if x % 2 == 0]
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
# Transform and filter
squared_evens = [x**2 for x in range(10) if x % 2 == 0]
# [0, 4, 16, 36, 64]
# String manipulation
words = ["hello", "world", "python"]
uppercase = [word.upper() for word in words]
# ['HELLO', 'WORLD', 'PYTHON']
# Nested comprehensions
matrix = [[i + j for j in range(3)] for i in range(3)]
# [[0, 1, 2], [1, 2, 3], [2, 3, 4]]
# Flattening nested lists
nested = [[1, 2], [3, 4], [5, 6]]
flat = [num for sublist in nested for num in sublist]
# [1, 2, 3, 4, 5, 6]
List comprehensions execute faster than equivalent for loops with append() operations because they’re optimized at the bytecode level.
Conditional Expressions in Comprehensions
Comprehensions support if-else expressions for more complex transformations.
# If-else in comprehension
numbers = [1, 2, 3, 4, 5, 6]
labels = ["even" if x % 2 == 0 else "odd" for x in numbers]
# ['odd', 'even', 'odd', 'even', 'odd', 'even']
# Multiple conditions
grades = [85, 92, 78, 90, 88]
results = [
"A" if g >= 90 else "B" if g >= 80 else "C"
for g in grades
]
# ['B', 'A', 'C', 'A', 'B']
# Processing with default values
data = [10, None, 20, None, 30]
cleaned = [x if x is not None else 0 for x in data]
# [10, 0, 20, 0, 30]
Creating Lists from Functions
Functions that return lists enable reusable list creation logic with parameterization.
def create_sequence(start, end, step=1):
"""Create a list with custom range parameters."""
return list(range(start, end, step))
def fibonacci(n):
"""Generate first n Fibonacci numbers."""
if n <= 0:
return []
if n == 1:
return [0]
fib = [0, 1]
for _ in range(2, n):
fib.append(fib[-1] + fib[-2])
return fib
# Usage
print(create_sequence(0, 10, 2)) # [0, 2, 4, 6, 8]
print(fibonacci(10)) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Multiplication and Repetition
The multiplication operator creates lists with repeated elements, though this requires caution with mutable objects.
# Repeat primitive values
zeros = [0] * 5 # [0, 0, 0, 0, 0]
pattern = [1, 2] * 3 # [1, 2, 1, 2, 1, 2]
# DANGER: Repeated mutable references
nested = [[]] * 3 # Creates 3 references to the SAME list
nested[0].append(1)
print(nested) # [[1], [1], [1]] - all modified!
# CORRECT: Use comprehension for independent lists
nested = [[] for _ in range(3)]
nested[0].append(1)
print(nested) # [[1], [], []] - only first modified
This pitfall occurs because multiplication creates references to the same object, not independent copies.
Generator Expressions for Memory Efficiency
Generator expressions use parentheses instead of brackets, creating iterators rather than lists. Convert to lists when needed.
# Generator expression (lazy evaluation)
gen = (x**2 for x in range(1000000))
# Convert to list when needed
first_ten = list(gen)[:10] # Only computes what's needed initially
# Memory comparison
import sys
list_comp = [x**2 for x in range(10000)]
gen_exp = (x**2 for x in range(10000))
print(sys.getsizeof(list_comp)) # ~87616 bytes
print(sys.getsizeof(gen_exp)) # ~128 bytes
Use generators for large datasets where you don’t need all values simultaneously.
Copying Lists
Python provides multiple ways to copy lists, each with different implications for nested structures.
original = [1, 2, 3, 4, 5]
# Shallow copies (all equivalent)
copy1 = original[:]
copy2 = original.copy()
copy3 = list(original)
# Deep copy for nested structures
import copy
nested_original = [[1, 2], [3, 4]]
shallow = nested_original.copy()
deep = copy.deepcopy(nested_original)
# Modify nested element
nested_original[0][0] = 99
print(shallow) # [[99, 2], [3, 4]] - affected!
print(deep) # [[1, 2], [3, 4]] - independent
Building Lists Dynamically
The append() and extend() methods build lists incrementally, useful when the final size is unknown.
# Building with append
results = []
for i in range(5):
if i % 2 == 0:
results.append(i**2)
# [0, 4, 16]
# Extend with another iterable
results.extend([100, 200])
# [0, 4, 16, 100, 200]
# Insert at specific position
results.insert(0, -1)
# [-1, 0, 4, 16, 100, 200]
# Reading file lines into list
with open('data.txt', 'w') as f:
f.write('line1\nline2\nline3')
with open('data.txt', 'r') as f:
lines = [line.strip() for line in f]
# ['line1', 'line2', 'line3']
Performance Considerations
Different list creation methods have varying performance characteristics.
import timeit
# List comprehension vs append loop
def with_append():
result = []
for i in range(1000):
result.append(i**2)
return result
def with_comprehension():
return [i**2 for i in range(1000)]
append_time = timeit.timeit(with_append, number=10000)
comp_time = timeit.timeit(with_comprehension, number=10000)
print(f"Append: {append_time:.4f}s")
print(f"Comprehension: {comp_time:.4f}s")
print(f"Speedup: {append_time/comp_time:.2f}x")
# Comprehension typically 2-3x faster
List comprehensions win for simple transformations. Use explicit loops when logic becomes complex enough to harm readability.
Choose list creation methods based on your specific requirements: literal notation for known values, comprehensions for transformations, constructors for type conversion, and generators for memory-constrained scenarios.