Python - List vs Tuple vs Set Differences
The three collection types have distinct memory footprints and performance profiles. Tuples consume less memory than lists because they're immutable—Python can optimize storage without reserving...
Key Insights
- Lists, tuples, and sets differ fundamentally in mutability, ordering, and duplicate handling—lists are mutable and ordered, tuples are immutable and ordered, sets are mutable but unordered with unique elements only
- Performance characteristics vary significantly: tuples offer faster iteration and lower memory overhead, sets provide O(1) lookup time, while lists excel at ordered operations with acceptable O(n) search complexity
- Choose lists for dynamic collections requiring order, tuples for immutable data and dictionary keys, and sets for membership testing and mathematical operations like unions and intersections
Memory and Performance Characteristics
The three collection types have distinct memory footprints and performance profiles. Tuples consume less memory than lists because they’re immutable—Python can optimize storage without reserving space for potential growth.
import sys
my_list = [1, 2, 3, 4, 5]
my_tuple = (1, 2, 3, 4, 5)
my_set = {1, 2, 3, 4, 5}
print(f"List size: {sys.getsizeof(my_list)} bytes")
print(f"Tuple size: {sys.getsizeof(my_tuple)} bytes")
print(f"Set size: {sys.getsizeof(my_set)} bytes")
# Output (approximate):
# List size: 104 bytes
# Tuple size: 88 bytes
# Set size: 224 bytes
Sets use more memory due to their hash table implementation, but this trades space for speed. Membership testing demonstrates the performance difference:
import timeit
large_list = list(range(10000))
large_tuple = tuple(range(10000))
large_set = set(range(10000))
# Search for element near the end
list_time = timeit.timeit(lambda: 9999 in large_list, number=10000)
tuple_time = timeit.timeit(lambda: 9999 in large_tuple, number=10000)
set_time = timeit.timeit(lambda: 9999 in large_set, number=10000)
print(f"List lookup: {list_time:.4f}s")
print(f"Tuple lookup: {tuple_time:.4f}s")
print(f"Set lookup: {set_time:.4f}s")
# Typical results:
# List lookup: 0.8234s
# Tuple lookup: 0.7891s
# Set lookup: 0.0003s
Mutability and Modification Operations
Lists support in-place modifications, making them ideal for dynamic data. Tuples are immutable—any “modification” creates a new tuple.
# List modifications
numbers = [1, 2, 3]
numbers.append(4)
numbers[0] = 10
numbers.extend([5, 6])
numbers.remove(2)
print(numbers) # [10, 3, 4, 5, 6]
# Tuple immutability
coordinates = (10, 20)
# coordinates[0] = 15 # TypeError: 'tuple' object does not support item assignment
# Creating new tuples
new_coordinates = (15,) + coordinates[1:]
print(new_coordinates) # (15, 20)
# Set modifications
unique_ids = {1, 2, 3}
unique_ids.add(4)
unique_ids.update([5, 6, 6]) # Duplicates ignored
unique_ids.discard(2)
print(unique_ids) # {1, 3, 4, 5, 6}
The immutability of tuples enables their use as dictionary keys and set elements:
# Tuples as dictionary keys
locations = {
(40.7128, -74.0060): "New York",
(51.5074, -0.1278): "London"
}
# Lists cannot be dictionary keys
# locations[[40.7128, -74.0060]] = "New York" # TypeError: unhashable type: 'list'
# Tuples in sets
coordinate_set = {(0, 0), (1, 1), (2, 2)}
coordinate_set.add((3, 3))
print(coordinate_set) # {(0, 0), (1, 1), (2, 2), (3, 3)}
Ordering and Indexing
Lists and tuples maintain insertion order and support indexing, slicing, and iteration in predictable sequence. Sets are unordered and don’t support index-based access.
# List and tuple indexing
fruits_list = ['apple', 'banana', 'cherry', 'date']
fruits_tuple = ('apple', 'banana', 'cherry', 'date')
print(fruits_list[1]) # banana
print(fruits_tuple[-1]) # date
print(fruits_list[1:3]) # ['banana', 'cherry']
print(fruits_tuple[:2]) # ('apple', 'banana')
# Sets don't support indexing
fruits_set = {'apple', 'banana', 'cherry', 'date'}
# print(fruits_set[0]) # TypeError: 'set' object is not subscriptable
# Sets maintain insertion order in Python 3.7+, but this is an implementation detail
# Don't rely on set order for logic
for fruit in fruits_set:
print(fruit) # Order not guaranteed across Python versions
Ordering matters for sorting and reversing operations:
numbers_list = [3, 1, 4, 1, 5]
numbers_list.sort()
print(numbers_list) # [1, 1, 3, 4, 5]
numbers_tuple = (3, 1, 4, 1, 5)
sorted_tuple = tuple(sorted(numbers_tuple))
print(sorted_tuple) # (1, 1, 3, 4, 5)
# Sets remove duplicates and don't maintain sort order
numbers_set = {3, 1, 4, 1, 5}
print(numbers_set) # {1, 3, 4, 5}
print(sorted(numbers_set)) # [1, 3, 4, 5] - returns list
Duplicate Handling
Sets automatically enforce uniqueness, while lists and tuples allow duplicates. This fundamental difference drives use case selection.
# Lists preserve all elements including duplicates
page_views = [101, 102, 101, 103, 102, 101]
print(f"Total views: {len(page_views)}") # 6
print(f"Views: {page_views}")
# Sets extract unique elements
unique_pages = set(page_views)
print(f"Unique pages: {len(unique_pages)}") # 3
print(f"Pages: {unique_pages}") # {101, 102, 103}
# Removing duplicates while preserving order
def unique_ordered(sequence):
seen = set()
return [x for x in sequence if not (x in seen or seen.add(x))]
print(unique_ordered(page_views)) # [101, 102, 103]
This characteristic makes sets ideal for deduplication and membership operations:
# Finding common elements
team_a = {'Alice', 'Bob', 'Charlie', 'David'}
team_b = {'Charlie', 'David', 'Eve', 'Frank'}
# Intersection
common_members = team_a & team_b
print(f"Common: {common_members}") # {'Charlie', 'David'}
# Union
all_members = team_a | team_b
print(f"All: {all_members}") # {'Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Frank'}
# Difference
only_team_a = team_a - team_b
print(f"Only in A: {only_team_a}") # {'Alice', 'Bob'}
# Symmetric difference
exclusive_members = team_a ^ team_b
print(f"Exclusive: {exclusive_members}") # {'Alice', 'Bob', 'Eve', 'Frank'}
Practical Use Cases
Each collection type solves specific problems. Lists handle ordered, mutable sequences:
# Task queue with priority
task_queue = []
task_queue.append(('send_email', 2))
task_queue.append(('backup_db', 1))
task_queue.append(('generate_report', 3))
task_queue.sort(key=lambda x: x[1])
for task, priority in task_queue:
print(f"Execute: {task} (priority: {priority})")
Tuples represent immutable records and function return values:
# Database record
user_record = (1001, 'john@example.com', '2024-01-15', True)
user_id, email, created_date, is_active = user_record
# Multiple return values
def get_user_stats(user_id):
return (user_id, 150, 4.5, 'premium')
uid, posts, rating, tier = get_user_stats(1001)
# Configuration that shouldn't change
DATABASE_CONFIG = ('localhost', 5432, 'mydb', 'readonly')
Sets excel at filtering and mathematical operations:
# Remove duplicate entries from log processing
def process_logs(log_entries):
processed_ids = set()
for entry in log_entries:
entry_id = entry['id']
if entry_id in processed_ids:
continue
processed_ids.add(entry_id)
# Process entry
print(f"Processing: {entry_id}")
logs = [
{'id': 'A1', 'data': 'x'},
{'id': 'A2', 'data': 'y'},
{'id': 'A1', 'data': 'z'}, # Duplicate
]
process_logs(logs)
# Access control
admin_permissions = {'read', 'write', 'delete', 'admin'}
user_permissions = {'read', 'write'}
can_delete = 'delete' in user_permissions # False - O(1) lookup
has_all_admin = user_permissions.issubset(admin_permissions) # True
Conversion Between Types
Converting between collection types is straightforward but has implications for ordering and duplicates:
# List to tuple (preserves order and duplicates)
my_list = [1, 2, 2, 3]
my_tuple = tuple(my_list)
print(my_tuple) # (1, 2, 2, 3)
# Tuple to set (removes duplicates, loses order guarantee)
my_set = set(my_tuple)
print(my_set) # {1, 2, 3}
# Set to list (arbitrary order)
back_to_list = list(my_set)
print(back_to_list) # [1, 2, 3]
# String to collections
text = "hello"
print(list(text)) # ['h', 'e', 'l', 'l', 'o']
print(tuple(text)) # ('h', 'e', 'l', 'l', 'o')
print(set(text)) # {'h', 'e', 'l', 'o'}
Choose the appropriate collection type based on your specific requirements: mutability needs, ordering constraints, duplicate handling, and performance characteristics. Lists provide flexibility, tuples ensure immutability, and sets deliver fast membership testing with automatic uniqueness.