4  Flow Control

Let’s take a look at a few syntax aspects that allow us to control how our program flows.

We can loop over things (we’ll get back to this later):

4.1 Loops

for number in range(2, 11, 2):
    print(number)
2
4
6
8
10
range?
Init signature: range(self, /, *args, **kwargs)
Docstring:     
range(stop) -> range object
range(start, stop[, step]) -> range object
Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step.  range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted!  range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).
Type:           type
Subclasses:     
players = [
    "Tilo",
    "Fiona",
    "Ray",
]

It is a good practice to directly loop over the elements of collections like so:

for player in players:
    print(player)
Tilo
Fiona
Ray

If you really need the indices to carry some other operation, you can get them as you go with enumerate, which returns a tuple:

for i, player in enumerate(players):
    print(i, player)
0 Tilo
1 Fiona
2 Ray

Notice: These tuples are just like dictionary items, so we can build a dictionary with them:

dict(enumerate(players))
{0: 'Tilo', 1: 'Fiona', 2: 'Ray'}

Importantly, Python doesn’t care about what data type we are looping over – all that matters is the data “knows” how to be looped over:

for player in players:
    print(player)
    for letter in player:
        print(letter)
    print("-" * 5)
Tilo
T
i
l
o
-----
Fiona
F
i
o
n
a
-----
Ray
R
a
y
-----

This is a powerful (and typical for Python) way to reason about the objects we deal with: We care more about how the behave than what they are, a concept related to what people informally call duck typing.

We also have while loops, that will run as long as the condition is true:

Tip: Use while loops sparingly

It’s very easy to make mistakes and run into infinite loops.

message, count = "end-of-loop", 1  # We can do multiple assignment with tuple unpacking
while True:
    print(count)
    count += 1   # equivalent to count = count + 1
    if count == 4:
        print(message)
        break  # we can interrupt the loop
1
2
3
end-of-loop

We say something is “falsy” when it evaluates to False, despite it not being the boolen value False.

Empty containers (lists, tuples, dicts, sets) are “falsy”, which can be handy:

list.pop?
Signature: list.pop(self, index=-1, /)
Docstring:
Remove and return item at index (default last).
Raises IndexError if list is empty or index is out of range.
Type:      method_descriptor
while players:  # when the list players is empty, it will become falsy
    player = players.pop()
    print(f"Removing: {player}")
Removing: Ray
Removing: Fiona
Removing: Tilo
players
[]

4.2 If Statement

if 1 > 0:
    print("kind of reasonable")
else:
    print("not quite")
kind of reasonable
x = 1
if x > 2:
    print("kind of crazy")
elif x == 2:
    print("same")    
elif x == 3:
    print("say what?")
else:
    print("I give up")
I give up