Section 3: Tuples

Tuples are like lists with a contract - once created, they can’t be modified. This immutability makes them perfect for storing coordinates, database records, or configuration settings that shouldn’t change during program execution. Think of tuples as the safety deposit box of Python data structures.

Introduction

Tuples are ordered collections of items that cannot be modified after creation. This immutability provides data integrity and makes tuples useful for storing related information that should remain constant, like coordinate pairs, RGB color values, or database records.

Creating Tuples

Tuples are defined using parentheses or by separating values with commas.

Basic Tuple Creation

# Method 1: Using parentheses
coordinates = (10, 20)
colors = ('red', 'green', 'blue')
mixed_data = ('Alice', 25, 75000, True)

# Method 2: Without parentheses (tuple packing)
point = 5, 15
person = 'Bob', 30, 'Engineer'

# Single-item tuple (note the comma)
single_item = (42,)  # Comma is required
# Without comma, it's just parentheses for grouping
not_a_tuple = (42)   # This is just an integer

print(f"Coordinates: {coordinates}")
print(f"Colors: {colors}")
print(f"Single item: {single_item}")
print(f"Type check: {type(single_item)}")

Empty and Multiple Element Tuples

# Empty tuple
empty = ()
empty_alt = tuple()

# Multiple elements
dimensions = (1920, 1080, 32)  # Width, height, color depth
rgb = (255, 128, 0)            # Orange color
employee = ('Sarah', 'Manager', 85000, 'IT')

print(f"Empty tuple: {empty}")
print(f"Dimensions: {dimensions}")
print(f"RGB color: {rgb}")
print(f"Employee data: {employee}")

Accessing Tuple Elements

Tuples support indexing and slicing like lists.

Indexing and Slicing

student_data = ('John Doe', 22, 'Computer Science', 3.8, True)

# Access individual elements
name = student_data[0]
age = student_data[1]
gpa = student_data[3]

print(f"Student: {name}")
print(f"Age: {age}")
print(f"GPA: {gpa}")

# Negative indexing
major = student_data[-3]  # Third from the end
enrolled = student_data[-1]  # Last element

print(f"Major: {major}")
print(f"Enrolled: {enrolled}")

# Slicing
personal_info = student_data[:2]  # First two elements
academic_info = student_data[2:]  # From third element onward

print(f"Personal: {personal_info}")
print(f"Academic: {academic_info}")

Tuple Unpacking

# Unpack tuple into individual variables
coordinates = (45.5, -122.7)
latitude, longitude = coordinates

print(f"Latitude: {latitude}")
print(f"Longitude: {longitude}")

# Multiple assignment
employee = ('Alice Johnson', 'Developer', 75000)
name, position, salary = employee

print(f"Employee: {name}")
print(f"Position: {position}")
print(f"Salary: ${salary:,}")

# Swap variables using tuple unpacking
a = 10
b = 20
print(f"Before swap: a={a}, b={b}")

a, b = b, a  # Elegant variable swap
print(f"After swap: a={a}, b={b}")

Tuple Operations and Methods

While tuples are immutable, they support various operations and methods.

Basic Operations

numbers = (1, 2, 3, 4, 5)
letters = ('a', 'b', 'c')

# Length
print(f"Length of numbers: {len(numbers)}")

# Concatenation (creates new tuple)
combined = numbers + letters
print(f"Combined: {combined}")

# Repetition
repeated = ('echo',) * 3
print(f"Repeated: {repeated}")

# Membership testing
print(f"3 in numbers: {3 in numbers}")
print(f"'x' in letters: {'x' in letters}")

# Iteration
print("Iterating through numbers:")
for num in numbers:
    print(f"Number: {num}")

Tuple Methods

data = (1, 2, 3, 2, 4, 2, 5)

# count() - Count occurrences
count_2 = data.count(2)
print(f"Number of 2s: {count_2}")

# index() - Find first occurrence
index_of_4 = data.index(4)
print(f"Index of 4: {index_of_4}")

# Find index with start and end parameters
first_2 = data.index(2)      # First occurrence
second_2 = data.index(2, 2)  # First occurrence after index 1

print(f"First 2 at index: {first_2}")
print(f"Second 2 at index: {second_2}")

Practical Applications

Tuples excel in scenarios requiring immutable data structures.

Database Records

