Branching#

When you come to a fork in the road, take it.
— Yogi Berra
Slides/PDF#
Conditional branching#
A computer often has to decide how information should be processed. To do this, the program must be able to execute certain statements or parts of the program only under certain conditions. For this purpose, all programming languages provide the concept of branching.
The simplest form of branching is conditional branching, which executes a block of code only when a logical condition is met. In Python, the if
statement (If-Then) is provided for this purpose, which precedes a statement that is executed only if the condition is met. It is common to write the statements on a new line. Then they must be indented.
condition = True
if condition:
# will only execute if condition is True
print(f"Der Block mit 'bedingung={condition}' wurde ausgeführt. 1")
condition = False
if condition:
# will only execute if condition is True
print(f"Der Block mit 'bedingung={condition}' wurde ausgeführt. 2")
Der Block mit 'bedingung=True' wurde ausgeführt. 1
With the logical operator not
, you can negate the conditions and combine them with and
, or
, and parentheses.
condition = True
if not condition:
# is only executed when the condition is True
print(f"Der Block mit 'bedingung={condition}' wurde ausgeführt.")
condition = 0
if condition:
# is only executed if the condition is equivalent to True
print(f"Der Block mit 'bedingung'={condition} wurde ausgeführt.")
condition = 2
if condition:
# is only executed if the condition is equivalent to True
print(f"Der Block mit 'bedingung'={condition} wurde ausgeführt.")
if not condition:
# is only executed when the condition is True
print(f"Der Block mit 'bedingung={condition}' wurde ausgeführt.")
Der Block mit 'bedingung'=2 wurde ausgeführt.
Strings that are empty and have length 0 correspond to the boolean value False
, and non-empty strings correspond to the boolean value True
.
condition = '' # empty string
if condition:
# will only execute when the condition evaluates to True
print(f"The block with 'condition'={condition} was executed.")
condition = ' '
if condition:
# will only execute when the condition evaluates to True
print(f"The block with 'condition'={condition} was executed.")
The block with 'condition'= was executed.
The same applies to lists and dictionaries when they are empty (False
) or filled (True
).
condition = [] # empty list
if condition:
# will only be executed when the condition evaluates to True
print(f"Der Block mit 'bedingung'={condition} wurde ausgeführt.")
condition = ['nicht_leer'] # non-empty list
if condition:
# will only be executed when the condition evaluates to True
print(f"Der Block mit 'bedingung'={condition} wurde ausgeführt.")
Der Block mit 'bedingung'=['nicht_leer'] wurde ausgeführt.
That makes it easy to quickly check in code whether, for example, a list already contains elements and whether they should be processed further.
For more complex data types, such as objects that can take the value None, this value is interpreted as False, and everything else as True, which in turn lets you check whether the variable has already been set.
condition=None # not set
if condition:
# will only execute if the condition is truthy
print(f"Der Block mit 'bedingung'={condition} wurde ausgeführt.")
condition="nicht_None" # set
if condition:
# will only execute if the condition is truthy
print(f"Der Block mit 'bedingung'={condition} wurde ausgeführt.")
condition=None # not set
if condition is not None:
# will only execute if the condition is truthy
print(f"Der Block mit 'bedingung'={condition} wurde ausgeführt.")
condition="nicht_None" # set
if condition is not None:
# will only execute if the condition is truthy
print(f"Der Block mit 'bedingung'={condition} wurde ausgeführt.")
Der Block mit 'bedingung'=nicht_None wurde ausgeführt.
Der Block mit 'bedingung'=nicht_None wurde ausgeführt.
The Alternative#
It often happens that not only something should be executed when the condition is true, but in the other case something should also be executed. For this there is the else
statement, which may only follow an if
and, when there are multiple lines, must be indented at the same level as the if
it belongs to.
condition = True
if condition:
# is only executed when Condition is True
print(f"Der Block mit 'bedingung={condition}' wurde ausgeführt.")
else:
# is only executed when Condition is False
print(f"Der alternative Block mit 'bedingung={condition}' wurde ausgeführt.")
Der Block mit 'bedingung=True' wurde ausgeführt.
condition = False
if condition:
# is only executed when the condition is True
print(f"Der Block mit 'condition={condition}' wurde ausgeführt.")
else:
# is only executed when the condition is False
print(f"Der alternative Block mit 'condition={condition}' wurde ausgeführt.")
Der alternative Block mit 'condition=False' wurde ausgeführt.
A typical example is catching division by zero. In computations you cannot always guarantee that the denominator will not be zero. Since division by zero is undefined, it can lead to a program error and, possibly, a program crash.
numerator = 5
denominator = 0
result = numerator / denominator
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
Cell In[8], line 3
1 numerator = 5
2 denominator = 0
----> 3 result = numerator / denominator
ZeroDivisionError: division by zero
To handle this, we can use an if-else
branch and assign the result the value None
(in the sense of ‘undefined’).
denominator = 0
if denominator: # the truth value of an int is False for 0 and True for values not equal to 0
result = numerator / denominator
else:
print("Teilung durch 0")
result = None
print(f"Das Ergebnis von {numerator}/{denominator} = {result}")
Teilung durch 0
Das Ergebnis von 5/0 = None
Since, with Python’s dynamic typing, you cannot guarantee that zaehler
and nenner
are of the correct data type int
, it makes sense to add a type check. For this, we first check the data type with the built-in function type()
before performing the zero-division check. For this, we nest the if-else
conditions.
numerator = 'keine_zahl'
denominator = 0
if type(numerator) == int:
if type(denominator) == int:
if denominator: # the truth value of an int is False for 0 and True for != 0
result = numerator / denominator
else:
print("Division by zero")
result = None
else:
print(f"Denominator not of type `int`, but of type {type(denominator)}")
result = None
else:
print(f"Numerator not of type `int`, but of type {type(numerator)}")
result = None
print(f"The result of {numerator}/{denominator} = {result}")
Numerator not of type `int`, but of type <class 'str'>
The result of keine_zahl/0 = None
Branching Alternatives#
If there are multiple nested alternatives as in the last example, the code quickly becomes hard to read and you need a more compact syntax. For this, Python has the elif
statement, which is simply a shorthand for else if
and does not require any further indentation. This way, you can write the previous code more readably as:
numerator = 'keine_zahl'
denominator = 0
result = None
if not isinstance(denominator, int):
print(f"Nenner nicht vom Datentyp `int`, sondern vom Typ {type(denominator)}")
elif not isinstance(numerator, int):
print(f"Zaehler nicht vom Datentyp `int`, sondern vom Typ {type(numerator)}")
elif not denominator:
print("Teilung durch 0")
else:
result = numerator / denominator
print(f"Das Ergebnis von {numerator}/{denominator} = {result}")
Zaehler nicht vom Datentyp `int`, sondern vom Typ <class 'str'>
Das Ergebnis von keine_zahl/0 = None
Here, we have:
first, the condition
type(zaehler) == int
replaced by the standard functionisinstance(zaehler, int)
which is identical and easier to read.we have addressed all error cases first, to ensure they are checked before executing the calculation
thus we first check whether the
zaehler
is not of typeint
and in this case output an error messageif that is not the case, then
nenner
could not be of typeint
and we would in this case output a different error messagethen the
nenner
must be of typeint
and we check whether it is 0 with an appropriate error messagesince in all three cases the
ergebnis
has the valueNone
, we set this assignment before theif
blockthe last
else
block is only reached if none of this applies, i.e.,zaehler
andnenner
are of typeint
andnenner
is not 0. Only in this case isergebnis
assigned the value of the division
numerator = 10
denominator = []
result = None
if not denominator:
print("Teilung durch 0")
elif not isinstance(denominator, int):
print(f"Nenner nicht vom Datentyp `int`, sondern vom Typ {type(denominator)}")
elif not isinstance(numerator, int):
print(f"Zaehler nicht vom Datentyp `int`, sondern vom Typ {type(numerator)}")
else:
result = numerator / denominator
print(f"Das Ergebnis von {numerator}/{denominator} = {result}")
Teilung durch 0
Das Ergebnis von 10/[] = None
Many Alternatives#
In Python 3.10, the match statement was introduced, which already exists in many other languages. It allows you to compare a variable against several possible values.
Traditionally (many elifs):
if Variable == ValueA:
StatementA
elif Variable == ValueB:
StatementB
else:
StatementC
New (match-case):
match Variable:
case ValueA:
StatementA
case ValueB:
StatementB
case _:
StatementC