From 51670fcdd407e7b7c51cb0737e12d5faf5276c74 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Sun, 9 Feb 2020 22:54:28 +0100 Subject: [PATCH 1/3] Streamline text --- 01_elements_00_lecture.ipynb | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/01_elements_00_lecture.ipynb b/01_elements_00_lecture.ipynb index ba54aa3..ba6e476 100644 --- a/01_elements_00_lecture.ipynb +++ b/01_elements_00_lecture.ipynb @@ -3237,7 +3237,11 @@ { "cell_type": "code", "execution_count": 96, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -3256,7 +3260,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "For sure, we need to include operators to achieve something useful." ] @@ -3266,7 +3274,7 @@ "execution_count": 97, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -3301,7 +3309,7 @@ "execution_count": 98, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -3336,7 +3344,7 @@ "execution_count": 99, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -3371,7 +3379,7 @@ "execution_count": 100, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ From 690b96d44d7e65bd02a08af5d8f3b07c65674669 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Sun, 9 Feb 2020 23:02:23 +0100 Subject: [PATCH 2/3] Refurbish chapter 03 - streamline text - streamline examples for if statement --- 03_conditionals_00_lecture.ipynb | 655 +++++++++++++++++++++++-------- 1 file changed, 481 insertions(+), 174 deletions(-) diff --git a/03_conditionals_00_lecture.ipynb b/03_conditionals_00_lecture.ipynb index a6902df..7b769c4 100644 --- a/03_conditionals_00_lecture.ipynb +++ b/03_conditionals_00_lecture.ipynb @@ -19,7 +19,7 @@ } }, "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 seems to do what we expect it to, there is a whole lot more we learn from 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 is *either* 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 our first way of **controlling** the **flow of execution** in a program.\n", + "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", "\n", "After deconstructing `if` in the first part of this chapter, we take a close look at a similar concept, namely handling **exceptions**." ] @@ -43,7 +43,7 @@ } }, "source": [ - "Any expression that is either true or not is called a **boolean expression**. It is such simple true-or-false \"statements\" 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", + "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", "\n", "A trivial example involves the equality operator `==` that evaluates to either `True` or `False` depending on its operands \"comparing equal\" or not." ] @@ -77,7 +77,7 @@ "execution_count": 2, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -104,7 +104,7 @@ } }, "source": [ - "The `==` operator handles objects of *different* types: 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 might say `False` here! Technically, this is yet another example of operator overloading." + "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." ] }, { @@ -139,7 +139,7 @@ } }, "source": [ - "There are, however, cases where even well-behaved Python does not make us happy. [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.\"" + "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.\"" ] }, { @@ -166,6 +166,17 @@ "42 == 42.000000000000001" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The `bool` Type" + ] + }, { "cell_type": "markdown", "metadata": { @@ -182,7 +193,7 @@ "execution_count": 5, "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "slide" } }, "outputs": [ @@ -230,7 +241,7 @@ "execution_count": 7, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "fragment" } }, "outputs": [ @@ -281,11 +292,11 @@ } }, "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-Definition) as the *implicit* return value of a function without a `return` statement.\n", + "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", "\n", - "We might think of `None` in a boolean context indicating a \"maybe\" or even an \"unknown\" answer; however, for Python, there are no \"maybe\" or \"unknown\" objects, as we see further below!\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", "\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.\"" + "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.\"" ] }, { @@ -293,7 +304,7 @@ "execution_count": 9, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "slide" } }, "outputs": [], @@ -306,7 +317,7 @@ "execution_count": 10, "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "fragment" } }, "outputs": [ @@ -349,6 +360,17 @@ "type(None)" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Singletons" + ] + }, { "cell_type": "markdown", "metadata": { @@ -367,7 +389,7 @@ "execution_count": 12, "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "slide" } }, "outputs": [ @@ -383,7 +405,21 @@ } ], "source": [ - "True is True" + "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." ] }, { @@ -391,14 +427,14 @@ "execution_count": 13, "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "True" + "False" ] }, "execution_count": 13, @@ -407,7 +443,10 @@ } ], "source": [ - "id(True) == id(True)" + "a = 789\n", + "b = 789\n", + "\n", + "a is b" ] }, { @@ -426,7 +465,7 @@ "execution_count": 14, "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "slide" } }, "outputs": [ @@ -464,7 +503,7 @@ } }, "source": [ - "The equality operator is only one of several **relational (i.e., \"comparison\") operators** who all evaluate to a boolean object." + "The equality operator is only one of several **relational** (i.e., \"comparison\") **operators** who all evaluate to a `bool` object." ] }, { @@ -512,7 +551,7 @@ } ], "source": [ - "42 != 123 # = \"not equal to\"; other languages may use \"<>\"" + "42 != 123 # \"not equal to\"; other languages may use \"<>\"" ] }, { @@ -523,7 +562,7 @@ } }, "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." + "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." ] }, { @@ -571,7 +610,7 @@ } ], "source": [ - "42 <= 123 # same as 42 < 123 or 42 == 123; cf., next section" + "42 <= 123" ] }, { @@ -619,7 +658,7 @@ } ], "source": [ - "42 >= 123 # same as 42 > 123 or 42 == 123; cf., next section" + "42 >= 123" ] }, { @@ -641,13 +680,13 @@ } }, "source": [ - "Boolean expressions may be combined or negated with the **logical operators** `and`, `or`, and `not` to form new boolean expressions. Of course, this may be done *recursively* as well to obtain boolean expressions of arbitrary complexity.\n", + "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", "\n", "Their usage is similar to how the equivalent words are used in everyday English:\n", "\n", - "- `and` evaluates to `True` if *both* sub-expressions evaluate to `True` and `False` otherwise,\n", - "- `or` evaluates to `True` if either one *or* both sub-expressions evaluate to `True` and `False` otherwise, and\n", - "- `not` evaluates to `True` if its *only* sub-expression evaluates to `False` and vice versa." + "- `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." ] }, { @@ -660,8 +699,8 @@ }, "outputs": [], "source": [ - "x = 42\n", - "y = 87" + "a = 42\n", + "b = 87" ] }, { @@ -696,7 +735,7 @@ } ], "source": [ - "x > 5 and y <= 100" + "a > 5 and b <= 100" ] }, { @@ -707,7 +746,7 @@ } }, "source": [ - "However, sometimes, it is good to use *parentheses* around each sub-expression for clarity." + "However, sometimes, it is good to use *parentheses* around each operand for clarity." ] }, { @@ -731,7 +770,7 @@ } ], "source": [ - "(x > 5) and (y <= 100)" + "(a > 5) and (b <= 100)" ] }, { @@ -742,7 +781,7 @@ } }, "source": [ - "This is especially the case when several logical operators are combined." + "This is especially so when several logical operators are combined." ] }, { @@ -766,7 +805,7 @@ } ], "source": [ - "x <= 5 or not y > 100" + "a <= 5 or not b > 100" ] }, { @@ -774,7 +813,7 @@ "execution_count": 25, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -790,7 +829,7 @@ } ], "source": [ - "(x <= 5) or not (y > 100)" + "(a <= 5) or not (b > 100)" ] }, { @@ -814,7 +853,7 @@ } ], "source": [ - "(x <= 5) or (not (y > 100)) # but no need to \"over do\" it" + "(a <= 5) or (not (b > 100)) # no need to \"over do\" it" ] }, { @@ -827,7 +866,7 @@ "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", "\n", - "Python allows **chaining** relational operators that 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." + "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." ] }, { @@ -842,7 +881,7 @@ { "data": { "text/plain": [ - "False" + "True" ] }, "execution_count": 27, @@ -851,7 +890,7 @@ } ], "source": [ - "(5 < x) and (x < 21)" + "5 < a and a < 87" ] }, { @@ -859,14 +898,14 @@ "execution_count": 28, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "False" + "True" ] }, "execution_count": 28, @@ -875,7 +914,7 @@ } ], "source": [ - "5 < x < 21" + "5 < a < 87" ] }, { @@ -897,11 +936,11 @@ } }, "source": [ - "The operands of the logical operators do not need to be *boolean* expressions but may be *any* expression. If a sub-expression does *not* evaluate to an object of type `bool`, Python automatically casts it as such.\n", + "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", "\n", - "For example, any non-zero numeric object becomes `True`. While this behavior allows writing more concise and thus more \"beautiful\" code, it is also a common source of confusion.\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", "\n", - "So, `(x - 9)` is cast as `True` and then the overall expression evaluates to `True` as well." + "So, `(a - 21)` is cast as `True` and then the overall expression evaluates to `True` as well." ] }, { @@ -925,7 +964,7 @@ } ], "source": [ - "(x - 9) and (y < 100) # = 33 and (y < 100) = 33 and True" + "(a - 21) and (b < 100) # => 21 and (b < 100) => 21 and True" ] }, { @@ -936,7 +975,7 @@ } }, "source": [ - "Whenever we are unsure as to 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 check it ourselves." + "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*." ] }, { @@ -960,7 +999,7 @@ } ], "source": [ - "bool(x - 9) # = bool(33)" + "bool(a - 21) # = bool(21)" ] }, { @@ -968,7 +1007,7 @@ "execution_count": 31, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -984,7 +1023,7 @@ } ], "source": [ - "bool(x - 42) # = bool(0)" + "bool(a - 42) # = bool(0)" ] }, { @@ -995,7 +1034,7 @@ } }, "source": [ - "Keep in mind that negative numbers also evaluate to `True`." + "Let's keep in mind that negative numbers also evaluate to `True`!" ] }, { @@ -1003,7 +1042,7 @@ "execution_count": 32, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -1019,7 +1058,7 @@ } ], "source": [ - "bool(x - 99) # = bool(-57)" + "bool(a - 43) # = bool(-1)" ] }, { @@ -1038,7 +1077,7 @@ "execution_count": 33, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "slide" } }, "outputs": [ @@ -1065,7 +1104,7 @@ } }, "source": [ - "Another good rule to know is that container types (e.g., `list`) evaluate to `True` whenever they are *not* empty and `False` if they are." + "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." ] }, { @@ -1097,7 +1136,7 @@ "execution_count": 35, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -1205,18 +1244,18 @@ } }, "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 sub-expressions are evaluated, and the result is *immediately* returned.\n", + "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", "\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", "The two rules can be summarized as:\n", "\n", - "- `x or y`: If `x` is truthy, it is returned *without* evaluating `y`. Otherwise, `y` is evaluated *and* returned.\n", - "- `x and y`: If `x` is falsy, it is returned *without* evaluating `y`. Otherwise, `y` is evaluated *and* returned.\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", "The rules may also be chained or combined.\n", "\n", - "Let's look at a couple of examples below. To visualize which sub-expressions are evaluated, we define a helper function `expr()` that prints out the only argument it is passed before returning it." + "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." ] }, { @@ -1243,7 +1282,7 @@ } }, "source": [ - "With the `or` operator, the first truthy sub-expression is returned." + "With the `or` operator, the first truthy operand is returned." ] }, { @@ -1283,7 +1322,7 @@ "execution_count": 40, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -1306,7 +1345,7 @@ } ], "source": [ - "expr(1) or expr(2) # 2 is not evaluated due to short-circuiting" + "expr(1) or expr(2) # 2 is not evaluated" ] }, { @@ -1314,7 +1353,7 @@ "execution_count": 41, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "slide" } }, "outputs": [ @@ -1338,7 +1377,7 @@ } ], "source": [ - "expr(0) or expr(1) or expr(2) # 2 is not evaluated due to short-circuiting" + "expr(0) or expr(1) or expr(2) # 2 is not evaluated" ] }, { @@ -1349,7 +1388,7 @@ } }, "source": [ - "If all sub-expressions are falsy, the last one is returned." + "If all operands are falsy, the last one is returned." ] }, { @@ -1393,7 +1432,7 @@ } }, "source": [ - "With the `and` operator, the first falsy sub-expression is returned." + "With the `and` operator, the first falsy operand is returned." ] }, { @@ -1424,7 +1463,7 @@ } ], "source": [ - "expr(0) and expr(1) # 1 is not evaluated due to short-circuiting" + "expr(0) and expr(1) # 1 is not evaluated" ] }, { @@ -1432,7 +1471,7 @@ "execution_count": 44, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -1464,7 +1503,7 @@ "execution_count": 45, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "slide" } }, "outputs": [ @@ -1488,7 +1527,7 @@ } ], "source": [ - "expr(1) and expr(0) and expr(2) # 2 is not evaluated due to short-circuiting" + "expr(1) and expr(0) and expr(2) # 2 is not evaluated" ] }, { @@ -1499,7 +1538,7 @@ } }, "source": [ - "If all sub-expressions are truthy, the last one is returned." + "If all operands are truthy, the last one is returned." ] }, { @@ -1542,7 +1581,7 @@ } }, "source": [ - "The crucial takeaway is that Python does *not* necessarily evaluate *all* sub-expressions and, therefore, our code should never rely on that assumption." + "The crucial takeaway is that Python does *not* necessarily evaluate *all* operands and, therefore, our code should never rely on that assumption." ] }, { @@ -1564,7 +1603,7 @@ } }, "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)**, even if \"real world\" refers to the domain of mathematics and not a business application.\n", + "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", "\n", "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", "\n", @@ -1572,75 +1611,34 @@ "- an *arbitrary* number of `elif`-clauses (i.e., \"else if\"), and\n", "- an *optional* `else`-clause.\n", "\n", - "The `if`- and `elif`-clauses each specify one *boolean* expression, also called **condition** in this context, while the `else`-clause serves as the \"catch everything else\" case.\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", "\n", "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", "\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 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." + "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." ] }, { "cell_type": "code", "execution_count": 47, "metadata": { - "code_folding": [], "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ - "z = 101" + "numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" ] }, { "cell_type": "code", "execution_count": 48, "metadata": { - "code_folding": [], "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "z is positive but odd\n" - ] - } - ], - "source": [ - "if (z % 2 == 0) and (z > 0):\n", - " print(\"z is even and positive\")\n", - "elif z % 2 == 0:\n", - " print(\"z is even but negative\")\n", - "elif z > 0:\n", - " print(\"z is positive but odd\")\n", - "else:\n", - " print(\"z is neither even nor positive\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "In many situations, we only need a reduced form of the `if` statement.\n", - "\n", - "We could **inject** code only at random, for example, to implement an [A/B testing](https://en.wikipedia.org/wiki/A/B_testing) strategy." - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": { - "slideshow": { - "slide_type": "slide" + "slide_type": "-" } }, "outputs": [], @@ -1650,7 +1648,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 49, "metadata": { "slideshow": { "slide_type": "-" @@ -1658,8 +1656,7 @@ }, "outputs": [], "source": [ - "if random.random() > 0.5:\n", - " print(\"You read this just as often as you see heads when tossing a coin\")" + "random.seed(789)" ] }, { @@ -1670,7 +1667,48 @@ } }, "source": [ - "More often than not, we model a **binary choice**." + "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." ] }, { @@ -1681,20 +1719,229 @@ "slide_type": "skip" } }, + "outputs": [], + "source": [ + "random.seed(789)" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "z is positive\n" + "6 is divisible by 2 and 3\n" ] } ], "source": [ - "if z > 0:\n", - " print(\"z is positive\")\n", + "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(\"z is negative\")" + " 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": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(789)" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "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 % 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", + "else:\n", + " print(number, \"is divisible by neither 2 nor 3\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Often, we only need a reduced form of the `if` statement.\n", + "\n", + "For example, below we **inject** code to print a message at random." + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "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" + ] + } + ], + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "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\")" + ] + }, + { + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(789)" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6 is even\n" + ] + } + ], + "source": [ + "number = random.choice(numbers)\n", + "\n", + "if number % 2: # Beware of the opposite meaning!\n", + " print(number, \"is odd\")\n", + "else:\n", + " print(number, \"is even\")" ] }, { @@ -1707,12 +1954,25 @@ "source": [ "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", - "The code cell below *either* checks if a number is even or odd *or* if it is positive or negative." + "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." ] }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(789)" + ] + }, + { + "cell_type": "code", + "execution_count": 61, "metadata": { "slideshow": { "slide_type": "slide" @@ -1723,21 +1983,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "z is positive\n" + "4 is even\n" ] } ], "source": [ + "signed_number = random.choice([-1, +1]) * random.choice(numbers)\n", + "\n", "if random.random() > 0.5:\n", - " if z % 2: # no need to write out the \"== 0\"\n", - " print(\"z is odd\")\n", + " if signed_number % 2:\n", + " print(signed_number, \"is odd\")\n", " else:\n", - " print(\"z is even\")\n", + " print(signed_number, \"is even\")\n", "else:\n", - " if z > 0:\n", - " print(\"z is positive\")\n", + " if signed_number > 0:\n", + " print(signed_number, \"is positive\")\n", " else:\n", - " print(\"z is negative\")" + " print(signed_number, \"is negative\")" ] }, { @@ -1755,7 +2017,20 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 62, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(789)" + ] + }, + { + "cell_type": "code", + "execution_count": 63, "metadata": { "slideshow": { "slide_type": "slide" @@ -1766,23 +2041,25 @@ "name": "stdout", "output_type": "stream", "text": [ - "z is positive\n" + "4 is even\n" ] } ], "source": [ - "check_oddness = (random.random() > 0.5)\n", - "is_odd = (z % 2)\n", - "is_positive = (z > 0)\n", + "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", "\n", "if check_oddness and is_odd:\n", - " print(\"z is odd\")\n", + " print(signed_number, \"is odd\")\n", "elif check_oddness and not is_odd:\n", - " print(\"z is even\")\n", + " print(signed_number, \"is even\")\n", "elif not check_oddness and is_positive:\n", - " print(\"z is positive\")\n", + " print(signed_number, \"is positive\")\n", "else:\n", - " print(\"z is negative\")" + " print(signed_number, \"is negative\")" ] }, { @@ -1828,7 +2105,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 64, "metadata": { "slideshow": { "slide_type": "slide" @@ -1852,10 +2129,10 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 65, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [], @@ -1868,7 +2145,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 66, "metadata": { "slideshow": { "slide_type": "-" @@ -1881,7 +2158,7 @@ "9" ] }, - "execution_count": 56, + "execution_count": 66, "metadata": {}, "output_type": "execute_result" } @@ -1903,10 +2180,10 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 67, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "slide" } }, "outputs": [], @@ -1916,10 +2193,10 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 68, "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "-" } }, "outputs": [ @@ -1929,7 +2206,7 @@ "9" ] }, - "execution_count": 58, + "execution_count": 68, "metadata": {}, "output_type": "execute_result" } @@ -1951,7 +2228,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 69, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1964,10 +2241,10 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 70, "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "-" } }, "outputs": [ @@ -1977,7 +2254,7 @@ "9" ] }, - "execution_count": 60, + "execution_count": 70, "metadata": {}, "output_type": "execute_result" } @@ -2014,23 +2291,23 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 71, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "skip" } }, "outputs": [], "source": [ - "user_input = 0" + "random.seed(123)" ] }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 72, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "slide" } }, "outputs": [ @@ -2043,6 +2320,8 @@ } ], "source": [ + "user_input = random.choice([0, 1])\n", + "\n", "try:\n", " 1 / user_input\n", "except:\n", @@ -2057,7 +2336,7 @@ } }, "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 reasoning why this is done is a bit involved. We only remark that the codebase becomes easier to understand as we communicate to any human reader what could go wrong during execution in an *explicit* way. 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", + "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", "\n", "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", "\n", @@ -2066,7 +2345,20 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(123)" + ] + }, + { + "cell_type": "code", + "execution_count": 74, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2082,6 +2374,8 @@ } ], "source": [ + "user_input = random.choice([0, 1])\n", + "\n", "try:\n", " 1 / user_input\n", "except ZeroDivisionError:\n", @@ -2100,12 +2394,25 @@ "\n", "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", "\n", - "To showcase everything together, we look at one last example. To spice it up a bit, we randomize the input. So run the cell several times and see for yourself." + "To showcase everything together, we look at one last example. It is randomized: So, run the cell several times and see for yourself." ] }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 75, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(123)" + ] + }, + { + "cell_type": "code", + "execution_count": 76, "metadata": { "slideshow": { "slide_type": "slide" @@ -2116,16 +2423,16 @@ "name": "stdout", "output_type": "stream", "text": [ - "Yes, division worked smoothly.\n", + "Oops. Division by 0. How does that work?\n", "I am always printed\n" ] } ], "source": [ - "divisor = random.choice([0, 1])\n", + "user_input = random.choice([0, 1])\n", "\n", "try:\n", - " 1 / divisor\n", + " 1 / user_input\n", "except ZeroDivisionError:\n", " print(\"Oops. Division by 0. How does that work?\")\n", "else:\n", From 0519b499892270f8f14d7481ad5cb9eda022b804 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Sun, 9 Feb 2020 23:03:31 +0100 Subject: [PATCH 3/3] Bump version number --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 9cc1c12..679d3d7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "intro-to-python" -version = "0.6.3" +version = "0.6.4" description = "An introduction to Python and programming for wanna-be data scientists" authors = ["Alexander Hess "] license = "MIT"