Watch a matatu conductor work for one trip and you will see a programmer at full speed. He counts heads. He knows the fare. He takes a KSh 500 note from someone who owes KSh 70 and hands back the exact change without a pause. He also decides, by feel, when the vehicle is full enough to leave.
Listen to what he tracks: a number plate, a seat count, a fuel bill and a yes or no on whether the vehicle is full. That is text, a whole number, a decimal number and a true-or-false answer. Python cares about that difference, and once you see why, the basics stop looking like rules to memorise.
This article covers variables, four data types (string, integer, float and boolean), input and output, and the syntax that binds them together. Every example follows one fictional conductor working for the Mwananchi Express sacco. The fares, fuel prices and plate number are invented, so change them to match real life. Each code block shows the output you will see in your terminal.
Variables: names, not boxes
Most tutorials say a variable is a box that holds a value. The picture fails quickly, so use this one. A variable is a sticky note with a name on it. Python sticks the note onto a value. You can peel the note off and stick it on something else, and you can put two notes on one value.
plate = "KDD 482P"
seats = 14
fare_kes = 100
is_full = True
print(plate, seats, fare_kes, is_full)
$ python matatu.py
KDD 482P 14 100 True
The = sign does not mean "equals" the way it does in maths. It means "stick this name on that value." Python works out the right side first, then attaches the name on the left.
That order explains a line that would be nonsense in algebra. Say the sacco raises the fare by KSh 20:
fare_kes = fare_kes + 20
print(fare_kes)
120
Python takes the current value of fare_kes (100), adds 20, and moves the sticky note onto the result. Nothing was changed in place. The note moved.
Here is the two-notes idea at work:
x = 14
y = x # y sits on the same value as x
x = 13 # x moves to a new value; y stays put
print(x, y)
13 14
Changing x did not touch y, because y never pointed at x. It pointed at the value 14.
Naming rules
A name can hold letters, digits and underscores. It cannot start with a digit, and it cannot be a word Python already uses, such as if or True. Names are case sensitive, so Seats and seats are two different notes.
Seats = 14
print(seats)
Traceback (most recent call last):
File "matatu.py", line 2, in <module>
print(seats)
^^^^^
NameError: name 'seats' is not defined. Did you mean: 'Seats'?
Read the error from the bottom. Python names the problem (NameError), names the culprit (seats), and guesses what you meant. Most Python errors help you this much once you learn to read them.
The community habit is snake_case: lowercase words joined by underscores. Put the unit in the name (fare_kes, fuel_litres) and you prevent a whole class of mistakes later. Values that should never change get capital letters, like SEATS. Python will let you change them anyway. The capitals are a note to the next reader.
The four data types
Every value has a type, and the type decides what you can do with it. Ask Python with type():
print(type(plate))
print(type(seats))
print(type(38.5))
print(type(is_full))
<class 'str'>
<class 'int'>
<class 'float'>
<class 'bool'>
Strings (str)
A string is text inside quotes. Single or double quotes both work. Pick one style and keep it.
sacco = "Mwananchi Express"
print(len(sacco))
print(sacco.upper())
print(sacco + " Sacco")
print(sacco[0])
print(sacco[:9])
print("Karibu! " * 2)
17
MWANANCHI EXPRESS
Mwananchi Express Sacco
M
Mwananchi
Karibu! Karibu!
len() counts characters, spaces included. The + sign glues strings together. Counting starts at zero, so sacco[0] is the first letter, and sacco[:9] means "everything before position 9." Multiplying a string by a number repeats it.
Strings suit phone numbers well. A Safaricom number is text, not a number, because nobody adds two phone numbers together and the leading zero matters. Here is how to turn a local number into the international form that M-Pesa integrations expect:
local_number = "0712345678"
print("+254" + local_number[1:])
print(len(local_number))
print(local_number.startswith("07"))
+254712345678
10
True
The slice [1:] means "everything from position 1 onward," which drops the leading zero. The startswith() method asks a question about the text, and the answer comes back as a boolean. We will meet booleans shortly.
A string that looks like a number is still a string. "14" and 14 are different things, and Python refuses to mix them:
print("Seats: " + 14)
Traceback (most recent call last):
File "matatu.py", line 1, in <module>
print("Seats: " + 14)
~~~~~~~~~~^~~~
TypeError: can only concatenate str (not "int") to str
You have to convert the number first. We cover that in a moment.
Integers (int)
An integer is a whole number. Python has three division-related operators, and beginners mix them up:
riders = 47
print(riders // 14)
print(riders % 14)
print(2 ** 10)
print(7 / 2)
3
5
1024
3.5
The // operator divides and drops the remainder. A 14-seater carrying 47 riders in a day needs 3 full loads. The % operator keeps only the remainder: 5 riders are left for a fourth, partly empty trip. Together they answer "how many full loads, and what is left?", a question that turns up all the time. The ** operator raises a number to a power. The single / always returns a float, even when the answer is whole:
print(type(8 / 2), 8 / 2)
<class 'float'> 4.0
Floats (float)
A float is a number with a decimal point. Fuel is measured in litres and priced to the cent, so floats fit. Mixing an integer with a float gives a float:
print(3 * 1.5)
4.5
Floats have one quirk that startles everyone once:
print(0.1 + 0.2)
print(round(0.1 + 0.2, 2))
0.30000000000000004
0.3
Computers store numbers in binary. One tenth has no exact binary form, just as one third has no exact decimal form, so the machine stores a very close neighbour. The error is tiny, and it appears when you print raw results. Two habits protect you. Use round() or a format specifier when you display a float, and never test two floats for exact equality.
Booleans (bool)
A boolean is True or False, with a capital first letter. You will seldom type them yourself. Comparisons produce them:
boarded = 14
is_full = boarded >= seats
traffic_heavy = False
print(is_full)
print(is_full and not traffic_heavy)
True
True
The keywords and, or and not combine booleans. The expression is_full and not traffic_heavy asks: is the vehicle full, and is the road clear? Both hold, so the answer is True. Set traffic_heavy to True and the answer flips to False.
Converting between types
Python will not guess when you mix types, so you convert on purpose with int(), float(), str() and bool():
print(int("120") + 30)
print(float("38.5") * 2)
print(str(14) + " seats")
print(int(9.99))
150
77.0
14 seats
9
Notice that int(9.99) gives 9. Converting to an integer cuts off the decimals. It does not round.
Converting to a boolean has a trap:
print(bool(0), bool(1), bool(""), bool("no"))
print(bool("False"))
False True False True
True
bool() asks one question: is there anything here? Zero and the empty string count as nothing, so they become False. Everything else becomes True, including the text "False", which is a non-empty string. If you read the word "false" from a user, compare it with the text yourself. Do not pass it to bool().
Input and output
Python talks through print() and listens through input().
Output with print()
print() takes several values and puts a space between them. Two options change that: sep sets the separator, and end sets what closes the line (a newline by default).
print("Nairobi", "Thika", "Kenol", sep=" > ")
print("Counting heads...", end=" ")
print("done")
Nairobi > Thika > Kenol
Counting heads... done
For anything longer, use an f-string. Put an f before the opening quote and write expressions inside curly braces. After a colon you control the format:
fuel_cost = 5.5 * 190
print(f"{sacco} spent KSh {fuel_cost:.2f} on fuel")
print(f"KSh {1234567.891:,.2f}")
Mwananchi Express spent KSh 1045.00 on fuel
KSh 1,234,567.89
The .2f means "two digits after the decimal point," and the comma adds thousands separators. F-strings also fix the "Seats: " + 14 problem, because they convert values to text for you.
Input with input()
input() shows a prompt, waits for the user to type and press Enter, then hands you what they typed. In the transcripts below, the text after each prompt is what the user typed.
name = input("Conductor name: ")
print("Karibu,", name)
Conductor name: Maxwell
Karibu, Maxwell
Remember this rule: input() always returns a string. If the user types 3, you receive the text "3".
passengers = input("Passengers: ")
print(passengers + 1)
Passengers: 3
Traceback (most recent call last):
File "matatu.py", line 2, in <module>
print(passengers + 1)
~~~~~~~~~~~^~~
TypeError: can only concatenate str (not "int") to str
The fix is to convert the moment the value arrives: passengers = int(input("Passengers: ")). If the user types something that is not a number, int() raises a ValueError. Handling that needs try and except, which sit outside this article. For now, know that it can happen.
Syntax rules that hold it together
Python's syntax is small. Five rules cover most of what you will write this month.
Indentation is part of the language. Where other languages use braces, Python uses the four spaces at the start of a line to show what belongs inside an if. Leave them out and Python stops you:
seats = 14
if seats > 10:
print("big")
File "matatu.py", line 3
print("big")
^
IndentationError: expected an indented block after 'if' statement on line 2
A colon ends the line that opens a block (if, elif, else). Comments start with #, and Python ignores them, so use them to explain why you did something. A line break ends a statement, so you need no semicolons. And names are case sensitive, as you saw with Seats.
The if statement is the one piece of flow control we need. It runs a block only when a boolean is True. The second and third projects below use it.
Practical example 1: the conductor's change counter
A passenger owes KSh 70 and hands over a KSh 500 note. The conductor must return the right change, fast, and sometimes the passenger has paid too little. This program uses integers, division, a boolean and an if statement.
# change_counter.py
fare_kes = int(input("Fare (KSh): "))
paid_kes = int(input("Passenger paid (KSh): "))
enough = paid_kes >= fare_kes
if enough:
change = paid_kes - fare_kes
hundreds = change // 100
small_change = change % 100
print(f"Change due: KSh {change}")
print(f"Give back: {hundreds} x KSh 100, plus KSh {small_change} in smaller notes or coins")
else:
print(f"Short by KSh {fare_kes - paid_kes}. Ask for the rest.")
Two runs, one where the passenger overpays and one where they come up short:
$ python change_counter.py
Fare (KSh): 70
Passenger paid (KSh): 500
Change due: KSh 430
Give back: 4 x KSh 100, plus KSh 30 in smaller notes or coins
$ python change_counter.py
Fare (KSh): 70
Passenger paid (KSh): 50
Short by KSh 20. Ask for the rest.
Follow the arithmetic: 500 minus 70 is 430. Then 430 // 100 is 4, the number of hundred-shilling notes, and 430 % 100 is 30, what remains. The same // and % pair that counted full loads now counts notes.
Practical example 2: the trip profit calculator
At the end of a trip, the owner wants to know whether the trip paid for itself. Income is the passenger count times the fare. Costs are the fuel and a flat sacco fee. This program mixes integers and floats and ends on a boolean.
# trip_profit.py
FARE_KES = 100
SACCO_FEE_KES = 150
passengers = int(input("Passengers on this trip: "))
fuel_litres = float(input("Fuel used (litres): "))
price_per_litre = float(input("Fuel price (KSh per litre): "))
income = passengers * FARE_KES
fuel_cost = fuel_litres * price_per_litre
profit = income - fuel_cost - SACCO_FEE_KES
made_profit = profit > 0
print()
print(f"Income: KSh {income:,.2f}")
print(f"Fuel: KSh {fuel_cost:,.2f}")
print(f"Sacco fee: KSh {SACCO_FEE_KES:,.2f}")
print(f"Profit: KSh {profit:,.2f}")
print("Trip paid for itself?", made_profit)
A full vehicle on one run, and a half-empty one on another:
$ python trip_profit.py
Passengers on this trip: 14
Fuel used (litres): 5.5
Fuel price (KSh per litre): 190
Income: KSh 1,400.00
Fuel: KSh 1,045.00
Sacco fee: KSh 150.00
Profit: KSh 205.00
Trip paid for itself? True
$ python trip_profit.py
Passengers on this trip: 9
Fuel used (litres): 5.5
Fuel price (KSh per litre): 190
Income: KSh 900.00
Fuel: KSh 1,045.00
Sacco fee: KSh 150.00
Profit: KSh -295.00
Trip paid for itself? False
The numbers show why conductors hate leaving half empty. Five fewer passengers turn a KSh 205 profit into a KSh 295 loss. Look at the types: passengers is an integer, fuel_litres is a float, and multiplying the integer by the integer constant gives an integer, while any product with a float gives a float. The :,.2f format makes every amount line up with two decimals and a thousands separator.
Practical example 3: the departure call
The hardest decision on the route is when to go. Leave too early and you lose fares. Wait too long and passengers get off. This program takes the number of people on board and the minutes spent waiting, turns them into three booleans, and decides.
# departure.py
SEATS = 14
boarded = int(input("Passengers on board: "))
minutes_waiting = int(input("Minutes waiting: "))
is_full = boarded >= SEATS
waited_long = minutes_waiting >= 20
half_full = boarded >= SEATS / 2
if is_full:
print("Decision: full. Close the door and go.")
elif waited_long and half_full:
print("Decision: enough people and enough waiting. Leave now.")
elif waited_long:
print("Decision: long wait, few riders. Keep calling, but tell them the delay.")
else:
print("Decision: keep calling for passengers.")
Four situations, four decisions:
$ python departure.py
Passengers on board: 14
Minutes waiting: 5
Decision: full. Close the door and go.
$ python departure.py
Passengers on board: 9
Minutes waiting: 25
Decision: enough people and enough waiting. Leave now.
$ python departure.py
Passengers on board: 4
Minutes waiting: 30
Decision: long wait, few riders. Keep calling, but tell them the delay.
$ python departure.py
Passengers on board: 6
Minutes waiting: 8
Decision: keep calling for passengers.
Python checks the if and elif lines from top to bottom and runs only the first block whose condition is true. That is why the full vehicle leaves even after five minutes: is_full is checked first. Notice that SEATS / 2 is a float (7.0), and comparing an integer with a float works without any conversion.
Try it yourself
You now have every piece for tools like these. Pick one and bend it:
- Extend the change counter to hand back KSh 50 notes as well as KSh 100 notes, using another
//and%step. - Change the profit calculator to ask for the fare, so it works for a route that charges KSh 60 or KSh 150.
- Add a fourth rule to the departure call: if the road is blocked, tell the passengers and stop counting.
Then pick a small problem from your own week. Splitting a shopping bill, converting shillings to dollars and tracking your data bundle all follow the same steps: ask for input, convert it, calculate, and print the answer. Each program you finish teaches you more than another hour of reading.
Top comments (0)