⚡ Phase 2 · Control Flow 🟡 Beginner+ MODULE 09

Dictionaries & Sets

⏱️ 40 min read
📖 Theory + Code
🧩 5 Quiz Questions
🏗️ 1 Challenge
Phase 2 progress67%
🎯 What you'll learn: Python dictionaries — the most important data structure in Python. Key-value pairs, CRUD operations, all dict methods, iteration patterns, nested dicts, and dict comprehensions. Then Python sets — unique unordered collections, set operations (union, intersection, difference), and when to use them.

What is a Dictionary?

A dictionary stores data as key-value pairs. Instead of accessing items by numeric index like a list, you access them by a meaningful key — a label you define. This makes dicts perfect for representing real-world objects like users, products, or settings.

Dicts are mutable, ordered (Python 3.7+), and do not allow duplicate keys. Looking up a value by key is extremely fast — O(1) — regardless of how many items the dict contains.

{
    "name" : "Ali Hassan",← key (str) : value (str)
    "age" : 22,← key (str) : value (int)
    "gpa" : 3.85,← keys must be immutable (str, int, tuple)
    "courses" : ["Python", "Math", "Physics"]← values can be anything
}
dict_basics.py
PYTHON
# Creating a dictionary
student = {
    "name"   : "Fatima Malik",
    "age"    : 20,
    "gpa"    : 3.92,
    "active" : True
}

# Accessing values by key
print(student["name"])    # Fatima Malik
print(student["gpa"])     # 3.92

# .get() — safe access (no KeyError)
print(student.get("age"))         # 20
print(student.get("email"))       # None (key missing)
print(student.get("email", "N/A")) # N/A (default provided)

# Add or update a key
student["email"] = "fatima@bwb.com"  # add new
student["age"]   = 21                # update existing

# Delete a key
del student["active"]
removed = student.pop("gpa")       # remove + return
print(f"Removed GPA: {removed}")

# Check if key exists
print("name" in student)    # True
print("phone" in student)   # False

print(len(student))          # number of key-value pairs

Iterating Dictionaries

Three views let you loop over different aspects of a dictionary. Each returns a special view object that always reflects the current state of the dict.

MethodReturnsYields
.keys()dict_keysAll keys — same as for k in d
.values()dict_valuesAll values
.items()dict_items(key, value) tuples — most useful for loops
.update(d2)NoneMerge d2 into dict — overwrites duplicate keys
.setdefault(k, v)valueGet value for k; if missing, insert k:v first
.copy()dictShallow copy of the dictionary
dict_iteration.py
PYTHON
scores = {"Ali": 88, "Sara": 95, "Zara": 72}

# Loop over keys
for name in scores:
    print(name, end=" ")   # Ali Sara Zara

# Loop over values
for score in scores.values():
    print(score, end=" ")  # 88 95 72

# Loop over key-value pairs — most common
for name, score in scores.items():
    grade = "A" if score >= 80 else "B"
    print(f"  {name:8} {score}  {grade}")

# Merge two dicts
extra  = {"Omar": 80, "Sara": 98}   # Sara's score updated
scores.update(extra)
print(scores)
# {'Ali': 88, 'Sara': 98, 'Zara': 72, 'Omar': 80}

# Dict comprehension
doubled = {k: v * 2 for k, v in scores.items()}
passing = {k: v for k, v in scores.items() if v >= 80}
print(passing)   # {'Ali': 88, 'Sara': 98, 'Omar': 80}

Nested Dicts & Real Patterns

Dictionaries are the building block of almost all Python data — JSON APIs, database records, configuration files, and objects are all represented as nested dicts in practice.

nested_dicts.py
PYTHON
# ── Nested dict — user record ─────────────────
user = {
    "id"      : 1001,
    "name"    : "Ali Hassan",
    "address" : {
        "city"   : "Lahore",
        "country": "Pakistan"
    },
    "courses" : ["Python", "Web Dev"]
}
print(user["address"]["city"])   # Lahore
print(user["courses"][0])         # Python

