intro-to-python/03_conditionals_00_lecture.ipynb

2518 lines
54 KiB
Text
Raw Normal View History

2019-09-24 19:45:56 +02:00
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Chapter 3: Conditionals & Exceptions"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"We analyzed every aspect of the `average_evens()` function in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions_00_lecture.ipynb) except for the `if`-related parts. While it does what we expect it to, there is a whole lot more to learn by taking it apart. In particular, the `if` may occur within both a **statement** or an **expression**, analogous as to how a noun in a natural language can be the subject of *or* an object in a sentence. What is common to both usages is that it leads to code being executed for *parts* of the input only. It is a way of controlling the **flow of execution** in a program.\n",
2019-09-24 19:45:56 +02:00
"\n",
2019-10-07 22:31:06 +02:00
"After deconstructing `if` in the first part of this chapter, we take a close look at a similar concept, namely handling **exceptions**."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Boolean Expressions"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Any expression that is either true or not is called a **boolean expression**. It is such simple true-or-false observations about the world on which mathematicians, and originally philosophers, base their rules of reasoning: They are studied formally in the field of [propositional logic](https://en.wikipedia.org/wiki/Propositional_calculus).\n",
2019-09-24 19:45:56 +02:00
"\n",
"A trivial example involves the equality operator `==` that evaluates to either `True` or `False` depending on its operands \"comparing equal\" or not."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"42 == 42"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"slideshow": {
"slide_type": "fragment"
2019-09-24 19:45:56 +02:00
}
},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"42 == 123"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"The `==` operator handles objects of *different* types: Because of that, it implements a notion of equality in line with how humans think of things being equal or not. After all, `42` and `42.0` are different $0$s and $1$s for a computer and other programming languages may say `False` here! Technically, this is yet another example of operator overloading."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"42 == 42.0"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"There are, however, cases where the `==` operator seems to not work intuitively. [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers_00_lecture.ipynb#Imprecision) provides more insights into this \"bug.\""
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"42 == 42.000000000000001"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## The `bool` Type"
]
},
2019-09-24 19:45:56 +02:00
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"`True` and `False` are built-in *objects* of type `bool`."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"slideshow": {
"slide_type": "slide"
2019-09-24 19:45:56 +02:00
}
},
"outputs": [
{
"data": {
"text/plain": [
"94426021241472"
2019-09-24 19:45:56 +02:00
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"id(True)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"94426021241440"
2019-09-24 19:45:56 +02:00
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"id(False)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"slideshow": {
"slide_type": "fragment"
2019-09-24 19:45:56 +02:00
}
},
"outputs": [
{
"data": {
"text/plain": [
"bool"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(True)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [
{
"data": {
"text/plain": [
"bool"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(False)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Let's not confuse the boolean `False` with `None`, another built-in object! We saw the latter before in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions_00_lecture.ipynb#Function-Definitions) as the *implicit* return value of a function without a `return` statement.\n",
2019-09-24 19:45:56 +02:00
"\n",
"We might think of `None` indicating a \"maybe\" or even an \"unknown\" answer; however, for Python, there are no \"maybe\" or \"unknown\" objects, as we see further below!\n",
2019-09-24 19:45:56 +02:00
"\n",
"Whereas `False` is of type `bool`, `None` is of type `NoneType`. So, they are unrelated! On the contrary, as both `True` and `False` are of the same type, we could call them \"siblings.\""
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"slideshow": {
"slide_type": "slide"
2019-09-24 19:45:56 +02:00
}
},
"outputs": [],
"source": [
"None"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"slideshow": {
"slide_type": "fragment"
2019-09-24 19:45:56 +02:00
}
},
"outputs": [
{
"data": {
"text/plain": [
"94426021228432"
2019-09-24 19:45:56 +02:00
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"id(None)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [
{
"data": {
"text/plain": [
"NoneType"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(None)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Singletons"
]
},
2019-09-24 19:45:56 +02:00
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"`True`, `False`, and `None` have the property that they each exist in memory only *once*. Objects designed this way are so-called **singletons**. This **[design pattern](https://en.wikipedia.org/wiki/Design_Patterns)** was originally developed to keep a program's memory usage at a minimum. It may only be employed in situations where we know that an object does *not* mutate its value (i.e., to reuse the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_lecture.ipynb#Objects-vs.-Types-vs.-Values), no flipping of $0$s and $1$s in the bag is allowed). In languages \"closer\" to the memory like C, we would have to code this singleton logic ourselves, but Python has this built in for *some* types.\n",
2019-09-24 19:45:56 +02:00
"\n",
2019-10-07 22:31:06 +02:00
"We verify this with either the `is` operator or by comparing memory addresses."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"slideshow": {
"slide_type": "slide"
2019-09-24 19:45:56 +02:00
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a = True\n",
"b = True\n",
"\n",
"a is b"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"To contrast this, we create *two* `789` objects."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"slideshow": {
"slide_type": "fragment"
2019-09-24 19:45:56 +02:00
}
},
"outputs": [
{
"data": {
"text/plain": [
"False"
2019-09-24 19:45:56 +02:00
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a = 789\n",
"b = 789\n",
"\n",
"a is b"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
2019-11-20 11:00:24 +01:00
"So the following expression regards *four* objects in memory: *One* `list` object holding ten references to *three* other objects."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"slideshow": {
"slide_type": "slide"
2019-09-24 19:45:56 +02:00
}
},
"outputs": [
{
"data": {
"text/plain": [
"[True, False, None, None, None, True, False, None, None, None]"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"[True, False, None, None, None, True, False, None, None, None]"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Relational Operators"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"The equality operator is only one of several **relational** (i.e., \"comparison\") **operators** who all evaluate to a `bool` object."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"42 == 123"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"42 != 123 # \"not equal to\"; other languages may use \"<>\""
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"The \"less than\" `<` or \"greater than\" `>` operators mean \"*strictly* less than\" or \"*strictly* greater than\" but may be combined with the equality operator into just `<=` and `>=`. This is a shortcut for using the logical `or` operator as described in the next section."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"42 < 123"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"42 <= 123"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"42 > 123"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"42 >= 123"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Logical Operators"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Boolean expressions may be combined or negated with the **logical operators** `and`, `or`, and `not` to form new boolean expressions. This may be done repeatedly to obtain boolean expressions of arbitrary complexity.\n",
2019-09-24 19:45:56 +02:00
"\n",
"Their usage is similar to how the equivalent words are used in everyday English:\n",
2019-09-24 19:45:56 +02:00
"\n",
"- `and` evaluates to `True` if *both* operands evaluate to `True` and `False` otherwise,\n",
"- `or` evaluates to `True` if either one *or* both operands evaluate to `True` and `False` otherwise, and\n",
"- `not` evaluates to `True` if its *only* operand evaluates to `False` and vice versa."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"a = 42\n",
"b = 87"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Relational operators have **[higher precedence](https://docs.python.org/3/reference/expressions.html#operator-precedence)** over logical operators. So the following expression means what we intuitively think it does."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a > 5 and b <= 100"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"However, sometimes, it is good to use *parentheses* around each operand for clarity."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"(a > 5) and (b <= 100)"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"This is especially so when several logical operators are combined."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"a <= 5 or not b > 100"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"slideshow": {
"slide_type": "fragment"
2019-09-24 19:45:56 +02:00
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"(a <= 5) or not (b > 100)"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"(a <= 5) or (not (b > 100)) # no need to \"over do\" it"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"For even better readability, some practitioners suggest to *never* use the `>` and `>=` operators (cf., [source](https://llewellynfalco.blogspot.com/2016/02/dont-use-greater-than-sign-in.html); note that the included example is written in [Java](https://en.wikipedia.org/wiki/Java_%28programming_language%29) where `&&` means `and` and `||` means `or`).\n",
2019-09-24 19:45:56 +02:00
"\n",
"We may **chain** operators if the expressions that contain them are combined with the `and` operator. For example, the following two cells implement the same logic, where the second is a lot easier to read."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
2019-09-24 19:45:56 +02:00
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"5 < a and a < 87"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"slideshow": {
"slide_type": "fragment"
2019-09-24 19:45:56 +02:00
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
2019-09-24 19:45:56 +02:00
]
},
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"5 < a < 87"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Truthy vs. Falsy"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"The operands of a logical operator do not need to be *boolean* expressions but may be *any* expression. If an operand does *not* evaluate to an object of type `bool`, Python automatically casts it as such. Then, Pythonistas say that the expression is evaluated in a boolean context.\n",
2019-09-24 19:45:56 +02:00
"\n",
"For example, any non-zero numeric object is cast as `True`. While this behavior allows writing more concise and thus more \"beautiful\" code, it may also be a source of confusion.\n",
2019-10-30 11:04:59 +01:00
"\n",
"So, `(a - 21)` is cast as `True` and then the overall expression evaluates to `True` as well."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"(a - 21) and (b < 100) # => 21 and (b < 100) => 21 and True"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Whenever we are unsure how Python evaluates a non-boolean expression in a boolean context, the [bool()](https://docs.python.org/3/library/functions.html#bool) built-in allows us to do it ourselves. [bool()](https://docs.python.org/3/library/functions.html#bool), like [int()](https://docs.python.org/3/library/functions.html#int), is yet another *constructor*."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bool(a - 21) # = bool(21)"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {
"slideshow": {
"slide_type": "fragment"
2019-09-24 19:45:56 +02:00
}
},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 31,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bool(a - 42) # = bool(0)"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Let's keep in mind that negative numbers also evaluate to `True`!"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {
"slideshow": {
"slide_type": "fragment"
2019-09-24 19:45:56 +02:00
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bool(a - 43) # = bool(-1)"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"In a boolean context, `None` is cast as `False`! So, `None` is *not* a \"maybe\" answer but a \"no.\""
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {
"slideshow": {
"slide_type": "slide"
2019-09-24 19:45:56 +02:00
}
},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 33,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bool(None)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Another good rule to know is that container types (e.g., `list`) evaluate to `False` whenever they are empty and `True` if they hold at least one element."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 34,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bool([])"
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {
"slideshow": {
"slide_type": "fragment"
2019-09-24 19:45:56 +02:00
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 35,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bool([False])"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"With `str` objects, the empty `\"\"` evaluates to `False`, and any other to `True`."
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 36,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bool(\"\")"
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 37,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bool(\"Lorem ipsum dolor sit amet, ...\")"
]
},
2019-09-24 19:45:56 +02:00
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
2019-10-30 11:04:59 +01:00
"Pythonistas use the terms **truthy** or **falsy** to describe a non-boolean expression's behavior when evaluated in a boolean context."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Short-Circuiting"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"When evaluating expressions involving the `and` and `or` operators, Python follows the **[short-circuiting](https://en.wikipedia.org/wiki/Short-circuit_evaluation)** strategy: Once it is clear what the overall truth value is, no more operands are evaluated, and the result is *immediately* returned.\n",
2019-10-30 11:04:59 +01:00
"\n",
"Also, if such expressions are evaluated in a non-boolean context, the result is returned as is and *not* cast as a `bool` type.\n",
"\n",
2019-10-30 11:04:59 +01:00
"The two rules can be summarized as:\n",
"\n",
"- `a or b`: If `a` is truthy, it is returned *without* evaluating `b`. Otherwise, `b` is evaluated *and* returned.\n",
"- `a and b`: If `a` is falsy, it is returned *without* evaluating `b`. Otherwise, `b` is evaluated *and* returned.\n",
"\n",
2019-10-30 11:04:59 +01:00
"The rules may also be chained or combined.\n",
"\n",
"Let's look at a couple of examples below. To visualize which operands are evaluated, we define a helper function `expr()` that prints out the only argument it is passed before returning it."
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"def expr(arg):\n",
" \"\"\"Print and return the only argument.\"\"\"\n",
" print(\"Arg:\", arg)\n",
" return arg"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"With the `or` operator, the first truthy operand is returned."
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Arg: 0\n",
"Arg: 1\n"
]
},
{
"data": {
"text/plain": [
"1"
]
},
"execution_count": 39,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
2019-10-30 11:04:59 +01:00
"expr(0) or expr(1)"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Arg: 1\n"
]
},
{
"data": {
"text/plain": [
"1"
]
},
"execution_count": 40,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"expr(1) or expr(2) # 2 is not evaluated"
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Arg: 0\n",
"Arg: 1\n"
]
},
{
"data": {
"text/plain": [
"1"
]
},
"execution_count": 41,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"expr(0) or expr(1) or expr(2) # 2 is not evaluated"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"If all operands are falsy, the last one is returned."
]
},
{
"cell_type": "code",
"execution_count": 42,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Arg: False\n",
"Arg: []\n",
"Arg: 0\n"
]
},
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 42,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
2019-10-30 11:04:59 +01:00
"expr(False) or expr([]) or expr(0)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"With the `and` operator, the first falsy operand is returned."
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Arg: 0\n"
]
},
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 43,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"expr(0) and expr(1) # 1 is not evaluated"
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Arg: 1\n",
"Arg: 0\n"
]
},
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
2019-10-30 11:04:59 +01:00
"expr(1) and expr(0)"
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Arg: 1\n",
"Arg: 0\n"
]
},
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 45,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"expr(1) and expr(0) and expr(2) # 2 is not evaluated"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"If all operands are truthy, the last one is returned."
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Arg: 1\n",
"Arg: 2\n"
]
},
{
"data": {
"text/plain": [
"2"
]
},
"execution_count": 46,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
2019-10-30 11:04:59 +01:00
"expr(1) and expr(2)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"The crucial takeaway is that Python does *not* necessarily evaluate *all* operands and, therefore, our code should never rely on that assumption."
]
},
2019-09-24 19:45:56 +02:00
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
2019-10-07 22:31:06 +02:00
"## The `if` Statement"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"To write useful programs, we need to control the flow of execution, for example, to react to user input. The logic by which a program follows the rules from the \"real world\" is referred to as **[business logic](https://en.wikipedia.org/wiki/Business_logic)**.\n",
2019-09-24 19:45:56 +02:00
"\n",
2019-11-06 11:10:29 +01:00
"One language feature to do so is the `if` statement (cf., [reference](https://docs.python.org/3/reference/compound_stmts.html#the-if-statement)). It consists of:\n",
2019-09-24 19:45:56 +02:00
"\n",
"- *one* mandatory `if`-clause,\n",
2019-11-06 11:10:29 +01:00
"- an *arbitrary* number of `elif`-clauses (i.e., \"else if\"), and\n",
2019-09-24 19:45:56 +02:00
"- an *optional* `else`-clause.\n",
"\n",
"The `if`- and `elif`-clauses each specify one *boolean* expression, also called **condition** here, while the `else`-clause serves as the \"catch everything else\" case.\n",
2019-09-24 19:45:56 +02:00
"\n",
2019-11-06 11:10:29 +01:00
"In contrast to our intuitive interpretation in natural languages, only the code in *one* of the alternatives, also called **branches**, is executed. To be precise, it is always the code in the first clause whose condition evaluates to `True`.\n",
2019-09-24 19:45:56 +02:00
"\n",
"In terms of syntax, the header lines end with a colon, and the code blocks are indented. Formally, any statement that is written across several lines is called a **[compound statement](https://docs.python.org/3/reference/compound_stmts.html#compound-statements)**, the code blocks are called **suites** and belong to one header line, and the term **clause** refers to a header line and its suite as a whole. So far, we have seen three compound statements: `for`, `if`, and `def`. On the contrary, **[simple statements](https://docs.python.org/3/reference/simple_stmts.html#simple-statements)**, for example, `=`, `del`, or `return`, are written on *one* line.\n",
"\n",
"As an example, let's write code that checks if a randomly drawn number is divisible by `2`, `3`, both, or none. The code should print out a customized message for each of the *four* cases."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 47,
2019-09-24 19:45:56 +02:00
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"import random"
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [],
"source": [
"random.seed(789)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"It turns out that translating this task into code is not so trivial. Whereas the code below looks right, it is *incorrect*. The reason is that the order of the `if`-, `elif`-, and `else`-clauses matters."
]
},
{
"cell_type": "code",
"execution_count": 50,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"6 is divisible by 2 only\n"
]
}
],
"source": [
"number = random.choice(numbers)\n",
"\n",
"if number % 2 == 0:\n",
" print(number, \"is divisible by 2 only\")\n",
"elif number % 3 == 0:\n",
" print(number, \"is divisible by 3 only\")\n",
"elif number % 2 == 0 and number % 3 == 0:\n",
" print(number, \"is divisible by 2 and 3\")\n",
"else:\n",
" print(number, \"is divisible by neither 2 nor 3\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"As a number divisible by both `2` and `3` is always a special (i.e., narrower) case of a number being divisible by either `2` or `3` on their own, we must check for that condition first. The order of the two latter cases is not important as they are mutually exclusive. Below is a correct implementation of the program."
]
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"random.seed(789)"
]
},
{
"cell_type": "code",
"execution_count": 52,
2019-09-24 19:45:56 +02:00
"metadata": {
"code_folding": [],
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"6 is divisible by 2 and 3\n"
]
}
],
"source": [
"number = random.choice(numbers)\n",
"\n",
"if number % 3 == 0 and number % 2 == 0:\n",
" print(number, \"is divisible by 2 and 3\")\n",
"elif number % 3 == 0:\n",
" print(number, \"is divisible by 3 only\")\n",
"elif number % 2 == 0:\n",
" print(number, \"is divisible by 2 only\")\n",
"else:\n",
" print(number, \"is divisible by neither 2 nor 3\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"A minor improvement could be to replace `number % 3 == 0 and number % 2 == 0` with the conciser `number % 6 == 0`. However, this has no effect on the order that is still essential for the code's correctness."
]
},
{
"cell_type": "code",
"execution_count": 53,
"metadata": {
2019-09-24 19:45:56 +02:00
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"random.seed(789)"
]
},
{
"cell_type": "code",
"execution_count": 54,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
2019-09-24 19:45:56 +02:00
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"6 is divisible by 2 and 3\n"
2019-09-24 19:45:56 +02:00
]
}
],
"source": [
"number = random.choice(numbers)\n",
"\n",
"if number % 6 == 0:\n",
" print(number, \"is divisible by 2 and 3\")\n",
"elif number % 3 == 0:\n",
" print(number, \"is divisible by 3 only\")\n",
"elif number % 2 == 0:\n",
" print(number, \"is divisible by 2 only\")\n",
2019-09-24 19:45:56 +02:00
"else:\n",
" print(number, \"is divisible by neither 2 nor 3\")"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Often, we only need a reduced form of the `if` statement.\n",
2019-09-24 19:45:56 +02:00
"\n",
"For example, below we **inject** code to print a message at random."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 55,
2019-09-24 19:45:56 +02:00
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"You read this as often as you see heads when tossing a coin\n"
]
}
],
2019-09-24 19:45:56 +02:00
"source": [
"if random.random() > 0.5:\n",
" print(\"You read this as often as you see heads when tossing a coin\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"More often than not, we model a **binary choice**. Then, we only need to write an `if`- and an `else`-clause."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 56,
2019-09-24 19:45:56 +02:00
"metadata": {
"slideshow": {
"slide_type": "skip"
2019-09-24 19:45:56 +02:00
}
},
2019-11-06 11:10:29 +01:00
"outputs": [],
2019-09-24 19:45:56 +02:00
"source": [
"random.seed(789)"
]
},
{
"cell_type": "code",
"execution_count": 57,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"6 is even\n"
]
}
],
"source": [
"number = random.choice(numbers)\n",
"\n",
"if number % 2 == 0:\n",
" print(number, \"is even\")\n",
"else:\n",
" print(number, \"is odd\")"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"To write the condition even conciser, we may take advantage of Python's implicit casting and leave out the `== 0`. However, then we *must* exchange the two suits! The `if`-clause below means \"If the `number` is odd\" in plain English. That is the opposite of the `if`-clause above."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 58,
2019-09-24 19:45:56 +02:00
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"random.seed(789)"
]
},
{
"cell_type": "code",
"execution_count": 59,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
2019-09-24 19:45:56 +02:00
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"6 is even\n"
2019-09-24 19:45:56 +02:00
]
}
],
"source": [
"number = random.choice(numbers)\n",
"\n",
"if number % 2: # Beware of the opposite meaning!\n",
" print(number, \"is odd\")\n",
2019-09-24 19:45:56 +02:00
"else:\n",
" print(number, \"is even\")"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
2019-10-07 22:31:06 +02:00
"We may **nest** `if` statements to control the flow of execution in a more granular way. Every additional layer, however, makes the code *less* readable, in particular, if we have more than one line per code block.\n",
"\n",
"As another example, the code cell below first tosses a coin and then *either* checks if `signed_number` is even or odd *or* if it is positive or negative."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"random.seed(789)"
]
},
{
"cell_type": "code",
"execution_count": 61,
2019-09-24 19:45:56 +02:00
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"4 is even\n"
2019-09-24 19:45:56 +02:00
]
}
],
"source": [
"signed_number = random.choice([-1, +1]) * random.choice(numbers)\n",
"\n",
2019-09-24 19:45:56 +02:00
"if random.random() > 0.5:\n",
" if signed_number % 2:\n",
" print(signed_number, \"is odd\")\n",
2019-09-24 19:45:56 +02:00
" else:\n",
" print(signed_number, \"is even\")\n",
2019-09-24 19:45:56 +02:00
"else:\n",
" if signed_number > 0:\n",
" print(signed_number, \"is positive\")\n",
2019-09-24 19:45:56 +02:00
" else:\n",
" print(signed_number, \"is negative\")"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"A way to make this code more readable is to introduce **temporary variables** *in combination* with the `and` operator to **flatten** the branching logic. The `if` statement then reads almost like plain English. In contrast to many other languages, creating variables is a computationally *cheap* operation in Python and also helps to document the code *inline* with meaningful variable names.\n",
2019-10-07 22:31:06 +02:00
"\n",
"Flattening the logic *without* temporary variables could lead to *more* sub-expressions in the conditions be evaluated than necessary. Do you see why?"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"random.seed(789)"
]
},
{
"cell_type": "code",
"execution_count": 63,
2019-09-24 19:45:56 +02:00
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"4 is even\n"
2019-09-24 19:45:56 +02:00
]
}
],
"source": [
"signed_number = random.choice([-1, +1]) * random.choice(numbers)\n",
"\n",
"check_oddness = random.random() > 0.5\n",
"is_odd = signed_number % 2\n",
"is_positive = signed_number > 0\n",
2019-09-24 19:45:56 +02:00
"\n",
"if check_oddness and is_odd:\n",
" print(signed_number, \"is odd\")\n",
2019-09-24 19:45:56 +02:00
"elif check_oddness and not is_odd:\n",
" print(signed_number, \"is even\")\n",
2019-09-24 19:45:56 +02:00
"elif not check_oddness and is_positive:\n",
" print(signed_number, \"is positive\")\n",
2019-09-24 19:45:56 +02:00
"else:\n",
" print(signed_number, \"is negative\")"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
2019-10-07 22:31:06 +02:00
"## The `if` Expression"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
2019-11-06 11:10:29 +01:00
"When an `if` statement assigns an object to a variable according to a true-or-false condition (i.e., a binary choice), there is a shortcut: We assign the variable the result of a so-called **[conditional expression](https://docs.python.org/3/reference/expressions.html#conditional-expressions)**, or `if` expression for short, instead.\n",
2019-09-24 19:45:56 +02:00
"\n",
"Think of a situation where we evaluate a piece-wise functional relationship $y = f(x)$ at a given $x$, for example:"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"$\n",
"y = f(x) =\n",
"\\begin{cases}\n",
"0, \\text{ if } x \\le 0 \\\\\n",
"x^2, \\text{ otherwise}\n",
"\\end{cases}\n",
"$"
]
},
{
"cell_type": "code",
"execution_count": 64,
2019-09-24 19:45:56 +02:00
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"x = 3"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Of course, we could use an `if` statement as above to do the job. Yet, this is rather lengthy."
]
},
{
"cell_type": "code",
"execution_count": 65,
2019-09-24 19:45:56 +02:00
"metadata": {
"slideshow": {
"slide_type": "fragment"
2019-09-24 19:45:56 +02:00
}
},
"outputs": [],
"source": [
"if x <= 0:\n",
" y = 0\n",
"else:\n",
" y = x ** 2"
]
},
{
"cell_type": "code",
"execution_count": 66,
2019-09-24 19:45:56 +02:00
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"outputs": [
{
"data": {
"text/plain": [
"9"
]
},
"execution_count": 66,
2019-09-24 19:45:56 +02:00
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"y"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
2019-11-06 11:10:29 +01:00
"On the contrary, the `if` expression fits into one line. The main downside is a potential loss in readability, in particular, if the functional relationship is not that simple. Also, some practitioners do *not* like that the condition is in the middle of the expression."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 67,
2019-09-24 19:45:56 +02:00
"metadata": {
"slideshow": {
"slide_type": "slide"
2019-09-24 19:45:56 +02:00
}
},
"outputs": [],
"source": [
"y = 0 if x <= 0 else x ** 2"
]
},
{
"cell_type": "code",
"execution_count": 68,
2019-09-24 19:45:56 +02:00
"metadata": {
"slideshow": {
"slide_type": "-"
2019-09-24 19:45:56 +02:00
}
},
"outputs": [
{
"data": {
"text/plain": [
"9"
]
},
"execution_count": 68,
2019-09-24 19:45:56 +02:00
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"y"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
2019-11-06 11:10:29 +01:00
"In this example, however, the most elegant solution is to use the built-in [max()](https://docs.python.org/3/library/functions.html#max) function."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 69,
2019-09-24 19:45:56 +02:00
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"y = max(0, x) ** 2"
]
},
{
"cell_type": "code",
"execution_count": 70,
2019-09-24 19:45:56 +02:00
"metadata": {
"slideshow": {
"slide_type": "-"
2019-09-24 19:45:56 +02:00
}
},
"outputs": [
{
"data": {
"text/plain": [
"9"
]
},
"execution_count": 70,
2019-09-24 19:45:56 +02:00
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"y"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
2019-10-07 22:31:06 +02:00
"## The `try` Statement"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"In the previous two chapters, we encountered a couple of *runtime* errors. A natural urge we might have after reading about conditional statements is to write code that somehow reacts to the occurrence of such exceptions. All we need is a way to formulate a condition for that.\n",
2019-09-24 19:45:56 +02:00
"\n",
2019-11-06 11:10:29 +01:00
"For sure, this is such a common thing to do that Python provides a language construct for it, namely the compound `try` statement (cf., [reference](https://docs.python.org/3/reference/compound_stmts.html#the-try-statement)).\n",
2019-09-24 19:45:56 +02:00
"\n",
2019-11-06 11:10:29 +01:00
"In its simplest form, it comes with just two clauses: `try` and `except`. The following tells Python to execute the code in the `try`-clause, and if *anything* goes wrong, continue in the `except`-clause instead of **raising** an error to us. Of course, if nothing goes wrong, the `except`-clause is *not* executed."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 71,
2019-09-24 19:45:56 +02:00
"metadata": {
"slideshow": {
"slide_type": "skip"
2019-09-24 19:45:56 +02:00
}
},
"outputs": [],
"source": [
"random.seed(123)"
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 72,
2019-09-24 19:45:56 +02:00
"metadata": {
"slideshow": {
"slide_type": "slide"
2019-09-24 19:45:56 +02:00
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Something went wrong\n"
]
}
],
"source": [
"user_input = random.choice([0, 1])\n",
"\n",
2019-09-24 19:45:56 +02:00
"try:\n",
" 1 / user_input\n",
"except:\n",
" print(\"Something went wrong\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"However, it is good practice *not* to **handle** *any* possible exception but only the ones we may *expect* from the code in the `try`-clause. The reason for that is that we do not want to risk *suppressing* an exception that we do *not* expect. Also, the code base becomes easier to understand as we communicate what could go wrong during execution in an *explicit* way to the (human) reader. Python comes with a lot of [built-in exceptions](https://docs.python.org/3/library/exceptions.html#concrete-exceptions) that we should familiarize ourselves with.\n",
2019-09-24 19:45:56 +02:00
"\n",
2019-11-06 11:10:29 +01:00
"Another good practice is to always keep the code in the `try`-clause short to not *accidentally* handle an exception we do *not* want to handle.\n",
2019-09-24 19:45:56 +02:00
"\n",
"In the example, we are dividing numbers and may expect a `ZeroDivisionError`."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 73,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"random.seed(123)"
]
},
{
"cell_type": "code",
"execution_count": 74,
2019-09-24 19:45:56 +02:00
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Something went wrong\n"
]
}
],
"source": [
"user_input = random.choice([0, 1])\n",
"\n",
2019-09-24 19:45:56 +02:00
"try:\n",
" 1 / user_input\n",
"except ZeroDivisionError:\n",
" print(\"Something went wrong\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
2019-11-06 11:10:29 +01:00
"Often, we may have to run some code *independent* of an exception occurring, for example, to close a connection to a database. To achieve that, we add a `finally`-clause to the `try` statement.\n",
2019-09-24 19:45:56 +02:00
"\n",
2019-11-06 11:10:29 +01:00
"Similarly, we may have to run some code *only if* no exception occurs, but we do not want to put it in the `try`-clause as per the good practice mentioned above. To achieve that, we add an `else`-clause to the `try` statement.\n",
2019-09-24 19:45:56 +02:00
"\n",
"To showcase everything together, we look at one last example. It is randomized: So, run the cell several times and see for yourself."
2019-09-24 19:45:56 +02:00
]
},
{
"cell_type": "code",
"execution_count": 75,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"random.seed(123)"
]
},
{
"cell_type": "code",
"execution_count": 76,
2019-09-24 19:45:56 +02:00
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Oops. Division by 0. How does that work?\n",
2019-09-24 19:45:56 +02:00
"I am always printed\n"
]
}
],
"source": [
"user_input = random.choice([0, 1])\n",
2019-09-24 19:45:56 +02:00
"\n",
"try:\n",
" 1 / user_input\n",
2019-09-24 19:45:56 +02:00
"except ZeroDivisionError:\n",
" print(\"Oops. Division by 0. How does that work?\")\n",
"else:\n",
" print(\"Yes, division worked smoothly.\")\n",
"finally:\n",
" print(\"I am always printed\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"## TL;DR"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
2019-10-01 17:47:45 +02:00
"- **boolean expressions** evaluate to either `True` or `False`\n",
2019-09-24 19:45:56 +02:00
"- **relational operators** compare operands according to \"human\" interpretations\n",
"- **logical operators** combine boolean sub-expressions to more \"complex\" expressions\n",
"- the **conditional statement** allows **controlling** the **flow of execution** depending on some **conditions**\n",
2019-09-24 19:45:56 +02:00
"- a **conditional expression** is a short form of a conditional statement\n",
"- **exception handling** is also a common way of **controlling** the **flow of execution**, in particular, if we have to be prepared for bad input data"
2019-09-24 19:45:56 +02:00
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.4"
2019-09-24 19:45:56 +02:00
},
"livereveal": {
"auto_select": "code",
"auto_select_fragment": true,
"scroll": true,
"theme": "serif"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": false,
"sideBar": true,
"skip_h1_title": true,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {
"height": "calc(100% - 180px)",
"left": "10px",
"top": "150px",
"width": "384px"
},
"toc_section_display": false,
"toc_window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 4
2019-09-24 19:45:56 +02:00
}