Chapter 9 of 24 · Python for Beginners — Complete Free Tutorial
Dictionaries and Sets
Lists answer “what is the third item?”; a dictionary answers “what is Ada's grade?”. This chapter of the Python for Beginners series introduces Python's key–value store, the methods that read and reshape it, the ordering guarantee you can rely on, and sets — unordered bags of unique values that make membership tests and de-duplication trivial.
Creating dictionaries and reading values
A dictionary is written with curly braces and key: value pairs. Look a value up with square brackets, or with get() when the key might legitimately be missing:
student = {"name": "Ada", "grade": "A", "absences": 2}
print(student["name"])
print(student.get("house"))
print(student.get("house", "unknown"))
Expected output:
Ada
None
unknown
Square brackets are the strict form: a missing key stops the program with a traceback whose last line is KeyError: 'house'. get() never raises — it answers None, or the default you supply in the second argument. Use brackets for keys you are certain exist (a lookup you just built), and get() for keys that are genuinely optional. Note the contrast with JavaScript objects, where obj.missing quietly answers undefined: Python prefers to say so out loud. More on that distinction in objects and JSON.
The methods you will use daily
| Method | What it does |
|---|---|
items() | Every (key, value) pair, ready to unpack in a loop |
keys() / values() | Just the keys, or just the values |
update(other) | Merges another dictionary in, overwriting shared keys |
setdefault(key, default) | Returns the value, creating it from default only if the key is absent |
pop(key) | Removes a key and returns its value (KeyError if missing) |
price = {"apple": 1.5}
price.update({"banana": 0.75, "apple": 1.75})
print(price)
print(price.setdefault("cherry", 2.0))
print(price.pop("banana"))
print(list(price.items()))
for key, value in price.items():
print(f"{key}: {value}")
Expected output:
{'apple': 1.75, 'banana': 0.75}
2.0
0.75
[('apple', 1.75), ('cherry', 2.0)]
apple: 1.75
cherry: 2.0
Ordering you can rely on, and dictionaries inside dictionaries
Python dictionaries remember the order keys were inserted. This was once an implementation detail; since Python 3.7 it is part of the language, so the first key you write is the first key you read back — config = {}, then config["debug"] = True and config["theme"] = "dark", answers ['debug', 'theme'] when converted with list(). Real data nests, and a dictionary of dictionaries is how you model a record with sections:
school = {
"classA": {"teacher": "Patel", "students": 28},
"classB": {"teacher": "Nguyen", "students": 26},
}
print(school["classA"]["teacher"])
print(school.get("classC", {}).get("teacher"))
Expected output:
Patel
None
The second line is a small defensive habit worth copying: get("classC", {}) supplies an empty dictionary so the second lookup cannot raise. JSON from an API arrives in exactly this shape, so the skill transfers directly.
Sets: unique values with fast membership
A set is an unordered collection of unique values. Duplicates collapse at creation, order is not preserved, and asking “is this here?” is what the type is built for. The operators map onto the familiar set diagram:
| Expression | Name | Result |
|---|---|---|
a | b | union | everything in either set |
a & b | intersection | values in both sets |
a - b | difference | values in a but not b |
first = {3, 1, 4, 1, 5}
second = {4, 5, 6}
print(sorted(first))
print(sorted(first | second))
print(sorted(first & second))
print(sorted(first - second))
Expected output:
[1, 3, 4, 5]
[1, 3, 4, 5, 6]
[4, 5]
[1, 3]
The duplicate 1 in the literal vanished. Because a set has no meaningful order, the example sorts before printing — otherwise the sequence of values depends on internal layout. set() builds an empty set, add() inserts, remove() deletes (raising KeyError when absent) and discard() deletes quietly — an empty set must be written set(), since {} creates an empty dictionary. The most common everyday use is de-duplication, because converting a list does it in one step:
raw = ["red", "blue", "red", "green", "blue"]
unique = set(raw)
print(len(raw), len(unique))
print(sorted(unique))
Expected output:
5 3
['blue', 'green', 'red']
Values must be hashable to live in a set, so lists and dictionaries are out and tuples are in — the same rule that governs dictionary keys in the previous chapter. Sets are also how you test membership cheaply when a list would mean scanning every element.
A worked program: counting word frequency
Putting the pieces together, here is a complete frequency counter. get() supplies a zero the first time a word appears, so one line does the whole accumulation:
words = "the cat sat on the mat the cat".split()
counts = {}
for word in words:
counts[word] = counts.get(word, 0) + 1
for word, n in counts.items():
print(f"{word}: {n}")
Expected output:
the: 3
cat: 2
sat: 1
on: 1
mat: 1
Five lines of loop, and the output order follows first appearance because dictionaries remember insertion order. The standard library ships a purpose-built version of exactly this pattern, collections.Counter, which you will meet in chapter 23.
Common mistakes
- Reading a possibly-missing key with brackets —
data["south"]ends the program withKeyError; useget()or supply a default when absence is normal. - Writing
{}for an empty set — that is an empty dictionary; callset()instead, oradd()will fail. - Assuming set output has a stable order — sort before printing, or expect the order to change between runs and versions.
- Using a list or dict as a key or set element — unhashable values raise
TypeError; tuples are the fix. - Confusing
remove()withdiscard()— one raises when the value is missing, the other does nothing; pick deliberately.
Practice checklist
- Create a contact record as a nested dictionary and print two fields, one of which may be missing, using
get(). - Merge two dictionaries with
update()and explain which value wins for a shared key. - Given two sets of product tags, print the shared tags and the tags only one product has.
- Convert a list with duplicates into a set, count the difference in
len(), then sort the unique values back into a list. - Extend the word-frequency program to print the most frequent word and how many times it appeared.
What's next?
Your programs can now store and count data, but the counting loops are starting to repeat themselves. Chapter 10 packages them into reusable blocks in Functions.
Frequently Asked Questions
Python is a general-purpose language first released in 1991, designed around one idea: code is read far more often than it is written. English keywords and indentation instead of braces make it readable, the standard library covers files, JSON, dates and testing, and pip adds thousands more packages. The same language handles scripts, web APIs, data work and machine learning.
None at all. Python is widely considered the friendliest first language: no declaration keywords, no braces, no build step, and error messages that name the problem. This series begins at your first print() call. If you already read JavaScript or TypeScript you will adjust quickly, but no previous language is assumed anywhere in these 24 chapters.
Python 3 only, any release from 3.12 onwards. Python 2 reached end of life in 2021 and its print "hi" statements and xrange() no longer run. One historical quirk remains: installers ship the interpreter as python3 on macOS and Linux, while Windows usually offers python or the py launcher, and every command in this series works with that one-word swap.
More than you might expect. Everyday automation such as renaming files, cleaning spreadsheets or scraping pages; web backends with Django, Flask or FastAPI; data analysis and machine learning with pandas, NumPy and PyTorch; DevOps tooling, desktop apps and games. Python is also the classic glue language, calling C, Rust or Fortran libraries from short readable scripts.
Pure Python can run 10 to 100 times slower than C, yet that rarely blocks you: heavy numeric work lives in C extensions such as NumPy, which Python coordinates. Dynamic typing does push errors to runtime, surfacing as a TypeError on the line that finally uses the bad value. Type hints checked by mypy move those mistakes back into your editor.
Only the interpreter. Install CPython from python.org, version 3.12 or newer, confirm it with python3 --version, and open a plain text editor; VS Code with the Python extension is the recommendation here. The early chapters use no third-party packages, so there is nothing else to set up until virtual environments and pip arrive.
A dict is a real runtime mapping: keys may be any hashable value rather than only strings, insertion order has been guaranteed since Python 3.7, and a missing key raises KeyError unless you ask with get(). JavaScript objects coerce every key to a string and carry a prototype chain. A TypeScript interface is erased at compile time and describes shape only; TypedDict is Python's checked equivalent.
Reach for a list when items will be added, removed or reordered: it is ordered, mutable and rich with methods. Pick a tuple for fixed records and function returns, because it is immutable and hashable, so it can be a dictionary key. Choose a set for unique values and fast membership checks; it has no order and de-duplicates on creation.
Yes, gradually. Annotate public function signatures first; the interpreter ignores hints at runtime, so a checker such as mypy is what gives them teeth. Add tests the same way: pytest collects any test_*.py file and plain assert statements, and a suite that finishes in a tenth of a second is one you will run after every change. Both habits cost little now and much more later.
JavaScript and TypeScript keep the browser, so anything rendered to the user stays theirs. Python takes the server: Django for full-featured applications, Flask for small ones, FastAPI for typed async APIs where your annotations become automatic documentation. Your TypeScript habits transfer directly, and asyncio mirrors the event loop you already know, just with different keywords.