<style> .markdown-body h1:first-of-type { margin-top: 24px; } .markdown-body h1 { margin-top: 64px; } .markdown-body h1 + h2 { margin-top: 32px; } .markdown-body h2 { margin-top: 48px; } .markdown-body h2.topics { font-size: 1.8em; border-bottom: none; } .markdown-body h3 { color: cornflowerblue; } .markdown-body p strong { font-weight: normal; color: red; } .exercise { font-size: 150%; font-weight: bold; color: rgb(227,112,183); } .note { color: red; } .reveal { background-color: black; margin-bottom: 16px; } .reveal p { color: black; padding-left: 16px; font-size: 16px; transition: color 1s ease; } .reveal:hover p { color: white; } </style> # ACIT 1515 - Lesson 3 <h2 class="topics">Topics</h2> - [Review](#Review) - [Conditional Statements](#Condition-Statements) - [Logical Operators](#Logical-Operators) - [Practical Example](#A-Practical-Example) - [The else Keyword](#The-else-Keyword) - [The elif Keyword](#The-elif-Keyword) - [Rules Of Conditional Statements](#Rules-Of-Conditional-Statements) - [Combining Multiple Conditions](#Combining-multiple-conditions-into-a-single-statement-using-and-or-and-not) ## Review Thus far in the course we have focused on two things: learning the basics of Python _syntax_, and learning how to _debug_ our code, so that we can fix any errors that arise. Below is an example of using [command-line I/O](https://hackmd.io/@charris17/acit1515-lesson2#Output-and-Input-aka-Command-Line-IO) from Lesson 2: ```python= birth_year = input('Please enter your birth year: ') current_year = 2024 print(f'You are {current_year - birth_year} years old') ``` This code has a problem: `input()` always returns a string, and the Python interpreter does not know how to subtract a string from a number, so the operation `current_year - birth_year` on line 3 fails. To solve this, we should probably _attempt_ to convert `birth_year` to a number using the `int()` function. But! What happens if the user enters a non-numeric string? The Python interpreter does not understand how to convert non-numeric strings to integers, so the conversion to `int` fails and a new error is generated. At this point we are aware of two potential problems: 1. We forget to convert a numeric string to int, and the subtraction fails 2. The user enters a non-numeric string, and the conversion to int fails The two cases mentioned above are not the only potential problems however! There is a third issue: what happens if the user enters nothing at all? If the user simply hits the enter key without entering any text (numeric or not), the conversion to int also fails. The Python interpreter does not understand how to convert an empty string to a number. This third issue (along with number 2 above, entering a non-numeric string) are not problems that we can solve simply by converting to a different data type. We need a way in code to ==test== the value entered by the user, and react accordingly. ==If== the user enters a blank string, or a non-numeric string, ==then== we can print a message indicating that their input was invalid and stop the program. ==Else== (i.e. otherwise) we can convert the string to a number and print their age. ## Conditional Statements Conditional statements (also called "if" statements) allow us to run code *conditionally*, depending on whether a condition is true or false. A basic conditional statement has to: 1. Start with the `if` keyword 2. Followed by a ==condition== 3. Followed by a colon 4. Followed by one or more **indented** lines Conditions can be one of three things: 1. A *comparison* between two values that results in a boolean value (True or False) ```python= x = 10 y = 5 if x > y: print('X is greater than y') ``` 2. A *test* that results in a boolean value ```python= keyword = 'CIT' course = 'ACIT1515' if keyword in course: print('CIT was found in the course') ``` 3. or, a boolean value itself ```python= understands = True if understands: print("Now you're getting it") ``` If a boolean condition results in the value `True`, any code indented underneath will run. If the boolean condition results in the value `False`, any code indented below is skipped. Note that certain values in Python (other than the keywords `True` and `False`) are considered to be inherently true or false. For example, all of the following values are considered false: 1. The value `None` 2. The number zero 3. Empty strings 4. Empty sequences, sets, and dictionaries (more on these types later in the course) and the following are considered true: 1. Any non-zero number 2. Any non-empty string 3. Any non-empty sequence, set, or dictionary This means that the following code *will* produce output: ```python= test_string = "testing 123" if test_string: print('The string is not empty') ``` If we want to ensure our print statement runs when the value of `test_string` **is** empty, we can ==negate== (reverse) the condition using the keyword ==not==: ```python= test_string = "" if not test_string: print('The string is empty') ``` The `not` keyword can be used before a boolean value (as in the example above), before the `in` operator: ```python= keyword = 'OOP' course = 'ACIT1515' if keyword not in course: print('OOP is not in the course') ``` or before a function that returns a boolean value: ```python= filename = 'index.css' if not filename.endswith('.html'): print('Not an html file') ``` ### Logical Operators As noted above, a boolean condition can be a comparison between two values: ```python if x > 10: ``` or a test: ```python if 'CIT' in 'BCIT': ``` Below is a list of ==comparison== operators that can be used to compare values: ``` > greater than >= greater than or equal to < less than <= less than or equal to == equal to != not equal to ``` and ==containment== operators, used to test if a value exists inside another value: ``` in is a value contained in another value not in is a value not contained in another value ``` As shown above, containment operators can be used to test whether a string (called a 'substring') appears *inside* another larger string. The substring can be a single character: ```python filename = '/test.txt' if '/' in filename: print('File is in the root directory') ``` or a longer string: ```python= haystack = 'This is the string to be searched' needle = 'the string' if needle in haystack: print('Found!') ``` ### A Practical Example Expanding on the initial example at the top of the page (using command-line I/O to calculate a person's age), let's add a conditional statement to: a) detect whether the value the user enters is numeric, and b) only perform the calculation if it *is* numeric Strings in Python have a method called `isnumeric()` that allow us to test whether a string is numeric or not. If the string we are testing is numeric, `isnumeric()` returns the boolean value `True`. If the string is not numeric, `isnumeric()` returns the boolean value `False`. Note that `isnumeric()` is a *method* of the string class, so it needs to be *attached to a string variable* using dot notation. ```python= birth_year = input('Please enter your birth year: ') current_year = 2024 if birth_year.numeric(): birth_year = int(birth_year) print(f'You are {current_year - birth_year} years old') print('Goodbye!') ``` In the example above, we have the `if` keyword. Our condition is `birth_year.isnumeric()`, followed by a colon. Below the colon are _two indented lines_, lines 5 and 6. These indented lines will only run **if the condition returns True**. If the condition returns False, lines 5 and 6 are skipped and we go straight to line 8. Recall that Python uses ==indentation== to indicate related ==blocks== of code. If lines 5 and 6 are not indented (or not indented to the same level), we will get a syntax error. This so-called "block" of code (one or more lines of code that either run together or are skipped together) is considered finished whenever the interpreter sees an unindented line - in this case, line 8. ### The `else` Keyword The previous example works well to detect if the user has entered a numeric string, but it has one glaring omission: if a user enters an incorrect string the program simply prints 'Goodbye' and ends - not a great user experience. If we need to test a value, but provide a fallback (i.e. capture all cases where the condition returns False), we can use the ==else== keyword. Below is an example of the code updated with ==else==: ```python= birth_year = input('Please enter your birth year: ') current_year = 2024 if birth_year.isnumeric(): birth_year = int(birth_year) print(f'You are {current_year - birth_year} years old') else: print("I'm sorry, you did not enter a number") print('Goodbye!') ``` This code can of course be improved, but at least we are now providing the user with some feedback so that they can successfully re-run the program. The ==else== block (note again the colon and indentation) allows us to catch the case where a user enters _any other value than a numeric string_. ### The `elif` Keyword Now let's look at an example where we have multiple conditions we want to test. Imagine we are testing whether a user can get to school every day. They can potentially a) drive, b) take the bus, or c) ride a bike. Each of these modes of transport can be individually tested using if and ==elif==, a contraction of "else if": ```python== hasCar = False hasBusPass = True hasBike = False if hasCar == True: print('You can drive to school') elif hasBusPass: print('You can take the bus to school') elif hasBike: print('You can bike to school') else: print('You cannot get to school') print('Goodbye!') ``` Recall that conditions can be one of two things: a *comparison* between two values that results in a boolean value, or a boolean value itself. `hasCar == True` is a _comparison_ that returns True or False. It is another way of saying "is the value inside the hasCar variable the same as the boolean value True?". We can rewrite this as just `hasCar` (in the same way that we have just `hasBusPass` and `hasBike` as conditions without comparisons). Without a comparison, the Python interpreter simply tests to see _if_ the value stored in a variable _is_ the value `True` or `False`. `hasCar == True` and `hasCar` are both valid conditions, but typically we use the latter (no comparison) because it is less redundant and shorter to type. ### Rules Of Conditional Statements An important thing to note is that when we have a group of conditional statements, as in the previous example, **only one 'block' can run**. To be more specific, the first condition that returns `True` is the block that is run, and all other blocks are skipped. If we re-read the code above, we can see that: a) `hasCar` is `False`, so the block is skipped and we next test `hasBusPass` b) `hasBusPass` is `True`, so its block is run and all other blocks are skipped. No other conditions are tested Our output then is: ``` You can take the bus to school Goodbye! ``` If _all_ conditions return `False`, then only the ==else== block (if present) is run. You may have noticed that there is a specific order to if, elif, and else blocks. Groups of conditional statements must: 1. Always start with a single, mandatory `if` 2. Followed by multiple optional `elif` blocks 3. Followed by a single optional `else` block This means that the following code is **invalid**: ```python= hasCar = False hasBusPass = True hasBike = False if hasCar == True: print('You can drive to school') else hasBusPass: print('You can take the bus to school') elif hasBike: print('You can bike to school') elif: print('You cannot get to school') print('Goodbye!') ``` `else` is always considered the last of a related group of conditional statements, so it cannot be followed by `elif` as in the example above. ### Combining multiple conditions into a single statement using `and`, `or`, and `not` If testing multiple conditions is necessary, it is often convenient to combine them into a single line rather than the if/elif/else style blocks written above. In order to combine these conditions into a single condition, we need to use the logical `and`, `or`, and `not` keywords. ```python= if hasCar or hasBusPass or hasBike: print('You can get to school') else: print('You cannot get to school') ``` Note that any condition combined using `or` has an equivalent representation using `and` and vice-versa. Here is the `and` version of the above condition: ```python= if not hasCar and not hasBusPass and not hasBike: print('You cannot get to school') else: print('You can get to school') ```