# Represent database records as tuples
customers = [
    (1, 'ABC Corp', 'alice@abc.com', '555-0101'),
    (2, 'XYZ Ltd', 'bob@xyz.com', '555-0102'),
    (3, 'Tech Inc', 'carol@tech.com', '555-0103')
]

print("Customer Database:")
print("ID | Company  | Email          | Phone")
print("-" * 40)

for customer_id, company, email, phone in customers:
    print(f"{customer_id:2} | {company:8} | {email:14} | {phone}")

# Process specific customer
target_customer = customers[1]
cust_id, company, email, phone = target_customer
print(f"\nProcessing: {company} ({email})")

Coordinates and Points

# Geographic coordinates
locations = [
    ('New York', 40.7128, -74.0060),
    ('London', 51.5074, -0.1278),
    ('Tokyo', 35.6762, 139.6503),
    ('Sydney', -33.8688, 151.2093)
]

def calculate_distance_info(locations):
    """Process geographic data."""
    print("City Coordinates:")
    for city, lat, lon in locations:
        hemisphere_lat = 'N' if lat >= 0 else 'S'
        hemisphere_lon = 'E' if lon >= 0 else 'W'
        print(f"{city}: {abs(lat):.2f}°{hemisphere_lat}, {abs(lon):.2f}°{hemisphere_lon}")

calculate_distance_info(locations)

# 2D/3D points
points_2d = [(0, 0), (1, 1), (2, 4), (3, 9)]
points_3d = [(1, 2, 3), (4, 5, 6), (7, 8, 9)]

print(f"\n2D Points: {points_2d}")
print(f"3D Points: {points_3d}")

Configuration and Settings

# Application configuration
config = (
    ('DEBUG', True),
    ('MAX_CONNECTIONS', 100),
    ('TIMEOUT', 30),
    ('DATABASE_URL', 'postgresql://localhost/mydb')
)

# Process configuration
settings = {}
for key, value in config:
    settings[key] = value
    print(f"{key}: {value}")

# RGB color definitions
colors = {
    'red': (255, 0, 0),
    'green': (0, 255, 0),
    'blue': (0, 0, 255),
    'white': (255, 255, 255),
    'black': (0, 0, 0)
}

def get_color_info(color_name):
    """Get RGB values for a color."""
    if color_name in colors:
        r, g, b = colors[color_name]
        return f"{color_name.title()}: RGB({r}, {g}, {b})"
    return f"Color '{color_name}' not found"

print(f"\n{get_color_info('red')}")
print(f"{get_color_info('blue')}")

Tuples vs Lists vs Sets

Understanding when to use each data structure.

Comparison

# Same data in different structures
data_tuple = (1, 2, 3, 2, 4)
data_list = [1, 2, 3, 2, 4]
data_set = {1, 2, 3, 4}  # Note: duplicates removed

print("Data Structures Comparison:")
print(f"Tuple:  {data_tuple}")
print(f"List:   {data_list}")
print(f"Set:    {data_set}")

# Memory usage (tuples are more memory efficient)
import sys
print(f"\nMemory usage:")
print(f"Tuple: {sys.getsizeof(data_tuple)} bytes")
print(f"List:  {sys.getsizeof(data_list)} bytes")

# Performance (tuples are faster for access)
import timeit

tuple_access = timeit.timeit(lambda: data_tuple[2], number=1000000)
list_access = timeit.timeit(lambda: data_list[2], number=1000000)

print(f"\nAccess performance (1M operations):")
print(f"Tuple: {tuple_access:.4f} seconds")
print(f"List:  {list_access:.4f} seconds")

When to Use Each

# Use tuples for:
# 1. Fixed collections that won't change
coordinates = (latitude, longitude)
rgb_color = (255, 128, 0)
database_record = (id, name, email, created_date)

# 2. Dictionary keys (tuples are hashable)
locations = {
    (0, 0): "Origin",
    (1, 1): "Point A", 
    (2, 2): "Point B"
}

# 3. Function returns with multiple values
def get_name_age():
    return "John", 25  # Returns a tuple

name, age = get_name_age()  # Tuple unpacking

# Use lists for:
# 1. Collections that need modification
shopping_cart = ['milk', 'eggs', 'bread']
shopping_cart.append('cheese')  # Can modify

# 2. Unknown final size
user_inputs = []
# Add items dynamically

# Use sets for:
# 1. Unique collections
unique_visitors = {'user1', 'user2', 'user3'}
# 2. Mathematical operations
tags_a = {'python', 'data', 'analysis'}
tags_b = {'python', 'machine', 'learning'}
common_tags = tags_a & tags_b  # Intersection

