# Python - Control Flow

## If / Else

You can conditionally run a line of code like this:

``````

if x == 5:
print('equal to five')

``````

Conditionally run a statement and run a different statement if the condition is not true:

``````

if x == 5:
print('equal to five')
else:
print('not equal to five')

``````

If one condition is true execute a statement, otherwise check another condition:

``````

if x < 5:
print('less than 5')
elif x == 50:
print('Medium')

``````

A more complete example:

``````

if x < 5:
print('less than 5')
x = x + 2
elif x == 50:
print('Medium')
elif x == 100:
print('Large')
else:
print('Default')

``````

## Loops

Loops are terrific. They allow you to repeat a task over and over in an iterative manner.

### Range - Quick Background

We’re going to breifly cover ranges because they are important and loops will be easier to understand when you’ve covered these. The range() function creates an iterator ( more on these in a later section ). This can’t be printed directly but can be looped over.

• Printing a range doesn’t print the series since it is an iterable.

Create a range of numbers 0 to 9 ( size 10 ):

``````

range(10)
# 0 1 2 3 4 5 6 7 8 9

``````

Range 5 to 14:

``````

range(5, 15)
# 5 6 7 8 9 10 11 12 13 14

``````

Range 5 to 24, step by 2:

``````

range(5, 25, 2)
# 5 7 9 11 13 15 17 19 21 23

``````
``````

sum(range(4))

``````

### For Loops

Here is an example of a very basic, standard for loop. It loops over a list and prints each element.

``````

a = ["pizza", "noodle", "burger"]

for i in a:
print(i)

``````

Loop over a range and print the numbers:

``````

for i in range(5):
print(i)

``````

Iterate over indices showing the index and value:

``````

a = ["pizza", "noodle", "burger"]

for i in range(len(a)):
print(i, a[i])

``````

Python C style for loop - you can just use a range for this:

``````

for i in range(0, 9, 2):
print(i)

``````

### While Loops

While loops can be useful but in most cases you will want to stick with for loops.

Generic while loop:

``````

x = 0

while x < 50:
print("x: " + str(x))
x = x + 1

``````

While loops are great for creating infinite loops:

``````

while True:
print("x")

``````

### Looping Over Collections - Tip

Modifying a list or other collection while looping over it can be tricky and has the potential to create issues. Here are a couple over other ways to do this.

Create a copy and loop over that:

``````

a = {'Breakfast': 'pizza', 'Lunch': 'noodle', 'Dinner': 'burger'}
for meal, item in a.copy().items():
if item != 'pizza':
del a[meal]

``````

Just create a new dictionary:

``````

a = {'Breakfast': 'pizza', 'Lunch': 'noodle', 'Dinner': 'burger'}
b = {}
for meal, item in a.items():
if item == 'pizza':
b[meal] = item

``````

### Break, Continue, Else

• continue - start next iteration of loop
• break - stop executing loop
• else - executes at the end when no break occurs

Break the loop early when we find “taco”:

``````

a = ["pizza", "noodle", "taco", "burger", "sushi"]

for i in a:
if i == "taco":
break

``````

Print out a message if the loop terminates without a match ( no break ):

``````

a = ["pizza", "noodle", "taco", "burger", "sushi"]

for i in a:
if i == "taco":
break
else:
print("no tacos found")

``````

Skip to the next iteration of the loop if we’ve found “taco”. Basically don’t bother checking food if we already have “taco”.

``````

a = ["pizza", "noodle", "taco", "burger", "sushi"]

for i in a:
if i == "taco":
continue
print("checking food...")
if i == "pizza":
print("found pizza")

``````

## Pass - Place Holder

• pass - does nothing, like a place holder that keeps syntax valid

Infinite loop, wait for keyboard interrupt:

``````

while True:
pass

``````

Create a class for future use but don’t define it yet:

``````

class SomeClass:
pass

``````

Define a fuction but don’t create an implementation yet. This way we can write all of the code that uses this function first.

``````

def function1(a, b, c):
pass

``````

## Match Statements

Match:

• like switch statement from other langs
• only first matched pattern executes
• can extract components

Multiple options:

``````

match option:
case 123:
print("First Option")
case 456:
print("Second Option")
case 1000:
print("Another Option")
case _:
print("Default - Something is wrong")

``````

Using multiple literals as single pattern:

``````

match option:
case 123 | 789 | 12:
print("found a match")

``````

Using multiple literals as single pattern:

Can also match both numbers and strings:

``````

match option:
case "abc" | 789 | 12:
print("found a match")

``````

Here is an example where do the following:

• unpack patterns ( also binds variables ):
• unpack variables combined with literals
``````

some_tuple = (5, 8)

match some_tuple:
case (0, 0):
print("Start")
case (0, y):
print(f"Y={y}")
case (x, 0):
print(f"X={x}")
case (x, y):
print(f"X={x}, Y={y}")
case _:
print("default")

``````

Weird example doing this with a class:

``````

class SomeClass:
def __init__(self, x, y):
self.x = x
self.y = y

sc = SomeClass(5, 8)

match sc:
case SomeClass(x=0, y=0):
print("Start")
case SomeClass(x=x, y=0):
print(f"X={x}")
case SomeClass(x=0, y=y):
print(f"Y={y}")
case SomeClass(x=x, y=y):
print(f"X={x} Y={y}")
case _:
print("default")

``````

Use an if statement ( called a guard ). This allows you to apply additional checks to each case.

``````

some_tuple = (5, 5)

match some_tuple:
case (x, y) if x == y:
print(f"X={x}, Y={y}")
case _:
print("default")

``````

Capture a subpattern with the “as” keyword:

``````

some_tuple = ((5, 5),(1,2))
match some_tuple:
case ((x, y), (a, b) as i2):
print(f"Sub:{i2}")
case _:
print("default")

``````

## More Conditions

The operator “in” can be used to test membership in a collection. The operator “is” can be used to test if objects are actually the same object ( not just that their values are the same ). The operator “not” can be used for negation.

``````

in         # test if item is in a collection
not in
is         # test if same object
is not

Check multiple conditions:

``````

You can terst multiple conditions in on expression like this:

``````

a < b == c

``````
• And/or/not have lower priority than comparison operators.

Priorities between and/or/not:

 and or lowest priority not highest priority

These are aquivalent:

``````

A and not B or C
(A and (not B)) or C

``````

Short-circuit operators:

• These stop evaluating when the outcome is known
• Return value is the last evaluated arg

Assign results of comparison:

``````

non_null = str1 or str2 or str3

``````

:= walrus operator, use to assign inside an expression

## Comparing Sequences

Same type sequences can be compared.

• Elements are compared one at a time. When a different element is found it is compared lexographically.
• If one sequence is a subset, it will be smaller )
• Strings are compared by the Unicode code point number.
• Objects of different types need to have a comparison method.

Example of comparrisions from official Python tutorial:

``````

(1, 2, 3)              < (1, 2, 4)
[1, 2, 3]              < [1, 2, 4]
'ABC' < 'C' < 'Pascal' < 'Python'
(1, 2, 3, 4)           < (1, 2, 4)
(1, 2)                 < (1, 2, -1)
(1, 2, 3)             == (1.0, 2.0, 3.0)
(1, 2, ('aa', 'ab'))   < (1, 2, ('abc', 'a'), 4)

``````