Pure functions
coreintermediateA pure function returns a value that depends only on its arguments, and changes nothing outside itself — no global variable, no file, no argument mutated in place. Call it twice with the same input and it gives the same answer, every time.
Think of it as
A vending machine, not a bank teller. Put in the same coins and the same button press, and the same snack drops out every time — it never remembers your last visit, and pressing the button never changes what is behind any other button.
What we're doing: Compare a pure tax calculation against an impure one that accumulates a running total in a global, and show how the impure version breaks "same input, same output."
- 2
- add_tax_pure reads only price and rate, its own arguments — nothing else exists in its body.
- 14
- Called twice with the same arguments, add_tax_pure returns the identical 108.0 both times.
- 9
- global total lets add_tax_impure both read and reassign a variable outside its own scope.
- 10
- This line is the side effect: every call quietly grows total, a piece of state the caller never asked for.
- 18
- total is 8.0 after one call — evidence that add_tax_impure changed something beyond its return value.
- 19
- The same call again returns the same 108.0, but total has now doubled to 16.0 — the hidden state kept moving.
108.0
108.0
108.0
8.0
108.0
16.0Why this works: add_tax_pure's entire behavior is determined by price and rate — there is nothing else in its body that could make one call differ from an identical one, so it is safe to call repeatedly, cache, or run in parallel without surprises. add_tax_impure returns the correct tax amount every time too, but it also silently grows total on every call — a second, hidden output no caller asked for and no return value reveals. That gap is exactly what 'pure' rules out: a pure function's contract is fully described by its parameters and its return value, an impure one's is not.
Assuming a function is pure because it "just computes something"
Wrong
Better
What you see: cart1 = add_item(cart, 'pen') looks like it produces a new cart, but the caller's original cart list has also silently grown by one item — checked with cart is cart1, which is True.
Why: cart.append(item) mutates the list the caller passed in, in place — the return value is not a new object, just the same list handed back. That makes add_item impure: calling it changes something the caller can observe beyond the return value. cart + [item] builds and returns a brand-new list instead, leaving the original untouched — the fix that actually makes the function pure.
- Pure
- Reads only its own arguments
- Returns a value, changes nothing else
- Same input always gives the same output
- Impure
- Reads or writes state outside itself
- A global here accumulates across calls
- Same input can give a different output
What makes a function impure
Together
Remember: A pure function only reads its arguments and only returns a value — no global, no file, no mutated argument. Same input always means same output.
See also: function composition · mutable vs immutable · immutability concepts · first class functions

