Quiz: Lists and Tuples

Test your understanding before moving on. Target: 70% or higher to proceed confidently.


Section 1: Multiple Choice (1 point each)

1. What is the output of the following code?

fruits = ["apple", "banana", "cherry"]
print(fruits[-1])
  • A) apple
  • B) banana
  • C) cherry
  • D) IndexError
Answer **C)** `cherry` *Why C:* Negative indexing starts from the end: `-1` is the last element, `-2` is the second-to-last, etc. *Why not A:* That would be `fruits[0]`. *Why not D:* `-1` is a valid index for any non-empty list. *Reference:* Section 8.2

2. Which of the following correctly adds all elements of [4, 5, 6] individually to my_list = [1, 2, 3]?

  • A) my_list.append([4, 5, 6])
  • B) my_list.extend([4, 5, 6])
  • C) my_list.insert([4, 5, 6])
  • D) my_list.add([4, 5, 6])
Answer **B)** `my_list.extend([4, 5, 6])` *Why B:* `extend()` adds each element from the iterable individually, resulting in `[1, 2, 3, 4, 5, 6]`. *Why not A:* `append()` would add the entire list as a single element: `[1, 2, 3, [4, 5, 6]]`. *Why not C:* `insert()` requires two arguments (index, element), so this would raise a `TypeError`. *Why not D:* Lists don't have an `add()` method; that's for sets. *Reference:* Section 8.4

3. What does my_list.sort() return?

  • A) A new sorted list
  • B) None
  • C) The sorted list (same object)
  • D) True if sorting was successful
Answer **B)** `None` *Why B:* `.sort()` sorts the list in place and returns `None`. This is a deliberate Python design choice — methods that modify an object return `None` to signal the modification. *Why not A:* That's what `sorted(my_list)` does (built-in function, not method). *Why not C:* The list is modified, but the return value is `None`, not the list. *Reference:* Section 8.4

4. What is the key difference between a list and a tuple?

  • A) Lists can hold any data type; tuples can only hold numbers
  • B) Lists are ordered; tuples are unordered
  • C) Lists are mutable; tuples are immutable
  • D) Lists use square brackets; tuples use curly braces
Answer **C)** Lists are mutable; tuples are immutable *Why C:* Mutability is the fundamental difference. Lists can be changed after creation; tuples cannot. *Why not A:* Both lists and tuples can hold any data type. *Why not B:* Both lists and tuples maintain insertion order. *Why not D:* Tuples use parentheses `()`, not curly braces `{}`. Curly braces are for dictionaries and sets. *Reference:* Section 8.7

5. What does enumerate() provide when iterating over a list?

  • A) Only the indices
  • B) Only the values
  • C) Both the index and the value
  • D) The total count of elements
Answer **C)** Both the index and the value *Why C:* `enumerate()` yields `(index, element)` pairs, making it easy to track position while iterating. *Reference:* Section 8.5

6. What is the output of this code?

a = [1, 2, 3]
b = a
b.append(4)
print(a)
  • A) [1, 2, 3]
  • B) [1, 2, 3, 4]
  • C) [4, 1, 2, 3]
  • D) TypeError
Answer **B)** `[1, 2, 3, 4]` *Why B:* `b = a` creates an alias — both variables point to the same list object. Appending through `b` modifies the shared list, which `a` also references. *Why not A:* This would be true if `b` were a copy (`b = a.copy()`), but `b = a` is an alias. *Reference:* Section 8.8

7. Which of the following creates an independent copy of a list?

  • A) new = old
  • B) new = old.copy()
  • C) new = old.sort()
  • D) new = old.reverse()
Answer **B)** `new = old.copy()` *Why B:* `.copy()` creates a shallow copy — a new list with the same elements. *Why not A:* This creates an alias, not a copy. *Why not C:* `.sort()` returns `None` (it sorts in place), so `new` would be `None`. *Why not D:* `.reverse()` also returns `None`. *Reference:* Section 8.8

8. What is the output of the following list comprehension?

result = [x * 2 for x in range(5) if x % 2 == 0]
print(result)
  • A) [0, 2, 4, 6, 8]
  • B) [0, 4, 8]
  • C) [2, 6, 10]
  • D) [0, 2, 4]
Answer **B)** `[0, 4, 8]` *Why B:* `range(5)` produces `0, 1, 2, 3, 4`. The filter `if x % 2 == 0` keeps `0, 2, 4`. The expression `x * 2` doubles them to `0, 4, 8`. *Why not A:* This would be `[x * 2 for x in range(5)]` (no filter). *Why not D:* This would be `[x for x in range(5) if x % 2 == 0]` (no doubling). *Reference:* Section 8.6

Section 2: True/False (1 point each)

9. sorted() modifies the original list and returns None.

Answer **False.** `sorted()` returns a new sorted list and does NOT modify the original. The `.sort()` method modifies in place and returns `None`. *Reference:* Section 8.4

10. Tuples can be used as dictionary keys, but lists cannot.

Answer **True.** Dictionary keys must be hashable (immutable). Tuples are immutable and therefore hashable. Lists are mutable and therefore not hashable. *Reference:* Section 8.7

11. my_list[:] creates a deep copy of my_list.

Answer **False.** `my_list[:]` creates a shallow copy — it duplicates the outer list but the elements themselves are shared references. For nested lists, changes to inner lists in the copy will affect the original. Use `copy.deepcopy()` for a deep copy. *Reference:* Section 8.8

12. A single-element tuple requires a trailing comma: (42,).

Answer **True.** Without the comma, `(42)` is just the integer `42` in parentheses. The comma is what makes it a tuple: `(42,)`. *Reference:* Section 8.7

