) is True\n",
"else:\n",
" the code within this indented block is executed only if \n",
" all preceding expressions were False\n",
"\n",
"```\n",
"\n",
"In practice this can look like:\n",
"\n",
"```python\n",
"x = [1, 2]\n",
"\n",
"if 3 < len(x):\n",
" # bool(3 < 2) returns False, this code \n",
" # block is skipped\n",
" print(\"`x` has more than three items in it\")\n",
"elif len(x) == 2:\n",
" # bool(len(x) == 2) returns True\n",
" # this code block is executed\n",
" print(\"`x` has two items in it\")\n",
"elif len(x) == 1:\n",
" # this statement is never reached\n",
" print(\"`x` has one items in it\")\n",
"else:\n",
" # this statement is never reached\n",
" print(\"`x` is an empty list\")\n",
"\n",
"\"`x` has two items in it\"\n",
"```\n",
"\n",
"In its simplest form, a conditional statement requires only an `if` clause. `else` and `elif` clauses can only follow an `if` clause.\n",
"\n",
"```python\n",
"# A conditional statement consisting of \n",
"# an \"if\"-clause, only.\n",
"\n",
"x = -1\n",
"\n",
"if x < 0:\n",
" x = x ** 2\n",
"# x is now 1\n",
"```\n",
"\n",
"Similarly, conditional statements can have an `if` and an `else` without an `elif`:\n",
"\n",
"```python\n",
"# A conditional statement consisting of\n",
"# an \"if\"-clause and an \"else\"\n",
"x = 4\n",
"\n",
"if x > 2:\n",
" x = -2\n",
"else:\n",
" x = x + 1\n",
"# x is now -2\n",
"```\n",
"\n",
"Conditional statements can also have an `if` and an `elif` without an `else`:\n",
"\n",
"```python\n",
"# A conditional statement consisting of\n",
"# an \"if\"-clause and an \"elif\"\n",
"x = 'abc'\n",
"\n",
"if len(x) < 9:\n",
" x = x * 3\n",
"elif len(x) > 40:\n",
" x = 'cba'\n",
"# x is now 'abcabcabc'\n",
"```\n",
"\n",
"Note that only one code block within a single if-elif-else statement can be executed: either the \"if-block\" is executed, or an \"elif-block\" is executed, or the \"else-block\" is executed. Consecutive if-statements, however, are completely independent of one another, and thus their code blocks can be executed in sequence, if their respective conditional statements resolve to `True`.\n",
"\n",
"```python\n",
"# consecutive if-statements are independent\n",
"x = 5\n",
"y = 0\n",
"\n",
"if x < 10:\n",
" y += 1 \n",
"\n",
"if x < 20:\n",
" y += 1\n",
" \n",
"# y is now 2\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"\n",
"**Reading Comprehension: Conditional statements**\n",
"\n",
"1\\. Assume `my_list` is a list. Given the following code:\n",
"```python\n",
"first_item = None\n",
"\n",
"if my_list:\n",
" first_item = my_list[0]\n",
"```\n",
"\n",
"What will happen if `my_list` is `[]`? Will `IndexError` be raised? What will `first_item` be?\n",
"\n",
"2\\. Assume variable `my_file` is a string storing a filename, where a period denotes the end of the filename and the beginning of the file-type. Write code that extracts only the filename.\n",
"\n",
"`my_file` will have at most one period in it. Accommodate cases where `my_file` does *not* include a file-type. \n",
"\n",
"That is: \n",
"\n",
"- `\"code.py\"` $\\rightarrow$ `\"code\"`\n",
"- `\"doc2.pdf\"` $\\rightarrow$ `\"doc2\"`\n",
"- `\"hello_world\"` $\\rightarrow$ `\"hello_world\"` \n",
"\n",
"
"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Inline if-else statements\n",
"Python supports a syntax for writing a restricted version of if-else statements in a single line. The following code:\n",
"\n",
"```python\n",
"num = 2\n",
"\n",
"if num >= 0:\n",
" sign = \"positive\"\n",
"else:\n",
" sign = \"negative\"\n",
"```\n",
"\n",
"can be written in a single line as:\n",
"\n",
"```python\n",
"sign = \"positive\" if num >=0 else \"negative\"\n",
"```\n",
"\n",
"This is suggestive of the general underlying syntax for inline if-else statements:\n",
"\n",
"\n",
"\n",
"**The inline if-else statement**: \n",
"\n",
"The expression `A if else B` returns `A` if `bool()` evaluates to `True`, otherwise this expression will return `B`.\n",
"
\n",
"\n",
"This syntax is highly restricted compared to the full \"if-elif-else\" expressions - no \"elif\" statement is permitted by this inline syntax, nor are multi-line code blocks within the if/else clauses.\n",
"\n",
"Inline if-else statements can be used anywhere, not just on the right side of an assignment statement, and can be quite convenient:\n",
"```python\n",
"# using inline if-else statements in different scenarios\n",
"\n",
">>> x = 2\n",
"\n",
"# will store 1 if `x` is non-negative\n",
"# will store 0 if `x` is negative\n",
">>> my_list = [1 if x >= 0 else 0]\n",
">>> my_list\n",
"[1]\n",
"\n",
">>> \"a\" if x == 1 else \"b\"\n",
"'b'\n",
"```\n",
"We will see this syntax shine when we learn about comprehension statements. That being said, this syntax should be used judiciously. For example, inline if-else statements ought not be used in arithmetic expressions, for therein lies madness:\n",
"\n",
"```python\n",
"# don't ever do this...ever!\n",
"2 - 3 if x < 1 else 1 + 6*2 if x >= 0 else 9\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Short-Circuiting Logical Expressions\n",
"Armed with our newfound understanding of conditional statements, we briefly return to our discussion of Python's logic expressions to discuss \"short-circuiting\". In Python, a logical expression is evaluated from left to right and will return its boolean value as soon as it is unambiguously determined, *leaving any remaining portions of the expression unevaluated*. That is, the expression may be *short-circuited*. \n",
"\n",
"For example, consider the fact that an `and` operation will only return `True` if both of its arguments evaluate to `True`. Thus the expression `False and ` is guaranteed to return `False`; furthermore, when executed, this expression will return `False` *without having evaluated* `bool()`.\n",
"\n",
"To demonstrate this behavior, consider the following example:\n",
"\n",
"```python\n",
"# demonstrating short-circuited logic expressions \n",
">>> False and 1/0 # evaluating `1/0` would raise an error \n",
"False\n",
"```\n",
"According to our discussion, the pattern `False and` short-circuits this expression without it ever evaluating `bool(1/0)`. Reversing the ordering of the arguments makes this clear.\n",
"\n",
"```python\n",
"# expressions are evaluated from left to right\n",
">>> 1/0 and False\n",
"---------------------------------------------------------------------------\n",
"ZeroDivisionError Traceback (most recent call last)\n",
" in ()\n",
"----> 1 1/0 and False\n",
"\n",
"ZeroDivisionError: division by zero\n",
"```\n",
"\n",
"In practice, short-circuiting can be leveraged in order to condense one's code. Suppose a section of our code is processing a variable `x`, which may be either a [number](https://www.pythonlikeyoumeanit.com/Module2_EssentialsOfPython/Basic_Objects.html#Number-Types) or a [string](https://www.pythonlikeyoumeanit.com/Module2_EssentialsOfPython/Basic_Objects.html#Strings). Suppose further that we want to process `x` in a special way if it is an all-uppercased string. The code\n",
"\n",
"```python\n",
"# this will raise an error if `x` is not a string\n",
"if x.isupper():\n",
" # do something with the uppercased string\n",
"```\n",
"is problematic because `isupper` can only be called once we are sure that `x` is a string; this code will raise an error if `x` is a number. We could instead write\n",
"\n",
"```python\n",
"# a valid but messy way to filter out non-string objects\n",
"if isinstance(x, str):\n",
" if x.isupper():\n",
" # do something with the uppercased string\n",
"```\n",
"\n",
"but the more elegant and concise way of handling the nestled checking is to leverage our ability to short-circuit logic expressions.\n",
"\n",
"```python\n",
"# utilizing short-circuiting to concisely perform all necessary checks\n",
"if isinstance(x, str) and x.isupper():\n",
" # do something with the uppercased string\n",
"```\n",
"\n",
"See, that if `x` is not a string, that `isinstance(x, str)` will return `False`; thus `isinstance(x, str) and x.isupper()` will short-circuit and return `False` without ever evaluating `bool(x.isupper())`. This is the preferable way to handle this sort of checking. This code is more concise and readable than the equivalent nested if-statements."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"\n",
"**Reading Comprehension: short-circuited expressions**\n",
"\n",
"Consider the preceding example of short-circuiting, where we want to catch the case where `x` is an uppercased string. What is the \"bug\" in the following code? Why does this fail to utilize short-circuiting correctly? \n",
"\n",
"```python\n",
"# what is wrong with me?\n",
"if x.isupper() and isinstance(x, str):\n",
" # do something with the uppercased string\n",
"```\n",
"\n",
"
"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Links to Official Documentation\n",
"\n",
"- [bool](https://docs.python.org/3/library/functions.html#bool)\n",
"- [Truth testing](https://docs.python.org/3/library/stdtypes.html#truth-value-testing)\n",
"- [Boolean operations](https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not)\n",
"- [Comparisons](https://docs.python.org/3/library/stdtypes.html#comparisons)\n",
"- ['if' statements](https://docs.python.org/3/reference/compound_stmts.html#the-if-statement)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Reading Comprehension Exercise Solutions:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Conditional statements**\n",
"\n",
"1\\. If `my_list` is `[]`, then `bool(my_list)` will return `False`, and the code block will be skipped. Thus `first_item` will be `None`.\n",
"\n",
"2\\. First, check to see if `.` is even contained in `my_file`. If it is, find its index-position, and slice the string up to that index. Otherwise, `my_file` is already the file name.\n",
"```python\n",
"\n",
"my_file = \"code.pdf\"\n",
"\n",
"if \".\" in my_file:\n",
" dot_index = my_file.index(\".\")\n",
" filename = my_file[:dot_index]\n",
"else:\n",
" filename = my_file\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Short-circuited expressions**\n",
"\n",
"The code\n",
"```python\n",
"# what is wrong with me?\n",
"if x.isupper() and isinstance(x, str):\n",
" # do something with the uppercased string\n",
"```\n",
"\n",
"fails to account for the fact that expressions are always evaluated from left to right. That is, `bool(x.isupper())` will always be evaluated first in this instance and will raise an error if `x` is not a string. Thus the following `isinstance(x, str)` statement is useless."
]
}
],
"metadata": {
"jupytext": {
"text_representation": {
"extension": ".md",
"format_name": "markdown",
"format_version": "1.3",
"jupytext_version": "1.13.6"
}
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}