The Quiet Terminal

← Beginner

Lists, tuples, sets, and dicts: which one when?

2026-04-10 · 3 min read

The first time I saw all four Python containers listed on one slide — list, tuple, set, dict — I assumed three of them were for other people. Six months of small scripts later the pattern was obvious: each one answers a different question, and picking the right one at the start makes the code around it fall into place.

The whole decision on one card

          ordered?    changeable?   duplicates?
list      yes         yes           yes
tuple     yes         no            yes
set       no          yes           no
dict      yes         yes           keys: no

(Dicts have kept their insertion order in every Python since 3.7 — old enough to rely on.) So the real questions are: do I care about order, will the contents change, can things appear twice, and do I need to reach items by name rather than by position?

list: the everyday one

todos = ["buy milk", "write post", "buy milk"]

todos.append("ship it")     # three items become four
todos[0]                    # 'buy milk' — counting starts at 0

Ordered, changeable, duplicates welcome. This is the container for anything you'd write on a whiteboard: a queue of jobs, the rows of a CSV, the folders to back up. When nothing fancier is asking for the job, a list is rarely wrong.

tuple: a list that froze

point = (48.8584, 2.2945)    # the Eiffel Tower, as floats

point.append(0.0)
# AttributeError: 'tuple' object has no attribute 'append'

Same square-bracket indexing, but no changing. That sounds like a limitation and is really a promise: tuples are for fixed shapes — coordinates, RGB colors, the two values a function hands back with return x, y. When changing a value would be a bug, a tuple makes the bug impossible instead of likely.

set: duplicates evaporate

tags = {"python", "blog", "python"}

len(tags)           # 2 — the second 'python' is gone
"python" in tags    # True — and this check stays fast

No order, no duplicates, membership checks that stay quick even on big collections. My most common set move: set(rows) to strip the duplicates out of something I just read from a file. That's it, and it's worth the whole container.

dict: reach it by name

user = {"name": "Ada", "lang": "en"}

user["name"]        # 'Ada'
user.get("age")     # None — key missing, but no crash

The container for anything with a natural label: settings, one record per id, counts per word. Where a list answers "what's third?", a dict answers "what's the email for user 4171?" — and it does it without scanning the whole collection.

Real data rarely stays one flat container. A CSV import lands as a list of dicts, one per row; a word count is a dict whose values are numbers; my backups script walks a dict of folders, each holding a list of files. The card above still applies — you just read it from the inside out: what is each item, and then what is the collection of them? That question has picked the right container for me every time.

Which ones do I actually reach for? list and dict, easily ten to one over the rest. Tuples show up whether I invite them or not — every function that returns two things hands me one — and sets earn their keep on deduplication days. If you're new and four containers feel like a lot: learn list and dict properly and you're equipped for most scripts. The other two will introduce themselves.