--- tags: python-course title: lesson-06 --- # If This, Then That [![hackmd-github-sync-badge](https://hackmd.io/Z5c0_ViZTJazc4rwy2cirA/badge)](https://hackmd.io/Z5c0_ViZTJazc4rwy2cirA) :::info :bulb: Sometimes your program needs to behave differently depending on a condition. :school: Teacher is James, student is Nina. ::: :::success :movie_camera: VIB background fading to course title slide. James and Ninas smiling faces appear. ::: :::warning :notes: Upbeat intro music ::: **Nina**: Hey James! Do you want to play FizzBuzz with me? **James**: Sure! :::warning :video_game: Play fizzbuzz until someone makes a mistake ::: **Nina**: Could we write a Python program to check our answers? **James**: Yes of course! But we need to learn a new concept before we can start: how do you change the behaviour of your program depending on the answer to a question. (pause) You can do this with an `if` statement. For example, if a number is a multiple of three, then say "Fizz". The body of the if statement, the lines that are indented, is not run if a number is not a multiple of 3. :::success :movie_camera: Animation in the background showing: ```python= if a number is a multiple of 3: print("Fizz") ``` Highlight each part: `if`, condition, `:`, indentation, body ::: **James** (to the camera): You can think of the `if` as "guarding" the indented block. We start by asking Python a question. In this case we are asking whether some number is a multiple of 3? If the answer to the question is "no" then Python won't run any of the code inside the indented block. Only if the answer to the question is "yes", will Python run the code inside the indented block. **Nina**: If I have a variable containing a number, how do I tell if it is a multiple of 3? **James** (over demonstration): There are a few ways you could check. One way would be to use the modulo operator, the percent sign. Your number will be a multiple of 3 if your number modulo 3 is equal to 0. Another way to say this is that multiples of 3 have a zero remainder after division by 3. :::success :movie_camera: Animation in the background showing: ```python= if a_number % 3 == 0: print("Fizz") ``` Highlight each part: `if`, condition, `:`, indentation, body ::: > [name=James] Talk about Fizz _and_ Buzz. Buzz arrives here suddenly. **Nina** (over background animation): Ok I have tried to write the check. **James**: Ok that seems reasonable. But the game also requires that you say "Buzz" too. Can you add that? **Nina**: Ok that should be easy... But that doesn't play the game properly... What's wrong with this code? :::success :movie_camera: Morph animation in the background showing: ```python= if a_number % 3 == 0: print("Fizz") print("Buzz") ``` ::: **James**: It is valid Python so there's nothing wrong with it in the sense that it will run without error. But it doesn't correctly check the answers to the FizzBuzz game so in that way it is wrong. There is a mistake in the logic of playing the game. **James** (to the camera): Can you spot the mistake? :::warning :notes: Waiting music ::: **James**: The problem is that once the indented block has finished running then Python will continue with the unindented code outside of the block. **Nina**: Maybe it would be clearer with an example? **James** (over animation of code running): Alright, if the variable, `a_number`, contains the integer 3, then 3 modulo 3 is equal to zero. The condition is true so we `print(Fizz)` then the block is over and the program _always_ `print(Buzz)` even when we're not supposed to. **Nina**: Can I do something different if the condition is `False`? **James** (over demonstration): You can indeed! With a keyword called, `else`. You can write it after an `if` block like this. :::success :movie_camera: Animation: ```python= if a_number % 3 == 0: print("Fizz") else: print("Buzz") ``` ::: **James**: Now you can try by experimenting with what would happen in this program with different values in the `a_number` variable. Let's start with 3. **Nina** (Over animation): Ok, I put 3 into `a_number`. Then ask if it's a multiple of 3. The answer is yes so I run the indented block and print "Fizz". Good so far. Next I can try putting 5 into the `a_number` variable. 5 isn't a multiple of 3 so I do not run the indented block this time, but I do run the else block. It's 5 so I print, "Buzz". Still good so far! What if I try with a number that isn't a multiple of 3 or 5? For instance, if `a_number` is 2 which isn't a multiple of 3 I still say "Buzz". That's not quite what I want. :::success :movie_camera: Animation of the value `3` inside `a_number` going through the computation. Then an animation of the value `5` inside `a_number`. These should be shown as correct (perhaps with a green tick). Then another number like `2` should show no output. ```python= a_number = 3 # then 5, 2 if a_number % 3 == 0: print("Fizz") else: print("Buzz") ``` ::: **James**: This is starting to get difficult to follow. So let's put your code into a function and test it. You already have some good test inputs. **James** (over the demo): Instead of printing you can return the string so you can print it to the console later or, as in our case, test it against expected outputs. Now let's include the test cases you thought of... :::success :movie_camera: Demo fizzbuzz testing ```python= def fizzbuzz(a_number: int) -> str: "Generate what a player should say on one round of the fizzbuzz game." if a_number % 3 == 0: return "Fizz" else: return "Buzz" assert "Fizz" == fizzbuzz(3) assert "Buzz" == fizzbuzz(5) assert "2" == fizzbuzz(2) ``` ::: **James** (over the above demo): The first 2 test cases pass just like you thought they would and the last one fails. After the `else` we should really be asking another question, something like, "ok it's not a multiple of 3, but is it a multiple of 5?". **James** (over the below demo): You can add a new guard `if` to that question like this. Don't forget to turn `a_number` into a string since we promised to return a string. :::success :movie_camera: Demo editing in the extra condition: ```python= def fizzbuzz(a_number: int) -> str: "Generate what a player should say on one round of the fizzbuzz game." if a_number % 3 == 0: return "Fizz" else: if a_number % 5 == 0: return "Buzz" else: return str(a_number) assert "Fizz" == fizzbuzz(3) assert "Buzz" == fizzbuzz(5) assert "2" == fizzbuzz(2) ``` ::: **Nina**: That works on those test cases but we're missing a test for multiples of 3 **and** 5. :::success :movie_camera: Demo editing in the extra test case for 15 and notice that the test fails. ::: **Nina** (over the demo): I can just add in an extra `if` just like before to test for multiples of 3 and 5. :::success :movie_camera: Demo editing in the extra condition: ```python= def fizzbuzz(a_number: int) -> str: "Generate what a player should say on one round of the fizzbuzz game." if a_number % 3 == 0: return "Fizz" else: if a_number % 5 == 0: return "Buzz" else: if a_number % 3 == 0 and a_number % 5 == 0: return "FizzBuzz" else: return str(a_number) assert "Fizz" == fizzbuzz(3) assert "Buzz" == fizzbuzz(5) assert "2" == fizzbuzz(2) assert "FizzBuzz" == fizzbuzz(15) ``` ::: **James** (To the camera): The test still fails. Can you trace the execution path through the function to notice what is wrong? :::warning :musical_note: Waiting music ::: **Nina** (over animation tracing path through the program): Let's run the fizzbuzz function with the argument `15`, so `a_number` contains the integer 15. On our first `if` we ask the question, "Is 15 a multiple of 3?". Yes it is, so the program returns "Fizz". Oops! (pause) **Nina** (over editing the program): I'm returning "Fizz" _before_ checking if `a_number` also contains a multiple of 5. So I need to check for "FizzBuzz" as the very first condition in the program. :::success :movie_camera: Demo editing in the extra condition: ```python= def fizzbuzz(a_number: int) -> str: "Generate what a player should say on one round of the fizzbuzz game." if a_number % 3 == 0 and a_number % 5 == 0: return "FizzBuzz" else: if a_number % 3 == 0: return "Fizz" else: if a_number % 5 == 0: return "Buzz" else: return str(a_number) assert "Fizz" == fizzbuzz(3) assert "Buzz" == fizzbuzz(5) assert "2" == fizzbuzz(2) assert "FizzBuzz" == fizzbuzz(15) ``` ::: **James**: Great! That passes the tests. You're finished. **Nina**: Now we can check if our answers to the fizzbuzz game are correct! Thanks James! **James**: You're very welcome Nina. In this lesson you learned how to "guard" an indented block of code with an if statement. This allows you to change what your program does depending on the answer to a yes/no question. You also learned how to use `else` as a catch-all alternative when the answer to the `if` question is "no". **Nina**: Can we play fizzbuzz again? **James**: Sure! 1. **Nina**: 2... :::warning :notes: Fade out audio of James and Nina playing FizzBuzz over an animation of the fizzbuzz function checking their answers ::: :::success :movie_camera: Fade to VIB logo slide. :::