Advanced Tuple Techniques

More sophisticated tuple usage patterns.

Named Tuples

from collections import namedtuple

# Create a named tuple class
Point = namedtuple('Point', ['x', 'y'])
Person = namedtuple('Person', ['name', 'age', 'city'])

# Create instances
origin = Point(0, 0)
point_a = Point(3, 4)

john = Person('John Doe', 30, 'New York')
jane = Person('Jane Smith', 25, 'Boston')

# Access by name or index
print(f"Origin: {origin}")
print(f"Point A: x={point_a.x}, y={point_a.y}")
print(f"John: {john.name}, {john.age} years old")

# Still works like regular tuple
print(f"John by index: {john[0]}, {john[1]}")

# Useful methods
print(f"Point fields: {point_a._fields}")
print(f"John as dict: {john._asdict()}")

# Create new instance with some fields changed
older_john = john._replace(age=31)
print(f"Older John: {older_john}")

Tuple Comprehensions and Generator Expressions

# Generator expression (not tuple comprehension)
squared_gen = (x**2 for x in range(5))
print(f"Generator: {squared_gen}")

# Convert to tuple
squared_tuple = tuple(x**2 for x in range(5))
print(f"Squared tuple: {squared_tuple}")

# Practical example: Process data into tuples
sales_data = [
    {'product': 'A', 'price': 100, 'quantity': 5},
    {'product': 'B', 'price': 200, 'quantity': 3},
    {'product': 'C', 'price': 150, 'quantity': 4}
]

# Create tuples of (product, total_value)
sales_tuples = tuple(
    (item['product'], item['price'] * item['quantity'])
    for item in sales_data
)

print(f"Sales totals: {sales_tuples}")

for product, total in sales_tuples:
    print(f"{product}: ${total}")

Practice Exercise

Work with tuples in a data analysis context:

def analyze_student_grades():
    """Analyze student grade data using tuples."""
    
    # Student data: (name, course, grade, credits)
    students = [
        ('Alice Johnson', 'Mathematics', 92, 3),
        ('Alice Johnson', 'Physics', 88, 4),
        ('Alice Johnson', 'Chemistry', 95, 3),
        ('Bob Smith', 'Mathematics', 78, 3),
        ('Bob Smith', 'Physics', 82, 4),
        ('Bob Smith', 'English', 90, 3),
        ('Carol Davis', 'Mathematics', 96, 3),
        ('Carol Davis', 'Physics', 91, 4),
        ('Carol Davis', 'Biology', 94, 4)
    ]
    
    print("Student Grade Analysis")
    print("=" * 50)
    
    # Calculate GPA for each student
    student_gpas = {}
    
    for name, course, grade, credits in students:
        if name not in student_gpas:
            student_gpas[name] = []
        
        # Convert grade to GPA points
        if grade >= 90:
            gpa_points = 4.0
        elif grade >= 80:
            gpa_points = 3.0
        elif grade >= 70:
            gpa_points = 2.0
        else:
            gpa_points = 1.0
        
        student_gpas[name].append((gpa_points, credits))
    
    # Calculate weighted GPA
    print("Student GPAs:")
    for name, grade_data in student_gpas.items():
        total_points = sum(gpa * credits for gpa, credits in grade_data)
        total_credits = sum(credits for gpa, credits in grade_data)
        weighted_gpa = total_points / total_credits
        
        print(f"{name}: {weighted_gpa:.2f} GPA")
    
    # Course statistics
    course_stats = {}
    for name, course, grade, credits in students:
        if course not in course_stats:
            course_stats[course] = []
        course_stats[course].append(grade)
    
    print(f"\nCourse Statistics:")
    for course, grades in course_stats.items():
        avg_grade = sum(grades) / len(grades)
        min_grade = min(grades)
        max_grade = max(grades)
        
        print(f"{course}:")
        print(f"  Average: {avg_grade:.1f}")
        print(f"  Range: {min_grade} - {max_grade}")

# Run the analysis
analyze_student_grades()

Summary

Tuples provide immutable, ordered collections perfect for fixed data sets. Key features include tuple packing/unpacking, use as dictionary keys, and memory efficiency. They excel in scenarios requiring data integrity, like coordinates, database records, and configuration settings.


© 2025 Prof. Tim Frenzel. All rights reserved. | Version 1.0.5