Python Lists and Dictionaries in Practice
AI-assistedDrafted with AI assistance and reviewed before publishing; examples are version-pinned. Verify against official docs when in doubt.
Lists and dictionaries carry most of the data in a Python program. This chapter sticks to the operations that appear again and again, plus the handful of traps that silently produce wrong results.
Lists: add, remove, inspect
nums = [10, 20, 30]
nums.append(40) # at the end
nums.insert(0, 5) # at a position
nums.remove(20) # by value, first match only
last = nums.pop() # remove and return the last item
print(nums, last) # [5, 10, 30] 40
del nums[0] removes by index. remove() raises ValueError when the value is absent, so check if x in nums first when absence is plausible.
append is amortized O(1); insert(0, x) and pop(0) are O(n) because every later element shifts. For queue-like work, use collections.deque.
Slicing
The syntax is seq[start:stop:step], with the end excluded:
nums = [0, 1, 2, 3, 4, 5]
nums[1:4] # [1, 2, 3]
nums[:3] # [0, 1, 2]
nums[::2] # [0, 2, 4]
nums[::-1] # [5, 4, 3, 2, 1, 0]
A slice creates a new list, which makes copy = nums[:] the shortest way to copy.
That copy is shallow. Nested lists are still shared:
matrix = [[1, 2], [3, 4]]
shallow = matrix[:]
shallow[0].append(9)
print(matrix) # [[1, 2, 9], [3, 4]]
If the inner objects must be independent, use copy.deepcopy(matrix).
Iterating correctly
names = ["ann", "bob", "cindy"]
for i, name in enumerate(names):
print(i, name)
for name, score in zip(names, [90, 85, 88]):
print(name, score)
Do not add or remove items while iterating:
nums = [1, 2, 3, 4]
for n in nums:
if n % 2 == 0:
nums.remove(n) # index shifts; [1, 3] here, wrong for [1, 2, 2, 3]
Build a new list instead:
nums = [n for n in nums if n % 2 != 0]
If you truly need to mutate in place, iterate over a copy: for n in nums[:].
Dictionaries: reading and iterating
user = {"name": "ann", "age": 30}
user["city"] = "Shenzhen" # insert or update
user.pop("age") # remove and return
Two ways to read a key, with different failure behavior:
user.get("nickname") # None when missing
user.get("nickname", "anonymous") # explicit default
user["nickname"] # KeyError when missing
Use get() when the key may be absent; use [] when its absence is a bug you want to surface early.
Three iteration styles:
for key in user: # keys
print(key)
for key, value in user.items(): # both
print(key, value)
for value in user.values(): # values only
print(value)
Since Python 3.7 dictionaries preserve insertion order, so list(user) returns keys in the order they were added.
Checking membership is O(1) on the key, not the value:
"name" in user # True, fast
"ann" in user.values() # True, but O(n)
Grouping without boilerplate
words = ["apple", "ant", "banana", "berry", "cherry"]
groups = {}
for word in words:
groups.setdefault(word[0], []).append(word)
print(groups)
# {'a': ['apple', 'ant'], 'b': ['banana', 'berry'], 'c': ['cherry']}
setdefault inserts the default only when the key is missing, then returns the list, so the append always works. The alternative is collections.defaultdict(list), which creates the default on access:
from collections import defaultdict
groups = defaultdict(list)
for word in words:
groups[word[0]].append(word)
Counting has its own tool:
from collections import Counter
Counter(words).most_common(2) # [('apple', 1), ('ant', 1)]
Common mistakes
1. Assuming a slice is a deep copy
matrix[:] copies only the outer list. Mutating matrix[0] is visible through both references.
2. Removing items during iteration
Skipped elements, not an exception. Filter into a new list.
3. Using [] where get() belongs
A KeyError from optional configuration crashes the program. Conversely, get() on a required field hides a real bug.
4. Relying on sort() returning a list
sorted_nums = nums.sort() # None
sorted_nums = sorted(nums) # new list
sort() mutates in place and returns None; sorted() returns a new list.
5. Comparing lists with is
is compares identity. == compares element by element.
6. Forgetting that dict keys must be hashable
Lists and dicts cannot be keys; tuples and frozenset can.
Summary
append/popat the end are cheap; front operations are O(n).- Slicing copies one level only; use
deepcopyfor nested data. - Never mutate a list while iterating it; build a new one.
get()for optional keys,[]for required ones.setdefault/defaultdict/Counterremove most manual grouping and counting.
Next: tuples, sets, and what immutability really guarantees.