Teodora Vlad keeps the membership list for a climbing gym in Cluj, and in March she was asked which members were sharing a locker. She wrote nine lines that compared every member against every other member. The gym has 4,200 members, the script made 17,640,000 comparisons, and it took a little over four minutes to answer a question about lockers.
The repair was one line long and none of it was Python. It was the observation that a dict lookup costs the same at ten items and at ten million, and that a scan through a list does not. That sentence was true before Python existed and it will be true after it, which is what puts it here rather than in the syntax and the ecosystem, one language at a time.
The eight clusters are one question asked at different sizes
Every idea filed under this topic answers some version of what a thing costs once there is more of it. A data structure is a bet about which operation you will do most. An algorithm is a named way of not paying a cost somebody already found a way around. A design pattern is the same trade made in the shape of a program rather than a loop. Even the habits in the habits that decide whether anyone can change it later are a cost claim: they are about the price of the second reader, who is usually you in eight months.
This is why the eight sit together, and why this topic is the one part of programming, from the first variable to a deployed system that keeps its value when the language changes. None of the eight is the first one. Teodora did not need to know the word for what she had written. She needed to know that the shape she reached for had a price, and that a different shape had a different one.
Three costs, before any cluster
The first is the one that fixed the locker script. A list has to be walked until the thing is found, so the work grows with the length of the list. A dict is addressed rather than searched: the key is turned into a position, and the position is looked at once. In Teodora's case the whole comparison collapsed into a single pass that filed each member under their locker number and noticed collisions on the way past.
seen = {}for m in members:if m.locker in seen: print(m.name, seen[m.locker])seen[m.locker] = m.name
Four thousand two hundred lookups instead of 17,640,000 comparisons, and it returns before the terminal has finished scrolling. Python's dict has kept insertion order since version 3.7 in 2018, which is worth knowing because a lot of older advice tells you to reach for a separate structure to get it back.
The second cost is what Teodora's first attempt actually was. Two nested loops over one list is n squared, and the number gets away from you faster than intuition allows: a thousand members is a million comparisons, and doubling the membership quadruples the work rather than doubling it. The useful habit is not computing the exact figure. It is noticing the second for indented under the first one and asking what the list is going to be a year from now.
The third has nothing to do with speed and it is the one that produces the strangest bugs. When Teodora made a working copy of the roster to mark off checked lockers, she wrote checked = roster, and then found that removing a name from checked removed it from roster as well. The same list passed to two names is one list with two names. A name is a label attached to an object, not a box holding a copy of one, and checked = list(roster) is the line that makes the second list actually exist. This is true of every language that passes references, which is nearly all of them, and it is spelled differently in each one.
When it stops, read the bottom of the stack
A program that raises an error prints the chain of calls that were open at the moment it stopped. A stack frame is a paused function, waiting for the one it called to come back, and the list of them is the route the program took to get where it died. The frame at the bottom is where the error was raised, and the useful one is almost always the lowest frame that names a file Teodora wrote rather than one inside a library she installed.
Reading that chain is the difference between fixing a fault and guessing at it, and it is the entry point to finding it, rather than guessing at it. The clusters on either side of it, what happens between your file and the processor and choosing the shape that makes your problem easy, are the two halves of understanding why the chain looks the way it does.
The order
Each step is what makes the next one mean anything
The stair is climbed backwards by almost everybody, because a language tutorial starts at the second step and never mentions the first.
- 01Be surprised by a number onceWatch something take four minutes that should take a second. Nothing above this lands without it.
- 02Learn what the shapes costA list is walked, a dict is addressed. The names can wait; the prices cannot.
- 03Meet a problem twiceA pattern read before the second time is a solution with no fault attached to it.
- 04Write for the second readerThe habits only make sense once you have opened your own file after eight months.
It says nothing about how long a step takes. The first one can happen in an afternoon and routinely does not happen for two years, which is the whole argument for going looking for it.
The order, and what taking it backwards costs
Almost nobody arrives here in order, because the language tutorial they started with went straight to the constructs and never mentioned cost at all. Taken backwards, the specific failure is learning the names before the prices: somebody who can recite what a hash map is and has never watched a nested loop take four minutes will reach for the wrong one under pressure and will not know why the program is slow. The names are cheap to acquire later. The instinct is not, and it is built by being surprised by a number at least once.
The other order that breaks is named answers to problems that keep recurring read before the problems have been met. A pattern learned early is a solution looking for a fault, and it produces programs with six files where one would do.
What to open, in 2026
For the cost questions, timeit is in the Python standard library and settles an argument about two versions of a loop in about ten seconds. It replaced the habit of putting a clock around the whole script, which measures the interpreter starting up as well as the thing being asked about.
For the stopping questions, pdb ships with Python and takes over from adding a print call and running the file again. Running python3 -m pdb -c continue script.py drops you at the frame where it failed with every value still in memory, which is the part a print statement cannot give back. The editor debuggers in VS Code sit on the same machinery with a mouse attached, and VS Code has been the default free editor since it took the ground from Sublime Text and from Atom, which was archived in December 2022.
A coding assistant in the editor will produce the dict version of the locker script correctly and immediately, because it is a common shape. It is also the reason to learn why the dict version is faster rather than only that it is: the assistant is reliable on the shapes that appear often and confident on the ones that do not, and telling those two cases apart is the skill being described on this page.
What none of this settles is which shape to reach for when the problem is not a lookup, which is the question the named solutions worth recognising rather than reinventing exists to answer.