DEV Community

Cover image for Python Loops at the Nairobi Animal Orphanage
David Mwandairo
David Mwandairo

Posted on

Python Loops at the Nairobi Animal Orphanage

Nairobi is one of the few capitals with a national park inside its limits. Lions hunt on the plains while the city skyline stands in view. Near the Main Gate on Lang'ata Road, the Kenya Wildlife Service runs the Nairobi Animal Orphanage, which takes in orphaned, injured and abandoned animals, treats them, and gives them a chance to return to the wild.

Now picture a day on the job. The park gates open at 6:00 a.m. Before they do, Wanjiru, a ranger at the orphanage, has a clipboard and a problem: dozens of small checks, and every one is the same. Look, tick, move on. A person does this with a pencil and a sore back. A programmer writes a loop.

A loop tells Python to run one block of code more than once. Python has two kinds, for and while. It also has two controls that change a loop mid-flight, break and continue, and a built-in helper, enumerate(), that does your counting for you.

One note before we start: Wanjiru, the enclosure codes and every number below are invented for teaching. They are not real KWS procedures. Each example is a complete script, so save it, run it, and compare your terminal with the output shown.

Note: "Python" in this article has been used to denote the programming language, not the snake.

The for loop: you have the items, so visit each one

A for loop takes a source of items and walks through them in order. On each pass, Python puts the next item into a variable you name, runs the indented block beneath, and moves on. When the items run out, the loop ends and the program carries on below it.

for item in source:
    # this indented block runs once per item
Enter fullscreen mode Exit fullscreen mode

Two sources cover most of what you will do early on: range(), which hands out numbers, and a string, which hands out its characters one at a time.

Counting with range()

The cheetah trough fills in five pump strokes, and each stroke adds 50 litres. Wanjiru wants a running total.

total_litres = 0

for pump in range(1, 6):
    total_litres += 50
    print(f"Pump {pump}: cheetah trough holds {total_litres} litres")

print("Cheetah trough is full.")
Enter fullscreen mode Exit fullscreen mode

Run it:

$ python trough.py
Pump 1: cheetah trough holds 50 litres
Pump 2: cheetah trough holds 100 litres
Pump 3: cheetah trough holds 150 litres
Pump 4: cheetah trough holds 200 litres
Pump 5: cheetah trough holds 250 litres
Cheetah trough is full.
Enter fullscreen mode Exit fullscreen mode

Two things to notice. The f before the quotes lets you drop a variable into text by wrapping it in braces. And range(1, 6) produces 1, 2, 3, 4, 5. It starts at the first number and stops before the second. That "stops before" rule trips up nearly everyone once. If you want to count to 5, the stop value is 6.

Give range() a third number and it counts in steps. The orphanage hosts ranger talks for school groups, one every 20 minutes during the first hour:

for minute in range(0, 60, 20):
    print(f"Ranger talk for school groups: {minute} minutes after opening")
Enter fullscreen mode Exit fullscreen mode
$ python talks.py
Ranger talk for school groups: 0 minutes after opening
Ranger talk for school groups: 20 minutes after opening
Ranger talk for school groups: 40 minutes after opening
Enter fullscreen mode Exit fullscreen mode

range(0, 60, 20) starts at 0, adds 20 each time, and stops before it reaches 60. If you give range() a single number, as in range(5), it starts at 0 and stops before that number.

Walking through a string

Six pens, labeled A to F, each hold a water bowl that needs a morning check. A string is a row of characters, so a for loop can visit each label directly.

for pen in "ABCDEF":
    print(f"Pen {pen}: water bowl checked")
Enter fullscreen mode Exit fullscreen mode
$ python pens.py
Pen A: water bowl checked
Pen B: water bowl checked
Pen C: water bowl checked
Pen D: water bowl checked
Pen E: water bowl checked
Pen F: water bowl checked
Enter fullscreen mode Exit fullscreen mode

The loop variable, pen, holds one letter per pass. You choose the name, so pick one that says what the item is. for x in "ABCDEF" works, but for pen in "ABCDEF" explains itself six months later.

The while loop: you have a condition, so repeat until it changes

A for loop knows how many passes it will make before it starts. Sometimes you don't. A rescued chick needs a warm brooder, and a heat lamp raises the temperature a few degrees at a time. Nobody can say in advance how many cycles it takes. You only know the goal.

A while loop fits that case. It checks a condition before every pass and runs the block only if the condition is true.

temperature = 22
target = 30

while temperature < target:
    temperature += 3
    print(f"Heat lamp cycle: brooder is at {temperature}°C")

print("Brooder ready for the rescued chick.")
Enter fullscreen mode Exit fullscreen mode
$ python brooder.py
Heat lamp cycle: brooder is at 25°C
Heat lamp cycle: brooder is at 28°C
Heat lamp cycle: brooder is at 31°C
Brooder ready for the rescued chick.
Enter fullscreen mode Exit fullscreen mode

