Python · Chapter 1 · Concept lesson · 6 min read

Names, types and decisions

A variable is a name pointing at a value, every value has a type that decides what an operator does, and an if chain runs the first branch that is true and nothing else.

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

What Python remembers
namevaluetype
qty3int
price24.99float
subtotal74.97float
label'Total: 74.97'str

Step 1 of 4 qty = 3 creates the value 3, an int (a whole number), and ties the name qty to it. The name is a label, the value is the thing.

  1. qty = 3 creates the value 3, an int (a whole number), and ties the name qty to it. The name is a label, the value is the thing.
  2. price = 24.99 is a float, a number with decimals. Money in Python is usually a float, rounded when you show it.
  3. subtotal = qty * price uses the names, not the numbers. Python looks up both values, multiplies them (an int times a float gives a float) and ties subtotal to 74.97.
  4. label = 'Total: ' + str(subtotal) glues text to text. The number had to be turned into a str first, 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

Packing 23 units into boxes of 6
coderesultmeaning
23 / 63.833plain division
23 // 63full boxes
23 % 65units left over
math.ceil(23 / 6)4boxes to ship

Step 1 of 4 The warehouse packs 6 units to a box and has 23 units. / always gives a float, 3.833 boxes, which nobody can ship.

  1. The warehouse packs 6 units to a box and has 23 units. / always gives a float, 3.833 boxes, which nobody can ship.
  2. // is floor division: how many whole times 6 fits in 23. Three full boxes.
  3. % is the remainder: what is left after those three boxes, 23 - 18 = 5 units. // and % are two halves of the same division.
  4. 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

  1. if subtotal >= 75: fee = 0
  2. elif subtotal >= 40: fee = 4.99
  3. else: fee = 6.95

Step 1 of 3 A cart of $74.97. Python tests the first condition: is 74.97 >= 75? No, so the branch is skipped.

  1. A cart of $74.97. Python tests the first condition: is 74.97 >= 75? No, so the branch is skipped.
  2. Next the elif: is 74.97 >= 40? Yes. Its branch runs and the fee is 4.99.
  3. The chain is over. The else is 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.

A cart has subtotal = 75. The chain is if subtotal > 75: fee = 0, then elif subtotal >= 40: fee = 4.99, then else: fee = 6.95. What is fee?

Pick what may run on this site. You can change this any time from "Cookie settings" at the bottom of every page.