Building Blocks Quiz
Quiz
s = "Python"
print(s[::2])[::2] starts at index 0, goes to the end, with step 2 (every 2nd character). This gives: P(0), t(2), o(4) = “Pto”.[::2] starts at index 0, goes to the end, with step 2 (every 2nd character). This gives: P(0), t(2), o(4) = “Pto”.x in list) is O(n) because it requires linear search.x in list) is O(n) because it requires linear search..title() method capitalizes the first letter of each word. .capitalize() only capitalizes the first letter of the entire string..title() method capitalizes the first letter of each word. .capitalize() only capitalizes the first letter of the entire string.words = ['apple', 'banana', 'cherry']
result = ', '._____(words)join() method is called on the separator string and takes an iterable as argument: separator.join(iterable). This produces ‘apple, banana, cherry’.join() method is called on the separator string and takes an iterable as argument: separator.join(iterable). This produces ‘apple, banana, cherry’..replace() or .upper() return new strings..replace() or .upper() return new strings."Ha" * 3?* operator with strings performs repetition, concatenating the string with itself n times. “Ha” * 3 = “HaHaHa”.* operator with strings performs repetition, concatenating the string with itself n times. “Ha” * 3 = “HaHaHa”.numbers = [1, 2, 3]
numbers.append(4)
numbers.extend([5, 6])
print(len(numbers))append(4) adds one element → [1, 2, 3, 4] (length 4). extend([5, 6]) adds two elements → [1, 2, 3, 4, 5, 6] (length 6).append(4) adds one element → [1, 2, 3, 4] (length 4). extend([5, 6]) adds two elements → [1, 2, 3, 4, 5, 6] (length 6)..remove() and .pop() for lists?.remove() and .pop() for lists?.remove(value) removes the first occurrence of a value and raises ValueError if not found.
.pop(index) removes and returns the element at an index (default: last item) and raises IndexError if index is out of range.
Key differences:
remove()searches by value,pop()removes by positionpop()returns the removed item,remove()returns None- Different error types when operation fails
Did you get it right?
if, can be nested, and always create new lists in memory.if, can be nested, and always create new lists in memory.matrix = [[j for j in range(2)] for i in range(2)]
print(matrix[1][0])matrix[1] accesses the second sublist [0, 1], then [0] gets the first element = 0.matrix[1] accesses the second sublist [0, 1], then [0] gets the first element = 0..sort() method returns a new sorted list without modifying the original..sort() sorts the list in-place and returns None. To get a new sorted list without modifying the original, use the sorted() function instead..sort() sorts the list in-place and returns None. To get a new sorted list without modifying the original, use the sorted() function instead.(1,) instead of (1) to create a single-element tuple?(1) is just integer 1 with parentheses (grouping). (1,) uses the comma to signal a tuple. This is necessary because parentheses are used for grouping in Python.(1) is just integer 1 with parentheses (grouping). (1,) uses the comma to signal a tuple. This is necessary because parentheses are used for grouping in Python.t = (1, 2, 3, 2, 4)?.index(value) method returns the index of the first occurrence of a value. For t = (1, 2, 3, 2, 4), t.index(3) returns 2..index(value) method returns the index of the first occurrence of a value. For t = (1, 2, 3, 2, 4), t.index(3) returns 2.Dictionary keys must be hashable (immutable).
Tuples are immutable → can be hashed → valid as keys
Lists are mutable → cannot be hashed → TypeError if used as keys
Example:
# Valid
locations = {(0, 0): "Origin", (1, 2): "Point A"}
# Invalid - TypeError
# bad_dict = {[0, 0]: "Origin"}Hashability ensures the key’s hash value never changes, which is critical for dictionary lookup performance.
Did you get it right?
numbers = (1, 2, 3, 4)
first, _____ = numbers
# first = 1, rest = [2, 3, 4]* operator in unpacking captures remaining elements into a list. first, *rest = (1, 2, 3, 4) assigns 1 to first and [2, 3, 4] to rest.* operator in unpacking captures remaining elements into a list. first, *rest = (1, 2, 3, 4) assigns 1 to first and [2, 3, 4] to rest.user.get('email', 'N/A') return if the key ’email’ doesn’t exist?.get(key, default) method returns the default value (‘N/A’) if the key doesn’t exist. Without a default, it returns None. It never raises KeyError..get(key, default) method returns the default value (‘N/A’) if the key doesn’t exist. Without a default, it returns None. It never raises KeyError.counts = {}
counts.setdefault('apple', 0)
counts['apple'] += 1
print(counts['apple'])setdefault('apple', 0) sets counts[‘apple’] = 0 (key doesn’t exist yet). Then counts['apple'] += 1 increments it to 1.setdefault('apple', 0) sets counts[‘apple’] = 0 (key doesn’t exist yet). Then counts['apple'] += 1 increments it to 1.dict[key] = value, deleting with del, and merging with .update(). .get() and .keys() only read data without modification.dict[key] = value, deleting with del, and merging with .update(). .get() and .keys() only read data without modification..get(key, default) never raises errors (returns default). .get(key) returns None if missing (no error). dict[key] raises KeyError if missing..get(key, default) never raises errors (returns default). .get(key) returns None if missing (no error). dict[key] raises KeyError if missing.original = {'a': 1, 'b': 2}
reversed_dict = {_____ for k, v in original.items()}{key_expr: value_expr for ...}. To reverse keys and values, use {v: k for k, v in original.items()}.{key_expr: value_expr for ...}. To reverse keys and values, use {v: k for k, v in original.items()}.dict.keys(), dict.values(), and dict.items()?dict.keys(), dict.values(), and dict.items()?.keys() → Returns view of all keys
- Example:
dict_keys(['name', 'age'])
.values() → Returns view of all values
- Example:
dict_values(['Alice', 30])
.items() → Returns view of (key, value) pairs as tuples
- Example:
dict_items([('name', 'Alice'), ('age', 30)])
All return dictionary views (not lists) that reflect changes to the original dictionary. Use list() to convert if needed.
Did you get it right?
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
print(a - b)- operator (or .difference()) returns elements in a that are NOT in b. Elements 1 and 2 are only in a, so the result is {1, 2}.- operator (or .difference()) returns elements in a that are NOT in b. Elements 1 and 2 are only in a, so the result is {1, 2}.|, &, and ^ return new sets. Methods .add() and .remove() modify the set in place and return None.|, &, and ^ return new sets. Methods .add() and .remove() modify the set in place and return None..discard(x) removes element x if present, but does nothing (no error) if x doesn’t exist. .remove(x) raises KeyError if x is not in the set..discard(x) removes element x if present, but does nothing (no error) if x doesn’t exist. .remove(x) raises KeyError if x is not in the set.a | b and a & b for sets?a | b and a & b for sets?a | b (Union) → All unique elements from both sets
- Example:
{1, 2} | {2, 3}={1, 2, 3} - Can also use
a.union(b)
a & b (Intersection) → Only elements present in BOTH sets
- Example:
{1, 2} & {2, 3}={2} - Can also use
a.intersection(b)
Think: Union = everything, Intersection = common elements
Did you get it right?
items = [1, 2, 2, 3, 3, 3, 4]
print(len(set(items)))set([1, 2, 2, 3, 3, 3, 4]) = {1, 2, 3, 4}. The length is 4 unique elements.set([1, 2, 2, 3, 3, 3, 4]) = {1, 2, 3, 4}. The length is 4 unique elements.dict.fromkeys() or Python 3.7+ dict keys.dict.fromkeys() or Python 3.7+ dict keys.map() return in Python 3?map() returns an iterator (lazy evaluation), not a list. You need to convert it with list() or consume it in a loop to get the actual values.map() returns an iterator (lazy evaluation), not a list. You need to convert it with list() or consume it in a loop to get the actual values.names = ['alice', 'bob']
result = list(map(str.upper, names))
print(result[1])map(str.upper, names) applies .upper() to each name, creating [‘ALICE’, ‘BOB’]. result[1] accesses the second element = ‘BOB’.map(str.upper, names) applies .upper() to each name, creating [‘ALICE’, ‘BOB’]. result[1] accesses the second element = ‘BOB’.map() with list() in Python 3?map() with list() in Python 3?map() returns an iterator (lazy evaluation), not a list.
Without list():
result = map(str.upper, names)
print(result) # <map object at 0x...>With list():
result = list(map(str.upper, names))
print(result) # ['ALICE', 'BOB']Benefits of lazy evaluation:
- Memory efficient (processes one item at a time)
- Only computes when needed
- Can work with infinite sequences
When to use list(): When you need the complete result immediately or need to use it multiple times.
Did you get it right?
numbers = [1, 2, 3, 4, 5, 6]
evens = list(filter(lambda x: _____, numbers))filter() keeps elements where the function returns True. lambda x: x % 2 == 0 returns True for even numbers (divisible by 2).filter() keeps elements where the function returns True. lambda x: x % 2 == 0 returns True for even numbers (divisible by 2).zip() receives iterables of different lengths?zip() stops when the shortest iterable is exhausted. Example: zip([1, 2, 3], ['a', 'b']) produces [(1, 'a'), (2, 'b')]. The 3 is ignored.zip() stops when the shortest iterable is exhausted. Example: zip([1, 2, 3], ['a', 'b']) produces [(1, 'a'), (2, 'b')]. The 3 is ignored.pairs = [('a', 1), ('b', 2), ('c', 3)]
letters, numbers = zip(*pairs)
print(numbers)zip(*pairs) unpacks the list and ‘unzips’ it into separate tuples. letters = ('a', 'b', 'c') and numbers = (1, 2, 3). Note: zip returns tuples, not lists.zip(*pairs) unpacks the list and ‘unzips’ it into separate tuples. letters = ('a', 'b', 'c') and numbers = (1, 2, 3). Note: zip returns tuples, not lists.enumerate() are true?enumerate() returns (index, value) tuples, starts at 0 by default, supports custom start index, and works with any iterable. It does NOT modify the original—it returns a new iterator.enumerate() returns (index, value) tuples, starts at 0 by default, supports custom start index, and works with any iterable. It does NOT modify the original—it returns a new iterator.sorted() function returns a new sorted list. The .sort() method sorts in-place and returns None.sorted() function returns a new sorted list. The .sort() method sorts in-place and returns None.words = ['apple', 'pie', 'banana']
result = max(words, key=len)
print(result)max(words, key=len) finds the word with maximum length. ‘banana’ has 6 characters (longest), so it returns ‘banana’, not the length.max(words, key=len) finds the word with maximum length. ‘banana’ has 6 characters (longest), so it returns ‘banana’, not the length.any([False, False, False]) returns True.any() returns True if at least one element is True. Since all elements are False, it returns False. any([False, False, True]) would return True.any() returns True if at least one element is True. Since all elements are False, it returns False. any([False, False, True]) would return True.any() and all()?any() and all()?any(iterable) → True if at least one element is True
any([False, False, True])=Trueany([False, False, False])=False- Short-circuits: stops at first True
all(iterable) → True if all elements are True
all([True, True, True])=Trueall([True, False, True])=False- Short-circuits: stops at first False
Common patterns:
# Check if string has any digits
any(c.isdigit() for c in "abc3x") # True
# Check if all numbers are positive
all(n > 0 for n in [1, 2, 3]) # TrueDid you get it right?
points = [(1, 5), (3, 2), (2, 8)]
sorted_points = sorted(points, key=lambda x: _____)x[1]. This gives [(3, 2), (1, 5), (2, 8)] sorted by second values: 2, 5, 8.x[1]. This gives [(3, 2), (1, 5), (2, 8)] sorted by second values: 2, 5, 8.map()?map() is better for lazy evaluation or when you already have a named function. Example: [x**2 for x in nums if x > 0] is clearer than list(map(lambda x: x**2, filter(lambda x: x > 0, nums))).map() is better for lazy evaluation or when you already have a named function. Example: [x**2 for x in nums if x > 0] is clearer than list(map(lambda x: x**2, filter(lambda x: x > 0, nums))).numbers = [1, 2, 3, 4]
result = all(n > 0 for n in numbers)
print(result)all() checks if all elements satisfy the condition. Since all numbers (1, 2, 3, 4) are greater than 0, it returns True.all() checks if all elements satisfy the condition. Since all numbers (1, 2, 3, 4) are greater than 0, it returns True.[1, 2, 3] + [4, 5] creates a new list without modifying the original lists.+ operator for lists creates a new list containing all elements. The original lists remain unchanged. To modify in place, use .extend().+ operator for lists creates a new list containing all elements. The original lists remain unchanged. To modify in place, use .extend().for item in list[:]) or use comprehension.for item in list[:]) or use comprehension.numbers = [1, 2, 3, 4, 5]
# Bad approach (causes issues):
# for num in numbers:
# if num % 2 == 0:
# numbers.remove(num)
# Good approach:
numbers = [_____ for n in numbers if _____]numbers = [n for n in numbers if n % 2 != 0]. This avoids modifying the list during iteration.numbers = [n for n in numbers if n % 2 != 0]. This avoids modifying the list during iteration..append() when adding multiple items to a list?.append() when adding multiple items to a list?.append() takes exactly ONE argument!
Wrong:
my_list = []
my_list.append(1, 2, 3) # TypeError!Correct options:
- One at a time:
my_list.append(1)
my_list.append(2)- Use
.extend()for multiple items:
my_list.extend([1, 2, 3]) # [1, 2, 3]- Append a single structured item (e.g., dict):
log = []
log.append({'ip': '192.168.1.1', 'method': 'GET'})Key: append = one item, extend = multiple items
Did you get it right?
enumerate(my_list[:]) iterates over a COPY while modifying the original by index. Option 2 might work for simple cases but can fail with insertions/deletions. Option 4 creates a new list (not in-place). Option 1 has a bug (undefined i).enumerate(my_list[:]) iterates over a COPY while modifying the original by index. Option 2 might work for simple cases but can fail with insertions/deletions. Option 4 creates a new list (not in-place). Option 1 has a bug (undefined i).Given:
students = {
"student1": {"name": "Alice", "age": 16, "classes": ["Math", "Physics"]},
"student2": {"name": "Bob", "age": 17, "classes": ["Chemistry", "Biology"]}
}Which expression accesses "Math"?
students["student1"] → Alice’s dict → ["classes"] → the list ["Math", "Physics"] → [0] → first element "Math". Option 1 gets "Physics" (index 1). Options 2 and 4 use the wrong key order.students["student1"] → Alice’s dict → ["classes"] → the list ["Math", "Physics"] → [0] → first element "Math". Option 1 gets "Physics" (index 1). Options 2 and 4 use the wrong key order.Given:
students = {
"student1": {"name": "Alice", "age": 16, "classes": ["Math", "Physics"]},
"student2": {"name": "Bob", "age": 17, "classes": ["Chemistry", "Biology"]}
}"grade" in students["student1"] returns True because students["student1"] is a non-empty dictionary.
in operator checks key existence, not whether the dict is non-empty. students["student1"] has keys "name", "age", "classes" — but no "grade" key. Result is False. Compare: "classes" in students["student2"] → True.in operator checks key existence, not whether the dict is non-empty. students["student1"] has keys "name", "age", "classes" — but no "grade" key. Result is False. Compare: "classes" in students["student2"] → True.in check on a dictionary?What does this raise?
scores = {}
scores["alice"]["math"] = 95scores["alice"] raises KeyError immediately — the key doesn’t exist yet, so there’s no nested dict to assign into. Fix: initialize first with scores["alice"] = {} then scores["alice"]["math"] = 95, or in one step: scores["alice"] = {"math": 95}.scores["alice"] raises KeyError immediately — the key doesn’t exist yet, so there’s no nested dict to assign into. Fix: initialize first with scores["alice"] = {} then scores["alice"]["math"] = 95, or in one step: scores["alice"] = {"math": 95}.scores["alice"] return when "alice" isn’t in the dict yet?Scheduled and Killing timestamps per pod. Which initialization pattern is correct?pod isn’t known before the loop. Option 3 resets the dict on every log line, wiping previously recorded timestamps for that pod. Option 4 calls .get() which returns a temporary {} that gets discarded — the assignment never reaches pod_events.pod isn’t known before the loop. Option 3 resets the dict on every log line, wiping previously recorded timestamps for that pod. Option 4 calls .get() which returns a temporary {} that gets discarded — the assignment never reaches pod_events.