Follow the numbers. The brooder starts at 22°C. The check 22 < 30 is true, so the lamp adds 3 and the temperature reads 25. Then 28, which still passes the check. Then 31. Now 31 < 30 is false, so the loop stops. The condition is tested before each pass, never during one, so the final value can overshoot the target. If overshooting matters, test for it inside the loop.

The trap: a loop that never ends

Every while loop needs something inside the block that moves the condition toward false. Take that line out and the loop runs forever. Here is the brooder script with the temperature += 3 line deleted:

temperature = 22
target = 30

while temperature < target:
    print(f"Brooder is at {temperature}°C")
Enter fullscreen mode Exit fullscreen mode
$ python stuck.py
Brooder is at 22°C
Brooder is at 22°C
Brooder is at 22°C
...
Enter fullscreen mode Exit fullscreen mode

The temperature stays at 22, so 22 < 30 stays true, and the terminal fills with that line until you press Ctrl+C. Whenever you write a while loop, ask yourself one question: what changes on each pass to make this stop?

Which loop do I pick?

Use for when you have a set of things to visit or a number of passes to make. Use while when you have a goal and no idea how many steps it takes. If you can do it with for, prefer for. It has no counter to forget and no way to run forever.

break and continue: change the loop from the inside

Sometimes the plan changes halfway through. Two keywords give you control.

break ends the whole loop

The side of the park that faces the city is fenced, which keeps wildlife off the roads and out of the neighborhoods. Rangers walk the fence checkpoint by checkpoint. The moment they find a breach, they call the response team. Checking the rest of the fence can wait.

In the status string below, I means intact and B means breached.

checkpoint = 0

for fence in "IIIBII":
    checkpoint += 1
    if fence == "B":
        print(f"Checkpoint {checkpoint}: fence BREACHED. Calling the response team.")
        break
    print(f"Checkpoint {checkpoint}: fence intact.")

print("Patrol ended.")
Enter fullscreen mode Exit fullscreen mode
$ python fence.py
Checkpoint 1: fence intact.
Checkpoint 2: fence intact.
Checkpoint 3: fence intact.
Checkpoint 4: fence BREACHED. Calling the response team.
Patrol ended.
Enter fullscreen mode Exit fullscreen mode

At checkpoint 4, break fires. Python leaves the loop at once, so checkpoints 5 and 6 are never checked. The last print sits outside the loop, so it runs anyway.

continue ends only the current pass

The morning feeding round is different. Hungry animals get a meal. A sleeping animal gets left alone, but the ranger still walks on to the next enclosure. Here H means hungry and S means sleeping.

enclosure = 0
meals_served = 0

for mood in "HHSHSH":
    enclosure += 1
    if mood == "S":
        print(f"Enclosure {enclosure}: animal is asleep, skipping.")
        continue
    meals_served += 1
    print(f"Enclosure {enclosure}: fed.")

print(f"Meals served: {meals_served}")
Enter fullscreen mode Exit fullscreen mode
$ python feeding.py
Enclosure 1: fed.
Enclosure 2: fed.
Enclosure 3: animal is asleep, skipping.
Enclosure 4: fed.
Enclosure 5: animal is asleep, skipping.
Enclosure 6: fed.
Meals served: 4
Enter fullscreen mode Exit fullscreen mode

When the mood is S, continue jumps back to the top of the loop for the next item. It skips the two lines below it, so the meal count stays put for sleepers. Enclosures 3 and 5 are skipped, and four meals go out.

Compare the two: break leaves the loop for good, while continue abandons one pass and keeps looping.

Using both in a while True loop

A while True loop has a condition that is always true, so only a break can end it. That sounds dangerous, but it is a common pattern when the exit rule is easier to state in the middle of the block than at the top.

Park tickets are cashless and bought in advance, so the Main Gate runs on a scanner. This script follows the scanner through the afternoon. It goes offline at 15:00 for a shift change, and the gate closes at 18:00:

hour = 12

while True:
    hour += 1
    if hour == 15:
        print("15:00 Shift change. Scanner offline.")
        continue
    if hour == 18:
        print("18:00 Last entry has passed. Gate closed.")
        break
    print(f"{hour}:00 Main Gate scanner reading tickets")
Enter fullscreen mode Exit fullscreen mode
$ python booth.py
13:00 Main Gate scanner reading tickets
14:00 Main Gate scanner reading tickets
15:00 Shift change. Scanner offline.
16:00 Main Gate scanner reading tickets
17:00 Main Gate scanner reading tickets
18:00 Last entry has passed. Gate closed.
Enter fullscreen mode Exit fullscreen mode

