Tuples & Sequence Unpacking
Master immutable sequences, CPython's compact PyTupleObject memory layout, the single-item comma rule, structural and starred unpacking (*rest), composite dictionary keys, and shallow hashability invariants.
01 / Concept Foundations: Immutability, Memory Architecture & Hashability
Mental Model: The Notarized Seal on a Fixed Ledger Record
A list is a whiteboard: items can be added, erased, or rearranged at will. A tuple is an official notarized legal contract: once stamped and delivered, the document cannot be expanded, truncated, or altered. Because the structure is permanently fixed, CPython packages it in a tight, read-only memory envelope that never wastes capacity.
What Is a Python Tuple?
In Python, a tuple is an ordered, immutable sequence of heterogeneous objects. Like lists, tuples maintain zero-based positive and negative indexing, slicing, and length queries. However, **tuples cannot be modified after instantiation**: items cannot be appended, removed, or overwritten.
Crucially, in Python's grammar, it is the comma that creates a tuple, not the parentheses:
x = (42)evaluates to a plainintinside mathematical parentheses.x = (42,)has a trailing comma, signaling to the compiler that this is a 1-item tuple.x = 1, 2, 3evaluates to(1, 2, 3)via automatic tuple packing.
Shallow Immutability & Hashability
A tuple's immutability is shallow: its pointer array cannot change, but if an element points to a mutable list ((1, [2, 3])), that inner list can still be modified! A tuple is only hashable (valid as a dictionary key or set element) if all of its constituent elements are also hashable.
CPython PyTupleObject Architecture
| Struct Field | Role | Architectural Benefit |
|---|---|---|
| ob_refcnt | Reference count | Standard PyObject GC header |
| ob_size | Logical length | O(1) read via len() |
| ob_item[1] | Inline pointer array | Stored inline; zero heap indirection |
| allocated | ABSENT | Zero over-allocation memory waste! |
| free_list | CPython pool (N ≤ 20) | Instant recycling without malloc() syscalls |
Side-by-Side: Dynamic Python List vs. Compact Immutable Tuple
# Dynamic array with over-allocation
users = ["alice", "bob"]
users.append("charlie") # Mutates in place
# Cannot be used as dictionary key:
# cache[users] = True -> TypeError: unhashable type: 'list'# Compact inline pointer array (zero over-allocation)
users = ("alice", "bob")
# users.append("charlie") -> AttributeError
# Hashable composite dictionary key:
cache[users] = True # Perfectly valid! O(1) hash lookup02 / Syntax Anatomy: Structural & Starred Unpacking
Click any component of this structural unpacking statement to inspect how Python distributes sequence items into variables, lists, and nested records.
Click any syntax token above to inspect how structural unpacking parses complex sequence payloads.
03 / Execution Architecture: PyTupleObject Memory Engine
Step through how CPython allocates compact inline structs, bypasses over-allocation, and recycles small tuples via its internal free list.
In CPython, a tuple struct is allocated as a single contiguous memory block: PyTupleObject includes the header and the array of 64-bit pointers directly inline (ob_item[1]). There is no secondary pointer buffer on the heap.
PyTupleObject (3 elements): +------------------+------------------+------------------+------------------+ | ob_refcnt (8B) | ob_type (8B) | ob_size: 3 (8B) | ob_item[0] (8B) | +------------------+------------------+------------------+------------------+ | ob_item[1] (8B) | ob_item[2] (8B) | <-- Contiguous inline memory! +------------------+------------------+
04 / Sequence Comparison Matrix: Architecture & Trade-Offs
| Dimension | Tuple (PyTupleObject) | List (PyListObject) |
|---|---|---|
| Mutability | Immutable (write-protected) | Mutable (append, pop, slice assign) |
| Memory Layout | Inline contiguous pointer block | Secondary over-allocated pointer buffer |
| Dict Key / Set Member | Yes (if contents hashable) | No (TypeError: unhashable type: 'list') |
| Memory Efficiency | ~20-30% smaller memory footprint | Over-allocated capacity headroom |
| Ideal Conceptual Domain | Fixed heterogeneous records (row, coords) | Dynamic homogeneous collections (queues, stacks) |
05 / Syntax Sandbox & Sequence Snippets
Explore 12 essential Python tuple manipulation patterns. Copy or run any snippet directly in the playground.
empty = () # Empty tuple
singleton = ("admin",) # Singleton tuple (MANDATORY trailing comma!)
not_a_tuple = ("admin") # Just a string! type is str
coords = (37.7749, -122.4194) # Standard parenthesized tuple
from_list = tuple([1, 2, 3]) # Constructor from iterable06 / Tiered Walkthrough Examples: Production Architecture
Geographic Waypoint Tracker & Distance Calculator
Problem Specification
Implement a geographic telemetry navigator. The system stores GPS waypoints as immutable (latitude, longitude, city_name) tuples. The program must unpack the starting location, find the northernmost waypoint using max() with a key function, and compute the Euclidean distance from the starting location to all subsequent waypoints, returning an itemized travel log.
Production Implementation
def calculate_waypoint_routes():
# Immutable GPS waypoints: (latitude, longitude, city_name)
waypoints = [
(37.7749, -122.4194, "San Francisco"),
(34.0522, -118.2437, "Los Angeles"),
(47.6062, -122.3321, "Seattle"),
(39.7392, -104.9903, "Denver")
]
print("=" * 52)
print(f"{'GPS WAYPOINT TELEMETRY NAVIGATOR':^52}")
print("=" * 52)
# 1. Unpack starting point
start_lat, start_lon, start_city = waypoints[0]
print(f"Origin: {start_city} ({start_lat:.4f}°N, {abs(start_lon):.4f}°W)")
print("-" * 52)
# 2. Identify northernmost destination (highest latitude)
northernmost = max(waypoints, key=lambda wp: wp[0])
print(f"Northernmost Destination: {northernmost[2]} (Lat: {northernmost[0]:.4f}°N)")
print("-" * 52)
# 3. Calculate Euclidean distance to all destinations
print("Legs from Origin:")
for wp in waypoints[1:]:
# Tuple unpacking directly in loop header
dest_lat, dest_lon, dest_city = wp
# Approximate planar coordinate distance
d_lat = dest_lat - start_lat
d_lon = dest_lon - start_lon
dist = round((d_lat**2 + d_lon**2)**0.5, 4)
print(f" -> Destination: {dest_city:<15} Distance Index: {dist:>8.4f}")
print("=" * 52)
return waypoints, northernmost
routes, north_city = calculate_waypoint_routes()
Engineering Deep Dive
This beginner example demonstrates why tuples are the universal standard for multi-field coordinate records. A location is conceptually a single, immutable point; individual latitude and longitude values should never drift or be modified independently. Notice the clean tuple unpacking: `start_lat, start_lon, start_city = waypoints[0]`. In the loop, `for wp in waypoints[1:]:` slices the sequence without copying the waypoint data itself. Unpacking `dest_lat, dest_lon, dest_city = wp` provides readable local variable names while guaranteeing that the underlying coordinate record remains tamper-proof.
Database Connection Pool & Host Router
Problem Specification
Construct an enterprise microservice database connection router. Database endpoints are identified by composite (host, port) tuples serving as dictionary lookup keys. The router must query connections, safely unpack database row records returned as tuples from simulated SQL queries (user_id, username, role, client_addr), and demonstrate how tuple immutability prevents unauthorized role elevation.
Production Implementation
class DatabaseConnectionRouter:
def __init__(self):
# Composite (host, port) tuples used as dictionary keys
self.connection_pool = {
("db-primary.internal", 5432): "PG_PRIMARY_ACTIVE",
("db-replica-01.internal", 5432): "PG_REPLICA_READONLY",
("redis-cache.internal", 6379): "REDIS_CACHE_ACTIVE"
}
def route_query(self, host: str, port: int) -> str:
endpoint_key = (host, port) # Tuple packing
return self.connection_pool.get(endpoint_key, "ERROR_HOST_NOT_FOUND")
def execute_auth_query() -> list[tuple]:
# Simulated SQL rows returned from psycopg/sqlite driver
# Schema: (user_id, username, role, (client_ip, client_port))
return [
(101, "alice", "administrator", ("10.0.0.15", 44322)),
(102, "bob", "developer", ("10.0.0.28", 51230)),
(103, "charlie", "contractor", ("192.168.1.100", 60102))
]
router = DatabaseConnectionRouter()
print("Connection Pool Routing:")
print(f" Master DB: {router.route_query('db-primary.internal', 5432)}")
print(f" Replica: {router.route_query('db-replica-01.internal', 5432)}")
print(f" Unknown: {router.route_query('external.com', 80)}")
print("\nProcessing Database Cursor Records:")
rows = execute_auth_query()
for row in rows:
# Nested tuple unpacking: extracts nested socket (client_ip, client_port)
uid, uname, role, (ip, port) = row
print(f" User #{uid}: {uname:<10} [{role:<13}] Source: {ip}:{port}")
# Verify security invariant: Attempting to mutate database row record fails
try:
rows[0][2] = "superuser" # Unauthorized privilege escalation attempt!
except TypeError as ex:
print(f"\nSecurity Invariant Verified: Mutating tuple raised {type(ex).__name__}!")
Engineering Deep Dive
In production backend infrastructure, tuples solve two critical engineering problems: 1. **Composite Hash Keys:** A list cannot be a dictionary key because it is mutable (its hash could change while stored in the hash table, destroying the hash invariant). Tuples containing strings and integers are immutable and hashable, making `(host, port)` an ideal composite key. 2. **Defensive Database Contracts:** Database drivers (like Psycopg or asyncpg) return query rows as tuples. If rows were returned as lists, any consumer function could accidentally mutate a user's role (`row[2] = 'superuser'`), polluting shared application caches. Tuples guarantee that database records remain strictly read-only.
Cryptographic Audit Ledger Block Integrity Engine
Problem Specification
Implement an immutable blockchain-style audit ledger block generator. Each block is structured as an immutable tuple: (index, previous_hash, timestamp_ms, transactions_tuple, block_hash). The engine must verify that the transaction payload is fully hashable, compute a deterministic SHA-256 block hash, and use starred unpacking to extract transaction history while guaranteeing tamper-evident immutability.
Production Implementation
import hashlib
import time
def create_audit_block(index: int, prev_hash: str, timestamp: int, transactions: tuple) -> tuple:
"""
Generates an immutable ledger block tuple with cryptographic SHA-256 seal.
"""
# 1. Enforce payload immutability: transactions MUST be a hashable tuple
try:
hash(transactions)
except TypeError:
raise ValueError("Transaction payload must consist solely of immutable, hashable elements!")
# 2. Compute deterministic cryptographic block hash
hasher = hashlib.sha256()
payload_repr = f"{index}|{prev_hash}|{timestamp}|{transactions}"
hasher.update(payload_repr.encode("utf-8"))
block_hash = hasher.hexdigest()
# 3. Return sealed 5-element block tuple
return (index, prev_hash, timestamp, transactions, block_hash)
# Immutable transaction records: ((sender, receiver, amount), ...)
ledger_transactions = (
("corp_treasury", "vendor_aws", 14250.00),
("corp_treasury", "vendor_cloudflare", 3200.50),
("client_alpha", "corp_treasury", 50000.00)
)
genesis_block = create_audit_block(
index=0,
prev_hash="0" * 64,
timestamp=1773680000000,
transactions=ledger_transactions
)
print("=" * 66)
print(f"{'SEALED AUDIT LEDGER BLOCK':^66}")
print("=" * 66)
# Starred unpacking: extracts block indices, header, and hash
block_idx, parent_hash, block_time, *tx_data, seal_hash = genesis_block
print(f"Block Index: {block_idx}")
print(f"Parent Hash: {parent_hash[:16]}...{parent_hash[-16:]}")
print(f"Timestamp: {block_time}ms")
print(f"Block Hash: {seal_hash}")
print("-" * 66)
print(f"Sealed Transactions ({len(tx_data[0])} records):")
for tx in tx_data[0]:
src, dst, val = tx
print(f" * {src:<16} -> {dst:<20} : ${val:>10.2f}")
print("=" * 66)
Engineering Deep Dive
This advanced example demonstrates cryptographic verification and structural unpacking in distributed ledgers and audit pipelines. The function verifies hashability with `hash(transactions)`. If a developer mistakenly passed a mutable list of transactions, Python raises a `TypeError`, rejecting the payload before signing. In the block unpacking expression `block_idx, parent_hash, block_time, *tx_data, seal_hash = genesis_block`, Python maps the first three variables to header metadata, collects intermediate fields into `tx_data`, and assigns the final cryptographic signature to `seal_hash`. Because the block is an immutable tuple, its state cannot be modified in-flight without allocating a completely new tuple, making unauthorized tampering immediately detectable.
07 / Anti-Patterns & Immutability Pitfalls
Study these 6 classic pitfalls: the missing comma singleton bug, augmented assignment exceptions on mutable children, and unhashable tuple keys.
🟢 Beginner Pitfall: The Missing Comma Singleton Bug
Symptom: Parentheses are treated as mathematical grouping, resulting in an int, str, or float rather than a tuple.
val = (42) # BUG: This is an int, not a tuple! print(type(val)) print(len(val)) # TypeError: object of type 'int' has no len()
val = (42,) # Note the trailing comma # Or: val = 42,
Why it happens: In Python grammar, commas define tuples. A single item in parentheses without a trailing comma is just an isolated expression.
🟢 Beginner Pitfall: Attempting In-Place Mutation
Symptom: TypeError: 'tuple' object does not support item assignment
point = (10, 20) point[0] = 99 # BUG: Raises TypeError!
# Tuples cannot be modified in-place; construct a new tuple: point = (99, point[1])
Why it happens: Tuples are immutable; their internal pointer arrays are read-only.
🟡 Intermediate Pitfall: The Augmented Assignment Exception Gotcha
Symptom: TypeError: 'tuple' object does not support item assignment is raised, yet t[1] becomes [2, 3, 4]!
t = (1, [2, 3]) t[1] += [4] # BUG: Raises TypeError BUT still mutates the list!
# Mutate the inner list directly without augmented assignment to the tuple index: t[1].extend([4])
Why it happens: += calls list.__iadd__ (which successfully mutates the list in place) and then attempts to assign the result back to t[1] (which raises TypeError because tuples reject assignment).
🟡 Intermediate Pitfall: Using an Unhashable Tuple as a Dictionary Key
Symptom: TypeError: unhashable type: 'list'
user_cache = {}
key = ("admin", ["read", "write"])
user_cache[key] = True # BUG: Raises TypeError!# Convert inner lists to tuples as well:
key = ("admin", ("read", "write"))
user_cache[key] = TrueWhy it happens: A tuple is only hashable if all of its constituent elements are hashable. Nested mutable lists cannot be hashed.
🔴 Advanced Pitfall: Unpacking Count Mismatch (Too Many / Too Few Values)
Symptom: ValueError: too many values to unpack (expected 2)
coords = (10, 20, 30) x, y = coords # BUG: 3 elements vs 2 variables!
# Match variable count, discard excess, or use starred unpacking: x, y, z = coords x, y, _ = coords x, *rest = coords
Why it happens: Standard unpacking requires an exact 1-to-1 match between tuple elements and variable names.
🔴 Advanced Pitfall: Assuming Tuples Provide Deep Immutability
Symptom: Inner objects mutate, breaking expected immutability guarantees.
permissions = (["read"], ["write"])
permissions[0].append("execute") # BUG: Modifies data inside immutable tuple!
print(permissions) # (['read', 'execute'], ['write'])# Use nested tuples for complete immutability:
permissions = (("read",), ("write",))Why it happens: Tuples only protect their immediate pointer array. Nested mutable objects remain mutable.
08 / Real-World Industry Systems
See how POSIX sockets, relational database drivers, game graphics pipelines, and spatial indexing engines leverage tuple immutability and unpacking.
🟢 Simple Script: BSD Socket Binding Address Tuple
Python's low-level socket module and web servers (Uvicorn, Gunicorn) bind TCP/UDP sockets to network addresses represented as (host, port) tuples.
import socket
def get_socket_endpoint_info(host: str, port: int) -> tuple[str, int]:
# Address represented as immutable pair
server_address = (host, port)
print(f"Allocated network socket on endpoint: {server_address[0]}:{server_address[1]}")
return server_address
endpoint = get_socket_endpoint_info("0.0.0.0", 8080)
print("Endpoint tuple:", endpoint)
print("Endpoint type:", type(endpoint).__name__)
In POSIX socket programming, IP address and port are coupled into a single immutable unit. Python's standard `socket.bind()` method requires a 2-tuple `(host, port)`.
🟡 Scaled Service: SQL Cursor Result Set Tuple Streaming
High-throughput database engines (PostgreSQL, SQLite) return query rows as tuples to prevent record tampering and minimize memory overhead.
sql_cursor_rows = [
(1, "Alice", "Engineering", 125000.0),
(2, "Bob", "Marketing", 95000.0),
(3, "Charlie", "Engineering", 110000.0)
]
print("Streaming Database Rows (Read-Only):")
for row in sql_cursor_rows:
emp_id, name, dept, salary = row
print(f" Emp #{emp_id}: {name:<10} Dept: {dept:<12} Salary: ${salary:,.2f}")
Tuples provide memory efficiency and safety when streaming millions of query records from database servers into application memory.
🔴 Enterprise Production: RGB Color Palette & Alpha Channel Vectors
Game engines (Pygame, Ursina) represent RGB/RGBA color vectors and 3D transformation coordinates as tuples to leverage fast tuple unpacking in shader loops.
PALETTE = {
"DARK_BG": (15, 23, 42),
"ACCENT_CYAN": (6, 182, 212),
"ALERT_RED": (239, 68, 68)
}
def format_css_rgb(color_tuple: tuple[int, int, int]) -> str:
r, g, b = color_tuple
return f"rgb({r}, {g}, {b})"
for name, rgb in PALETTE.items():
print(f"Color '{name}': {format_css_rgb(rgb)} | Hex: #{rgb[0]:02x}{rgb[1]:02x}{rgb[2]:02x}")
Color channels (R, G, B) are fixed-length mathematical vectors. Using tuples ensures color constants remain immutable throughout rendering loops.
🔴 Enterprise Production: 2D Grid Coordinate Spatial Hashing
Geospatial engines (GeoDjango, PostGIS) partition maps into spatial hash buckets using (grid_x, grid_y) tuples as fast dictionary keys.
spatial_grid = {}
def register_entity(entity_id: str, x: float, y: float, cell_size: float = 10.0):
cell_coord = (int(x // cell_size), int(y // cell_size))
if cell_coord not in spatial_grid:
spatial_grid[cell_coord] = []
spatial_grid[cell_coord].append(entity_id)
register_entity("sensor_01", 14.2, 22.5)
register_entity("sensor_02", 18.9, 29.1)
register_entity("sensor_03", 35.0, 42.0)
print("Spatial Hash Buckets:")
for cell, entities in spatial_grid.items():
print(f" Cell Grid {cell}: {entities}")
Because tuples are hashable, integer coordinate pairs `(1, 2)` serve as $O(1)$ spatial bucket keys, grouping nearby entities together efficiently.
09 / Deep Dive Case Study: PostgreSQL / Psycopg Driver Architecture
Psycopg (the canonical PostgreSQL adapter for Python) and Python's built-in `sqlite3` module return row records as tuples from cursor query executions. When a backend query runs (`cursor.execute('SELECT id, email, role FROM users')`), the C-extension driver parses PostgreSQL binary wire protocol packets directly into CPython `PyTupleObject` structures: 1. **Zero Over-Allocation Waste:** When loading a 500,000-row analytical dataset, using tuples saves ~24 bytes per row over lists, conserving dozens of megabytes of RAM. 2. **Garbage Collection Optimization:** Python's tuple free list caches recently deallocated row tuples, eliminating kernel memory allocation overhead across repetitive query cycles. 3. **Data Integrity:** Preventing application code from accidentally altering retrieved database fields in-memory before writing to caches or audit logs.
Database Cursor Tuple Record Stream Pattern
def fetch_user_rows_simulated() -> list[tuple]:
# Returns immutable database row tuples
return [
(1, "alice@pylabs.org", "admin"),
(2, "bob@pylabs.org", "user")
]
rows = fetch_user_rows_simulated()
# Unpack records cleanly
for uid, email, role in rows:
print(f"User [{uid}] Email: {email} | Role: {role}")
Architectural Takeaways
- Database drivers emit tuples to guarantee row data immutability and prevent cache pollution.
- Tuples eliminate over-allocation memory overhead when streaming massive recordsets.
- Tuple unpacking simplifies reading multi-column query projections.
10 / Guided Exercises
Step-by-step programming tasks to master waypoint unpacking, composite tuple dictionary caching, and starred outlier trimming.
GPS Waypoint Unpacker & Formatter
Unpack waypoint into lat, lon, and city. Construct formatted as f'{city} is located at ({lat}, {lon})' and print it.
waypoint = (37.7749, -122.4194, "San Francisco")
# 1. Unpack waypoint into lat, lon, and city
# 2. Format into a string: '{city} is located at ({lat}, {lon})'
formatted = ""
print(formatted)
- lat, lon, city = waypoint
- formatted = f"{city} is located at ({lat}, {lon})"
Composite Tuple Dictionary Key Cache
Populate grid_cache where each (x, y) tuple is a key and the value is x + y.
grid_cache = {}
points = [(0, 0), (1, 2), (3, 4)]
# For each (x, y) point in points, store the sum (x + y) in grid_cache using (x, y) as the key
print("Grid cache:", grid_cache)
print("Point (1, 2) value:", grid_cache.get((1, 2)))
- for x, y in points: grid_cache[(x, y)] = x + y
Starred Unpacking & Outlier Median Trimmer
Unpack latencies into lowest, *mid, highest. Calculate mid_avg = round(sum(mid) / len(mid), 2).
# A sorted tuple of latency measurements in milliseconds
latencies = (12, 24, 30, 42, 58, 95)
# 1. Use starred unpacking to extract lowest, mid (as a list), and highest
# 2. Compute mid_avg as sum(mid) / len(mid) rounded to 2 decimal places
print(f"Lowest: {lowest}ms | Highest: {highest}ms")
print(f"Trimmed Core Latencies: {mid}")
print(f"Core Average: {mid_avg:.2f}ms")
- lowest, *mid, highest = latencies
- mid_avg = round(sum(mid) / len(mid), 2)
11 / Hands-On Practice Tasks
Autonomous implementation challenges with unit assertions. Solve these in your local IDE or PyLabs playground.
Network Server Connection Registry
Maintain active client socket connections in a dictionary. Clients arrive as a list of (ip, port) tuples. Store each connection with a status of 'CONNECTED', verify lookups, and report total connections.
- Input client list: [('10.0.0.1', 5001), ('10.0.0.2', 5002), ('10.0.0.1', 5003)].
- Create registry dict mapping each (ip, port) tuple to 'CONNECTED'.
- Verify registry contains 3 distinct endpoints.
- Print the registry contents.
clients = [("10.0.0.1", 5001), ("10.0.0.2", 5002), ("10.0.0.1", 5003)]
registry = {}
# Register client endpoints
Immutable Transaction Record Reconstruct-on-Update
Demonstrate functional record updates with immutable tuples. Given a financial transaction tuple (tx_id, user, amount), simulate an update to the amount ($75.0) by generating a new tuple while verifying that the original tuple remains untouched.
- Original transaction: (1, 'TX_001', 50.0).
- Construct updated_tx with amount 75.0 preserving id and user.
- Assert original transaction amount remains 50.0.
- Print both transaction records.
original_tx = (1, "TX_001", 50.0) # Construct updated_tx
12 / Key Takeaways & Core Invariants
Tuples are ordered, immutable sequences defined by commas (parentheses are optional for assignment packing).
A single-item tuple strictly requires a trailing comma: (item,); without it, Python treats it as parenthesized expression.
Tuples store pointers inline in a compact C struct, consuming less memory than lists and avoiding dynamic over-allocation.
CPython caches small deallocated tuples on an internal free list, eliminating malloc syscalls for function returns and unpacks.
A tuple is hashable and valid as a dictionary key if and only if all of its constituent elements are also immutable and hashable.
Starred unpacking (*rest) gathers excess elements into a Python list, simplifying head/tail sequence processing.
Tuples support only two methods: .count() and .index(); all mutating methods (.append, .sort, .pop) are absent.
13 / Knowledge Check: Quiz Booster
Validate your understanding of singleton comma syntax, starred unpacking types, augmented assignment edge cases, and dictionary hashability.
x = (42)
a, *rest, b = (1, 2, 3, 4)
t = (1, [2, 3]) t[1] += [4]
t = (1, 2, 3, 4) print(type(t[1:3]))
14 / Recommended Searches, Cheat Sheet & FAQ
Curated Google Searches & Deep Dive References
Study how CPython caches up to 20 deallocated small tuples to eliminate malloc calls.
Read the official Python enhancement proposal introducing starred unpacking syntax.
Understand why tuple hashing recursively requires all child members to be hashable.
Analyze memory profiler results comparing inline pointers to over-allocated dynamic arrays.
Explore why t[1] += [4] mutates the list yet still raises a TypeError exception.
Compare runtime lookup speeds and memory footprints across structured tuple alternatives.
Standard Library Sequence Reference Cards
n = tup.count(value)Returns the number of occurrences of value in the tuple. O(n) linear scan.
idx = tup.index(value)Returns the smallest index where value appears. Raises ValueError if not found.
h = hash(tup)Computes integer hash. Succeeds if and only if all items inside tup are hashable.
Frequently Asked Questions
In Python grammar, parentheses are overloaded for mathematical expression grouping (e.g. (2 + 3) * 4). Without a trailing comma, Python cannot distinguish between an integer surrounded by parentheses (42) and a 1-element tuple containing 42. The trailing comma (42,) unambiguously signals a tuple to the parser.
Tuples & Sequence Unpacking practice
Make a tuple city = ('Paris', 'France'). Unpack it into name and country, then print name.
Expected output: Paris
Coding Practice is Locked
Sign in to access the interactive Python editor, write and run code checks against automated tests, use hints, and earn XP.
Practice quiz
Check yourself
15 questions with explanations after submission, so you can correct course as you go.
Tuples & Sequence Unpacking quiz
LockedYou must be signed in to take this quiz. Sign in to test your understanding, track your answers, and earn XP.
Lesson incomplete
Not completedCreate an ID or sign in to save lesson completion across devices.