# ── Word frequency counter ────────────────────
text  = "apple banana apple mango banana apple"
freq  = {}
for word in text.split():
    freq[word] = freq.get(word, 0) + 1
print(freq)   # {'apple': 3, 'banana': 2, 'mango': 1}

# ── Grouping items ────────────────────────────
students = [
    {"name": "Ali",  "grade": "A"},
    {"name": "Sara", "grade": "B"},
    {"name": "Zara", "grade": "A"},
]
by_grade = {}
for s in students:
    g = s["grade"]
    by_grade.setdefault(g, []).append(s["name"])
print(by_grade)   # {'A': ['Ali', 'Zara'], 'B': ['Sara']}
dict.get(key, 0) + 1 is the frequency counter pattern
The word frequency counter is one of the most important dictionary patterns. .get(word, 0) safely returns 0 if the word hasn't been seen yet, so you can safely add 1. This pattern shows up in interview questions, log analysers, text processors, and data science constantly.

Sets — Unique Unordered Collections

A set is an unordered collection of unique items. Duplicate values are automatically discarded. Sets are incredibly fast for membership testing and are the go-to tool for deduplication and mathematical set operations.

Sets use { } but without key-value pairs. Creating an empty set requires set() — not {}, which creates an empty dict.

|
Union
A | B or A.union(B)
All unique items from both sets combined
&
Intersection
A & B or A.intersection(B)
Only items that exist in BOTH sets
-
Difference
A - B or A.difference(B)
Items in A that are NOT in B
^
Symmetric Difference
A ^ B or A.symmetric_difference(B)
Items in either A or B but NOT both
sets.py
PYTHON
# Creating sets — duplicates auto-removed
colours = {"red", "blue", "green", "red"}
print(colours)    # {'blue', 'green', 'red'} — order not guaranteed

empty_set = set()   # NOT {} (that's an empty dict!)

# Set from a list — fast deduplication
nums   = [1, 2, 2, 3, 3, 3, 4]
unique = set(nums)
print(unique)      # {1, 2, 3, 4}

# Set operations
python_students = {"Ali", "Sara", "Zara", "Omar"}
webdev_students = {"Sara", "Bilal", "Zara"}

print(python_students | webdev_students)   # Union — all students
print(python_students & webdev_students)   # {'Sara', 'Zara'} — in both
print(python_students - webdev_students)   # {'Ali', 'Omar'} — Python only
print(python_students ^ webdev_students)   # {'Ali', 'Omar', 'Bilal'} — not shared

# Membership test — O(1) speed
print("Sara" in python_students)    # True — instant lookup

# Add and remove
python_students.add("Nadia")
python_students.discard("Omar")   # safe — no error if missing

# Subset / superset checks
print({"Sara", "Zara"}.issubset(python_students))    # True
💡
The fastest way to deduplicate a list
unique = list(set(my_list)) removes all duplicates instantly. The conversion to set discards duplicates, then converting back to list gives you a clean list. Note: this does NOT preserve order. If order matters, use list(dict.fromkeys(my_list)) instead (Python 3.7+).

Choosing the Right Collection

Python's four main collection types each have a distinct purpose. Picking the right one for your data makes code cleaner, faster, and less error-prone.

📋
list [ ]
Ordered, mutable, duplicates OK. Shopping cart, queue, history, rankings.
🔒
tuple ( )
Ordered, immutable. Coordinates, RGB, DB rows, function return values.
📖
dict { : }
Key-value, fast lookup. User profiles, config, JSON, word counts, grouping.
🎯
set { }
Unique items, unordered. Deduplication, membership test, set math.
choosing_collections.py
PYTHON
# list — ordered, can change, can repeat
cart = ["apple", "milk", "apple"]   # duplicates fine

