Python dict vs set: Lookup, Deduplication and 8 Tested Examples
A Python dictionary stores key-value pairs; a set stores unique elements. Use a dict when a key must retrieve a value, a set when membership or unique values are the result, and a list when position and repeated items matter. This lesson explains dict vs set with eight tested examples, a frequency-count trace, predictable output and exam-practice answers.
1. Dict vs set vs list
| Property | dict | set | list |
|---|---|---|---|
| Main purpose | Map keys to values | Unique members | Ordered sequence |
| Membership | Checks keys | Checks elements | Checks elements |
| Repeated entries | One value per equal key | Equal elements merge | Duplicates allowed |
| Order guarantee | Insertion order, Python3.7+ | No insertion-order guarantee | Position order |
| Required hashability | Keys only | Elements | Not required |
| Empty value | {} | set() | [] |
Dictionary values can repeat and may be mutable. Neither a dict nor a set provides positional indexing: data[0] on a dict requests the key0, not its first entry. A set cannot be indexed at all. Insertion order in a dictionary is not sorted order.
2. Trace a word counter
| Step | Word | Old count | Dictionary afterward |
|---|---|---|---|
| 1 | os | 0 | {'os': 1} |
| 2 | dbms | 0 | {'os': 1, 'dbms': 1} |
| 3 | os | 1 | {'os': 2, 'dbms': 1} |
3. Eight tested examples
Example 1: Dictionary membership checks keys
marks = {"Aman": 91, "Riya": 82}
print("Aman" in marks)
print(91 in marks)
print(91 in marks.values())
print(list(marks.items()))Output
True
False
True
[('Aman', 91), ('Riya', 82)]A dictionary maps unique keys to values. The in operator checks keys, not values. Searching values is a different operation that may scan many entries.
Example 2: Missing keys: indexing versus get
marks = {"Aman": 91}
try:
print(marks["Neha"])
except KeyError:
print("missing key")
print(marks.get("Neha", 0))
print("Neha" in marks)
marks["Riya"] = None
print(marks.get("Riya", 0))Output
missing key 0 False None
get returns its default only when the key is absent; a stored None remains None. get does not insert the missing key. A zero fallback may be wrong when absence must be distinguished from a real zero.
Example 3: Count repeated words
words = ["os", "dbms", "os", "cn", "dbms", "os"]
counts = {}
for word in words:
counts[word] = counts.get(word, 0) + 1
print(counts)
print(sorted(counts.items(), key=lambda row: (-row[1], row[0])))Output
{'os': 3, 'dbms': 2, 'cn': 1}
[('os', 3), ('dbms', 2), ('cn', 1)]Each word becomes a key and its frequency becomes the value. Updating a key replaces the old count without moving its insertion position. The final sorted list uses an explicit alphabetical tie-breaker.
Example 4: Deduplicate without promising set order
names = ["Riya", "Aman", "Riya", "Neha", "Aman"]
print(sorted(set(names)))
print(list(dict.fromkeys(names)))
print(type({}).__name__)
print(type(set()).__name__)Output
['Aman', 'Neha', 'Riya'] ['Riya', 'Aman', 'Neha'] dict set
A set retains unique elements but has no insertion-order guarantee. sorted makes its display deterministic here. dict.fromkeys preserves first appearance under the dictionary insertion-order guarantee in Python3.7+. Empty braces create a dictionary; set() creates an empty set.
Example 5: Set union, intersection and differences
a = {"os", "dbms", "cn"}
b = {"dbms", "python"}
print(sorted(a | b))
print(sorted(a & b))
print(sorted(a - b))
print(sorted(a ^ b))
print({"os"} <= a)Output
['cn', 'dbms', 'os', 'python'] ['dbms'] ['cn', 'os'] ['cn', 'os', 'python'] True
Union combines elements, intersection keeps shared elements, a-b keeps only elements in a, and symmetric difference keeps elements in exactly one set. <= tests a subset, including equality; < requires a proper subset.
Example 6: Keys must be hashable
mapping = {(1, 2): "seat"}
print(mapping[(1, 2)])
for key in ([1, 2], (1, [2])):
try:
mapping[key] = "value"
except TypeError:
print("unhashable")
print(len({True, 1, 1.0}))Output
seat unhashable unhashable 1
Lists cannot be dictionary keys or set elements. A tuple is hashable only when its contents are hashable. True, 1 and 1.0 compare equal and have compatible hashes, so this set has one element. Choose keys that represent the distinctions your application needs.
Example 7: Dictionary order after updates and reinsertion
data = {"a": 1, "b": 2, "c": 3}
data["b"] = 20
print(list(data))
del data["b"]
data["b"] = 200
print(list(data))
view = data.keys()
data["d"] = 4
print(list(view))Output
['a', 'b', 'c'] ['a', 'c', 'b'] ['a', 'c', 'b', 'd']
Replacing a value does not move an existing key. Deleting and reinserting a key puts it at the end. keys is a dynamic view, not a frozen snapshot. Do not change dictionary size while iterating the live view; use a separate list when deleting selected keys.
Example 8: Safe removals and set comprehensions
data = {"a": 0, "b": 2, "c": 0}
for key in list(data):
if data[key] == 0:
del data[key]
print(data)
values = {1, 2, 3}
values.discard(9)
try:
values.remove(9)
except KeyError:
print("missing element")
print(sorted({n * n for n in range(-2, 3)}))Output
{'b': 2}
missing element
[0, 1, 4]list(data) snapshots the keys before deletion. discard ignores an absent element; remove raises KeyError. A set comprehension discards duplicate results, so squaring negative and positive inputs produces fewer values than inputs.
4. Hashable does not simply mean immutable
Hashability means an object has a hash value that stays unchanged during its lifetime and supports compatible equality: equal objects must have equal hashes. Built-in lists, dictionaries and mutable sets are unhashable. A tuple containing a list is also unhashable. A frozenset can represent a hashable collection of hashable members.
Hash collisions are allowed: different objects can have the same hash. Equality checks distinguish unequal keys when collisions occur. Never treat hash(value) as a globally unique ID, a persistent storage key or a password hash. Some built-in hash values vary between interpreter runs. Do not rely on raw set display order in an expected-output question.
5. Operation costs and practical limits
In typical CPython hash-table use, dictionary key lookup and set membership are average O(1) with well-behaved hashes and bounded hash/equality costs. This is an average-case model, not a language promise that every lookup takes constant time. Collisions can make lookup O(n), and computing a hash or comparing a large key also costs work. Checking a value in a list or dict.values() can require a linear scan.
The frequency counter processes n words with average O(n) dictionary work under that model and O(u) dictionary entries for u distinct words, before accounting for the storage of the words themselves. Sorting u results afterward adds O(u log u) comparisons. Building a set for one membership test also costs a pass over the input; a prebuilt set is more useful for repeated tests. These examples are semantic checks, not timing benchmarks.
6. Common traps
| Trap | Better contract |
|---|---|
| Use {} for an empty set | Use set(); {} is a dict |
| Expect dict membership to search values | Choose key lookup or a value scan deliberately |
| Use set to deduplicate while preserving input order | Use dict.fromkeys for hashable items, or a stated custom strategy |
| Treat get as insertion | Assign explicitly when the missing key should be stored |
| Delete keys while iterating a live dict | Iterate a separate key list or build a new dict |
| Assume all tuples are valid keys | Every tuple component must be hashable |
| Use one mutable value with dict.fromkeys | Build independent values with a comprehension when required |
For example, dict.fromkeys(["a","b"], []) gives both keys the same list, not two independent lists. A dictionary comprehension with a fresh [] for each key avoids that shared-value trap. This is separate from the immutable None values used for order-preserving deduplication above.
7. Practice and exam Q&A
Whether x is a key. It is True even though its value is zero.
One key a with value2. The later value replaces the earlier one.
Yes. Key uniqueness does not require value uniqueness.
No. It returns a fallback without inserting a missing key.
For hashable items, list(dict.fromkeys(items)) preserves first appearance in Python3.7+. It does not deep-copy the items.
Only when all its components are hashable. A tuple containing a list cannot be used as a key.
a-b keeps members only in a. a^b keeps members present in exactly one operand.
They compare equal and have compatible hashes. Use a different key representation if type distinctions matter.
No. Hash collisions and expensive key operations can change the cost; worst-case lookup can be linear.
It gives deterministic output for these mutually orderable strings or numbers. It does not add an ordering guarantee to the original set.
8. Revision exercise
Input subjects are ["os", "dbms", "os", "cn", "dbms", "os"]. Predict the count dictionary, the alphabetical unique list and the first-appearance list before running Example3 and Example4 with subjects instead of names. The answers are {'os': 3, 'dbms': 2, 'cn': 1}, ['cn', 'dbms', 'os'] and ['os', 'dbms', 'cn']. Explain why these are three different outputs for three different requirements.
Then add an absent subject lookup with a zero fallback. Confirm that the dictionary still has three keys afterward. Finally, write a comprehension that creates independent empty lists for the three subject keys; append a score to one list and check that the other two remain empty.
Official sources and related lessons
- Python tutorial: sets, dictionaries and looping techniques
- Python built-in types: dict ordering, views and methods
- Python built-in types: set and frozenset operations
- Python wiki: CPython average and worst-case operation costs
Continue with sorted vs list.sort, itertools grouping and totals, or the Exam Notes hub. These are learning and exam-practice notes, not official university questions.
Comments
Post a Comment