Collections - Lists, Tuples, Dicts & Sets
A single value only gets you so far. Real programs juggle groups of things: a list of users, a price per product, a set of tags. Python's four built-in collection types each have a job - reach for the wrong one and your code fights you. Let's meet all four, then the trap that catches everyone.
List - an ordered, changeable sequence
What it actually is. A list is an ordered row of values you can add to, remove from, or
rearrange - your default "bunch of things in order." Write one with square brackets [].
=
What just happened: A list of three strings. len() reports how many items it holds:
['apple', 'banana', 'cherry']
3
Indexing - reach in by position. Python counts from 0; negative numbers count from the end:
=
What just happened: fruits[0] is the first item (position zero, not one - this catches everyone
early); fruits[-1] is the last, counting backward:
apple
cherry
⚠️ Counting starts at 0. A 3-item list has positions 0, 1, 2. fruits[3] runs off the end and
raises IndexError: list index out of range - the last valid position is always len - 1.
Changing it - a list is mutable (changeable), so you can add and modify in place:
=
=
What just happened: .append() added an item to the end; fruits[0] = "apricot" replaced the first:
['apricot', 'banana', 'cherry']
📝 Terminology. Mutable means "can be changed after it's created." Immutable means "fixed once created." This distinction governs how every collection behaves - keep it in mind.
Tuple - an ordered, fixed sequence
What it actually is. A tuple is like a list, but immutable - once made, you can't add,
remove, or change items. Write one with parentheses (), for a group of values that belongs together
and shouldn't change: coordinates, a row from a database, an RGB color.
=
What just happened: A tuple of two numbers, read by index, exactly like a list:
4
5
The difference shows up the moment you try to change one:
=
= 9
What just happened: Python refuses, because tuples are immutable:
Traceback (most recent call last):
File "point.py", line 2, in <module>
point[0] = 9
~~~~~^^^
TypeError: 'tuple' object does not support item assignment
That "can't change me" guarantee is the point - it tells anyone reading the code (and Python itself) these values are fixed.
💡 Key point. List vs tuple is about intent. List: expected to grow, shrink, or reorder. Tuple: a fixed group of related values that travels together and won't change.
Dict - lookups by key
What it actually is. A dictionary (dict) stores key → value pairs, looked up by a
meaningful key instead of position - the tool for "given X, what's its Y?": given a username, their
age; given a product, its price. Write one with curly braces and key: value pairs.
=
What just happened: ages["ada"] looked up the value under key "ada"; in asks whether a key
exists, giving a boolean:
36
True
Adding and updating use the same square-bracket syntax:
=
= 85
= 37
What just happened: Assigning to a new key added a pair; assigning to an existing one updated it:
{'ada': 37, 'grace': 85}
⚠️ KeyError - asking for a key that isn't there. Looking up a missing key with [] doesn't return
None - it crashes:
Traceback (most recent call last):
File "ages.py", line 2, in <module>
print(ages["bob"])
~~~~^^^^^^^
KeyError: 'bob'
When unsure a key exists, use .get(), which returns None (or a chosen default) instead of crashing:
=
What just happened: .get("bob") returned None for the missing key; .get("bob", 0) returned your
supplied default 0:
None
0
Set - a bag of unique items
What it actually is. A set holds unique items with no duplicates and no particular order -
reach for it for "what distinct things are here?" or "is this present?": membership and uniqueness, not
position. Write one with curly braces (just values, no key: value).
=
What just happened: Duplicates collapsed automatically - a set keeps only one of each. in checks
membership:
{1, 2, 3}
True
A favorite real use: strip duplicates from a list by passing it through a set.
=
=
What just happened: set(tags) built a set from the list, discarding repeats. Order isn't guaranteed,
so yours may print differently:
{'python', 'web', 'api'}
Slicing - grab a range of a sequence
For lists, tuples, and strings, pull out a slice - a sub-range - with [start:stop]. start is
included; stop is not.
=
What just happened: nums[1:3] took positions 1 and 2 - up to but not including 3. Omitting start
means "from the beginning"; omitting stop means "to the end":
[20, 30]
[10, 20]
[30, 40, 50]
The same works on strings, since a string is also a sequence:
=
What just happened: Took characters at positions 0, 1, 2 - again, stopping before 3:
Pyt
📝 Terminology. "Stop is exclusive" means the stop index is the first one left out - nums[1:3]
gives two items, not three. Feels odd at first, becomes second nature.
The trap: aliasing - two names, one list
This produces the most baffling beginner bugs, so meet it on purpose.
What's really going on. Write b = a where a is a list, and you do not get a copy - both
names point at the exact same list in memory. Change it through one name and the other shows the
change too, because there's only one list.
=
=
What just happened: b = a made b a second name for the same list. b.append(4) changed that
shared list, so a shows the 4 too:
[1, 2, 3, 4]
[1, 2, 3, 4]
For a separate, independent copy, ask for one explicitly with .copy() (or list(a)):
=
=
What just happened: .copy() made a brand-new list with the same contents - a and b are now
independent, so appending to b leaves a untouched:
[1, 2, 3]
[1, 2, 3, 4]
⚠️ This only bites mutable collections. Lists, dicts, and sets are mutable, so aliasing matters. Numbers, strings, and tuples are immutable - you can't change them in place, so sharing one is harmless. The rule: assignment never copies; it makes another name for the same object.
Recap
- List
[]- ordered and changeable; your default sequence. Index from0,-1is the last. - Tuple
()- ordered but fixed (immutable); for groups of values that shouldn't change. - Dict
{key: value}- look up values by key; use.get()to avoidKeyErroron missing keys. - Set
{a, b, c}- unique items, no order; great for deduping and membership tests. - Slicing
[start:stop]grabs a sub-range;stopis excluded. - Aliasing:
b = amakes two names for one list, not a copy. Use.copy()for an independent one - assignment never copies.
Next: making programs decide and repeat with if/else, loops, and functions - plus the famous
mutable-default-argument trap.
← Phase 2: Syntax, Values & Types · Guide overview · Phase 4: Control Flow & Functions →
Before the quiz: without looking back, say (or jot down) the core idea of this phase in your own words.
Check your understanding 3 questions
1. Which collection is ordered and changeable, written with square brackets?
2. When should you use a dict's `.get(key)` instead of `[key]`?
3. What does `b = a` do when `a` is a list?