The idea
When you write qty = 3, Python does not put 3 inside a box called qty. It creates the value 3 somewhere in memory and ties a name to it. Later lines that mention qty look the name up and find the value. Assigning again moves the name; the old value just loses its label.
Figure 1
Four lines of code, four names, four types
| name | value | type |
|---|---|---|
| qty | 3 | int |
| price | 24.99 | float |
| subtotal | 74.97 | float |
| label | 'Total: 74.97' | str |
qty = 3creates the value 3, anint(a whole number), and ties the nameqtyto it. The name is a label, the value is the thing.price = 24.99is afloat, a number with decimals. Money in Python is usually a float, rounded when you show it.subtotal = qty * priceuses the names, not the numbers. Python looks up both values, multiplies them (aninttimes afloatgives afloat) and tiessubtotalto 74.97.label = 'Total: ' + str(subtotal)glues text to text. The number had to be turned into astrfirst, because+between a string and a number is an error. The type decided what+could do.
Every value carries a type: int, float, str (text), bool (True or False). The type is what decides what an operator is allowed to do. * multiplies two numbers, repeats a string, and refuses a string times a decimal. The names never matter to Python; the types always do.
Why it exists
A spreadsheet cell shows a value and you click it to see how it got there. A script has no cells, so it needs something that plays the same role: a way to say "this number, I will need it again" without typing 24.99 in five places. Names are that. Change price once and every line that uses it follows.
Types exist because a computer stores the number 3 and the text "3" very differently. Without types, "3" + 4 would have to guess whether you meant 7 or "34". Python refuses and tells you, which is annoying for ten seconds and saves you from a silent wrong total.
How it works under the hood
Python evaluates the right side, then names it
For subtotal = qty * price, Python works out the right-hand side first, completely, then ties the left-hand name to the result. That is why total = total + 10 is legal: the old total is read, 10 is added, and only then does the name move to the new value.
Numbers have a few jobs
Division in Python has three forms, and each answers a different question about the same numbers. / gives the exact answer as a float. // gives how many whole times the divisor fits. % gives what is left over.
Figure 2
23 units, boxes of 6: // and % split the work
| code | result | meaning |
|---|---|---|
| 23 / 6 | 3.833 | plain division |
| 23 // 6 | 3 | full boxes |
| 23 % 6 | 5 | units left over |
| math.ceil(23 / 6) | 4 | boxes to ship |
- The warehouse packs 6 units to a box and has 23 units.
/always gives a float, 3.833 boxes, which nobody can ship. //is floor division: how many whole times 6 fits in 23. Three full boxes.%is the remainder: what is left after those three boxes, 23 - 18 = 5 units.//and%are two halves of the same division.- Five units still need a box, so the boxes to ship is the quotient rounded up:
math.ceil(23 / 6)gives 4.
Whenever you convert a quantity into containers, pages, weeks or boxes, you want // and %. Whenever something must be rounded up because a partial box is still a box, you want math.ceil. A float also carries tiny binary errors: 0.1 + 0.2 is 0.30000000000000004, which is why money is rounded with round(x, 2) before it is shown or compared.
Text is built, not added
An f-string such as f"Total: {subtotal:.2f}" drops a value into text and formats it in the same step: .2f means "two digits after the decimal point". It spares you the str() and + of the diagram above, and it is how every label and message in the later chapters is made.
if, elif, else
Conditions are expressions that give a bool. subtotal >= 75 is True or False, and and, or, not combine those. An if chain tests its conditions in order and runs the first branch whose condition is true. Then it stops.
Figure 3
An if / elif / else chain, top to bottom
-
if subtotal >= 75:fee = 0 -
elif subtotal >= 40:fee = 4.99 -
else:fee = 6.95
- A cart of $74.97. Python tests the first condition: is 74.97 >= 75? No, so the branch is skipped.
- Next the
elif: is 74.97 >= 40? Yes. Its branch runs and the fee is 4.99. - The chain is over. The
elseis never looked at, because only the first branch that is true runs. One cent more and the first test would have caught it.
That order is the whole logic. A rule like "free over $75, $4.99 from $40, otherwise $6.95" only works if the biggest threshold is tested first. Reverse the two tests and every cart over $40 pays $4.99, including the $200 ones. Boundaries are the other place chains go wrong: > and >= differ in exactly one case, a cart of exactly $75.
Reading the type when something looks wrong
Most beginner bugs in this chapter are a type or a boundary in disguise. A shipping fee that is charged at exactly $75 is a > where a >= belongs. A delivery time that shows 2.5 hours when you wanted "2 h 30 min" is a / where // and % belong. A label that crashes with TypeError is a number glued to text without str() or an f-string. When a result looks wrong, do not re-read the whole script. Print the inputs with their types, print(qty, type(qty)), and check the one expression that produced the bad value. Ten seconds of printing beats ten minutes of staring.
Why the order of lines matters
A script runs from the top, one line at a time, and a name exists only after the line that creates it. Using subtotal before it is assigned is an error, and changing price after subtotal was computed does not change subtotal. The value was calculated at that moment and stays as it was, unlike a spreadsheet formula that follows its inputs. If you want the new number, run the calculation again.
Common misconceptions
"A variable is a box that holds the value"
It is closer to a sticky note on a value. Two names can point at the same value, and giving a name a new value never changes the old one. This matters from chapter 2 on: when a list has two names and you change it through one, both names see the change, because there is only one list.
"The number 5 and the text '5' are the same thing"
They look alike on screen and behave nothing alike. 5 + 5 is 10, while '5' + '5' is '55', and '5' + 5 is an error. Data from files and web forms often arrives as text, so before doing maths on it, check type(value) and convert with int() or float().
"If several conditions are true, they all run"
Only the first true branch of an if / elif / else chain runs; the rest are skipped without being tested. If you want several rules to apply together, write separate if statements, not elif. Mixing the two up is the usual cause of "my discount was applied twice" and "my discount never applied".
Check yourself
One question. Answering it ticks this lesson off on the map; it never locks a level.
Not quite. The first test is 75 > 75, which is false: strictly greater means the boundary itself does not count.Try another answer.
Right. The first condition fails on the boundary, the elif is true, and its branch is the only one that runs.
Not quite. else runs only when every test above it was false, and 75 >= 40 is true.Try another answer.
Not quite. Only the first branch that is true runs. Later conditions are not evaluated once one has matched.Try another answer.
Lesson done: it is ticked on the map.