# 8. If/Else

Activity

Using only what we have learned so far, write a function `smush(a_number)`

that checks if the number is
positive or negative. If `a_number`

is positive, the function will return half the value of `a_number`

. If
the number is not positive, simply return `a_number`

.

## 8.1. Conditional Expressions

Booleans were discussed in the previous topic

We discussed them as a type

How to use Boolean operators

How to use comparison operators

But Booleans allow us to do a lot more than just evaluate an expression to

`True`

/`False`

Consider

`smush`

from the above activityWe know how to

*ask*if`a_number`

is positive`a_number > 0`

The key now is to tell Python that,

`if`

some condition is`True`

, do something

```
1def smush(a_number: float) -> float:
2 """
3 Returns half the value of the parameter a_number if the value is positive,
4 otherwise, return the value of a_number.
5
6 :rtype: float
7 :param a_number: Some arbitrary number.
8 :return: Half of a_number when it is positive, a_number when not positive.
9 """
10 return_value = a_number
11 if a_number > 0:
12 return_value = return_value / 2
13 return return_value
14
15
16# Tests for smush
17assert 5 == smush(10)
18assert -10 == smush(-10)
19assert 0 == smush(0)
```

In the above example we made use of an

`if`

statementWhen the evaluated Boolean value after the

`if`

is`True`

, the indented code is executed, otherwise the indented code block is ignoredIf we follow the code within the function when

`a_number`

is`10`

, the execution is as followsAssign the value of

`a_number`

(`10`

) to`return_value`

Evaluate

`a_number > 0`

`10 > 0`

`True`

Since the expression evaluated to

`True`

, the indented code is runDivide

`return_value`

by`2`

and assign it back into`return_value`

Return the value stored in

`return_value`

(`5`

)

Similarly, if we follow the code when

`a_number`

is`-10`

, the execution is as followsAssign the value of

`a_number`

(`-10`

) to`return_value`

Evaluate

`a_number > 0`

`-10 > 0`

`False`

Since the expression evaluated to

`False`

, the indented code is skippedReturn the value stored in

`return_value`

(`-10`

)

Activity

Write a function `is_negative(a_number)`

that returns `True`

if `a_number`

is negative and `False`

otherwise.

## 8.2. Compound Conditions

When we make use of an

`if`

statement, the value being checked needs to ultimately be evaluated to a BooleanThis means that we can make use of more complex compound Boolean expressions

Comparison operators

Arithmatic operators

Boolean operators

```
1def three_five_divisible(a_number: float) -> str:
2 """
3 Checks if a number is divisible by both three and five. If it is, return
4 a string "It is!", otherwise "Nope".
5
6 :rtype: str
7 :param a_number: Some arbitrary number.
8 :return: String indicating if the number is divisible by three and five
9 """
10 if a_number % 3 == 0 and a_number % 5 == 0:
11 return "It is!"
12 return "Nope"
13
14
15# Tests for three_five_divisible
16assert "It is!" == three_five_divisible(0)
17assert "It is!" == three_five_divisible(15)
18assert "It is!" == three_five_divisible(-30)
19assert "Nope" == three_five_divisible(3) # Divisible by 3 but not 5
20assert "Nope" == three_five_divisible(-50) # Divisible by 5 but not 3
21assert "Nope" == three_five_divisible(1) # Divisible by neither
```

Note

The modulo operator `%`

(often called just “mod”) effectively does division and returns the **remainder**. For
example, `10 % 3`

is `1`

since `10/3`

is `3`

remainder `1`

.

In the `three_five_divisible`

example, we are checking if the remainder of the division is `0`

, which would mean
that the value can be evenly divided.

Another common use of `%`

is checking if a value is even or not — `x % 2`

is `0`

when `x`

is even since
it would mean that `x`

can be evenly divided by `2`

.

The above function

`three_five_divisible`

needs to check if a number is divisible by 3`and`

5This means that there are two conditions we need to check for being

`True`

If we follow the code within the function when

`a_number`

is`15`

, the execution is as followsEvaluate

`a_number % 3 == 0 and a_number % 5 == 0`

`15 % 3 == 0 and 15 % 5 == 0`

`0 == 0 and 0 == 0`

`True and True`

`True`

Since the expression evaluated to

`True`

, the indented code is runReturn

`"It is!"`

, function ends

If we follow the code within the function when

`a_number`

is`9`

, the execution is as followsEvaluate

`a_number % 3 == 0 and a_number % 5 == 0`

`9 % 3 == 0 and 9 % 5 == 0`

`0 == 0 and 4 == 0`

`True and False`

`False`

Since the expression evaluated to

`False`

, the indented code is skippedReturn

`"Nope"`

## 8.3. Alternative Execution

This pattern is very common

```
1if x > 10:
2 do_something()
3if not(x > 10):
4 do_something_else()
```

When we have an either/or situation we make use of

`else`

```
1if x > 10:
2 do_something()
3else:
4 do_something_else()
```

The two examples above will effectively do the same thing, but the 2nd is nicer

Write less

Intuitive and easy to read/understand

Eliminate potential bugs

Activity

Write a function called `hail`

that takes an integer as an argument. If the integer is even, return the value of
that integer divided by 2. If it is odd, return the value multiplied by 3 and with one added. In other words,
given a number \(n\), return \(n/2\) when it is even and \(3n + 1\) when it is odd. **Hint:** Don’t
forget about `%`

.

