You've already used tuples a dozen times without a formal introduction: every multi-value return, every enumerate() pair, every (city, revenue, cost) record in the exercises. Time to meet them properly — and learn why 'you can't change it' is the selling point, not the drawback.
What you'll learn
What
A tuple is an ordered, IMMUTABLE sequence: (lat, lon), ('mia', 990, 88). Indexing and slicing work exactly like lists — but once created, a tuple's contents can never change.
Why
Immutability is a guarantee. A function that returns a tuple promises nobody downstream can quietly edit it; a coordinate pair can't lose its longitude; a record's shape is stable. Data code is full of fixed-shape facts, and tuples are their honest container.
Where it's used
Multiple return values, (row, col) coordinates, database rows, RGB colors, dictionary keys (coming next lesson!), and enumerate/zip pairs.
Where this runs in production
A pickup point is (latitude, longitude) — two values whose ORDER is the meaning and which must never be partially edited. Location pipelines pass millions of such pairs hourly.
Python's standard database drivers return each query row as a tuple — cursor.fetchall() hands you a list of tuples, the exact shape you've practiced unpacking.
Chart dimensions, axis ranges, and RGB colors are all passed as tuples — small fixed-shape values where mutation would only ever be a bug.
reading = ('sensor-7', 21.5, 'ok')
print(reading[0]) # sensor-7
print(reading[-1]) # ok
print(len(reading)) # 3
# reading[1] = 22.0 # TypeError: does not support item assignmentEverything you know about indexing transfers. The one new fact is the last line: assignment into a tuple raises TypeError. There's no .append(), no .remove(), no way in.
A list is a whiteboard: anyone with a marker can add, erase, reorder. A tuple is a laminated card: printed once, then sealed. You hand a whiteboard to a colleague nervously — what will it say when it comes back? You hand a laminated card to anyone, any number of times, without a second thought. That difference in TRUST is the entire point of immutability.
The deeper distinction is semantic. A tuple is a fixed-SHAPE record where each POSITION has its own meaning: (lat, lon), (name, score, minutes) — position 2 is always minutes, and length 3 is part of the meaning. A list is a variable-length collection of LIKE items: readings, names, prices — every element means the same kind of thing and the count varies. Ask 'do positions have distinct meanings?' and the choice makes itself.
# One-element tuple: the comma, not the parens single = (42,) not_a_tuple = (42) # just the int 42 in parentheses! print(type(single).__name__, type(not_a_tuple).__name__) empty = () print(len(empty))
(42) is arithmetic grouping; (42,) is a tuple. Forgetting the trailing comma is THE classic tuple typo — usually surfacing later as 'TypeError: object is not iterable'.
There's no .append() — so code that 'grows' a tuple actually rebuilds it from scratch each time (t = t + (x,)), copying everything, every iteration. If you're accumulating, you want a list; convert at the end with tuple(items) if immutability matters. tuple(list_) and list(tuple_) convert freely in both directions.
Same syntax family, different contracts. Click each side, then the test in the middle.
Select a type to see its full definition, operations, and data science usage.
An RGB color as a tuple — and proof it's sealed.
Indexing and slicing work exactly as on lists — a slice of a tuple is a new tuple.
Your task
A weather feed delivers (city, high, low) tuples. For each record print 'city: spread N°' where spread is high minus low, then find and print the city with the biggest spread using max with a tuple-aware key.
cairo: spread 13° oslo: spread 7° lima: spread 6° widest range: cairo
Write your solution in the editor on the right, then hit Run.
Which creates a one-element tuple?
point = (3, 4); point[0] = 9 — what happens?
Which data is the best fit for a tuple rather than a list?
What's the correct explanation?
stock = ('ACME', 41.25, 43.10) holds (ticker, open, close). Unpack it and print 'ACME: +4.5%' — the percent change from open to close, one decimal, with a leading + for gains. Expected output: ACME: +4.5%
first, *rest = (10, 20, 30, 40) — what are first and rest?
employees = [('mia', 'data', 95000), ('kai', 'infra', 88000), ('ada', 'data', 102000)]. Sort by (department ASC, salary DESC) using one tuple key, then print each as 'dept | name | $salary'. Expected: data | ada | $102000 data | mia | $95000 infra | kai | $88000
Tuple vs list — when do you use each, beyond 'one is immutable'?
The mechanical difference is mutability, but the design difference is meaning. A tuple is a fixed-shape RECORD: each position has its own semantic — (lat, lon), (name, dept, salary) — the length is part of the meaning, and elements are typically heterogeneous. A list is a variable-length COLLECTION of like items — readings, filenames — where elements are homogeneous and the length is incidental. Practical consequences: tuples can be dict keys and set members (immutability makes them hashable), returning tuples gives callers a tamper-proof result, and accumulating belongs in lists since tuples have no append. My quick test: 'would inserting an element in the middle even make sense?' If no — positions have fixed meanings — it's a tuple.
A tuple contains a list. Is it still immutable? Explain precisely.
The tuple itself remains immutable in the only sense Python defines: its slots can never be rebound — t[1] = something always raises TypeError, and the tuple will reference the same objects forever. But immutability is not recursive: if slot 1 holds a list, that list is still a fully mutable list, and t[1].append(x) succeeds because it mutates the list object, not the tuple. Consequences worth stating: such a tuple is no longer hashable (so it can't be a dict key — the hash would need the list's contents, which can change), and the 'safe to share' guarantee only covers the tuple's structure, not the nested data. For a truly frozen record, make the contents immutable too — strings, numbers, nested tuples.
What is tuple unpacking and where does Python use it beyond simple assignment?
Unpacking destructures a sequence into names in one step: name, score = record, with a loud ValueError on count mismatch. It's woven through the language: for k, v in pairs unpacks per iteration (enumerate and zip exist to be unpacked); the classic swap a, b = b, a packs the right side into a tuple and unpacks it into the left; star syntax collects a variable middle or tail — first, *rest = values — always into a list; multi-value returns are just a returned tuple met by an unpacking assignment; and function calls star-unpack sequences into arguments (f(*point)). In data code the daily version is unpacking rows in loop headers — for region, units, price in rows — which replaces opaque row[2] indexing with named, reviewable meaning.
Common Mistakes to Avoid
1) (42) is not a tuple — the comma makes it: (42,). 2) Trying .append() on a tuple — accumulate in a list, convert with tuple() at the end. 3) Assuming immutability is recursive — a list inside a tuple is still mutable. 4) Unpacking with the wrong count — ValueError; use _ for slots you skip and *rest for variable tails. 5) 'Growing' tuples with t += (x,) in a loop — quadratic copying; that's a list's job. 6) Using indexes (row[2]) where unpacking would name the meaning — readable code unpacks.
Ask the AI Tutor
Try these prompts in the AI Tutor panel: • 'Quiz me: tuple or list for each of these eight datasets?' • 'Show me the nested-list-in-tuple mutation gotcha step by step.' • 'Drill me on starred unpacking with five examples.' • 'Explain how tuple comparison makes multi-key sorting work.' • 'Interview mode: ask me why tuples can be dict keys and grade my answer.'
Glossary
Tuple — an ordered, immutable sequence. Immutable — unchangeable after creation. Record — a fixed-shape value whose positions carry distinct meanings. Unpacking — destructuring a sequence into names (a, b = pair). Starred unpacking — first, *rest = seq; the star collects into a list. Packing — the comma building a tuple (return a, b). Hashable — usable as a dict key/set member; tuples of immutables qualify. Heterogeneous — elements of different types/meanings (typical of tuples). tuple()/list() — conversions between the two. _ — conventional name for an unpacked-but-unused slot.
Recommended Resources
• Docs: 'Tuples and Sequences' in the official Python tutorial. • Read: the collections.namedtuple docs for a preview of tuples with named fields — the bridge between tuples and classes. • Practice: take any list-of-tuples from earlier lessons and rewrite every row[i] access as unpacking; feel the readability shift. • Next in DSM: tuples' immutability earns them a superpower — being KEYS. Dictionaries, the most important data structure in Python, are next.
Recap
✓ Tuples are ordered, immutable sequences — the comma creates them, (x,) for one element. ✓ Indexing, slicing, len, and iteration work exactly as with lists. ✓ Tuple = fixed-shape record (positions mean things); list = variable collection of like items. ✓ Immutability means safe sharing, honest function returns — and dict-key eligibility. ✓ Immutability isn't recursive: mutable contents stay mutable. ✓ Unpack everywhere: loop headers, swaps, returns, *rest tails. Next up: Dictionaries. Tuples gave you positional records — dictionaries give you NAMED lookup: the key→value structure underlying JSON, API responses, and half of pandas.
Run your code to see the output here.