Python lists are ordered, mutable sequences that can hold items of any type — including other lists. They are Python’s most-used built-in data structure and the direct equivalent of JavaScript arrays, though with a richer set of built-in methods and more consistent behaviour. In FastAPI applications, lists appear everywhere: the results of a database query are typically a list of model objects, a paginated response body contains a list of serialised items, and validation logic iterates over lists of accepted values or incoming tags. Mastering list creation, indexing, slicing, and mutation is foundational to writing any Python application.
Creating and Accessing Lists
# Create a list with square brackets
fruits = ["apple", "banana", "cherry"]
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", True, 3.14, None] # any types
nested = [[1, 2], [3, 4], [5, 6]] # list of lists
empty = []
# Indexing — 0-based, negative indexes from the end
fruits[0] # "apple" — first item
fruits[1] # "banana" — second item
fruits[-1] # "cherry" — last item
fruits[-2] # "banana" — second to last
# Slicing — [start:stop:step] — stop is exclusive
fruits[0:2] # ["apple", "banana"]
fruits[1:] # ["banana", "cherry"] — from index 1 to end
fruits[:2] # ["apple", "banana"] — first 2 items
fruits[::-1] # ["cherry", "banana", "apple"] — reversed
# Length
len(fruits) # 3
# Check membership
"apple" in fruits # True
"mango" in fruits # False
"mango" not in fruits # True
items[-1] is always the last item, items[-2] is the second-to-last. This is more readable and less error-prone than items[len(items)-1]. Negative indexing is used constantly when working with SQLAlchemy query results — for example, getting the most recently created record from an ordered result set.copy.deepcopy().Modifying Lists
posts = ["First", "Second", "Third"]
# append — add one item to the end
posts.append("Fourth")
# ["First", "Second", "Third", "Fourth"]
# extend — add multiple items from another iterable
posts.extend(["Fifth", "Sixth"])
# ["First", "Second", "Third", "Fourth", "Fifth", "Sixth"]
posts += ["Seventh"] # equivalent to extend
# insert — add item at a specific index
posts.insert(0, "Zero") # insert at position 0
# ["Zero", "First", ...]
# remove — delete first occurrence of a value
posts.remove("Second") # raises ValueError if not found
# Check first: if "Second" in posts: posts.remove("Second")
# pop — remove and return item by index (default: last)
last = posts.pop() # removes and returns last item
third = posts.pop(2) # removes and returns item at index 2
# del — delete by index or slice
del posts[0] # delete first item
del posts[1:3] # delete items at index 1 and 2
# clear — empty the list
posts.clear() # posts is now []
# Assignment — replace a value
posts = ["a", "b", "c"]
posts[1] = "B" # ["a", "B", "c"]
Sorting and Ordering
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
# sort() — in-place sort, returns None
numbers.sort() # [1, 1, 2, 3, 4, 5, 6, 9]
numbers.sort(reverse=True) # [9, 6, 5, 4, 3, 2, 1, 1]
# sorted() — returns a NEW sorted list, original unchanged
original = [3, 1, 4, 1, 5]
sorted_copy = sorted(original) # [1, 1, 3, 4, 5]
print(original) # [3, 1, 4, 1, 5] — unchanged
# reverse() — in-place reverse
items = [1, 2, 3]
items.reverse() # [3, 2, 1]
items[::-1] # new reversed list (non-mutating)
# Sort dicts by field
posts = [
{"title": "C", "views": 100},
{"title": "A", "views": 500},
{"title": "B", "views": 250},
]
by_title = sorted(posts, key=lambda p: p["title"])
by_views = sorted(posts, key=lambda p: p["views"], reverse=True)
Useful List Methods and Functions
items = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
items.count(5) # 3 — how many times 5 appears
items.index(4) # 2 — index of first occurrence of 4
min(items) # 1
max(items) # 9
sum(items) # 44
# Copy
shallow = items.copy() # shallow copy
shallow = items[:] # equivalent shallow copy
import copy
deep = copy.deepcopy(items) # deep copy (needed for nested structures)
# Flatten a nested list
nested = [[1, 2], [3, 4], [5, 6]]
flat = [x for row in nested for x in row] # [1, 2, 3, 4, 5, 6]
# Check if all items match a condition
all_positive = all(n > 0 for n in items) # True
any_negative = any(n < 0 for n in items) # False
# Convert to/from other types
"hello".split(",") # ["hello"] — string to list
",".join(["a", "b", "c"]) # "a,b,c" — list to string
list(range(5)) # [0, 1, 2, 3, 4]
list("hello") # ["h", "e", "l", "l", "o"]
Common Mistakes
Mistake 1 — sort() vs sorted() confusion
❌ Wrong — assigning result of sort() (returns None):
items = [3, 1, 2]
result = items.sort() # sort() returns None, sorts in place
print(result) # None — not what you wanted!
✅ Correct — use sorted() to get a new sorted list:
result = sorted(items) # ✓ returns a new sorted list
# Or sort in place and use the original:
items.sort() # ✓ items is now sorted
Mistake 2 — Iterating and modifying a list simultaneously
❌ Wrong — skips items when removing during iteration:
items = [1, 2, 3, 4, 5]
for item in items:
if item % 2 == 0:
items.remove(item) # skips items — bug!
✅ Correct — build a new list with a comprehension:
items = [item for item in items if item % 2 != 0] # [1, 3, 5] ✓
Mistake 3 — Using a list when you need a set for membership testing
❌ Wrong — O(n) lookup in a long list:
ALLOWED_ROLES = ["user", "editor", "admin"] # list — O(n) search
if role in ALLOWED_ROLES: # scans the whole list each time
✅ Correct — O(1) lookup with a set:
ALLOWED_ROLES = {"user", "editor", "admin"} # set — O(1) lookup ✓
if role in ALLOWED_ROLES:
Quick Reference
| Operation | Code | Notes |
|---|---|---|
| Create | [1, 2, 3] |
Square brackets |
| Access by index | lst[0], lst[-1] |
0-based, negative from end |
| Slice | lst[1:3], lst[::2] |
start:stop:step |
| Add to end | lst.append(x) |
Mutates in place |
| Add many | lst.extend(other) |
Mutates in place |
| Remove by value | lst.remove(x) |
First occurrence |
| Remove by index | lst.pop(i) |
Returns the item |
| Sort in place | lst.sort() |
Returns None |
| New sorted copy | sorted(lst) |
Original unchanged |
| Length | len(lst) |
— |