List Fundamentals
Creating Lists
# Empty list
empty = []
empty = list()
# List with values
numbers = [1, 2, 3, 4, 5]
fruits = ['apple', 'banana', 'cherry']
mixed = [1, 'hello', 3.14, True, None]
# List from range
numbers = list(range(10)) # [0, 1, 2, ..., 9]
# List from string
chars = list('hello') # ['h', 'e', 'l', 'l', 'o']
Indexing and Slicing
fruits = ['apple', 'banana', 'cherry', 'date', 'elderberry']
# Indexing (0-based)
first = fruits[0] # 'apple'
last = fruits[-1] # 'elderberry'
# Slicing [start:stop:step]
fruits[1:3] # ['banana', 'cherry']
fruits[:3] # ['apple', 'banana', 'cherry']
fruits[2:] # ['cherry', 'date', 'elderberry']
fruits[::2] # ['apple', 'cherry', 'elderberry']
fruits[::-1] # ['elderberry', 'date', 'cherry', 'banana', 'apple']
# Slice assignment
fruits[1:3] = ['blueberry', 'cranberry']
print(fruits) # ['apple', 'blueberry', 'cranberry', 'date', 'elderberry']
Common List Methods
numbers = [3, 1, 4, 1, 5, 9]
# Adding elements
numbers.append(2) # [3, 1, 4, 1, 5, 9, 2]
numbers.insert(0, 0) # [0, 3, 1, 4, 1, 5, 9, 2]
numbers.extend([6, 7]) # [0, 3, 1, 4, 1, 5, 9, 2, 6, 7]
# Removing elements
numbers.remove(1) # Remove first occurrence of 1
popped = numbers.pop() # Remove and return last element
popped = numbers.pop(0) # Remove and return element at index 0
numbers.clear() # []
# Searching
numbers = [3, 1, 4, 1, 5, 9]
numbers.index(4) # 2 (first occurrence)
numbers.count(1) # 2
4 in numbers # True
# Sorting
numbers.sort() # In-place sort
numbers.sort(reverse=True) # Descending
sorted_numbers = sorted(numbers) # New sorted list
# Reversing
numbers.reverse() # In-place reverse
reversed_numbers = numbers[::-1] # New reversed list
List Complexity
| Operation | Time | Space |
|---|---|---|
| Access by index | O(1) | O(1) |
| Search (in) | O(n) | O(1) |
| Append | O(1)* | O(1) |
| Insert at beginning | O(n) | O(n) |
| Remove | O(n) | O(1) |
| Pop from end | O(1) | O(1) |
| Pop from beginning | O(n) | O(1) |
| Sort | O(n log n) | O(n) |
*Amortized O(1)
List Comprehensions
Basic Syntax
# [expression for item in iterable]
squares = [x ** 2 for x in range(10)]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# Equivalent to:
squares = []
for x in range(10):
squares.append(x ** 2)
With Condition
# [expression for item in iterable if condition]
evens = [x for x in range(20) if x % 2 == 0]
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
# Filter and transform
words = ['hello', 'world', 'python', 'hi']
long_upper = [w.upper() for w in words if len(w) > 3]
# ['HELLO', 'WORLD', 'PYTHON']
Nested Comprehensions
# Flatten a matrix
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [num for row in matrix for num in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
# Create a matrix
matrix = [[i * j for j in range(1, 4)] for i in range(1, 4)]
# [[1, 2, 3], [2, 4, 6], [3, 6, 9]]
Complex Examples
# Transpose a matrix
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
transpose = [[row[i] for row in matrix] for i in range(3)]
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
# Filter and transform
numbers = [1, -2, 3, -4, 5]
abs_evens = [abs(x) for x in numbers if x % 2 == 0]
# [2, 4]
# Dictionary from two lists
keys = ['name', 'age', 'city']
values = ['Alice', 30, 'NYC']
dict_from_lists = {k: v for k, v in zip(keys, values)}
# {'name': 'Alice', 'age': 30, 'city': 'NYC'}
When NOT to Use Comprehensions
# ❌ Too complex - use regular loop
result = [
transform(x)
for x in items
if condition1(x)
if condition2(x)
if condition3(x)
]
# ✅ Better: use filter() and map()
result = list(map(transform, filter(lambda x: condition1(x) and condition2(x), items)))
# ❌ Side effects in comprehension
[print(x) for x in items] # Don't do this!
# ✅ Use a regular loop for side effects
for x in items:
print(x)
List Copying & References
The Reference Problem
# Lists are mutable - assignment creates a reference
original = [1, 2, 3]
copy_ref = original # Same object!
append(4) to copy_ref
copy_ref.append(4)
print(original) # [1, 2, 3, 4] - original changed!
Shallow Copy
# Creating a shallow copy
original = [1, 2, 3]
# Method 1: slice
copy1 = original[:]
# Method 2: copy()
copy2 = original.copy()
# Method 3: list constructor
copy3 = list(original)
# Method 4: list comprehension
copy4 = [x for x in original]
# All create independent copies
copy1.append(4)
print(original) # [1, 2, 3] - unchanged
print(copy1) # [1, 2, 3, 4]
Deep Copy (Nested Lists)
import copy
# ⚠️ Shallow copy doesn't work for nested lists
original = [[1, 2], [3, 4]]
shallow = original.copy()
shallow[0].append(5)
print(original) # [[1, 2, 5], [3, 4]] - nested list changed!
# ✅ Use deep copy for nested structures
deep = copy.deepcopy(original)
deep[0].append(6)
print(original) # [[1, 2, 5], [3, 4]] - unchanged
print(deep) # [[1, 2, 5, 6], [3, 4]]
Common Patterns
# Initialize list with default values
grid = [[0] * 3 for _ in range(3)]
# [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
# ⚠️ Don't use * for mutable defaults!
grid = [[0] * 3] * 3 # WRONG!
grid[0][0] = 1
print(grid) # [[1, 0, 0], [1, 0, 0], [1, 0, 0]] - all rows changed!
# Flatten and copy in one step
nested = [[1, 2], [3, 4], [5, 6]]
flat_copy = [num for row in nested for num in row]
# [1, 2, 3, 4, 5, 6]