Look at where hour += 1 sits: first line of the loop. That placement is deliberate. At 15:00, continue skips the rest of the block and goes back to the top. Had the hour += 1 line sat below the continue, the hour would stay at 15 forever, and the loop would print the shift-change message until you killed it. In a while loop, put the update before any continue.

enumerate(): let Python do the counting

Look back at the fence patrol and the feeding round. Both carry a hand-made counter, checkpoint += 1 and enclosure += 1, whose only job is to say which item we are on. This is such a common need that Python built a tool for it.

enumerate() wraps your source of items and hands you two things on every pass: a count, then the item. You catch both by writing two names before in.

Three cheetahs wear colored tags, R, G and B. Here is what enumerate() gives you with no extra settings:

for index, tag in enumerate("RGB"):
    print(f"Index {index}: cheetah with {tag} tag")
Enter fullscreen mode Exit fullscreen mode
$ python enum_default.py
Index 0: cheetah with R tag
Index 1: cheetah with G tag
Index 2: cheetah with B tag
Enter fullscreen mode Exit fullscreen mode

The count starts at 0 by default. That suits programmers, but Wanjiru's clipboard starts at checkpoint 1. Pass start=1 and the count follows. Here is the fence patrol again:

for checkpoint, fence in enumerate("IIIBII", start=1):
    if fence == "B":
        print(f"Checkpoint {checkpoint}: fence BREACHED. Calling the response team.")
        break
    print(f"Checkpoint {checkpoint}: fence intact.")

print("Patrol ended.")
Enter fullscreen mode Exit fullscreen mode
$ python fence_enum.py
Checkpoint 1: fence intact.
Checkpoint 2: fence intact.
Checkpoint 3: fence intact.
Checkpoint 4: fence BREACHED. Calling the response team.
Patrol ended.
Enter fullscreen mode Exit fullscreen mode

The output matches the earlier version line for line, and the checkpoint = 0 and checkpoint += 1 lines are gone. Fewer lines mean fewer places for a counter bug to hide. Whenever you catch yourself writing i += 1 inside a for loop, reach for enumerate() instead.

Morning round at the orphanage: all four at once

Here is the full picture. Wanjiru pushes a food cart that holds up to 3 meals and starts with 2. She walks eight enclosures, each coded H (hungry), S (sleeping) or X (emergency). Sleepers get skipped. An emergency ends the round so she can call the vet. When the cart runs out, she restocks it before feeding.

cart = 2
fed = 0

for enclosure, status in enumerate("HSHHSXHH", start=1):
    if status == "X":
        print(f"Enclosure {enclosure}: emergency! Calling the vet. Round over.")
        break
    if status == "S":
        print(f"Enclosure {enclosure}: asleep, skipping.")
        continue
    if cart == 0:
        print("  Cart empty. Restocking...")
        while cart < 3:
            cart += 1
            print(f"  Cart holds {cart} meal(s)")
    cart -= 1
    fed += 1
    print(f"Enclosure {enclosure}: fed. Cart now holds {cart}.")

print(f"Animals fed: {fed}")
Enter fullscreen mode Exit fullscreen mode
$ python morning_round.py
Enclosure 1: fed. Cart now holds 1.
Enclosure 2: asleep, skipping.
Enclosure 3: fed. Cart now holds 0.
  Cart empty. Restocking...
  Cart holds 1 meal(s)
  Cart holds 2 meal(s)
  Cart holds 3 meal(s)
Enclosure 4: fed. Cart now holds 2.
Enclosure 5: asleep, skipping.
Enclosure 6: emergency! Calling the vet. Round over.
Animals fed: 3
Enter fullscreen mode Exit fullscreen mode

Trace it against the output:

  • enumerate(..., start=1) numbers the enclosures from 1, so no counter variable exists.
  • Enclosure 2 is S, so continue skips the feeding code and the round moves on.
  • After enclosure 3, the cart holds 0. At enclosure 4 the if cart == 0 check triggers a while loop that adds one meal per pass until the cart holds 3. The number of passes depends on the cart's level, which is exactly the situation while is for.
  • Enclosure 6 is X, so break ends the for loop. Enclosures 7 and 8 are never visited, and the final count reports 3 animals fed.

Each tool does one job. for visits the enclosures, enumerate() numbers them, continue skips the sleepers, break answers the emergency, and while handles the restock.

Try it yourself

Copy the morning round script and change one thing at a time. Predict the output before you run it.

  1. Move the X to the last position. How many animals get fed now?
  2. Change start=1 to start=10. Which lines of output change?
  3. Set the starting cart to 0. What prints first?
  4. Add a second X earlier in the string. Does the second one ever fire?

Predicting, then running, then reading the gap between the two is how loops stop feeling like magic. If you rebuild the example around another Kenyan setting, such as a tea factory in Kericho, a matatu stage, or an M-Pesa agent's day, share it in the comments.

Next time you catch yourself pasting the same line three times, stop and ask which loop wants the job.

Top comments (0)