Quiz: Dictionaries and Sets
Test your understanding before moving on. Target: 70% or higher to proceed confidently.
Section 1: Multiple Choice (1 point each)
1. Which of the following correctly creates a dictionary?
- A)
d = ["name": "Alice", "age": 30] - B)
d = {"name": "Alice", "age": 30} - C)
d = ("name": "Alice", "age": 30) - D)
d = set("name": "Alice", "age": 30)
Answer
**B)** `d = {"name": "Alice", "age": 30}` *Why B:* Dictionaries use curly braces with key-value pairs separated by colons. *Why not A:* Square brackets create lists, and lists don't support the colon key-value syntax. *Why not C:* Parentheses create tuples, not dictionaries. *Why not D:* Sets don't store key-value pairs and this isn't valid set syntax. *Reference:* Section 9.2.12. What happens when you execute inventory["shield"] and "shield" is not a key in the dictionary?
- A) Returns
None - B) Returns
0 - C) Raises a
KeyError - D) Raises a
ValueError
Answer
**C)** Raises a `KeyError` *Why C:* Bracket access on a missing key raises `KeyError` — Python's way of telling you the key doesn't exist. *Why not A:* `.get()` returns `None` by default, but bracket access does not. *Why not B:* No default numeric value is returned. *Why not D:* `ValueError` is for wrong values in the right type. Missing keys are `KeyError`. *Reference:* Section 9.2.23. What does config.get("timeout", 30) return if "timeout" IS a key in config with value 60?
- A)
30 - B)
60 - C)
None - D)
(60, 30)
Answer
**B)** `60` *Why B:* `.get()` returns the actual value when the key exists. The default `30` is only used if the key is *missing*. *Why not A:* `30` is the default, only used when the key doesn't exist. *Why not C:* `None` is the default when no second argument is given to `.get()` and the key is missing. *Why not D:* `.get()` returns a single value, not a tuple. *Reference:* Section 9.2.34. Which method gives you both keys and values when iterating over a dictionary?
- A)
.keys() - B)
.values() - C)
.items() - D)
.pairs()
Answer
**C)** `.items()` *Why C:* `.items()` returns `(key, value)` tuples, allowing you to unpack both in a `for` loop. *Why not A:* `.keys()` returns only the keys. *Why not B:* `.values()` returns only the values. *Why not D:* `.pairs()` doesn't exist as a dictionary method. *Reference:* Section 9.4.35. What is the output of this code?
d = {"a": 1, "b": 2, "c": 3}
result = {v: k for k, v in d.items()}
print(result)
- A)
{"a": 1, "b": 2, "c": 3} - B)
{1: "a", 2: "b", 3: "c"} - C)
{"1": "a", "2": "b", "3": "c"} - D)
KeyError
Answer
**B)** `{1: "a", 2: "b", 3: "c"}` *Why B:* The comprehension swaps keys and values: `v` (the original value) becomes the new key, `k` (the original key) becomes the new value. *Why not A:* That's the original dictionary unchanged. *Why not C:* The values (integers) become keys as-is; they don't get converted to strings. *Why not D:* All the keys exist; no missing-key access happens. *Reference:* Section 9.5.36. What does {} create in Python?
- A) An empty set
- B) An empty dictionary
- C) An empty tuple
- D) A syntax error
Answer
**B)** An empty dictionary *Why B:* Due to historical reasons, `{}` defaults to an empty dictionary. To create an empty set, you must use `set()`. *Why not A:* This is a common misconception. `{}` is a dict, not a set. *Why not C:* `()` creates an empty tuple. *Why not D:* `{}` is valid syntax — it creates an empty dict. *Reference:* Section 9.7.17. Which of the following CANNOT be used as a dictionary key?
- A)
"hello" - B)
(1, 2, 3) - C)
[1, 2, 3] - D)
42
Answer
**C)** `[1, 2, 3]` *Why C:* Lists are mutable and therefore unhashable. Dictionary keys must be hashable (immutable). *Why not A:* Strings are immutable and hashable. *Why not B:* Tuples (of immutable elements) are immutable and hashable. *Why not D:* Integers are immutable and hashable. *Reference:* Section 9.9.28. What is the average-case time complexity of looking up a key in a dictionary?
- A) O(n)
- B) O(log n)
- C) O(1)
- D) O(n^2)
Answer
**C)** O(1) *Why C:* Dictionaries use hash tables, which provide constant-time lookup on average — the time doesn't increase with the size of the dictionary. *Why not A:* O(n) is the time for searching an unsorted list. *Why not B:* O(log n) is the time for binary search on a sorted sequence. *Why not D:* O(n^2) would be nested loops, far worse than any standard lookup. *Reference:* Section 9.9, Threshold Concept9. Given s1 = {1, 2, 3, 4} and s2 = {3, 4, 5, 6}, what is s1 & s2?
- A)
{1, 2, 3, 4, 5, 6} - B)
{3, 4} - C)
{1, 2} - D)
{1, 2, 5, 6}
Answer
**B)** `{3, 4}` *Why B:* `&` is the intersection operator — it returns elements that appear in *both* sets. *Why not A:* That's the union (`|`), which includes all elements from both sets. *Why not C:* That's the difference (`s1 - s2`), elements in `s1` but not `s2`. *Why not D:* That's the symmetric difference (`s1 ^ s2`), elements in exactly one set. *Reference:* Section 9.810. What does set([1, 2, 2, 3, 3, 3]) produce?
- A)
[1, 2, 3] - B)
{1, 2, 3} - C)
{1: 1, 2: 2, 3: 3} - D)
{1, 2, 2, 3, 3, 3}
Answer
**B)** `{1, 2, 3}` *Why B:* Sets automatically remove duplicates. Converting a list to a set keeps only unique elements. *Why not A:* `set()` returns a set, not a list. *Why not C:* Sets don't have key-value pairs. *Why not D:* Sets cannot contain duplicate elements — that's their defining property. *Reference:* Section 9.7.1Section 2: Short Answer (2 points each)
11. Write a single line of code that safely gets the value for key "email" from a dictionary user, returning "not provided" if the key doesn't exist.
Answer
user.get("email", "not provided")
The `.get()` method takes an optional second argument as the default value to return when the key is missing.
*Reference:* Section 9.2.3
12. What's wrong with this code, and how would you fix it?
for key in inventory:
if inventory[key] == 0:
del inventory[key]
Answer
You cannot modify a dictionary's keys while iterating over it — this raises a `RuntimeError: dictionary changed size during iteration`. **Fix:** Iterate over a copy of the keys:for key in list(inventory):
if inventory[key] == 0:
del inventory[key]
Or use a dictionary comprehension:
inventory = {k: v for k, v in inventory.items() if v != 0}
*Reference:* Section 9.3.3
13. Write a dictionary comprehension that creates a dictionary mapping each word in the list ["hello", "world", "python"] to its length.
Answer
{word: len(word) for word in ["hello", "world", "python"]}
Result: `{"hello": 5, "world": 5, "python": 6}`
*Reference:* Section 9.5
14. Explain why [1, 2, 3] cannot be a dictionary key but (1, 2, 3) can.
Answer
Lists are mutable — their contents can change after creation. If a list were used as a key, and then its contents changed, the hash would no longer match, and the key-value pair would be "lost" in the hash table. Tuples are immutable, so their hash never changes, making them safe to use as dictionary keys. In Python's terms: lists are "unhashable" and tuples are "hashable." *Reference:* Section 9.9.215. Given sets a = {1, 2, 3, 4, 5} and b = {4, 5, 6, 7, 8}, write expressions for:
- Elements in a but not in b
- Elements in either set but not both
Answer
a - b # {1, 2, 3} — difference
a ^ b # {1, 2, 3, 6, 7, 8} — symmetric difference
The `-` operator gives the difference (elements in the left set only), and `^` gives the symmetric difference (elements in exactly one of the two sets).
*Reference:* Section 9.8
Section 3: Code Analysis (2 points each)
16. What is the output of this code?
counts = {}
for char in "banana":
counts[char] = counts.get(char, 0) + 1
print(counts)
Answer
{'b': 1, 'a': 3, 'n': 2}
The code counts character frequencies using the `.get()` pattern. Each character's count starts at 0 (via the default) and gets incremented by 1 each time it appears.
*Reference:* Section 9.4.4
17. What is the output of this code?
d = {"x": 10, "y": 20, "z": 30}
result = d.pop("y")
print(result, d)
Answer
20 {'x': 10, 'z': 30}
`.pop("y")` removes the key `"y"` from the dictionary and returns its value (`20`). The dictionary now has only two entries.
*Reference:* Section 9.3.3
18. What is the output of this code?
from collections import Counter
c = Counter("abracadabra")
print(c.most_common(3))
Answer
[('a', 5), ('b', 2), ('r', 2)]
`Counter` counts each character: a=5, b=2, r=2, c=1, d=1. `.most_common(3)` returns the three most frequent as a list of (element, count) tuples, sorted by count descending.
*Reference:* Section 9.10.2
19. What is the output of this code?
a = {1, 2, 3}
b = {2, 3, 4}
print(a | b)
print(a - b)
print(a ^ b)
Answer
{1, 2, 3, 4}
{1}
{1, 4}
- `a | b` is the union: all elements from both sets.
- `a - b` is the difference: elements in `a` but not in `b`.
- `a ^ b` is the symmetric difference: elements in exactly one set (not both).
*Reference:* Section 9.8
Section 4: Applied Problem (3 points)
20. Write a function analyze_text(text) that takes a string and returns a dictionary with:
- "word_count": total number of words
- "unique_words": number of unique words (case-insensitive)
- "most_common": the most frequent word and its count as a tuple
- "average_word_length": average length of all words, rounded to 1 decimal
Test with: "The quick brown fox jumps over the lazy dog the fox"
Answer
def analyze_text(text):
words = text.lower().split()
word_counts = {}
for word in words:
word_counts[word] = word_counts.get(word, 0) + 1
most_common_word = max(word_counts, key=word_counts.get)
avg_length = sum(len(w) for w in words) / len(words)
return {
"word_count": len(words),
"unique_words": len(word_counts),
"most_common": (most_common_word, word_counts[most_common_word]),
"average_word_length": round(avg_length, 1),
}
result = analyze_text("The quick brown fox jumps over the lazy dog the fox")
print(result)
# {'word_count': 11, 'unique_words': 8, 'most_common': ('the', 3),
# 'average_word_length': 3.5}
*Reference:* Sections 9.4.4, 9.5, 9.7