Lists: Working with Collections
Introduction to Lists
A list is a mutable, ordered sequence that can hold any type of data. Unlike variables that store single values, lists group multiple items under one name. They're defined with square brackets [] and items separated by commas. Lists are the workhorse of Python—used everywhere from simple todo apps to complex data processing.
Python# Creating lists fruits = ["apple", "banana", "cherry"] mixed = [1, "Hello", 3.14, True] # Mixed types OK! empty = [] # Empty list print(len(fruits)) # 3 - number of items
Indexing: Accessing Elements
Every item has a position (index) starting from 0. Use square brackets to access elements. Python also supports negative indexing: -1 is the last item, -2 is second-to-last, and so on. This makes it easy to access elements from either end.
Pythonnums = [10, 20, 30, 40, 50] # Positive indexing (from start) print(nums[0]) # 10 (first) print(nums[2]) # 30 (third) # Negative indexing (from end) print(nums[-1]) # 50 (last) print(nums[-2]) # 40 (second-to-last)
Basic Operations: Append & Extend
append() adds a single item to the end. extend() adds multiple items from another iterable. Both modify the list in-place. Use + to concatenate and create a new list instead.
Pythoncart = ["milk", "bread"] # append() - add ONE item cart.append("eggs") print(cart) # ['milk', 'bread', 'eggs'] # extend() - add MULTIPLE items cart.extend(["butter", "cheese"]) print(cart) # ['milk', 'bread', 'eggs', 'butter', 'cheese'] # Difference: append adds list AS item cart.append(["jam"]) # Adds ["jam"] as single element!
Insert: Adding at Specific Position
insert(index, item) adds an element at any position. All items after that index shift right. This is slower than append() because it requires moving elements.
Pythontasks = ["wake up", "work", "sleep"] # Insert at index 1 tasks.insert(1, "breakfast") print(tasks) # ['wake up', 'breakfast', 'work', 'sleep'] # Insert at beginning tasks.insert(0, "alarm") # Insert at end (same as append) tasks.insert(len(tasks), "dream")
Remove, Pop & Clear
remove(value) deletes the first occurrence of a value. pop(index) removes and returns an item (default: last). clear() empties the entire list. Use del for removing by index without returning.
Pythonitems = ["a", "b", "c", "b", "d"] # remove() - by VALUE (first match) items.remove("b") print(items) # ['a', 'c', 'b', 'd'] # pop() - by INDEX, returns removed item last = items.pop() # 'd' (default: last) first = items.pop(0) # 'a' (specific index) # clear() - remove ALL items.clear() print(items) # []
Searching & Membership
Use 'in' for fast membership testing. index(value) returns the position of first occurrence (raises error if not found). count(value) tells how many times an item appears.
Pythongrades = [85, 90, 78, 90, 92, 90] # Membership test - O(n) if 90 in grades: print("Found 90!") # Find position - O(n) pos = grades.index(90) # 1 (first occurrence) # Count occurrences - O(n) count = grades.count(90) # 3 # Safe search (avoid error) if 100 in grades: idx = grades.index(100)
Updating Elements
Lists are mutable—you can change any element by assigning to its index. You can also replace a range of elements using slice assignment.
Pythonscores = [70, 80, 90, 85] # Update single element scores[0] = 75 print(scores) # [75, 80, 90, 85] # Update multiple via slicing scores[1:3] = [82, 95] print(scores) # [75, 82, 95, 85] # Replace with different length! scores[1:3] = [100] print(scores) # [75, 100, 85]
Slicing Basics
Slicing extracts a portion of a list using [start:end]. Start is inclusive, end is exclusive. Omit start to begin at 0, omit end to go to the end. Slicing creates a NEW list (shallow copy).
Pythonnums = [0, 1, 2, 3, 4, 5, 6, 7] print(nums[2:5]) # [2, 3, 4] - index 2 to 4 print(nums[:3]) # [0, 1, 2] - first 3 print(nums[5:]) # [5, 6, 7] - from index 5 print(nums[:]) # Full copy # Negative indices work too! print(nums[-3:]) # [5, 6, 7] - last 3 print(nums[:-2]) # [0,1,2,3,4,5] - all but last 2
Advanced Slicing: Step Values
Add a third parameter [start:end:step] to skip elements. A step of 2 takes every other item. Negative step reverses direction—[::-1] is the classic way to reverse a list!
Pythonnums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # Every 2nd element print(nums[::2]) # [0, 2, 4, 6, 8] # Every 3rd, starting at index 1 print(nums[1::3]) # [1, 4, 7] # REVERSE the list! print(nums[::-1]) # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] # Reverse portion print(nums[5:1:-1]) # [5, 4, 3, 2]
Copying & Cloning Lists
Assignment (=) creates a reference, not a copy! Both variables point to the SAME list. Use slicing [:], list(), or copy() for a shallow copy. For nested lists, use deepcopy() to clone everything.
Pythonimport copy original = [1, 2, [3, 4]] # WRONG - both point to same list! ref = original ref[0] = 99 print(original) # [99, 2, [3, 4]] - Changed! # Shallow copy - new outer list shallow = original[:] shallow[0] = 1 # Doesn't affect original shallow[2][0] = 33 # DOES affect original! # Deep copy - fully independent deep = copy.deepcopy(original) deep[2][0] = 333 # original unchanged
Sorting & Reversing
sort() modifies the list in-place (returns None). sorted() returns a NEW sorted list. Both accept key= for custom sorting and reverse=True for descending order. reverse() flips the list in-place.
Pythonnums = [3, 1, 4, 1, 5, 9, 2, 6] # In-place sort - O(n log n) nums.sort() print(nums) # [1, 1, 2, 3, 4, 5, 6, 9] # Descending order nums.sort(reverse=True) # sorted() returns NEW list original = [3, 1, 2] new_sorted = sorted(original) print(original) # [3, 1, 2] - unchanged! # Custom key (sort by length) words = ["python", "is", "awesome"] words.sort(key=len) # ['is', 'python', 'awesome']
Nested Lists & 2D Grids
Lists can contain other lists, creating matrices or grids. Access elements with chained indices: grid[row][col]. Useful for tables, game boards, images, and spreadsheet-like data.
Python# 3x3 grid matrix = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ] # Access element at row 1, col 2 print(matrix[1][2]) # 6 # Modify element matrix[0][0] = 99 # Iterate over 2D list for row in matrix: for cell in row: print(cell, end=' ') print()
Nested Slicing
Combine indexing and slicing to extract portions of 2D lists. First index selects rows, then slice the inner list for columns. This is powerful for matrix operations.
Pythonmatrix = [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12] ] # Get row 1 print(matrix[1]) # [5, 6, 7, 8] # Get element at [1][2] print(matrix[1][2]) # 7 # Slice columns from row 1 print(matrix[1][1:3]) # [6, 7] # Get column (need loop) col_1 = [row[1] for row in matrix] print(col_1) # [2, 6, 10]
Real-World: Shopping Cart
Lists are perfect for managing collections of items. Here's how you might implement a simple shopping cart with add, remove, and total calculations.
Pythoncart = [] prices = {"apple": 1.5, "bread": 2.0, "milk": 3.0} # Add items cart.append("apple") cart.append("bread") cart.append("milk") cart.append("apple") # Can have duplicates # Remove item if "bread" in cart: cart.remove("bread") # Calculate total total = sum(prices[item] for item in cart) print(f"Cart: {cart}") print(f"Total: ${total:.2f}") # $6.00
Real-World: Grade Management
Process student grades with list operations. Calculate averages, find highest/lowest scores, and filter results—common data processing patterns.
Pythongrades = [85, 92, 78, 90, 88, 76, 95, 89] # Statistics average = sum(grades) / len(grades) highest = max(grades) lowest = min(grades) # Filter passing grades (>= 80) passing = [g for g in grades if g >= 80] # Count A grades (>= 90) a_count = len([g for g in grades if g >= 90]) print(f"Avg: {average:.1f}") # 86.6 print(f"Highest: {highest}") # 95 print(f"Passing: {len(passing)}") # 6 print(f"A grades: {a_count}") # 3
Performance & Best Practices
Know your complexities: index access O(1), append O(1), insert/remove O(n), search O(n). Use deque for frequent insert/remove at both ends. Avoid modifying lists while iterating—use a copy or comprehension instead.
Python# Time Complexity Cheat Sheet: # list[i] → O(1) constant # list.append → O(1) constant # list.pop() → O(1) from end # list.pop(0) → O(n) shifts all! # list.insert → O(n) shifts right # list.remove → O(n) search + shift # x in list → O(n) linear scan # list.sort → O(n log n) # For frequent ops at both ends: from collections import deque dq = deque([1, 2, 3]) dq.appendleft(0) # O(1) at front!
Master this concept
Hands-on practice is the fastest way to learn. Head over to our interactive workspace to solve this topic's challenge.