# Booleans and Conditions¶

In this lecture, we investigate the notion of variable scope when calling functions.

Then we explore the boolean types `True` and `False` in Python, relational and logical operators, and how they help making decisions using the `if`- `else` conditional blocks.

## Variable Scope¶

Local variables. An assignment to a variable within a function definition creates/changes a local variable. Local variables exist only within a function’s body, and cannot be referred to outside of it. Parameters are also local variables that are assigned a value when the function is invoked.

```def myfunc (val):
val = val + 1
print('local val = ', val)
return val
```
```val = 3
newVal = myfunc(val)
```
```local val =  4
```
```newVal
```
```4
```
```print('global val =', val)
```
```global val = 3
```

## Boolean Type and Relational Operators¶

`True` and `False` are of type `bool` in Python and naturally occur as a result of relational operators (<, >, ==, !).

```4 < 5
```
```True
```
```10 == 10
```
```True
```
```'a' == 'b'
```
```False
```
```True == 1
```
```True
```
```False == 0
```
```True
```
```1000/3 < 300
```
```False
```
```num1 = int(input("Enter first number: "))
num2 = int(input("Enter second number: "))
num1 <= num2
```
```num1 != num2
```
```num1 % 3 == 1
```

## Conditional Statement: If Else¶

We can ensure that some statements in the program are evaluated conditionally only if the result of a Boolean expression evaluates to `True` using an `if` statement. If the Boolean expression evaluates to `False`, then the control flow skips statements under the `if` block and evaluates the statements under the `else` block.

### If Else Statement Syntax¶

statement 1
statement 2
`if` (boolean expression):
statement 3
statement 4
…
`else`:
statement 5
statement 6
…
statement 7

#### Indentation matters in Python¶

Indented statements form a logical block of code in Python:

• If the boolean expression next to the `if` statement evaluates to `True`, then statements `3, 4,...` in the `if` block are executed, after which the control flow will skip over all of the statements under the `else` block, and go straight to statement `7`

• If the boolean expression next to the `if` statement evaluates to `False`, then the control flow skips over statements `3, 4,..` and exectutes statements `5, 6,...` in the `else` block, after which the control flow goes to statement `7`.

### Checking if a Number is Even¶

Let us write a function `printEven` that takes a number as input. If the number is even, it prints “Even”, else it prints “Odd”.

Question. How can we check if a number is even?

```3 % 2
```
```1
```
```10 % 2
```
```0
```
```17 % 2
```
```1
```
```9 % 2 == 0
```
```False
```

Exercise. Let us write the function `isEven(num)` below.

```def printEven(num):
"""Takes a number as input, prints Even if
it is even, else prints Odd"""
if num % 2 == 0: # if even
return True
print("Even")
else:
return False
print("Odd")
```
```printEven(16)
```
```True
```
```printEven(77)
```
```False
```

Exercise. Suppose instead of printing, we want to return `True` if `num` is even, and `False` if number is odd. Let us define an `isEven(num)` that does this.

```def isEven(num):
"""Takes a number as input, returns True if
it is even, else returns False"""
if num % 2 == 0: # if even
return True
else:
return False
```
```isEven(8)
```
```True
```
```isEven(75)
```
```False
```

## Else block is optional¶

An `if` statement does not need an `else`, and there are often times when removing the `else` block makes the program shorter. (Although stylistically, you may prefer to include the `else`.)

Simplify. We can simplify the `isEven` function by removing the `else` block, and return `False` if the `if` condition fails.

```def isEven(num):
"""Takes a number as input, returns True if
it is even, else returns False"""
if num % 2 == 0: # if even
return True
return False
```

Simplify further. We can shorten it even further and just return the result of the Boolean expression. This is the best approach!

```def isEven(num):
"""Takes a number as input, returns True if
it is even, else returns False"""
return num % 2 == 0
```

### Tracing Control Flow Through Conditionals¶

Consider the following example of a function `zeroToOne()` that takes a number `num` as input. If the number is equal to zero, it adds one to `num` and returns it. Otherwise it just returns `num`.

Let us trace the control flow when the function is called with different values of `num`. We can use print statements in our code to see the control flow of the program.

In situations like this function, it is a good idea to have a single return statement, rather than a return statement in each conditional block.

Notice: Statements above the `if` block and after the `else` block are always executed.

```# adding more prints and return
def zeroToOne(num):
"""If input number num is 0, adds one and returns,
else returns num itself"""
print("You called this function with num =", num)

if num == 0:
print("Incrementing to 1")
num += 1 # update to 1

else:
print("No need to increment.")

print("Just before return")
return num

# will this ever get printed?
print("Just after return")
```
```zeroToOne(1)
```
```You called this function with num = 1
No need to increment.
Just before return
```
```1
```