Technology  /  Python

🐍 Python 78 guides · updated 2026

From first variable to OOP, generators, and real projects — the language that runs everything from data pipelines to AI agents, taught the practical way.

Python Lists: Methods, Mutability, and the Patterns You’ll Use Every Day

Lists are Python’s most versatile general-purpose container. They’re ordered, mutable, and can hold any mix of types. Understanding how lists work — not just what methods exist, but how memory and mutation behave — prevents a whole category of bugs that confuse developers at every experience level.


What a List Is

A list is an ordered, mutable sequence enclosed in square brackets. The items can be any Python object, and a single list can mix types:

numbers = [1, 2, 3, 4, 5]
mixed = [42, "hello", True, None, [1, 2]] # nesting works
empty = []

Unlike strings, lists are mutable — you can change their contents in place without creating a new object.


Creating Lists

# Direct literal
fruits = ["apple", "banana", "cherry"]
# From another iterable
letters = list("Python") # ['P', 'y', 't', 'h', 'o', 'n']
range_list = list(range(5)) # [0, 1, 2, 3, 4]
# Repeated pattern
zeros = [0] * 5 # [0, 0, 0, 0, 0]
# List comprehension
squares = [x**2 for x in range(6)] # [0, 1, 4, 9, 16, 25]

Indexing and Slicing

Lists use zero-based indexing. Negative indices count from the end. Slicing returns a new list:

items = ["a", "b", "c", "d", "e"]
print(items[0]) # "a"
print(items[-1]) # "e"
print(items[1:4]) # ["b", "c", "d"]
print(items[::2]) # ["a", "c", "e"] — every other item
print(items[::-1]) # ["e", "d", "c", "b", "a"] — reversed

Modifying Lists

colours = ["red", "green", "blue"]
# Change by index
colours[1] = "yellow" # ["red", "yellow", "blue"]
# Add items
colours.append("purple") # add to end
colours.insert(0, "black") # insert at position 0
colours.extend(["white", "grey"]) # add multiple items
# Remove items
colours.remove("red") # remove first occurrence
del colours[0] # remove by index
last = colours.pop() # remove and return last item
second = colours.pop(1) # remove and return item at index 1
# Clear all items
colours.clear()

Sorting

Lists have an in-place sort() method and there’s a built-in sorted() that returns a new list:

nums = [3, 1, 4, 1, 5, 9, 2, 6]
nums.sort() # modifies nums in place
print(nums) # [1, 1, 2, 3, 4, 5, 6, 9]
nums.sort(reverse=True) # descending
print(nums) # [9, 6, 5, 4, 3, 2, 1, 1]
# sorted() returns a new list, leaves original unchanged
original = [3, 1, 4, 1, 5]
ascending = sorted(original)
print(original) # [3, 1, 4, 1, 5] — unchanged
print(ascending) # [1, 1, 3, 4, 5]

Sorting by a key

Both sort() and sorted() accept a key parameter — a function applied to each element before comparing:

words = ["banana", "apple", "cherry", "fig"]
# Sort by string length
words.sort(key=len)
print(words) # ['fig', 'apple', 'banana', 'cherry']
# Sort by last character
words.sort(key=lambda w: w[-1])
# Sort a list of dicts by a field
students = [
{"name": "Alice", "score": 88},
{"name": "Bob", "score": 95},
{"name": "Charlie", "score": 72},
]
students.sort(key=lambda s: s["score"], reverse=True)
# Bob (95), Alice (88), Charlie (72)

Copying Lists — The Mutation Trap

This is the most common beginner trap with lists. Assignment does not copy a list — it creates a second reference to the same object:

original = [1, 2, 3]
alias = original # both point to the same list
alias.append(4)
print(original) # [1, 2, 3, 4] — original was modified!

To get an independent copy:

# Shallow copy — three equivalent ways
copy1 = original.copy()
copy2 = original[:]
copy3 = list(original)
copy1.append(99)
print(original) # [1, 2, 3, 4] — unchanged

A shallow copy works when the list contains only simple values (numbers, strings, booleans). If the list contains nested mutable objects, you need a deep copy:

import copy
matrix = [[1, 2], [3, 4]]
shallow = matrix.copy()
deep = copy.deepcopy(matrix)
shallow[0].append(99) # modifies matrix[0] too — same inner list
print(matrix) # [[1, 2, 99], [3, 4]]
# deep is fully independent
deep[0].append(0)
print(matrix) # still [[1, 2, 99], [3, 4]]

Other Useful Methods

nums = [3, 1, 4, 1, 5, 1, 9]
print(nums.count(1)) # 3 — how many times 1 appears
print(nums.index(5)) # 4 — index of first 5
nums.reverse() # reverse in place
# Check membership
print(4 in nums) # True
print(7 not in nums) # True
# Length, min, max, sum
print(len(nums)) # 7
print(min(nums)) # 1
print(max(nums)) # 9
print(sum(nums)) # 24

Common Patterns

Filtering a list

scores = [45, 82, 67, 90, 55, 78]
# Keep scores above 70
passing = [s for s in scores if s >= 70] # [82, 90, 78]
# Filter with built-in filter()
passing = list(filter(lambda s: s >= 70, scores))

Transforming a list

names = ["alice", "bob", "charlie"]
capitalised = [name.title() for name in names] # ["Alice", "Bob", "Charlie"]

Flattening nested lists

nested = [[1, 2, 3], [4, 5], [6]]
flat = [item for sublist in nested for item in sublist]
# [1, 2, 3, 4, 5, 6]

Removing duplicates while preserving order

items = [3, 1, 4, 1, 5, 9, 2, 6, 5]
seen = set()
unique = []
for item in items:
if item not in seen:
seen.add(item)
unique.append(item)
# [3, 1, 4, 5, 9, 2, 6]

Or in Python 3.7+ using dict to preserve insertion order:

unique = list(dict.fromkeys(items))

When Not to Use a List

Lists work for most use cases, but consider alternatives when: