Essay
Learning Python, Part 5: Tuples, Dictionaries, Sets and Exceptions
Study notes on tuples, dictionaries, sets, sorting with keys, lambda, and handling errors with try and except. Written entirely by an AI model.
Lists are not the only way to hold a collection. This part covers three more, how to sort them, and how to deal with the errors that real input causes.
Tuples
A tuple is an ordered, immutable sequence written with brackets: point = (3, 4). It supports indexing, slicing and looping like a list, but you cannot change it. That makes it a good fit for a fixed bundle of related values, such as a name and a score.
Unpacking assigns each part to its own name:
name, score = ("Ana", 91)
A one-item tuple needs a trailing comma, (5,). Without it, (5) is just the number 5 in brackets.
Dictionaries
A dictionary maps keys to values. Lookup by key is fast, and keys must be unique and immutable (strings, numbers, tuples).
stock = {"tea": 12, "rice": 5}
stock["jam"] = 3 # add a key
stock["tea"] += 1 # update a value
print(stock["tea"]) # 13
print("salt" in stock) # False
Reading a missing key with stock["salt"] raises KeyError. The method get gives a default instead:
print(stock.get("salt", 0)) # 0
Looping gives the keys. Use .items() for key and value together:
for item, qty in stock.items():
print(f"{item}: {qty}")
Other everyday methods: keys(), values(), pop(key) and del stock[key].
Counting with a dictionary
Counting how often things occur is the most common dictionary job:
found = ["umbrella", "scarf", "umbrella", "keys", "umbrella", "scarf"]
counts = {}
for thing in found:
counts[thing] = counts.get(thing, 0) + 1
print(counts) # {'umbrella': 3, 'scarf': 2, 'keys': 1}
counts.get(thing, 0) + 1 means “the current count, or 0 if this is the first time, plus one”.
Sets
A set holds unique items with no order. Adding something twice does nothing.
morning = {"Ana", "Ben", "Cyd"}
evening = {"Ben", "Dee"}
morning & evening # {'Ben'} in both
morning | evening # all four names in either
morning - evening # {'Ana', 'Cyd'} only in morning
An easy way to remove duplicates from a list is set(items). The result has no particular order, so wrap it in sorted() if you need one. {} on its own makes an empty dictionary, not an empty set. Write set() for that.
Sorting
sorted(items) returns a new sorted list, and items.sort() sorts a list in place. Both accept two options:
reverse=Truefor descending order.key=function, where each item is passed to the function and the results decide the order.
names = ["maya", "Zed", "al", "Bea"]
sorted(names) # ['Bea', 'Zed', 'al', 'maya']
sorted(names, key=str.lower) # ['al', 'Bea', 'maya', 'Zed']
sorted(names, key=len) # ['al', 'Zed', 'Bea', 'maya']
A lambda is a small unnamed function. It is handy for keys:
games = [("chess", 4), ("go", 9), ("ludo", 4)]
games.sort(key=lambda g: g[1], reverse=True)
Sorting by two things
Tuples compare left to right, so a key can return a tuple. To sort numbers from high to low and ties alphabetically, negate the number:
games.sort(key=lambda g: (-g[1], g[0]))
# [('go', 9), ('chess', 4), ('ludo', 4)]
Another method relies on stability: Python’s sort keeps equal items in their existing order. Sort by the tie-breaker first, then by the main key.
games.sort() # alphabetical first
games.sort(key=lambda g: g[1], reverse=True) # then by score
The results match, but the single tuple key is easier to reason about.
map(function, items) applies a function to every item, and filter(function, items) keeps those where the function is true. Both give lazy results, so wrap them in list(...) to see them. A list comprehension often reads better: [g[0] for g in games if g[1] > 4].
Exceptions
Some failures cannot be prevented by checking first: a file that is not there, a user who types abc where a number is needed. Python signals them with exceptions, and try and except let you respond.
def read_age(text):
try:
return int(text)
except ValueError:
return None
Rules of thumb:
- Name the exception you expect. A bare
except:also hides typos and keyboard interrupts. - Keep the
tryblock small: only the line that can fail. - You can list several:
except (ValueError, IndexError):. - An optional
elseblock runs when nothing went wrong, andfinallyalways runs. raise ValueError("age must be positive")raises one yourself.
The common types: ValueError (right type, bad value), TypeError (wrong type), KeyError (missing dictionary key), IndexError (position out of range), ZeroDivisionError and OSError (file problems).
Ask until it’s valid
def ask_number(prompt):
while True:
try:
return float(input(prompt))
except ValueError:
print("Please type a number.")
The loop only ends when return happens, which only happens when float succeeded. That small structure appears in a great many programs.
Check first, or catch?
Both styles are fine. Checking first (if key in stock) suits conditions you expect often. Catching suits failures that are rare or hard to predict. For files, catching OSError is better, because the file can disappear between a check and the open.
Where to practise
In Set A, “The lantern contest podium” uses a dictionary and a two-part sort key. In Set B, “The restock list” does the same with tuples as values, and “Loading the pantry list” practises exceptions. Part 6 finishes with recursion, searching and classes.