This is actually some neat math stuff. Isn’t it cool that we’re writing a Python function that’s doing exactly what the math is saying?

Note

If we revisit `smush`

, we can rewrite the function in a few different ways that are all correct.

```
1def smush_version_2(a_number: float) -> float:
2 """
3 Returns half the value of the parameter a_number if the value is positive,
4 otherwise, return the value of a_number.
5
6 :rtype: float
7 :param a_number: Some arbitrary number.
8 :return: Half of a_number when it is positive, a_number when not positive.
9 """
10 if a_number > 0:
11 return_value = a_number / 2
12 else:
13 return_value = a_number
14 return return_value
```

In `smush_version_2`

, an `else`

is used and the function has only one `return`

. The use of the `else`

here
is not required (as seen in the original `smush`

), but the use of `else`

in this situation may make the function
a little clearer. Additionally, some programmers prefer having their functions have only one `return`

, but this is
by no means *more correct*.

```
1def smush_version_3(a_number: float) -> float:
2 """
3 Returns half the value of the parameter a_number if the value is positive,
4 otherwise, return the value of a_number.
5
6 :rtype: float
7 :param a_number: Some arbitrary number.
8 :return: Half of a_number when it is positive, a_number when not positive.
9 """
10 if a_number > 0:
11 return a_number / 2
12 else:
13 return a_number
```

Another possibility is `smush_version_3`

. You will notice how similar it is to version 2, but here we use two
`return`

s in the `if`

and `else`

blocks. Again, this is not *more correct* and it is only shown here to
demonstrate how the same functionality can be implemented differently.

## 8.4. Exclusive Alternatives

Sometimes we need to check various conditions and

`if`

/`else`

isn’t good enoughFor example, what if I want a function to take a percentage grade and return a letter grade

```
1def letter_grade_broken(percent_grade: float) -> str:
2 """
3 Calculate the letter grade associated with the provided percent grade.
4
5 :rtype: str
6 :param percent_grade: A grade as a percent
7 :return: Letter grade for the provided percentage
8 """
9 letter_grade = ""
10 if percent_grade >= 90:
11 letter_grade = "A+"
12 if percent_grade >= 80:
13 letter_grade = "A"
14 if percent_grade >= 70:
15 letter_grade = "B"
16 if percent_grade >= 60:
17 letter_grade = "C"
18 if percent_grade >= 50:
19 letter_grade = "D"
20 else:
21 letter_grade = "F"
22 return letter_grade
```

The above example

`letter_grade_broken`

may be one of the first ideas you come up with, but unfortunately it has a problemIf we run

`assert "A+" == letter_grade_broken(99)`

`letter_grade_broken(99)`

would actually return`"D"`

The trick to understanding the problem is to take our time and look at the code

Call

`letter_grade_broken(99)`

`percent_grade`

is assigned the value`99`

Check if

`percent_grade >= 90`

`percent_grade >= 90`

`99 >= 90`

`True`

Since the expression is evaluated to

`True`

, the indented code is runAssign

`letter_grade`

the value`"A+"`

The execution continues

Check if

`percent_grade >= 80`

`percent_grade >= 80`

`99 >= 80`

`True`

Since the expression is evaluated to

`True`

, the indented code is runAssign

`letter_grade`

the value`"A"`

…

The trouble here is that we really only want one of these

`if`

code blocks to runWe want them to be

*mutually exclusive*alternatives

There are a few ways one could fix this

Have a

`return`

in each indented block since that would stop execution of the function once a`return`

is reachedReverse the order of the

`if`

sCheck upper and lower bounds (e.g.

`percent_grade >= 80 and percent_grade < 90`

)

But arguably the better way to address this is with

`elif`

sCan be read as

*else, if…*

These allow us to have at most one of the code blocks in the chain of conditions to run

In other words, as soon as one of the

`if`

s is true, all other`if`

s are skipped and the program continues running after the`else`

When using

`elif`

s, always end with a final`else`

```
1def letter_grade(percent_grade: float) -> str:
2 """
3 Calculate the letter grade associated with the provided percent grade.
4
5 :rtype: str
6 :param percent_grade: A grade as a percent
7 :return: Letter grade for the provided percentage
8 """
9 letter_grade = ""
10 if percent_grade >= 90:
11 letter_grade = "A+"
12 elif percent_grade >= 80:
13 letter_grade = "A"
14 elif percent_grade >= 70:
15 letter_grade = "B"
16 elif percent_grade >= 60:
17 letter_grade = "C"
18 elif percent_grade >= 50:
19 letter_grade = "D"
20 else:
21 letter_grade = "F"
22 return letter_grade
```

## 8.5. Nesting Conditionals

You can “nest” conditionals inside other conditionals

```
1# Find quadrant with 'nested If's
2if x > 0:
3 if y > 0:
4 print("First Quadrant")
5 else:
6 print("Fourth Quadrant")
7else:
8 if y > 0:
9 print("Second Quadrant")
10 else:
11 print("Third Quadrant")
```

For simplicity, ignore point \((0,0)\) being in the third quadrant

In the above example, we

*could*have done it without nesting by using`and`

sBut some may find the nested version of the code more intuitive and readable

```
1# Find quadrant with 'and's
2if x > 0 and y > 0:
3 print("First Quadrant")
4elif x > 0 and y < 0
5 print("Fourth Quadrant")
6elif x < 0 and y > 0:
7 print("Second Quadrant")
8else:
9 print("Third Quadrant")
```