# tuple — fixed record, hashable (can be dict key)
coord = (33.6844, 73.0479)   # Islamabad lat/lon
locations = {coord: "Islamabad"}  # tuple as dict key

# dict — label your data
config = {"debug": False, "port": 8000, "host": "0.0.0.0"}

# set — fast membership, remove duplicates
visited_pages = {"/home", "/about", "/home"}  # only 2 stored
if "/contact" not in visited_pages:
    print("First visit to /contact")

# Practical: find students enrolled in ALL three courses
python = {"Ali", "Sara", "Zara", "Omar"}
webdev = {"Sara", "Zara", "Bilal"}
data   = {"Zara", "Sara", "Nadia"}
all_three = python & webdev & data
print(f"Enrolled in all 3: {all_three}")   # {'Sara', 'Zara'}
🧩 Knowledge Check
5 questions — Dictionaries & Sets
1. What does d.get("email", "N/A") return when "email" is not in dict d?
2. Which dict method gives you (key, value) pairs for looping?
3. What is {1, 2, 3} & {2, 3, 4}?
4. What does {} create in Python?
5. What is the output of {"a":1,"b":2,"c":3}["b"]?
🏗️
Coding Challenge — Contact Book App
Dicts, sets, nested structures, comprehensions combined
Task: Build a contact book that:

1. Stores contacts as a dict of dicts: {"Ali": {"phone": "...", "email": "...", "tags": {...}}}
2. Lets the user add, view, search, and delete contacts via a while-loop menu
3. Contact tags are stored as a set (e.g. {"friend", "work"})
4. Uses a dict comprehension to find all contacts tagged with a specific tag
5. Uses .get() for safe lookups and in for existence checks
6. On exit, shows a summary: total contacts and all unique tags (union of all tag sets)
💡 Show hints
  • Structure: contacts = {"Ali": {"phone": "...", "tags": {"friend"}}}
  • Add tags as set: {"friend", "work"} — auto-deduplicates
  • Find by tag: {n: c for n, c in contacts.items() if tag in c["tags"]}
  • All unique tags: all_tags = set().union(*[c["tags"] for c in contacts.values()])
contact_book.py — Sample Solution
PYTHON
# ── Contact Book ──────────────────────────────
contacts = {}

def show_menu():
    print("\n  1) Add contact   2) View contact")
    print("  3) Search by tag 4) Delete contact")
    print("  5) List all     6) Quit")

while True:
    show_menu()
    choice = input("  Choose: ").strip()

    if choice == "1":
        name  = input("  Name  : ").strip().title()
        phone = input("  Phone : ").strip()
        email = input("  Email : ").strip().lower()
        tags  = set(input("  Tags (comma-sep): ").split(","))
        contacts[name] = {"phone": phone, "email": email, "tags": tags}
        print(f"  ✅ {name} added.")

    elif choice == "2":
        name = input("  Name: ").strip().title()
        c    = contacts.get(name)
        if c:
            print(f"  📞 {c['phone']}  📧 {c['email']}  🏷️  {', '.join(c['tags'])}")
        else: print("  ❌ Not found.")

    elif choice == "3":
        tag    = input("  Tag: ").strip().lower()
        result = {n: c for n, c in contacts.items() if tag in c["tags"]}
        for n in result: print(f"  → {n}")

    elif choice == "4":
        name = input("  Name: ").strip().title()
        contacts.pop(name, None)
        print(f"  🗑️  {name} deleted.")

    elif choice == "5":
        for n, c in contacts.items():
            print(f"  {n:15} {c['phone']}")

    elif choice == "6":
        all_tags = set().union(*[c["tags"] for c in contacts.values()]) if contacts else set()
        print(f"\n  Contacts: {len(contacts)}  |  All tags: {', '.join(all_tags) or 'none'}")
        break
🎉
Lesson 9 Complete!
All Python collections mastered. One lesson left in Phase 2 — Functions — the most important concept in programming!
← Course Home
Phase 2 · Control FlowLesson 9 of 6