Section 3: Short Answer (2 points each)

13. Given the code below, what are the values of a, b, and c?

first, *middle, last = [10, 20, 30, 40, 50]
a = first
b = middle
c = last
Answer - `a = 10` (first element) - `b = [20, 30, 40]` (everything between first and last, collected into a list by `*`) - `c = 50` (last element) Star unpacking (`*middle`) collects all remaining elements between `first` and `last` into a list. *Reference:* Section 8.7

14. What is the output of this code?

grid = [[0] * 3] * 2
grid[0][1] = 5
print(grid)
Answer `[[0, 5, 0], [0, 5, 0]]` The `* 2` creates two references to the *same* inner list, not two independent copies. Modifying one row modifies both because they're aliases. To create independent rows, use a comprehension: `[[0] * 3 for _ in range(2)]`. *Reference:* Section 8.9

15. Explain the difference between remove() and pop() for lists. When would you use each?

Answer - `remove(value)` searches for the first occurrence of `value` and removes it. Raises `ValueError` if not found. Use it when you know *what* to remove but not *where* it is. - `pop(index)` removes the element at `index` and returns it. Defaults to the last element if no index given. Raises `IndexError` if the index is out of range. Use it when you know *where* the element is, or when you need the removed value. Key difference: `remove()` searches by value; `pop()` removes by position and returns the removed element. *Reference:* Section 8.4

16. This code has a bug. What's wrong, and how would you fix it?

scores = [85, 92, 78, 95, 88]
sorted_scores = scores.sort()
print(f"Sorted: {sorted_scores}")
Answer The output is `Sorted: None` because `.sort()` modifies the list in place and returns `None`. Two possible fixes:
# Fix 1: Use sorted() to get a new sorted list
sorted_scores = sorted(scores)

# Fix 2: Sort in place, then use the original variable
scores.sort()
print(f"Sorted: {scores}")
*Reference:* Section 8.4

Section 4: Code Analysis (3 points each)

17. Trace through this code and determine the final values of x, y, and z:

x = [1, 2, 3, 4, 5]
y = x[1:4]
z = x
x.append(6)
y.append(7)
z[0] = 99
print(x)
print(y)
print(z)
Answer
[99, 2, 3, 4, 5, 6]
[2, 3, 4, 7]
[99, 2, 3, 4, 5, 6]
Step by step: 1. `x = [1, 2, 3, 4, 5]` — creates a list 2. `y = x[1:4]` — creates a NEW list `[2, 3, 4]` (slicing creates a copy) 3. `z = x` — `z` is an alias for `x` (same object) 4. `x.append(6)` — `x` (and `z`) becomes `[1, 2, 3, 4, 5, 6]` 5. `y.append(7)` — `y` becomes `[2, 3, 4, 7]` (independent) 6. `z[0] = 99` — modifies the shared list; `x` (and `z`) becomes `[99, 2, 3, 4, 5, 6]` Key insight: `y` is independent (created by slicing); `z` is an alias (created by assignment). *Reference:* Section 8.8

18. What does this function do? Describe it in one sentence. What does it return for mystery([3, 1, 4, 1, 5, 9, 2, 6])?

def mystery(lst):
    result = []
    for item in lst:
        if item not in result:
            result.append(item)
    return result
Answer The function removes duplicates from a list while preserving the original order of first appearances. For `mystery([3, 1, 4, 1, 5, 9, 2, 6])`, it returns `[3, 1, 4, 5, 9, 2, 6]`. The `1` at index 3 is skipped because `1` is already in `result` from index 1. Note: This is an O(n^2) approach because `item not in result` scans the entire result list each time. In [Chapter 9](../chapter-09-dictionaries-and-sets/index.md), you'll learn about sets, which make this O(n). *Reference:* Sections 8.5, 8.10

19. The following function is supposed to remove all negative numbers from a list. It has a bug. Find it, explain why it fails, and fix it.

def remove_negatives(numbers):
    for i in range(len(numbers)):
        if numbers[i] < 0:
            numbers.pop(i)
    return numbers

print(remove_negatives([1, -2, 3, -4, 5, -6]))
Answer **Bug:** Modifying a list while iterating over it by index causes skipped elements. When a negative number is removed, all subsequent elements shift left, but `i` continues incrementing, so the element that moved into position `i` is never checked. This also causes an `IndexError` when `i` exceeds the shortened list's length. **Fix 1: Iterate backwards**
def remove_negatives(numbers):
    for i in range(len(numbers) - 1, -1, -1):
        if numbers[i] < 0:
            numbers.pop(i)
    return numbers
**Fix 2: Create a new list (preferred)**
def remove_negatives(numbers):
    return [n for n in numbers if n >= 0]
**Fix 3: Use a while loop with manual index control**
def remove_negatives(numbers):
    i = 0
    while i < len(numbers):
        if numbers[i] < 0:
            numbers.pop(i)
        else:
            i += 1
    return numbers
*Reference:* Sections 8.4, 8.8

20. What does this list comprehension produce? Break it down step by step.

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
result = [row[i] for i in range(3) for row in matrix]
print(result)
Answer `[1, 4, 7, 2, 5, 8, 3, 6, 9]` This extracts columns from the matrix. The outer loop iterates over column indices (`i = 0, 1, 2`), and the inner loop iterates over rows. So it collects: - `i=0`: `row[0]` for each row → `1, 4, 7` - `i=1`: `row[1]` for each row → `2, 5, 8` - `i=2`: `row[2]` for each row → `3, 6, 9` This is effectively a transpose operation, but flattened into a single list instead of a list of lists. *Reference:* Sections 8.6, 8.9