A tuple is an immutable object in Python that cannot be changed. Tuples are also sequences, just like Python lists. This Python Tuple exercise aims to help you learn and practice tuple operations.
In this article, you’ll find 32 Python tuple practice questions, sorted by difficulty. These questions cover topics like indexing, slicing, unpacking, tuple methods, data conversion, using map and filter, and working with namedtuple.
Each coding challenge includes a Practice Problem, Hint, Solution code, and detailed Explanation, ensuring you don’t just copy code, but genuinely practice and understand how and why it works.
- All solutions have been fully tested on Python 3.
- Use our Online Code Editor to solve these exercises in real time.
Also Read:
- Python Tuples
- Python Tuple Quiz: MCQs to understand Python tuple.
Let us know if you have any alternative solutions. It will help other developers.
+ Table of Contents (32 Exercises)
Table of contents
- Exercise 1: Basic Tuple Operations
- Exercise 2: The Trailing Comma
- Exercise 3: Tuple Repetition
- Exercise 4: Tuple Concatenation
- Exercise 5: Tuple Slicing
- Exercise 6: Tuple Reversal
- Exercise 7: Type Casting
- Exercise 8: Tuple to String
- Exercise 9: Tuple Membership Testing
- Exercise 10: Counting
- Exercise 11: Tuple Unpacking
- Exercise 12: The Swap Trick
- Exercise 13: Nested Tuple Access
- Exercise 14: Tuple Statistics
- Exercise 22: Sort Tuple of Tuples
- Exercise 23: Tuple Filtering
- Exercise 24: Tuple Mapping
- Exercise 25: Tuple Dictionary Mapping
- Exercise 26: Tuple Intersection
- Exercise 27: The “Modification” Hack
- Exercise 28: Tuple Mutability
- Exercise 29: Nested Tuple Flattening
- Exercise 30: Memory Efficiency
- Exercise 31: NamedTuples
- Exercise 32: The Hashability Paradox
Exercise 1: Basic Tuple Operations
Problem Statement: Write a Python program to create a tuple, access its elements by index, and find its length.
Purpose: This exercise introduces you to the fundamental building blocks of working with tuples: creating them, retrieving individual elements using index positions, and measuring their size using len(). These are the first skills you need before working with any tuple-based data.
Given Input: fruits = ("apple", "banana", "cherry", "date")
Expected Output: First element: apple, Last element: date, and Length: 4
▼ Hint
- Define the tuple using parentheses with comma-separated string values.
- Use index
0to access the first element and index-1(or3) to access the last. - Use the built-in
len()function to get the total number of elements.
▼ Solution & Explanation
Explanation:
fruits = ("apple", "banana", "cherry", "date"): Creates a tuple by enclosing comma-separated values in parentheses. Tuples are ordered and immutable once created.fruits[0]: Accesses the first element using zero-based indexing. The first item is always at index0.fruits[-1]: Uses negative indexing to access the last element.-1always refers to the final item, regardless of the tuple’s length.len(fruits): Returns the total count of elements in the tuple. Here it returns4.
Exercise 2: The Trailing Comma
Problem Statement: Write a Python program to create a tuple containing a single item, the number 50, and confirm its type.
Purpose: This exercise highlights one of the most common beginner mistakes with tuples: assuming that parentheses alone create a tuple. A trailing comma is required when the tuple has only one element, and this exercise trains you to remember that rule.
Given Input: A single integer value 50
Expected Output: (50,) and <class 'tuple'>
▼ Hint
- Writing
t = (50)does not create a tuple. It is just an integer in parentheses. - To create a single-element tuple, add a trailing comma:
t = (50,). - Use
type()to verify the result is indeed atuple.
▼ Solution & Explanation
Explanation:
t = (50,): The trailing comma after50is what tells Python this is a tuple, not just an integer wrapped in parentheses. Without the comma,type(t)would return<class 'int'>.print(t): Displays(50,). Python always includes the trailing comma in the output of a single-element tuple to make its type visually unambiguous.type(t): Confirms the variable is of typetuple. This is a useful sanity check when debugging single-item containers.
Exercise 3: Tuple Repetition
Problem Statement: Write a Python program to repeat a tuple three times using the * operator.
Purpose: This exercise shows how the * operator works with sequences. Repeating a tuple is a concise way to generate patterned data without writing loops, and it reinforces the idea that tuples support sequence operations just like strings and lists.
Given Input: colors = ("red", "green")
Expected Output: ('red', 'green', 'red', 'green', 'red', 'green')
▼ Hint
- Use the
*operator directly with the tuple and an integer:tuple * n. - This creates a new tuple – the original
colorstuple remains unchanged.
▼ Solution & Explanation
Explanation:
colors = ("red", "green"): Defines a two-element tuple to use as the base for repetition.colors * 3: The*operator on sequences means repetition, not multiplication. It concatenates the tuple with itself three times, producing a new six-element tuple.repeated: Stores the result. The originalcolorstuple is unaffected because tuples are immutable and*always produces a new object.
Exercise 4: Tuple Concatenation
Problem Statement: Write a Python program to join three separate tuples into one new tuple using the + operator.
Purpose: This exercise demonstrates how to combine multiple tuples without modifying any of the originals. Concatenation is useful when you need to assemble a final ordered collection from several independent parts.
Given Input: a = (1, 2), b = (3, 4), and c = (5, 6)
Expected Output: (1, 2, 3, 4, 5, 6)
▼ Hint
- Use the
+operator to join tuples:a + b + c. - Both operands must be tuples. Adding a tuple to a list or any other type will raise a
TypeError.
▼ Solution & Explanation
Explanation:
a = (1, 2),b = (3, 4),c = (5, 6): Three separate tuples, each holding two integers. None of them will be modified during this operation.a + b + c: The+operator on tuples performs concatenation, joining them left to right into a single new tuple. The elements fromacome first, followed byb, thenc.combined: Holds the resulting six-element tuple(1, 2, 3, 4, 5, 6). The originalsa,b, andcremain intact.
Exercise 5: Tuple Slicing
Problem Statement: Write a Python program to extract a specific portion of a tuple using slice notation.
Purpose: This exercise teaches you how to retrieve a contiguous subset of elements from a tuple without a loop. Slicing is a fundamental Python skill used across strings, lists, and tuples alike, and it is essential for tasks like pagination, windowing, and data partitioning.
Given Input: numbers = (10, 20, 30, 40, 50, 60, 70)
Expected Output: (30, 40, 50)
▼ Hint
- Use the slice syntax
tuple[start:stop], wherestartis inclusive andstopis exclusive. - To get
(30, 40, 50), identify the indices of those values in the tuple first. - Remember: index counting starts at
0, so30is at index2.
▼ Solution & Explanation
Explanation:
numbers[2:5]: Extracts elements from index2up to but not including index5. This gives(30, 40, 50), which are the elements at indices 2, 3, and 4.- Start index (inclusive): The slice begins at index
2, which is the value30. - Stop index (exclusive): The slice stops before index
5, which is the value60. That element is not included in the result. sliced: Stores the new tuple. Slicing never modifies the original tuple – it always returns a new object.
Exercise 6: Tuple Reversal
Problem Statement: Write a Python program to reverse the order of elements in a tuple.
Purpose: This exercise shows how to reverse a tuple even though tuples have no built-in .reverse() method (unlike lists). You will practice using slice notation with a step value, which is a widely used Python idiom for reversing any sequence.
Given Input: items = (1, 2, 3, 4, 5)
Expected Output: (5, 4, 3, 2, 1)
▼ Hint
- Use the slice
[::-1]to step backwards through the entire tuple. - Alternatively, use the built-in
reversed()function and wrap the result intuple()to convert it back.
▼ Solution & Explanation
Explanation:
items[::-1]: Uses extended slice notation[start:stop:step]. Omittingstartandstopmeans the slice covers the entire tuple. The step-1tells Python to walk backwards, producing a reversed copy.reversed_items: Stores the new reversed tuple. The originalitemstuple is unchanged, because tuples are immutable and slicing always creates a new object.- Alternative:
tuple(reversed(items))achieves the same result.reversed()returns an iterator, so wrapping it intuple()is necessary to get a tuple back. The[::-1]slice is generally preferred for its brevity.
Exercise 7: Type Casting
Problem Statement: Write a Python program to convert a list into a tuple using the tuple() constructor.
Purpose: This exercise demonstrates how to convert between mutable and immutable sequence types. Converting a list to a tuple is a common pattern when you want to protect data from accidental modification, use it as a dictionary key, or pass it to a function that expects an immutable sequence.
Given Input: my_list = [10, 20, 30, 40, 50]
Expected Output: (10, 20, 30, 40, 50) and <class 'tuple'>
▼ Hint
- Pass the list directly to the built-in
tuple()constructor:tuple(my_list). - Use
type()afterwards to confirm the result is atupleand not a list.
▼ Solution & Explanation
Explanation:
my_list = [10, 20, 30, 40, 50]: A standard Python list – ordered and mutable. Lists support operations like.append()and.remove()that tuples do not.tuple(my_list): Thetuple()constructor accepts any iterable and returns a new tuple containing the same elements in the same order. The original list is not modified.print(my_tuple): Confirms the values are preserved and now stored in a tuple:(10, 20, 30, 40, 50).type(my_tuple): Returns<class 'tuple'>, confirming the conversion was successful.
Exercise 8: Tuple to String
Problem Statement: Write a Python program to convert a tuple of characters into a single joined string.
Purpose: This exercise shows how to bridge the gap between tuples and strings. The str.join() method is a core Python tool for assembling strings from iterable sequences, and practising it on a tuple reinforces that join() works on any iterable, not just lists.
Given Input: chars = ('a', 'b', 'c')
Expected Output: abc
▼ Hint
- Use the
str.join()method with an empty string as the separator:"".join(chars). - The separator string goes before
.join(), and the tuple is passed as the argument inside it. - To join with a separator (e.g., a hyphen), replace
""with"-".
▼ Solution & Explanation
Explanation:
chars = ('a', 'b', 'c'): A tuple of individual single-character strings. Each element must be a string forjoin()to work. Passing a tuple of integers would raise aTypeError."".join(chars): Callsjoin()on an empty string, which acts as the separator placed between each element. With an empty separator, the characters are concatenated directly with nothing in between.result: Stores the final string"abc". The original tuple is unchanged.- Alternative: Using
"-".join(chars)would produce"a-b-c", which is useful when you need a readable separator between items.
Exercise 9: Tuple Membership Testing
Problem Statement: Write a Python program to check whether a specific element exists inside a tuple using the in keyword.
Purpose: This exercise introduces membership testing, one of the most readable and expressive features of Python. Checking for the presence of a value in a collection is a task that appears constantly in real-world code, from input validation to search logic.
Given Input: fruits = ("apple", "banana", "cherry", "date")
Expected Output: True and False
▼ Hint
- Use the expression
value in tupleto check for membership. It returnsTrueorFalse. - Use
value not in tupleto check for the absence of an element. - Membership testing is case-sensitive for strings, so
"Apple"and"apple"are treated as different values.
▼ Solution & Explanation
Explanation:
"cherry" in fruits: Scans the tuple from left to right and returnsTrueas soon as a matching element is found. Since"cherry"is present at index2, the result isTrue."mango" in fruits: Scans the entire tuple and finds no match, so it returnsFalse.- Performance note: The
inoperator on a tuple performs a linear scan, checking each element one by one. For very large collections where frequent lookups are needed, asetoffers faster average-case membership testing.
Exercise 10: Counting
Problem Statement: Write a Python program to use the .count() method to find how many times a specific element appears in a tuple.
Purpose: This exercise introduces one of the two built-in methods that tuples provide. Knowing how to count occurrences without writing a manual loop is a practical skill used in frequency analysis, data validation, and duplicate detection.
Given Input: votes = ("yes", "no", "yes", "yes", "no", "yes")
Expected Output: yes appears 4 times and no appears 2 times
▼ Hint
- Call
tuple.count(value)to get the number of timesvalueappears in the tuple. - If the element is not present at all,
.count()returns0rather than raising an error.
▼ Solution & Explanation
Explanation:
votes.count("yes"): Scans the entire tuple and returns the number of elements equal to"yes". The comparison is exact and case-sensitive, so"Yes"would not be counted.votes.count("no"): Repeats the scan for"no", returning2. Each call to.count()is a separate linear scan of the tuple.- Context: Tuples have only two built-in methods:
.count()and.index(). This is intentional – tuples are designed to be lightweight and immutable, so they expose far fewer methods than lists.
Exercise 11: Tuple Unpacking
Problem Statement: Write a Python program to unpack a four-element tuple into four distinct variables in a single assignment.
Purpose: This exercise introduces tuple unpacking, one of Python’s most elegant features. Unpacking makes code more readable by giving meaningful names to positional data, and it is widely used when working with function return values, database rows, and coordinate pairs.
Given Input: person = ("Alice", 30, "Engineer", "Pune")
Expected Output: Name: Alice, Age: 30, Job: Engineer, and City: Pune
▼ Hint
- Assign the tuple to multiple variables separated by commas on the left side:
name, age, job, city = person. - The number of variables on the left must exactly match the number of elements in the tuple, or Python will raise a
ValueError.
▼ Solution & Explanation
Explanation:
name, age, job, city = person: Python matches each variable on the left to the corresponding element in the tuple by position.namegets"Alice",agegets30,jobgets"Engineer", andcitygets"Pune".- Count must match: If the number of variables does not equal the number of tuple elements, Python raises
ValueError: too many values to unpackorValueError: not enough values to unpack. - Alternative: If you only need some values, use an underscore
_as a throwaway variable for positions you want to skip, e.g.,name, _, job, _ = person.
Exercise 12: The Swap Trick
Problem Statement: Write a Python program to swap the values of two variables using tuple unpacking, without using a temporary third variable.
Purpose: This exercise demonstrates one of Python’s most well-known idioms. The swap trick works because Python evaluates the entire right-hand side as a tuple before performing any assignment, making it both concise and safe. It is a practical showcase of how unpacking can replace verbose boilerplate code.
Given Input: a = 100 and b = 200
Expected Output: After swap: a = 200, b = 100
▼ Hint
- Use a single line:
a, b = b, a. Python packs the right side into a temporary tuple before unpacking it intoaandb. - There is no need for a third variable like
temp. The tuple packing handles the intermediate storage automatically.
▼ Solution & Explanation
Explanation:
a, b = b, a: Python first evaluates the right-hand sideb, aas a whole, creating the temporary tuple(200, 100). It then unpacks that tuple intoaandbfrom left to right, assigning200toaand100tob.- Why no temp variable is needed: The right-hand side is fully evaluated before any assignment takes place. This means the original values of both
aandbare captured in the temporary tuple before either variable is overwritten. - Classic alternative: In most other languages, swapping requires a third variable:
temp = a; a = b; b = temp. Python’s tuple unpacking makes this unnecessary and is considered the idiomatic approach.
Exercise 13: Nested Tuple Access
Problem Statement: Write a Python program to access a specific element that is stored inside a tuple which is itself nested inside another tuple.
Purpose: This exercise builds your understanding of nested data structures. Tuples can contain other tuples as elements, and chaining index operators is the standard way to drill down into them. This pattern appears frequently when working with coordinate grids, database records, and configuration structures.
Given Input: matrix = ((1, 2, 3), (4, 5, 6), (7, 8, 9))
Expected Output: 6
▼ Hint
- Use two index operators in sequence:
matrix[row][column]. - The first index selects the inner tuple (the row), and the second index selects the element within it (the column).
- Remember that indexing starts at
0, so the second row is at index1and the third column is at index2.
▼ Solution & Explanation
Explanation:
matrix[1]: Accesses the element at index1of the outer tuple, which is the inner tuple(4, 5, 6).matrix[1][2]: Chains a second index onto the result. This accesses index2of the inner tuple(4, 5, 6), which is the value6.- General pattern: For any level of nesting, you chain one additional index operator per level. A triple-nested tuple would require three index operators:
data[i][j][k]. - Immutability note: Even inside a nested structure, you cannot reassign any element. An attempt like
matrix[1][2] = 99raises aTypeErrorbecause tuples do not support item assignment at any level.
Exercise 14: Tuple Statistics
Problem Statement: Write a Python program to calculate the total, highest value, and lowest value from a tuple of integers using the built-in sum(), max(), and min() functions.
Purpose: This exercise shows that Python’s built-in aggregate functions work directly on tuples, not just lists. Being able to derive quick statistics from an immutable sequence without converting it first is a practical time-saver in data processing and reporting tasks.
Given Input: scores = (88, 95, 70, 62, 99, 74, 85)
Expected Output: Sum: 573, Max: 99, and Min: 62
▼ Hint
- Pass the tuple directly to
sum(),max(), andmin(). No conversion to a list is required. - All three functions accept any iterable, so they work identically on tuples and lists.
▼ Solution & Explanation
Explanation:
sum(scores): Iterates over the tuple and adds all elements together, returning the total573. It accepts an optional second argument as a starting value, e.g.,sum(scores, 100)would return673.max(scores): Scans the tuple and returns the largest value. For numeric tuples, it compares by arithmetic value. For string tuples, it would compare lexicographically.min(scores): Scans the tuple and returns the smallest value, in this case62. Likemax(), it raises aValueErrorif the tuple is empty.- Combining results: You can derive the average by combining
sum()andlen():sum(scores) / len(scores)gives81.857..., extending this exercise naturally into a statistics summary.
Exercise 22: Sort Tuple of Tuples
Problem Statement: Write a Python program to sort a tuple of tuples based on the second item in each nested tuple.
Purpose: This exercise teaches you how to use the key parameter of Python’s sorted() function with a lambda to sort structured data by a specific field – a technique used constantly when ordering records, rankings, and tabular data.
Given Input: students = (("Alice", 88), ("Bob", 73), ("Charlie", 95), ("Diana", 61))
Expected Output:
Sorted: (('Diana', 61), ('Bob', 73), ('Alice', 88), ('Charlie', 95))
▼ Hint
- Use
sorted(students, key=lambda x: x[1])to sort by the second element of each nested tuple. sorted()returns a list, so wrap the result intuple()to keep the type consistent.- To reverse the order (highest first), add
reverse=Trueto thesorted()call.
▼ Solution & Explanation
Explanation:
sorted(students, key=lambda x: x[1]): Thekeyparameter accepts a callable that is applied to each element before comparison. Here,lambda x: x[1]extracts the second item of every nested tuple as the sort criterion.lambda x: x[1]: A compact anonymous function that takes one argumentx(a nested tuple) and returns its element at index 1. This avoids importingoperator.itemgetterfor a simple one-off sort.tuple(...):sorted()always returns a list. Wrapping the result intuple()restores the immutable tuple type to match the original data structure.
Exercise 23: Tuple Filtering
Problem Statement: Write a Python program to filter a tuple and keep only the elements that satisfy a given condition, using both filter() and a list comprehension approach.
Purpose: This exercise demonstrates two idiomatic ways to select a subset of items from a sequence based on a condition. Filtering is a foundational operation in data processing, validation, and functional-style programming.
Given Input: numbers = (3, 14, 7, 22, 9, 41, 18, 5), keep only values greater than 10
Expected Output: Filtered: (14, 22, 41, 18)
▼ Hint
- With
filter(): pass alambdaas the first argument and the tuple as the second, then wrap the result intuple(). - With a comprehension: use
tuple(x for x in numbers if x > 10). - Both approaches produce identical results. The comprehension is generally considered more readable in Python.
▼ Solution & Explanation
Explanation:
filter(lambda x: x > 10, numbers): Applies the lambda to every element and keeps only those for which it returnsTrue.filter()returns a lazy iterator, sotuple()is needed to materialise the result.tuple(x for x in numbers if x > 10): A generator expression with an inlineifcondition. This is the more Pythonic style and is easier to read when the condition grows more complex.- Order is preserved: Both approaches iterate through
numbersfrom left to right, so qualifying elements appear in their original sequence in the output.
Exercise 24: Tuple Mapping
Problem Statement: Write a Python program to apply a square function to every item in a tuple using map(), and also demonstrate the equivalent generator expression approach.
Purpose: This exercise introduces the functional programming concept of mapping a transformation over a sequence. The map() function and its comprehension equivalent appear regularly in data transformation, preprocessing pipelines, and mathematical computations.
Given Input: numbers = (1, 2, 3, 4, 5, 6)
Expected Output: Squared: (1, 4, 9, 16, 25, 36)
▼ Hint
- Define a
square(n)function that returnsn * n, or use alambdadirectly. - Pass the function and the tuple to
map(), then wrap the result intuple()to get the final output. - The generator expression equivalent is
tuple(x ** 2 for x in numbers).
▼ Solution & Explanation
Explanation:
map(square, numbers): Appliessquareto each element ofnumbersone at a time and returns a lazy map iterator. Wrapping it intuple()forces evaluation and collects all results.def square(n): A named function is used here instead of alambdato keep the code readable and to make the intent self-documenting. For a one-liner,map(lambda x: x * x, numbers)works identically.x ** 2: The exponentiation operator is a clear and concise alternative tox * x. In the generator expression, it makes the transformation immediately obvious without needing a separate function definition.
Exercise 25: Tuple Dictionary Mapping
Problem Statement: Write a Python program to zip two tuples together – one holding keys and the other holding values – to create a dictionary.
Purpose: This exercise demonstrates a common pattern for building dictionaries from paired data sources. Combining zip() with dict() is widely used when parsing CSV headers, mapping configuration keys to values, and constructing lookup tables from separate lists.
Given Input: keys = ("name", "age", "city") and values = ("Alice", 30, "Pune")
Expected Output: {'name': 'Alice', 'age': 30, 'city': 'Pune'}
▼ Hint
- Use
zip(keys, values)to pair each key with its corresponding value into a sequence of two-item tuples. - Pass the zipped result directly to
dict()to build the dictionary in one step. - If the two tuples have different lengths,
zip()stops at the shorter one. Useitertools.zip_longest()if you need to handle unequal lengths.
▼ Solution & Explanation
Explanation:
zip(keys, values): Pairs elements from both tuples by position, producing a lazy iterator of two-item tuples:('name', 'Alice'),('age', 30), and so on.dict(...): Consumes the zipped iterator and interprets each two-item tuple as a key-value pair, building the final dictionary in a single call with no explicit loop needed.{k: v for k, v in zip(keys, values)}: The dictionary comprehension equivalent. It is slightly more verbose but gives you room to add conditions or transform keys and values inline during construction.
Exercise 26: Tuple Intersection
Problem Statement: Write a Python program to find all elements that are common to two different tuples.
Purpose: This exercise introduces set intersection as a tool for comparing collections. Finding shared elements between two sequences is a core operation in data analysis, deduplication, access control (shared permissions), and any scenario where you need to identify overlap between datasets.
Given Input: t1 = (1, 2, 3, 4, 5, 6) and t2 = (4, 5, 6, 7, 8, 9)
Expected Output: Common elements: (4, 5, 6)
▼ Hint
- Convert both tuples to sets and use the
&operator or the.intersection()method to find common elements. - Wrap the result in
tuple(sorted(...))to get a consistent, ordered output. - Alternatively, use a generator expression:
tuple(x for x in t1 if x in t2). This preserves the order oft1but is slower for large tuples sinceinon a tuple is O(n).
▼ Solution & Explanation
Explanation:
set(t1) & set(t2): Converts both tuples to sets and applies the&intersection operator, which returns a new set containing only the values present in both. This is an O(min(n, m)) operation, making it efficient for large inputs.tuple(sorted(...)): Since set intersection produces an unordered result,sorted()ensures the output is in ascending order before it is converted back to a tuple.x for x in t1 if x in set(t2): The generator expression approach convertst2to a set once for O(1) membership checks, then iterates throught1in order. This preserves the original sequence oft1while still benefiting from fast set lookups.
Exercise 27: The “Modification” Hack
Problem Statement: Write a Python program to “modify” a tuple by converting it to a list, changing a specific item, and converting it back to a tuple.
Purpose: This exercise highlights the immutability of tuples and demonstrates the standard workaround used when a one-off change is needed. Understanding this pattern also helps you appreciate why tuples are immutable by design and when to choose a list instead.
Given Input: colours = ("red", "green", "blue"), replace "green" with "yellow"
Expected Output:
Original: ('red', 'green', 'blue')
Modified: ('red', 'yellow', 'blue')
▼ Hint
- Use
list(colours)to create a mutable copy of the tuple. - Change the desired element by index, e.g.
temp[1] = "yellow". - Convert the list back with
tuple(temp)and assign it to a new variable. The original tuple remains unchanged throughout.
▼ Solution & Explanation
Explanation:
list(colours): Creates a brand-new mutable list containing the same elements as the tuple. The original tuple is not touched at any point in this process.temp[1] = "yellow": Performs the desired in-place change on the list. This would raise aTypeErrorif attempted directly on the tuple, because tuples do not support item assignment.tuple(temp): Converts the modified list back into a new immutable tuple. The result is a separate object in memory; the variablecoloursstill points to the original, unmodified tuple.
Exercise 28: Tuple Mutability
Problem Statement: Create a tuple that contains a list as one of its elements. Modify the list in place and observe that the tuple’s identity stays the same while its contents appear to change.
Purpose: This exercise uncovers one of Python’s most instructive subtleties: a tuple is immutable in the sense that its references cannot be reassigned, but if a reference points to a mutable object such as a list, that object itself can still be changed. Understanding this distinction is essential for writing predictable, bug-free code.
Given Input: t = (1, 2, [3, 4, 5]), append 99 to the inner list
Expected Output:
Before: (1, 2, [3, 4, 5])
After: (1, 2, [3, 4, 5, 99])
Same object? True
▼ Hint
- Access the inner list with
t[2]and call.append()on it directly. - Use
id(t)before and after the change to confirm the tuple object itself has not been replaced. - Think of the tuple as holding a reference to the list, not a copy. Mutating the list does not change the reference stored in the tuple.
▼ Solution & Explanation
Explanation:
t[2].append(99):t[2]retrieves the reference to the list stored at index 2. Calling.append()mutates that list object in place. The tuple’s slot at index 2 still holds the exact same reference; only the list’s contents have grown.id(t): Returns the unique memory address of the tuple object. Printing it before and after confirms the tuple itself is the same object in memory – its identity has not changed, even though its printed representation looks different.- The immutability rule clarified: Tuple immutability means the tuple’s references cannot be rebound. You cannot do
t[2] = something_else. But the object that a reference points to can change freely if that object is mutable – which is exactly what a list is.
Exercise 29: Nested Tuple Flattening
Problem Statement: Write a recursive Python function to flatten a deeply nested tuple of tuples into a single flat tuple containing all the individual values.
Purpose: This exercise strengthens your understanding of recursion and type checking. Flattening nested structures is a practical requirement when processing tree-shaped data, parsed expressions, or hierarchical configurations where depth is not known in advance.
Given Input: nested = (1, (2, 3), (4, (5, (6, 7))))
Expected Output: Flattened: (1, 2, 3, 4, 5, 6, 7)
▼ Hint
- Use
isinstance(item, tuple)inside the function to decide whether to recurse or to yield the value as-is. - A generator function using
yield fromkeeps the recursive logic clean and avoids building intermediate tuples on every call. - Wrap the final generator call in
tuple()to collect all yielded values into the result.
▼ Solution & Explanation
Explanation:
isinstance(item, tuple): The base condition that separates recursive cases from base cases. If the current item is itself a tuple, the function dives into it; otherwise, the item is a plain value and is yielded directly to the caller.yield from flatten(item): Delegates iteration to the recursive call, forwarding every value it produces upward through the call stack. This avoids manually looping over the sub-results and keeps the function body concise.tuple(flatten(nested)): Sinceflattenis a generator function, calling it returns a lazy iterator. Passing that iterator totuple()forces full evaluation and collects every yielded value into the final flat tuple.
Exercise 30: Memory Efficiency
Problem Statement: Use the sys module to measure and compare the memory consumed by a list and a tuple, each holding the same one million integer elements.
Purpose: This exercise gives you concrete, measurable evidence for one of the key practical advantages of tuples over lists: lower memory overhead. This matters in performance-sensitive applications that hold large collections of fixed data in memory.
Given Input: A range of one million integers, range(1_000_000), stored as both a list and a tuple
Expected Output: Printed byte sizes for the list and the tuple, followed by the difference in bytes (exact values vary by Python version and platform)
▼ Hint
- Import
sysand usesys.getsizeof()to retrieve the shallow byte size of each collection. - Note that
sys.getsizeof()measures only the container itself, not the memory used by the elements it points to. - Subtract the tuple size from the list size to find the saving, and consider expressing it in kilobytes for readability.
▼ Solution & Explanation
Explanation:
sys.getsizeof(): Returns the memory footprint of the container object itself in bytes. For a list, this includes extra capacity reserved for future appends (over-allocation). A tuple allocates exactly as much space as it needs and no more.- Over-allocation in lists: Python lists maintain a buffer of spare slots to make
.append()efficient. This means a list nearly always occupies more memory than a tuple of the same length, because the tuple has no need to grow and carries no such buffer. - Shallow vs. deep size:
sys.getsizeof()measures only the container, not the objects it references. Both the list and the tuple point to the same integer objects in memory, so the element memory is identical for both. The difference shown is purely the container overhead.
Exercise 31: NamedTuples
Problem Statement: Use collections.namedtuple to define an Employee data structure with named fields, create a few instances, and perform a calculation such as finding the highest-paid employee.
Purpose: This exercise introduces namedtuple as a lightweight way to create readable, self-documenting data objects without the overhead of a full class. Named tuples are used extensively in standard library code, data pipelines, and anywhere structured records need to be passed around efficiently.
Given Input: Three employees – ("Alice", "Engineering", 95000), ("Bob", "Marketing", 72000), ("Charlie", "Engineering", 88000)
Expected Output:
Alice works in Engineering and earns $95,000
Bob works in Marketing and earns $72,000
Charlie works in Engineering and earns $88,000
Highest paid: Alice ($95,000)
▼ Hint
- Define the named tuple with
Employee = namedtuple("Employee", ["name", "department", "salary"]). - Create instances just like calling a function:
Employee("Alice", "Engineering", 95000). - Access fields by name (e.g.
emp.salary) or by index (e.g.emp[2]). Usemax(employees, key=lambda e: e.salary)to find the top earner.
▼ Solution & Explanation
Explanation:
namedtuple("Employee", [...]): Dynamically creates a new tuple subclass calledEmployeewhose fields can be accessed by name. The result behaves exactly like a regular tuple for indexing, unpacking, and memory usage, but adds attribute-style access for clarity.emp.name,emp.department,emp.salary: Named field access makes the code self-documenting. Compare this to a plain tuple whereemp[0],emp[1],emp[2]would give no indication of what each position represents.max(employees, key=lambda e: e.salary): Finds theEmployeeinstance with the highestsalaryvalue. Because named tuples are still tuples, they work seamlessly with all built-in functions likemax(),min(), andsorted().
Exercise 32: The Hashability Paradox
Problem Statement: Write a Python program that experiments with using tuples as dictionary keys. Demonstrate why a fully immutable tuple like (1, 2) works as a key, while a tuple containing a mutable object like (1, [2]) raises a TypeError.
Purpose: This exercise builds a deep understanding of Python’s hashing rules. Dictionary keys must be hashable, meaning their value must never change after being inserted. A tuple containing a mutable list violates this guarantee, which is why Python rejects it. This concept is fundamental to understanding how sets and dictionaries work internally.
Given Input: Attempt to use (1, 2) and (1, [2]) as dictionary keys
Expected Output:
(1, 2) as key: success, value = "immutable tuple"
(1, [2]) as key: TypeError - unhashable type: 'list'
▼ Hint
- Try assigning
d[(1, 2)] = "value"first to confirm it works. - Wrap the second attempt
d[(1, [2])] = "value"in atry/except TypeErrorblock so the program does not crash. - Use
hash((1, 2))and tryhash((1, [2]))to see the same rule applied directly to thehash()built-in.
▼ Solution & Explanation
Explanation:
- Hashability requirement: Python dictionaries and sets store keys by their hash value. For this to work correctly, a key’s hash must remain constant for its entire lifetime in the container. Objects whose value can change – such as lists – are deliberately made unhashable to prevent silent data corruption.
- Why
(1, 2)is hashable: Both elements are integers, which are immutable. A tuple is hashable if and only if every element it contains is also hashable. Python computes the tuple’s hash from the hashes of its elements, so a fully immutable tuple produces a stable, reliable hash value. - Why
(1, [2])is not hashable: The list[2]inside the tuple is mutable. If it were allowed as a dictionary key and then modified, the hash would change, making the key unretrievable. Python prevents this entirely by raising aTypeErrorat the moment you attempt to hash any tuple that contains a mutable element, however deeply nested.
