Python Course
Tuples in Python
In this lesson, you will learn one of Python's most powerful and often misunderstood data structures — the Tuple. By the end of this lesson, you will understand what tuples are, why they exist, how they differ from lists, and how to use them confidently in real-world programs.
1. What is a Tuple?
A tuple is an ordered collection of items in Python — just like a list. The key difference is that a tuple is immutable, which means once you create a tuple, you cannot change, add, or remove its items.
Think of a tuple like a sealed package. You can look inside and use what is there, but you cannot modify the contents once the package is sealed. This immutability is not a limitation — it is a deliberate design choice that brings safety, performance, and clarity to your programs.
Why does a tuple exist? In real-world programming, there is a lot of data that should never change after it is set. The coordinates of a fixed location on a map, the RGB colour values of a colour theme, the days of the week, the months of the year — all of these are perfect candidates for tuples. Using a tuple signals to every developer reading your code: "This data is fixed. Do not try to change it."
Where are tuples used?
- Returning multiple values from a function
- Storing database records that should not be accidentally modified
- Using data as dictionary keys (lists cannot be used as keys; tuples can)
- Passing fixed configuration settings through a program
- Representing coordinates, RGB colours, and fixed data pairs
2. Creating a Tuple
Tuples are created using parentheses () and items are separated by commas. You can store any type of data inside a tuple — integers, strings, floats, booleans, or even other tuples.
# Creating a simple tuple of city names
cities = ("Mumbai", "Delhi", "Bangalore", "Chennai")
# Creating a tuple of numbers
temperatures = (36.5, 37.0, 35.8, 38.2)
# Creating a tuple with mixed data types
employee_record = ("E1042", "Software Engineer", 85000, True)
# Displaying each tuple
print(cities)
print(temperatures)
print(employee_record)
(36.5, 37.0, 35.8, 38.2)
('E1042', 'Software Engineer', 85000, True)
What just happened?
- Three tuples were created using parentheses
()with comma-separated values. - The
citiestuple holds only strings;temperaturesholds only floats. employee_recorddemonstrates that a single tuple can hold values of different data types — this is called a mixed or heterogeneous tuple.- When printed, Python always displays tuples with parentheses around them, which visually distinguishes them from lists.
3. Tuple with One Item — The Singleton Tuple
This is one of the most common mistakes beginners make. If you create a single item inside parentheses without a trailing comma, Python will NOT treat it as a tuple. It will simply treat it as a regular value wrapped in brackets.
To create a tuple with just one item, you must always add a trailing comma after the item.
# This looks like a tuple but is NOT a tuple
not_a_tuple = ("Python")
print(type(not_a_tuple)) # Will show <class 'str'>
# This IS a tuple — notice the trailing comma
single_item_tuple = ("Python",)
print(type(single_item_tuple)) # Will show <class 'tuple'>
# Another example with a number
not_a_tuple_2 = (100)
print(type(not_a_tuple_2)) # <class 'int'>
single_number_tuple = (100,)
print(type(single_number_tuple)) # <class 'tuple'>
<class 'tuple'>
<class 'int'>
<class 'tuple'>
What just happened?
("Python")is treated as a string, not a tuple. Python ignores the parentheses here because they can appear in any expression.("Python",)— the trailing comma tells Python: "This is a tuple containing one item." This is called a singleton tuple.- Always use a trailing comma when creating a single-element tuple. This is a beginner-to-professional rule worth memorising.
4. Accessing Tuple Elements
Just like lists, tuples are indexed starting from 0. You access individual items using square brackets with the index number. Tuples also support negative indexing, which lets you count from the end of the tuple.
# A tuple of programming languages
languages = ("Python", "Java", "C++", "JavaScript", "Go")
# Positive indexing — from the start
print(languages[0]) # First element
print(languages[2]) # Third element
print(languages[4]) # Fifth (last) element
# Negative indexing — from the end
print(languages[-1]) # Last element
print(languages[-2]) # Second from last
print(languages[-5]) # Same as index 0
C++
Go
Go
JavaScript
Python
What just happened?
- Index
0accesses the first element; index4accesses the last element in a 5-item tuple. - Negative index
-1always accesses the last element regardless of how long the tuple is — this is very useful in real programs where tuple length may vary. - Negative index
-5on a 5-element tuple is equivalent to index0. - Accessing an index beyond the tuple's length will cause an
IndexError— always be mindful of the tuple's length.
5. Tuple Slicing
Slicing allows you to extract a portion of a tuple and work with it as a new tuple. The slice syntax is tuple[start:stop:step]. The start index is inclusive, and the stop index is exclusive, meaning Python stops just before the stop index.
Slicing is extremely useful when you only need to process a specific segment of a larger data set — for example, extracting the first five items from a tuple of 100 records, or skipping every other item.
# A tuple of months
months = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec")
# Extract the first quarter (Jan to Mar)
q1 = months[0:3]
print("Q1:", q1)
# Extract second quarter (Apr to Jun)
q2 = months[3:6]
print("Q2:", q2)
# Extract from index 8 to end
last_four = months[8:]
print("Last four months:", last_four)
# Extract every second month using step
alternate = months[0:12:2]
print("Alternate months:", alternate)
# Reverse the entire tuple using step -1
reversed_months = months[::-1]
print("Reversed:", reversed_months)
Q2: ('Apr', 'May', 'Jun')
Last four months: ('Sep', 'Oct', 'Nov', 'Dec')
Alternate months: ('Jan', 'Mar', 'May', 'Jul', 'Sep', 'Nov')
Reversed: ('Dec', 'Nov', 'Oct', 'Sep', 'Aug', 'Jul', 'Jun', 'May', 'Apr', 'Mar', 'Feb', 'Jan')
What just happened?
months[0:3]extracts indices 0, 1, and 2 — stop index 3 is excluded. This gives the first 3 months.months[8:]— leaving the stop empty means "go to the end of the tuple." This is a very common and clean pattern.months[0:12:2]— the step of2skips every other item, giving every odd-indexed month.months[::-1]— using step-1with no start or stop reverses the entire tuple. This is a very Pythonic trick used in many real programs.
6. Tuple Immutability — Why You Cannot Change a Tuple
The defining characteristic of a tuple is that it is immutable. This means you cannot add, remove, or change any element after the tuple has been created. If you try to do so, Python will raise a TypeError.
This is not a bug — it is Python's way of protecting data that should never change. When you use a tuple, you are making a clear declaration to yourself and other developers: "This data is fixed and should never be accidentally modified."
# Demonstrating immutability
coordinates = (28.6139, 77.2090) # Latitude, Longitude of a fixed location
# Attempting to change an element — this will cause a TypeError
try:
coordinates[0] = 19.0760 # Trying to change latitude
except TypeError as e:
print("Error:", e)
# Attempting to append — tuples have no append() method
try:
coordinates.append(100)
except AttributeError as e:
print("Error:", e)
# The original tuple is completely unchanged
print("Original coordinates:", coordinates)
Error: 'tuple' object has no attribute 'append'
Original coordinates: (28.6139, 77.209)
What just happened?
- Trying to assign a new value to
coordinates[0]immediately raises aTypeError: "tuple object does not support item assignment." Python protects the data. - Calling
.append()on a tuple raises anAttributeErrorbecause tuples simply do not haveappend,remove, orinsertmethods. - After both failed attempts, the original tuple is completely unchanged. This is exactly the behaviour we want for fixed data.
- The
try/exceptblock is used here to demonstrate errors gracefully — a technique you will study in Lesson 20 (Exception Handling).
7. Tuple Packing and Unpacking
Packing means collecting multiple values into a single tuple. Unpacking means extracting those individual values back into separate variables in one clean step. This is one of the most elegant and widely used features of Python tuples.
In real programs, unpacking is used constantly — when a function returns multiple values, when reading rows from a database, or when working with coordinate pairs. Understanding tuple unpacking will make your Python code dramatically more readable and professional.
# TUPLE PACKING — combining values into one tuple
product = ("Laptop", "Electronics", 75000, True)
print("Packed tuple:", product)
# TUPLE UNPACKING — assigning each element to its own variable
name, category, price, in_stock = product
print("Name:", name)
print("Category:", category)
print("Price:", price)
print("In Stock:", in_stock)
# UNPACKING WITH * (star) — capture remaining elements into a list
scores = (92, 87, 95, 78, 88, 91)
first, second, *remaining = scores
print("\nFirst score:", first)
print("Second score:", second)
print("Remaining scores:", remaining)
Name: Laptop
Category: Electronics
Price: 75000
In Stock: True
First score: 92
Second score: 87
Remaining scores: [95, 78, 88, 91]
What just happened?
- Packing: four separate values were assigned to the variable
productas a single tuple. - Unpacking: in one line —
name, category, price, in_stock = product— Python automatically assigns each tuple element to the corresponding variable from left to right. - The number of variables on the left must match the number of elements in the tuple, otherwise Python raises a
ValueError. - The star operator
*(extended unpacking) captures all "leftover" elements into a regular Python list. This is powerful when you want the first N elements individually and the rest as a group.
8. Built-in Tuple Methods
Because tuples are immutable, they have only two built-in methods: count() and index(). These are simple but useful, especially when working with data that repeats or when you need to find the position of a specific value.
# A tuple of exam grades
grades = (85, 92, 78, 85, 90, 85, 72, 92)
# count() — how many times a value appears in the tuple
count_85 = grades.count(85)
count_92 = grades.count(92)
print("Count of 85:", count_85)
print("Count of 92:", count_92)
# index() — returns the FIRST position where the value is found
pos_78 = grades.index(78)
pos_92 = grades.index(92)
print("First position of 78:", pos_78)
print("First position of 92:", pos_92)
# You can also search within a specific range: index(value, start, end)
second_92 = grades.index(92, pos_92 + 1)
print("Second occurrence of 92 is at index:", second_92)
Count of 92: 2
First position of 78: 2
First position of 92: 1
Second occurrence of 92 is at index: 7
What just happened?
count(85)scans the entire tuple and returns how many times85appears — the answer is 3.index(78)returns the first index where78is found — which is index2.index(92, pos_92 + 1)searches for the next occurrence of92starting from the index after the first one. This is the technique for finding a second, third, or subsequent occurrence.- If you call
index()on a value that does not exist in the tuple, Python raises aValueError. Always ensure the value exists before callingindex()or use it inside atry/exceptblock.
9. Tuple Operations — Concatenation, Repetition, and Membership
Even though tuples are immutable, you can still perform operations on them that produce new tuples without modifying the originals. The three most important operations are concatenation (joining), repetition (multiplying), and membership testing (checking existence).
# CONCATENATION — joining two tuples with +
frontend = ("HTML", "CSS", "JavaScript")
backend = ("Python", "SQL", "Django")
full_stack = frontend + backend
print("Full Stack:", full_stack)
# REPETITION — repeating a tuple with *
warning = ("Check input!",)
repeated_warning = warning * 3
print("Repeated:", repeated_warning)
# MEMBERSHIP TESTING — checking if an item exists with 'in'
print("Is Python in full_stack?", "Python" in full_stack)
print("Is PHP in full_stack?", "PHP" in full_stack)
print("Is HTML NOT in full_stack?", "HTML" not in full_stack)
# LENGTH — finding the total number of elements
print("Total skills:", len(full_stack))
Repeated: ('Check input!', 'Check input!', 'Check input!')
Is Python in full_stack? True
Is PHP in full_stack? False
Is HTML NOT in full_stack? False
Total skills: 6
What just happened?
- The
+operator does not modify either original tuple — it creates a brand new tuplefull_stackcontaining all elements from both. - The
*operator creates a new tuple by repeating the original tuple's contents the given number of times. - The
inkeyword checks if a value exists anywhere in the tuple and returnsTrueorFalse. Thenot inkeyword checks the opposite. These are used constantly in conditional logic. len()returns the total number of elements in the tuple — this is the same function used for lists, strings, and other sequences.
10. Nested Tuples
A nested tuple is a tuple that contains other tuples as its elements. This is useful for representing structured data — such as a table of records, a grid of coordinates, or a collection of grouped items. You access inner elements using double indexing.
# A tuple of employee records (each record is itself a tuple)
employees = (
("E101", "Ananya", "Developer", 90000),
("E102", "Rajan", "Designer", 75000),
("E103", "Suman", "Manager", 110000)
)
# Accessing the entire second record
print("Second employee:", employees[1])
# Accessing a specific field — row index, then column index
print("Name of first employee:", employees[0][1])
print("Salary of third employee:", employees[2][3])
# Looping through all records
print("\nAll Employees:")
for emp in employees:
print(f" ID: {emp[0]} | Name: {emp[1]} | Role: {emp[2]} | Salary: {emp[3]}")
Name of first employee: Ananya
Salary of third employee: 110000
All Employees:
ID: E101 | Name: Ananya | Role: Developer | Salary: 90000
ID: E102 | Name: Rajan | Role: Designer | Salary: 75000
ID: E103 | Name: Suman | Role: Manager | Salary: 110000
What just happened?
employees[1]accesses the second inner tuple — the entire record for the second employee.employees[0][1]uses double indexing: first[0]accesses the first inner tuple, then[1]accesses the second element (name) within it.- The
forloop iterates through each inner tuple. Since eachempis itself a tuple, we can index into it withemp[0],emp[1], etc. - Nested tuples are commonly used to represent rows of database query results in real Python applications, especially when using libraries like
sqlite3orpsycopg2.
11. Iterating Over a Tuple
You can loop through every element of a tuple using a for loop. You can also use enumerate() to get both the index and value at each step, which is especially useful when you need to display or process items with their positions.
# A tuple of tasks
tasks = ("Design UI", "Write Backend", "Set up Database", "Test Application", "Deploy")
# Basic iteration
print("All tasks:")
for task in tasks:
print(" -", task)
# Iteration with enumerate() — gives index + value
print("\nTask List with Numbers:")
for index, task in enumerate(tasks, start=1):
print(f" {index}. {task}")
- Design UI
- Write Backend
- Set up Database
- Test Application
- Deploy
Task List with Numbers:
1. Design UI
2. Write Backend
3. Set up Database
4. Test Application
5. Deploy
What just happened?
- The basic
forloop iterates through each element one at a time, exactly like looping through a list. enumerate(tasks, start=1)provides both the counter and the value on each iteration. Thestart=1argument makes the counter begin at 1 instead of 0, which is useful for human-readable numbering.- Iterating over tuples is identical in syntax to iterating over lists — this is intentional in Python's design to keep things consistent.
12. Converting Between Tuple and List
There will be situations where you have a tuple but need to modify its contents. The standard approach is to convert the tuple to a list, make your changes, and then convert it back to a tuple. This preserves the immutability contract while still allowing occasional controlled modification.
# Starting with an immutable tuple
colour_codes = ("#FF5733", "#28B463", "#2E86C1")
print("Original tuple:", colour_codes)
# Convert to list to allow modification
colour_list = list(colour_codes)
colour_list.append("#F39C12") # Add a new colour
colour_list[0] = "#E74C3C" # Modify the first colour
print("Modified list:", colour_list)
# Convert back to tuple
colour_codes = tuple(colour_list)
print("Updated tuple:", colour_codes)
# Converting a list of values to a tuple
raw_data = [10, 20, 30, 40, 50]
fixed_data = tuple(raw_data)
print("Converted to tuple:", fixed_data)
print("Type:", type(fixed_data))
Modified list: ['#E74C3C', '#28B463', '#2E86C1', '#F39C12']
Updated tuple: ('#E74C3C', '#28B463', '#2E86C1', '#F39C12')
Converted to tuple: (10, 20, 30, 40, 50)
Type: <class 'tuple'>
What just happened?
list(colour_codes)creates a brand new list with the same elements as the tuple — the original tuple is untouched at this point.- Modifications are freely made to the list. Then
tuple(colour_list)creates a new tuple from the updated list and reassigns it tocolour_codes. - This pattern — convert to list → modify → convert back to tuple — is the correct and accepted way to "update" tuple data in Python when absolutely necessary.
tuple(raw_data)shows the reverse conversion: turning any list into a tuple to make it immutable.
13. Tuples as Dictionary Keys
One of the most powerful and unique uses of tuples — something lists cannot do — is serving as dictionary keys. In Python, dictionary keys must be hashable (meaning their value cannot change), which is why lists are not allowed as keys. Tuples, being immutable, are hashable and can be used as keys.
This is extremely useful in real applications such as storing location data, caching results indexed by parameter combinations, and representing grid positions.
# Using tuples as dictionary keys to store city details
# Key = (latitude, longitude), Value = city name
location_map = {
(28.6139, 77.2090): "New Delhi",
(19.0760, 72.8777): "Mumbai",
(12.9716, 77.5946): "Bangalore",
(22.5726, 88.3639): "Kolkata"
}
# Looking up a city by its coordinates
search_coords = (19.0760, 72.8777)
print("City at", search_coords, ":", location_map[search_coords])
# Iterating through the location map
print("\nAll Locations:")
for coords, city in location_map.items():
print(f" {city}: Lat {coords[0]}, Lon {coords[1]}")
All Locations:
New Delhi: Lat 28.6139, Lon 77.209
Mumbai: Lat 19.076, Lon 72.8777
Bangalore: Lat 12.9716, Lon 77.5946
Kolkata: Lat 22.5726, Lon 88.3639
What just happened?
- Each dictionary key is a tuple of
(latitude, longitude). This works because tuples are hashable — their hash value never changes. - If you tried to use a list like
[28.6139, 77.2090]as a key, Python would raise aTypeError: unhashable type: 'list'. - This pattern is widely used in geographic applications, game grid systems, and caching layers in production software.
14. Tuples vs Lists — When to Use Which
A common question beginners ask is: if a tuple does everything a list does but cannot be changed, why use a list at all? The answer is that both serve different purposes and the choice communicates intent in your code.
Use a tuple when the data represents a fixed collection that should not change — like coordinates, RGB values, days of the week, or a record returned from a database. Use a list when the data is dynamic and will change over time — like a shopping cart, a queue of tasks, or a list of user inputs.
Tuples also have a slight performance advantage over lists because Python can optimise them more aggressively in memory, since it knows they will never change. For large-scale applications processing millions of records, this difference can matter significantly.
import sys
# Comparing memory usage of a tuple vs a list with the same data
sample_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
sample_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print("Tuple size in memory:", sys.getsizeof(sample_tuple), "bytes")
print("List size in memory: ", sys.getsizeof(sample_list), "bytes")
# Comparing speed using a simple timing test
import timeit
tuple_time = timeit.timeit("(1,2,3,4,5)", number=1000000)
list_time = timeit.timeit("[1,2,3,4,5]", number=1000000)
print(f"\nTuple creation time (1M times): {tuple_time:.4f} seconds")
print(f"List creation time (1M times): {list_time:.4f} seconds")
List size in memory: 136 bytes
Tuple creation time (1M times): 0.0183 seconds
List creation time (1M times): 0.0421 seconds
What just happened?
sys.getsizeof()reveals that a tuple uses less memory than a list with the same data. The difference grows as the collection gets larger.timeit.timeit()measures how long it takes to create each structure one million times. Tuples are created significantly faster because Python can handle their allocation more efficiently.- This performance difference is the reason experienced developers choose tuples for fixed data even when a list would technically work.
- Your actual times will vary depending on your machine — what matters is the relative difference, not the absolute values.
15. Lesson Summary
Here is a complete reference of everything covered in this lesson. Bookmark this table — it will serve as a quick reference as you build more complex programs.
| Concept | Syntax / Example | Key Point |
|---|---|---|
| Creating a tuple | t = (1, 2, 3) | Ordered, immutable collection |
| Singleton tuple | t = (5,) | Trailing comma is required |
| Positive indexing | t[0], t[2] | Starts at 0 from the left |
| Negative indexing | t[-1], t[-2] | -1 is the last element |
| Slicing | t[1:4], t[::2] | Stop index is exclusive |
| Immutability | t[0] = 5 → TypeError | Cannot change elements |
| Packing | t = 1, 2, 3 | Brackets optional when packing |
| Unpacking | a, b, c = t | Variables must match count |
| Star unpacking | a, *rest = t | Remainder goes into a list |
count() |
t.count(5) | Counts occurrences of a value |
index() |
t.index(5) | Returns first position of value |
| Concatenation | t1 + t2 | Creates a new tuple |
| Repetition | t * 3 | Repeats elements in new tuple |
| Membership | x in t | Returns True or False |
| Nested tuples | t[0][1] | Double indexing to access inner |
| Tuple → List | list(t) | Use to modify, then convert back |
| List → Tuple | tuple(lst) | Freezes mutable list into tuple |
| Tuple as dict key | d[(1,2)] = "val" | Tuples are hashable; lists are not |
🧪 Practice Questions
Answer the following questions based on what you learned in this lesson.
1. A tuple is __________, which means its elements cannot be changed after creation.
2. Write the correct Python syntax to create a tuple containing a single integer 42.
3. What index is used to access the last element of a tuple using negative indexing?
4. Which tuple method is used to find how many times a specific value appears in a tuple?
5. To modify the contents of a tuple, you should first convert it to a __________ using a built-in function.
🎯 Quiz — Test Your Understanding
Q1. What type of error does Python raise when you try to change an element of a tuple?
Q2. What is the output of ('a', 'b') + ('c', 'd')?
Q3. Can a tuple be used as a dictionary key in Python?
Q4. Given t = (10, 20, 30, 40, 50), what does t.index(40) return?
Q5. What is the output of (1, 2, 3) * 2?