Dictionaries and sets are two of Python's most useful built-in data structures. A dictionary stores data as key-value pairs — the Python equivalent of JavaScript objects. A set stores unique values with no duplicates and no guaranteed order. Both are implemented using hash tables under the hood, which gives them very fast lookup operations regardless of how large they grow.
Dictionaries
A dictionary is an ordered (since Python 3.7), mutable collection of key-value pairs. Keys must be unique and immutable (strings, numbers, or tuples). Values can be anything.
user = {
"name": "Wariz",
"age": 20,
"city": "Lagos",
"is_active": True
}
Accessing Values
user["name"] # "Wariz" — bracket notation
user.get("age") # 20 — get() method
user.get("email") # None — returns None if key doesn't exist
user.get("email", "not provided") # "not provided" — custom default
The difference between [] and .get() is important: accessing a non-existent key with [] raises a KeyError. .get() returns None (or a default) instead — making it safer for keys you are not sure exist.
user["email"] # ❌ KeyError: 'email'
user.get("email") # ✅ None
Adding and Modifying Items
user["email"] = "wariz@example.com" # Add a new key
user["age"] = 21 # Update an existing key
Removing Items
user.pop("city") # Removes "city" and returns its value
user.pop("phone", None) # Returns None if key doesn't exist — no error
del user["is_active"] # Removes "is_active" — KeyError if missing
user.clear() # Removes all items
Checking for Keys
"name" in user # True
"email" in user # False
"name" not in user # False
Dictionary Methods
Accessing Keys, Values, and Pairs
user = {"name": "Wariz", "age": 20, "city": "Lagos"}
user.keys() # dict_keys(["name", "age", "city"])
user.values() # dict_values(["Wariz", 20, "Lagos"])
user.items() # dict_items([("name", "Wariz"), ("age", 20), ("city", "Lagos")])
These return view objects — they reflect changes to the dictionary in real time. Convert to a list if you need a static snapshot:
list(user.keys()) # ["name", "age", "city"]
Iterating Over a Dictionary
# Iterate over keys (default)
for key in user:
print(key)
# Iterate over values
for value in user.values():
print(value)
# Iterate over key-value pairs
for key, value in user.items():
print(f"{key}: {value}")
Merging Dictionaries
defaults = {"theme": "light", "language": "en"}
settings = {"theme": "dark", "notifications": True}
# Python 3.9+ — merge operator
merged = defaults | settings
# {"theme": "dark", "language": "en", "notifications": True}
# Update in place
defaults.update(settings)
# defaults is now {"theme": "dark", "language": "en", "notifications": True}
When keys overlap, the right-hand dictionary's values take precedence.
setdefault()
Returns the value of a key if it exists. If not, inserts it with a default value:
user = {"name": "Wariz"}
user.setdefault("age", 0) # Inserts age: 0, returns 0
user.setdefault("name", "Unknown") # "name" already exists — returns "Wariz"
copy()
original = {"name": "Wariz", "age": 20}
copy = original.copy() # Shallow copy
Same caveat as lists: assigning a dictionary to a new variable does not copy it.
Dictionary Comprehensions
Like list comprehensions, dictionary comprehensions provide a concise way to create dictionaries:
# {key_expression: value_expression for item in iterable}
squares = {x: x ** 2 for x in range(1, 6)}
# {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
With a condition:
even_squares = {x: x ** 2 for x in range(1, 11) if x % 2 == 0}
# {2: 4, 4: 16, 6: 36, 8: 64, 10: 100}
Inverting a dictionary (swapping keys and values):
original = {"a": 1, "b": 2, "c": 3}
inverted = {v: k for k, v in original.items()}
# {1: "a", 2: "b", 3: "c"}
Nested Dictionaries
Dictionaries can be nested — values can themselves be dictionaries:
users = {
"user_1": {"name": "Wariz", "age": 20},
"user_2": {"name": "Ada", "age": 25}
}
users["user_1"]["name"] # "Wariz"
users["user_2"]["age"] # 25
Sets
A set is an unordered collection of unique values. Sets are mutable but their items must be immutable (no lists or dictionaries as set items). Because sets are unordered, they do not support indexing or slicing.
colours = {"red", "green", "blue"}
numbers = {1, 2, 3, 3, 4, 4, 5} # Duplicates are automatically removed
print(numbers) # {1, 2, 3, 4, 5}
Creating an Empty Set
empty_set = set() # ✅ Correct
empty_dict = {} # ❌ This creates an empty dictionary, not a set
Adding and Removing Items
colours = {"red", "green", "blue"}
colours.add("yellow") # Add a single item
colours.update(["pink", "purple"]) # Add multiple items
colours.remove("red") # Removes "red" — KeyError if not found
colours.discard("orange") # Removes if present — no error if not found
colours.pop() # Removes and returns an arbitrary item
colours.clear() # Removes all items
Membership Testing
Sets have O(1) lookup — checking membership is extremely fast regardless of the set's size. This is one of the primary reasons to use a set over a list when you need to check for existence:
allowed = {"admin", "editor", "moderator"}
"editor" in allowed # True — very fast
"viewer" not in allowed # True
Set Operations
Sets support the classical mathematical set operations:
Union — all items from both sets
a = {1, 2, 3}
b = {3, 4, 5}
a | b # {1, 2, 3, 4, 5}
a.union(b) # Same result
Intersection — items present in both sets
a & b # {3}
a.intersection(b) # Same result
Difference — items in a but not in b
a - b # {1, 2}
a.difference(b) # Same result
Symmetric Difference — items in either but not both
a ^ b # {1, 2, 4, 5}
a.symmetric_difference(b) # Same result
Subset and Superset
{1, 2}.issubset({1, 2, 3}) # True — {1, 2} is a subset of {1, 2, 3}
{1, 2, 3}.issuperset({1, 2}) # True
{1, 2}.isdisjoint({3, 4}) # True — no items in common
Practical Use Cases for Sets
Removing duplicates from a list
numbers = [1, 2, 2, 3, 3, 3, 4]
unique = list(set(numbers))
print(unique) # [1, 2, 3, 4] — order not guaranteed
Fast membership testing
# List lookup — O(n), slower for large collections
allowed_list = ["admin", "editor", "moderator"]
"editor" in allowed_list # Scans the list
# Set lookup — O(1), always fast
allowed_set = {"admin", "editor", "moderator"}
"editor" in allowed_set # Hash lookup — nearly instant
Finding common or unique items between two collections
team_a = {"Wariz", "Ada", "Aliyu"}
team_b = {"Ada", "Bola", "Chidi"}
both_teams = team_a & team_b # {"Ada"} — in both
only_a = team_a - team_b # {"Wariz", "Aliyu"} — only in A
all_members = team_a | team_b # All five members
Tuples — Immutable Sequences
While tuples are covered more fully in the Python-Specific Features topic, they are closely related to lists and worth introducing here. A tuple is an immutable, ordered sequence — once created, its contents cannot be changed.
coordinates = (40.7128, -74.0060)
rgb = (255, 0, 128)
single = (42,) # Trailing comma required for a single-item tuple
Tuples support the same indexing and slicing as lists, but none of the modification methods (append, remove, etc.).
Tuples are commonly used for:
- Fixed data that should not change (coordinates, RGB values, database rows).
- Returning multiple values from a function (Python implicitly packs them into a tuple).
- Dictionary keys (lists cannot be dictionary keys; tuples can).
Summary
Dictionary Methods
| Method | What It Does |
|---|---|
d[key] | Access value — KeyError if missing |
d.get(key, default) | Access value — returns default if missing |
d[key] = value | Add or update a key |
d.pop(key) | Remove and return value |
del d[key] | Remove key — KeyError if missing |
d.keys() | Returns all keys |
d.values() | Returns all values |
d.items() | Returns all key-value pairs |
d.update(other) | Merges another dict in place |
d1 | d2 | Returns merged dict (Python 3.9+) |
d.setdefault(key, val) | Insert key with default if not present |
d.copy() | Shallow copy |
d.clear() | Remove all items |
Set Methods
| Method | What It Does |
|---|---|
s.add(item) | Add a single item |
s.update(iterable) | Add multiple items |
s.remove(item) | Remove item — KeyError if missing |
s.discard(item) | Remove item — no error if missing |
s.pop() | Remove and return arbitrary item |
s | t | Union |
s & t | Intersection |
s - t | Difference |
s ^ t | Symmetric difference |
s.issubset(t) | True if s is a subset of t |
s.issuperset(t) | True if s is a superset of t |
s.isdisjoint(t) | True if s and t share no items |