diff --git a/01_elements_00_lecture.ipynb b/01_elements_00_lecture.ipynb index f7583ba..89beede 100644 --- a/01_elements_00_lecture.ipynb +++ b/01_elements_00_lecture.ipynb @@ -1308,7 +1308,7 @@ } }, "source": [ - "The `c` object is a so-called **string** type (i.e., `str`), which is Python's way of representing \"text.\" Strings also come with peculiar behaviors, for example, to convert a text to lower, upper, or title case." + "The `c` object is a so-called **string** type (i.e., `str`), which is Python's way of representing \"text.\" Strings also come with peculiar behaviors, for example, to make a text lower or upper case." ] }, { @@ -1383,30 +1383,6 @@ "c.upper()" ] }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "'Python Rocks'" - ] - }, - "execution_count": 41, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c.title()" - ] - }, { "cell_type": "markdown", "metadata": { diff --git a/03_conditionals_02_exercises.ipynb b/03_conditionals_02_exercises.ipynb index 650b063..ac4acd1 100644 --- a/03_conditionals_02_exercises.ipynb +++ b/03_conditionals_02_exercises.ipynb @@ -19,7 +19,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Read [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_00_lecture.ipynb) of the book. Then, work through the exercises below." + "Read [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_00_lecture.ipynb) of the book. Then, work through the exercises below. The `...` indicate where you need to fill in your answers. You should not need to create any additional code cells." ] }, { @@ -136,7 +136,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q1.4**: Looking at the `if`-`else`-logic in the function, why do you think the four example line items in **Q9.2** were chosen as they were?" + "**Q1.4**: Looking at the `if`-`else`-logic in the function, why do you think the four example line items in **Q1.2** were chosen as they were?" ] }, { diff --git a/04_iteration_00_lecture.ipynb b/04_iteration_00_lecture.ipynb index fb89130..61858c5 100644 --- a/04_iteration_00_lecture.ipynb +++ b/04_iteration_00_lecture.ipynb @@ -52,7 +52,7 @@ "cell_type": "markdown", "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "slide" } }, "source": [ diff --git a/05_numbers_00_lecture.ipynb b/05_numbers_00_lecture.ipynb index 531de04..771a0ad 100644 --- a/05_numbers_00_lecture.ipynb +++ b/05_numbers_00_lecture.ipynb @@ -29,7 +29,7 @@ "\n", "- [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_lecture.ipynb#%28Data%29-Type-%2F-%22Behavior%22) reveals that numbers may come in *different* data types (i.e., `int` vs. `float` so far),\n", "- [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_00_lecture.ipynb#Boolean-Expressions) raises questions regarding the **limited precision** of `float` numbers (e.g., `42 == 42.000000000000001` evaluates to `True`), and\n", - "- [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_lecture.ipynb#Infinite-Recursion) shows that sometimes a `float` \"walks\" and \"quacks\" like an `int`, whereas the reverse is true in other cases.\n", + "- [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_lecture.ipynb#Infinite-Recursion) shows that sometimes a `float` \"walks\" and \"quacks\" like an `int`, whereas the reverse is true.\n", "\n", "This chapter introduces all the [built-in numeric types](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex): `int`, `float`, and `complex`. To mitigate the limited precision of floating-point numbers, we also look at two replacements for the `float` type in the [standard library](https://docs.python.org/3/library/index.html), namely the `Decimal` type in the [decimals](https://docs.python.org/3/library/decimal.html#decimal.Decimal) and the `Fraction` type in the [fractions](https://docs.python.org/3/library/fractions.html#fractions.Fraction) module." ] @@ -53,7 +53,9 @@ } }, "source": [ - "The simplest numeric type is the `int` type: It behaves like an [integer in ordinary math](https://en.wikipedia.org/wiki/Integer) (i.e., the set $\\mathbb{Z}$) and supports operators in the way we saw in the section on arithmetic operators in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_lecture.ipynb#%28Arithmetic%29-Operators)." + "The simplest numeric type is the `int` type: It behaves like an [integer in ordinary math](https://en.wikipedia.org/wiki/Integer) (i.e., the set $\\mathbb{Z}$) and supports operators in the way we saw in the section on arithmetic operators in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_lecture.ipynb#%28Arithmetic%29-Operators).\n", + "\n", + "One way to create `int` objects is by simply writing its value as a literal with the digits `0` to `9`." ] }, { @@ -66,7 +68,7 @@ }, "outputs": [], "source": [ - "i = 789" + "a = 42" ] }, { @@ -77,7 +79,7 @@ } }, "source": [ - "Just like any other object, `789` has an identity, a type, and a value." + "Just like any other object, the `42` has an identity, a type, and a value." ] }, { @@ -92,7 +94,7 @@ { "data": { "text/plain": [ - "140166838695792" + "94784085682240" ] }, "execution_count": 2, @@ -101,7 +103,7 @@ } ], "source": [ - "id(i)" + "id(a)" ] }, { @@ -109,7 +111,7 @@ "execution_count": 3, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -125,7 +127,7 @@ } ], "source": [ - "type(i)" + "type(a)" ] }, { @@ -133,14 +135,14 @@ "execution_count": 4, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "789" + "42" ] }, "execution_count": 4, @@ -149,7 +151,7 @@ } ], "source": [ - "i" + "a" ] }, { @@ -160,7 +162,7 @@ } }, "source": [ - "A nice feature is using underscores `_` as (thousands) separator in numeric literals. For example, `1_000_000` evaluates to `1000000` in memory." + "A nice feature in newer Python versions is using underscores `_` as (thousands) separators in numeric literals. For example, `1_000_000` evaluates to `1000000` in memory; the `_` is ignored by the interpreter." ] }, { @@ -195,7 +197,7 @@ } }, "source": [ - "Whereas mathematicians argue what the term $0^0$ means (cf., this [article](https://en.wikipedia.org/wiki/Zero_to_the_power_of_zero)), programmers are pragmatic about this and define $0^0 = 1$." + "We may place the `_`s anywhere we want." ] }, { @@ -210,7 +212,7 @@ { "data": { "text/plain": [ - "1" + "123456789" ] }, "execution_count": 6, @@ -218,6 +220,252 @@ "output_type": "execute_result" } ], + "source": [ + "1_2_3_4_5_6_7_8_9" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "It is syntactically invalid to write out leading `0` in numeric literals. The reason for that will become apparent in the next section." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid token (, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 042\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid token\n" + ] + } + ], + "source": [ + "042" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Another way to create `int` objects is with the [int()](https://docs.python.org/3/library/functions.html#int) built-in that casts `float` or properly formatted `str` objects as integers. So, decimals are truncated (i.e., \"cut off\")." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(42.11)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(42.87)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Whereas the floor division operator `//` effectively rounds towards negative infinity (cf., the \"*(Arithmetic) Operators*\" section in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_lecture.ipynb#%28Arithmetic%29-Operators)), the [int()](https://docs.python.org/3/library/functions.html#int) built-in effectively rounds towards `0`." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-42" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(-42.87)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When casting `str` objects as `int`, the [int()](https://docs.python.org/3/library/functions.html#int) built-in is less forgiving. We must not include any decimals as shows by the `ValueError`. Yet, leading and trailing whitespace is gracefully ignored." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(\"42\")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "invalid literal for int() with base 10: '42.0'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"42.0\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: invalid literal for int() with base 10: '42.0'" + ] + } + ], + "source": [ + "int(\"42.0\")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(\" 42 \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `int` type follows all rules we know from math, apart from one exception: Whereas mathematicians to this day argue what the term $0^0$ means (cf., this [article](https://en.wikipedia.org/wiki/Zero_to_the_power_of_zero)), programmers are pragmatic about this and simply define $0^0 = 1$." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "0 ** 0" ] @@ -270,17 +518,17 @@ "\n", "To encode the integer $3$, for example, we need to find a combination of $0$s and $1$s such that the sum of digits marked with a $1$ is equal to the number we want to encode. In the example, we set all bits to $0$ except for the first ($i=0$) and second ($i=1$) as $2^0 + 2^1 = 1 + 2 = 3$. So the binary representation of $3$ is $00~00~00~11$. To borrow some terminology from linear algebra, the $3$ is a linear combination of the digits where the coefficients are either $0$ or $1$: $3 = 0*128 + 0*64 + 0*32 + 0*16 + 0*8 + 0*4 + 1*2 + 1*1$. It is *guaranteed* that there is exactly *one* such combination for each number between $0$ and $255$.\n", "\n", - "As each bit in the binary representation is one of two values, we say that this representation has a base of $2$. Often, the base is indicated with a subscript to avoid confusion. For example, we write $3_{10} = 00000011_2$ or $3_{10} = 11_2$ for short omitting leading $0$s. A subscript of $10$ implies a decimal number as we know it from grade school.\n", + "As each bit in the binary representation is one of two values, we say that this representation has a base of $2$. Often, the base is indicated with a subscript to avoid confusion. For example, we write $3_{10} = 00000011_2$ or $3_{10} = 11_2$ for short omitting leading $0$s. A subscript of $10$ implies a decimal number as we know it from elementary school.\n", "\n", "We use the built-in [bin()](https://docs.python.org/3/library/functions.html#bin) function to obtain an `int` object's binary representation: It returns a `str` object starting with `\"0b\"` indicating the binary format and as many $0$s and $1$s as are necessary to encode the integer omitting leading $0$s." ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 15, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "slide" } }, "outputs": [ @@ -290,7 +538,7 @@ "'0b11'" ] }, - "execution_count": 7, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -312,7 +560,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 16, "metadata": { "slideshow": { "slide_type": "fragment" @@ -325,7 +573,7 @@ "3" ] }, - "execution_count": 8, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -347,7 +595,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 17, "metadata": { "slideshow": { "slide_type": "fragment" @@ -360,7 +608,7 @@ "3" ] }, - "execution_count": 9, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -377,31 +625,31 @@ } }, "source": [ - "Another example is the integer `177` that is the sum of $128 + 32 + 16 + 1$: Thus, its binary representation is the sequence of bits $10~11~00~01$, or to use our new notation, $177_{10} = 10110001_2$." + "It is convenient to use the underscore `_` to separate the `\"0b\"` prefix from the bits." ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 18, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "'0b10110001'" + "3" ] }, - "execution_count": 10, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "bin(177)" + "0b_11" ] }, { @@ -412,31 +660,66 @@ } }, "source": [ - "Analogous to typing `177` into a code cell, we may write `0b10110001`, or `0b_1011_0001` to make use of the underscores, and create an `int` object with the value `177`." + "Another example is the integer `123` that is the sum of $64 + 32 + 16 + 8 + 2 + 1$: Thus, its binary representation is the sequence of bits $01~11~10~11$, or to use our new notation, $123_{10} = 1111011_2$." ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 19, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ - "177" + "'0b1111011'" ] }, - "execution_count": 11, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "0b_1011_0001" + "bin(123)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Analogous to typing `123` into a code cell, we may write `0b1111011`, or `0b_111_1011` to make use of the underscores, and create an `int` object with the value `123`." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "123" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0b_111_1011" ] }, { @@ -452,7 +735,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 21, "metadata": { "slideshow": { "slide_type": "slide" @@ -465,7 +748,7 @@ "'0b0'" ] }, - "execution_count": 12, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } @@ -476,10 +759,10 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 22, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ @@ -489,7 +772,7 @@ "'0b1'" ] }, - "execution_count": 13, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } @@ -500,10 +783,10 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 23, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ @@ -513,7 +796,7 @@ "'0b10'" ] }, - "execution_count": 14, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -524,10 +807,10 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 24, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -537,7 +820,7 @@ "'0b11111111'" ] }, - "execution_count": 15, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -556,12 +839,12 @@ "source": [ "Groups of eight bits are also called a **byte**. As a byte can only represent non-negative integers up to $255$, the table above is extended conceptually with greater digits to the left to model integers beyond $255$. The memory management needed to implement this is built into Python, and we do not need to worry about it.\n", "\n", - "For example, the `789` from above is encoded with ten bits and $789_{10} = 1100010101_2$." + "For example, `789` is encoded with ten bits and $789_{10} = 1100010101_2$." ] }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 25, "metadata": { "slideshow": { "slide_type": "fragment" @@ -574,13 +857,13 @@ "'0b1100010101'" ] }, - "execution_count": 16, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "bin(i) # = bin(789)" + "bin(789)" ] }, { @@ -598,7 +881,254 @@ "| Digit |$10^3$|$10^2$|$10^1$|$10^0$|\n", "| $=$ |$1000$| $100$| $10$ | $1$ |\n", "\n", - "Now, an integer is a linear combination of the digits where the coefficients are one of *ten* values, and the base is now $10$. For example, the number $123$ can be expressed as $0*1000 + 1*100 + 2*10 + 3*1$. So, the binary representation follows the same logic as the decimal system taught in grade school. The decimal system is intuitive to us humans, mostly as we learn to count with our *ten* fingers. The $0$s and $1$s in a computer's memory are therefore no rocket science; they only feel unintuitive for a beginner." + "Now, an integer is a linear combination of the digits where the coefficients are one of *ten* values, and the base is now $10$. For example, the number $123$ can be expressed as $0*1000 + 1*100 + 2*10 + 3*1$. So, the binary representation follows the same logic as the decimal system taught in elementary school. The decimal system is intuitive to us humans, mostly as we learn to count with our *ten* fingers. The $0$s and $1$s in a computer's memory are therefore no rocket science; they only feel unintuitive for a beginner." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Arithmetic with Bits" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Adding two numbers in their binary representations is straightforward and works just like we all learned addition in elementary school. Going from right to left, we add the individual digits, and ..." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 + 2" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1 + 0b10 = 0b11'" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(1) + \" + \" + bin(2) + \" = \" + bin(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... if any two digits add up to $2$, the resulting digit is $0$ and a $1$ carries over." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 + 3" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1 + 0b11 = 0b100'" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(1) + \" + \" + bin(3) + \" = \" + bin(4)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Multiplication is also quite easy. All we need to do is to multiply the left operand by all digits of the right operand separately and then add up the individual products, just like in elementary school." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "4 * 3" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b100 * 0b11 = 0b1100'" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(4) + \" * \" + bin(3) + \" = \" + bin(12)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b100 * 0b1 = 0b100'" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(4) + \" * \" + bin(1) + \" = \" + bin(4) # multiply with first digit" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b100 * 0b10 = 0b1000'" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(4) + \" * \" + bin(2) + \" = \" + bin(8) # multiply with second digit" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The \"*Further Resources*\" section at the end of this chapter provides video tutorials on addition and multiplication in binary. Subtraction and division are a bit more involved but essentially also easy to understand." ] }, { @@ -676,7 +1206,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 34, "metadata": { "slideshow": { "slide_type": "slide" @@ -689,7 +1219,7 @@ "'0x0'" ] }, - "execution_count": 17, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } @@ -700,10 +1230,10 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 35, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -713,7 +1243,7 @@ "'0x1'" ] }, - "execution_count": 18, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } @@ -735,10 +1265,10 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 36, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "skip" } }, "outputs": [ @@ -748,7 +1278,7 @@ "'0x3'" ] }, - "execution_count": 19, + "execution_count": 36, "metadata": {}, "output_type": "execute_result" } @@ -770,7 +1300,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 37, "metadata": { "slideshow": { "slide_type": "fragment" @@ -783,7 +1313,7 @@ "'0xa'" ] }, - "execution_count": 20, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } @@ -794,10 +1324,10 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 38, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -807,7 +1337,7 @@ "'0xf'" ] }, - "execution_count": 21, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } @@ -824,12 +1354,12 @@ } }, "source": [ - "The binary representation of `177`, `\"0b10110001\"`, can be viewed as *two* groups of four bits, $1011$ and $0001$, that are encoded as $\\text{b}$ and $1$ in hexadecimal (cf., table above)." + "The binary representation of `123`, `0b_111_1011`, can be viewed as *two* groups of four bits, $0111$ and $1011$, that are encoded as $7$ and $\\text{b}$ in hexadecimal (cf., table above)." ] }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 39, "metadata": { "slideshow": { "slide_type": "slide" @@ -839,40 +1369,40 @@ { "data": { "text/plain": [ - "'0b10110001'" + "'0b1111011'" ] }, - "execution_count": 22, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "bin(177)" + "bin(123)" ] }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 40, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "'0xb1'" + "'0x7b'" ] }, - "execution_count": 23, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "hex(177)" + "hex(123)" ] }, { @@ -883,12 +1413,12 @@ } }, "source": [ - "To obtain a *new* `int` object with the value `177`, we call the [int()](https://docs.python.org/3/library/functions.html#int) built-in with a properly formatted `str` object and `base=16` as arguments." + "To obtain a *new* `int` object with the value `123`, we call the [int()](https://docs.python.org/3/library/functions.html#int) built-in with a properly formatted `str` object and `base=16` as arguments." ] }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 41, "metadata": { "slideshow": { "slide_type": "fragment" @@ -898,16 +1428,16 @@ { "data": { "text/plain": [ - "177" + "123" ] }, - "execution_count": 24, + "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "int(\"0xb1\", base=16)" + "int(\"0x7b\", base=16)" ] }, { @@ -923,7 +1453,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 42, "metadata": { "slideshow": { "slide_type": "fragment" @@ -933,16 +1463,16 @@ { "data": { "text/plain": [ - "177" + "123" ] }, - "execution_count": 25, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "0xb1" + "0x_7b" ] }, { @@ -958,7 +1488,31 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0x0'" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 44, "metadata": { "slideshow": { "slide_type": "skip" @@ -971,7 +1525,7 @@ "'0xff'" ] }, - "execution_count": 26, + "execution_count": 44, "metadata": {}, "output_type": "execute_result" } @@ -993,7 +1547,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 45, "metadata": { "slideshow": { "slide_type": "skip" @@ -1006,13 +1560,13 @@ "'0x315'" ] }, - "execution_count": 27, + "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "hex(i) # = hex(789)" + "hex(789)" ] }, { @@ -1045,14 +1599,14 @@ } }, "source": [ - "While there are conventions that model negative integers with $0$s and $1$s in memory (cf., [Two's Complement](https://en.wikipedia.org/wiki/Two%27s_complement)), Python manages that for us, and we do not look into the theory here for brevity. We have learned all we need to know about how integers are modeled in a computer.\n", + "While there are conventions that model negative integers with $0$s and $1$s in memory (cf., [Two's Complement](https://en.wikipedia.org/wiki/Two%27s_complement)), Python manages that for us, and we do not look into the theory here for brevity. We have learned all that a practitioner needs to know about how integers are modeled in a computer. The \"*Further Resources*\" section at the end of this chapter provides a video tutorial on how the [Two's Complement](https://en.wikipedia.org/wiki/Two%27s_complement) idea works.\n", "\n", - "The binary and hexadecimal representations of negative integers are identical to their positive counterparts except that they start with a minus sign `-`." + "The binary and hexadecimal representations of negative integers are identical to their positive counterparts except that they start with a minus sign `-`. However, as the video tutorial at the end of the chapter reveals, that is *not* how the bits are organized in memory." ] }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 46, "metadata": { "slideshow": { "slide_type": "skip" @@ -1065,7 +1619,7 @@ "'-0b11'" ] }, - "execution_count": 28, + "execution_count": 46, "metadata": {}, "output_type": "execute_result" } @@ -1076,7 +1630,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 47, "metadata": { "slideshow": { "slide_type": "skip" @@ -1089,7 +1643,7 @@ "'-0x3'" ] }, - "execution_count": 29, + "execution_count": 47, "metadata": {}, "output_type": "execute_result" } @@ -1100,7 +1654,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 48, "metadata": { "slideshow": { "slide_type": "skip" @@ -1113,7 +1667,7 @@ "'-0b11111111'" ] }, - "execution_count": 30, + "execution_count": 48, "metadata": {}, "output_type": "execute_result" } @@ -1124,7 +1678,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 49, "metadata": { "slideshow": { "slide_type": "skip" @@ -1137,7 +1691,7 @@ "'-0xff'" ] }, - "execution_count": 31, + "execution_count": 49, "metadata": {}, "output_type": "execute_result" } @@ -1170,7 +1724,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 50, "metadata": { "slideshow": { "slide_type": "slide" @@ -1183,7 +1737,7 @@ "1" ] }, - "execution_count": 32, + "execution_count": 50, "metadata": {}, "output_type": "execute_result" } @@ -1194,10 +1748,10 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 51, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -1207,7 +1761,7 @@ "42" ] }, - "execution_count": 33, + "execution_count": 51, "metadata": {}, "output_type": "execute_result" } @@ -1218,10 +1772,10 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 52, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -1231,13 +1785,13 @@ "0.0" ] }, - "execution_count": 34, + "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "42.0 * False" + "42.87 * False" ] }, { @@ -1253,7 +1807,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 53, "metadata": { "slideshow": { "slide_type": "slide" @@ -1266,7 +1820,7 @@ "1" ] }, - "execution_count": 35, + "execution_count": 53, "metadata": {}, "output_type": "execute_result" } @@ -1277,10 +1831,10 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 54, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -1290,7 +1844,7 @@ "0" ] }, - "execution_count": 36, + "execution_count": 54, "metadata": {}, "output_type": "execute_result" } @@ -1312,7 +1866,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 55, "metadata": { "slideshow": { "slide_type": "slide" @@ -1325,7 +1879,7 @@ "'0b1'" ] }, - "execution_count": 37, + "execution_count": 55, "metadata": {}, "output_type": "execute_result" } @@ -1336,10 +1890,10 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 56, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -1349,7 +1903,7 @@ "'0b0'" ] }, - "execution_count": 38, + "execution_count": 56, "metadata": {}, "output_type": "execute_result" } @@ -1371,10 +1925,10 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 57, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "skip" } }, "outputs": [ @@ -1384,7 +1938,7 @@ "'0x1'" ] }, - "execution_count": 39, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" } @@ -1395,10 +1949,10 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 58, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ @@ -1408,7 +1962,7 @@ "'0x0'" ] }, - "execution_count": 40, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } @@ -1425,12 +1979,12 @@ } }, "source": [ - "As a reminder, the `None` literal is a type on its own, namely the `NoneType`, and different from `False`. It *cannot* be cast as an integer as the `TypeError` indicates." + "As a reminder, the `None` object is a type on its own, namely the `NoneType`, and different from `False`. It *cannot* be cast as an integer as the `TypeError` indicates." ] }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 59, "metadata": { "slideshow": { "slide_type": "slide" @@ -1444,7 +1998,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: int() argument must be a string, a bytes-like object or a number, not 'NoneType'" ] } @@ -1476,12 +2030,12 @@ "\n", "We keep this overview rather short as such \"low-level\" operations are not needed by the data science practitioner regularly. Yet, it is worthwhile to have heard about them as they form the basis of all of arithmetic in computers.\n", "\n", - "The first operator is the **bitwise AND** operator `&`: It looks at the bits of its two operands, `11` and `13` in the example, in a pairwise fashion and *iff* both operands have a $1$ in the same position, will the resulting integer have a $1$ in this position as well. The binary representations of `11` and `13` have $1$s in their respective first and fourth bits, which is why `bin(11 & 13)` evaluates to `\"Ob1001\"`." + "The first operator is the **bitwise AND** operator `&`: It looks at the bits of its two operands, `11` and `13` in the example, in a pairwise fashion and if *both* operands have a $1$ in the *same* position, the resulting integer will have a $1$ in this position as well. Otherwise, the resulting integer will have a $0$ in this position. The binary representations of `11` and `13` both have $1$s in their respective first and fourth bits, which is why `bin(11 & 13)` evaluates to `Ob_1001` or `9`." ] }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 60, "metadata": { "slideshow": { "slide_type": "slide" @@ -1491,24 +2045,48 @@ { "data": { "text/plain": [ - "'0b1011 & 0b1101'" + "9" ] }, - "execution_count": 47, + "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "bin(11) + \" & \" + bin(13) # to show the operands' binary representations" + "11 & 13" ] }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 61, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1011 & 0b1101'" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(11) + \" & \" + bin(13) # to show the operands' bits" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "slideshow": { + "slide_type": "fragment" } }, "outputs": [ @@ -1518,7 +2096,7 @@ "'0b1001'" ] }, - "execution_count": 48, + "execution_count": 62, "metadata": {}, "output_type": "execute_result" } @@ -1535,12 +2113,12 @@ } }, "source": [ - "`0b1001` is the binary representation of `9`." + "`0b_1001` is the binary representation of `9`." ] }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 63, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1553,37 +2131,13 @@ "9" ] }, - "execution_count": 49, + "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "0b1001" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "9" - ] - }, - "execution_count": 50, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "11 & 13" + "0b_1001" ] }, { @@ -1594,12 +2148,12 @@ } }, "source": [ - "The **bitwise OR** operator `|` evaluates to an `int` object whose bits are set to $1$ if the corresponding bits of either *one* or *both* operands are $1$. So in the example `9 | 13` only the second bit is $0$ for both operands, which is why the expression evaluates to `\"0b1101\"`." + "The **bitwise OR** operator `|` evaluates to an `int` object whose bits are set to $1$ if the corresponding bits of either *one* or *both* operands are $1$. So in the example `9 | 13` only the second bit is $0$ for both operands, which is why the expression evaluates to `0b_1101` or `13`." ] }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 64, "metadata": { "slideshow": { "slide_type": "slide" @@ -1609,24 +2163,48 @@ { "data": { "text/plain": [ - "'0b1001 | 0b1101'" + "13" ] }, - "execution_count": 51, + "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "bin(9) + \" | \" + bin(13) # to show the operands' binary representations" + "9 | 13" ] }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 65, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1001 | 0b1101'" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(9) + \" | \" + bin(13) # to show the operands' bits" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "slideshow": { + "slide_type": "fragment" } }, "outputs": [ @@ -1636,7 +2214,7 @@ "'0b1101'" ] }, - "execution_count": 52, + "execution_count": 66, "metadata": {}, "output_type": "execute_result" } @@ -1653,12 +2231,12 @@ } }, "source": [ - "`0b1101` evaluates to the `int` object `13`." + "`0b_1101` evaluates to an `int` object with the value `13`." ] }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 67, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1671,37 +2249,13 @@ "13" ] }, - "execution_count": 53, + "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "0b1101" - ] - }, - { - "cell_type": "code", - "execution_count": 54, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "13" - ] - }, - "execution_count": 54, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "9 | 13" + "0b_1101" ] }, { @@ -1712,12 +2266,12 @@ } }, "source": [ - "The **bitwise XOR** operator `^` is a special case of the `|` operator in that it evaluates to an `int` object whose bits are set to $1$ if the corresponding bit of *exactly one* of the two operands is $1$. Colloquially, the \"X\" stands for \"exclusive.\" The `^` operator must *not* be confused with the exponentiation operator `**`! In the example, `9 ^ 13`, only the third bit differs between the two operands, which is why it evaluates to `\"0b100\"` omitting the fourth bit." + "The **bitwise XOR** operator `^` is a special case of the `|` operator in that it evaluates to an `int` object whose bits are set to $1$ if the corresponding bit of *exactly one* of the two operands is $1$. Colloquially, the \"X\" stands for \"exclusive.\" The `^` operator must *not* be confused with the exponentiation operator `**`! In the example, `9 ^ 13`, only the third bit differs between the two operands, which is why it evaluates to `0b_100` omitting the leading $0$." ] }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 68, "metadata": { "slideshow": { "slide_type": "slide" @@ -1727,24 +2281,48 @@ { "data": { "text/plain": [ - "'0b1001 ^ 0b1101'" + "4" ] }, - "execution_count": 55, + "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "bin(9) + \" ^ \" + bin(13) # to show the operands' binary representations" + "9 ^ 13" ] }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 69, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1001 ^ 0b1101'" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(9) + \" ^ \" + bin(13) # to show the operands' bits" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "slideshow": { + "slide_type": "fragment" } }, "outputs": [ @@ -1754,7 +2332,7 @@ "'0b100'" ] }, - "execution_count": 56, + "execution_count": 70, "metadata": {}, "output_type": "execute_result" } @@ -1771,12 +2349,12 @@ } }, "source": [ - "`0b100` evaluates to the `int` object `4`." + "`0b_100` evaluates to an `int` object with the value `4`." ] }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 71, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1789,37 +2367,13 @@ "4" ] }, - "execution_count": 57, + "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "0b100" - ] - }, - { - "cell_type": "code", - "execution_count": 58, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "4" - ] - }, - "execution_count": 58, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "9 ^ 13" + "0b_100" ] }, { @@ -1830,14 +2384,14 @@ } }, "source": [ - "The **bitwise NOT** operator `~`, sometimes also called **inversion** operator, is said to \"flip\" the $0$s into $1$s and the $1$s into $0$s. However, it is based on the aforementioned [Two's Complement](https://en.wikipedia.org/wiki/Two%27s_complement) convention and `~x = -(x + 1)` by definition (cf., the [reference](https://docs.python.org/3/reference/expressions.html#unary-arithmetic-and-bitwise-operations)). The full logic behind this is considered out of scope in this book.\n", + "The **bitwise NOT** operator `~`, sometimes also called **inversion** operator, is said to \"flip\" the $0$s into $1$s and the $1$s into $0$s. However, it is based on the aforementioned [Two's Complement](https://en.wikipedia.org/wiki/Two%27s_complement) convention and `~x = -(x + 1)` by definition (cf., the [reference](https://docs.python.org/3/reference/expressions.html#unary-arithmetic-and-bitwise-operations)). The full logic behind this, while actually quite simple, is considered out of scope in this book.\n", "\n", - "We can at least verify the definition by comparing the binary representations of `8` and `-9`: They are indeed the same." + "We can at least verify the definition by comparing the binary representations of `7` and `-8`: They are indeed the same." ] }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 72, "metadata": { "slideshow": { "slide_type": "slide" @@ -1847,40 +2401,88 @@ { "data": { "text/plain": [ - "'-0b1001'" + "-8" ] }, - "execution_count": 59, + "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "bin(~8)" + "~7" ] }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 73, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "'-0b1001'" + "True" ] }, - "execution_count": 60, + "execution_count": 73, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "bin(-(8 + 1))" + "~7 == -(7 + 1) # = Two's Complement" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'-0b1000'" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(~7)" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'-0b1000'" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(-(7 + 1))" ] }, { @@ -1896,10 +2498,10 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 76, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "skip" } }, "outputs": [ @@ -1909,13 +2511,13 @@ "-1" ] }, - "execution_count": 61, + "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "~8 + 8" + "~7 + 7" ] }, { @@ -1926,14 +2528,14 @@ } }, "source": [ - "Lastly, the **bitwise left and right shift** operators, `<<` and `>>`, shift all the bits either to the left or to the right. This corresponds to multiplying or dividing an integer by powers of $2$.\n", + "Lastly, the **bitwise left and right shift** operators, `<<` and `>>`, shift all the bits either to the left or to the right. This corresponds to multiplying or dividing an integer by powers of `2`.\n", "\n", "When shifting left, $0$s are filled in." ] }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 77, "metadata": { "slideshow": { "slide_type": "slide" @@ -1943,64 +2545,88 @@ { "data": { "text/plain": [ - "'0b1001'" + "28" ] }, - "execution_count": 62, + "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "bin(9)" + "7 << 2" ] }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 78, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "'0b100100'" + "'0b111'" ] }, - "execution_count": 63, + "execution_count": 78, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "bin(9 << 2)" + "bin(7)" ] }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 79, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "36" + "'0b11100'" ] }, - "execution_count": 64, + "execution_count": 79, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "9 << 2" + "bin(7 << 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "28" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0b_1_1100" ] }, { @@ -2016,7 +2642,31 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 81, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "7 >> 1" + ] + }, + { + "cell_type": "code", + "execution_count": 82, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2026,40 +2676,64 @@ { "data": { "text/plain": [ - "'0b10'" + "'0b111'" ] }, - "execution_count": 65, + "execution_count": 82, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "bin(9 >> 2)" + "bin(7)" ] }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 83, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "2" + "'0b11'" ] }, - "execution_count": 66, + "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "9 >> 2" + "bin(7 >> 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0b_11" ] }, { @@ -2092,7 +2766,7 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 85, "metadata": { "slideshow": { "slide_type": "slide" @@ -2100,12 +2774,12 @@ }, "outputs": [], "source": [ - "f = 1.23" + "b = 42.0" ] }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 86, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2115,24 +2789,24 @@ { "data": { "text/plain": [ - "140166567715664" + "139663468493296" ] }, - "execution_count": 68, + "execution_count": 86, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "id(f)" + "id(b)" ] }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 87, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -2142,37 +2816,37 @@ "float" ] }, - "execution_count": 69, + "execution_count": 87, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "type(f)" + "type(b)" ] }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 88, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "1.23" + "42.0" ] }, - "execution_count": 70, + "execution_count": 88, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "f" + "b" ] }, { @@ -2188,7 +2862,7 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 89, "metadata": { "slideshow": { "slide_type": "skip" @@ -2201,7 +2875,7 @@ "0.123456789" ] }, - "execution_count": 71, + "execution_count": 89, "metadata": {}, "output_type": "execute_result" } @@ -2223,7 +2897,7 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 90, "metadata": { "slideshow": { "slide_type": "slide" @@ -2233,45 +2907,80 @@ { "data": { "text/plain": [ - "1.0" + "42.0" ] }, - "execution_count": 72, + "execution_count": 90, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "1. # 1 without a dot creates an int object" + "42." ] }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 91, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "1.0" + "42.0" ] }, - "execution_count": 73, + "execution_count": 91, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "float(1)" + "float(42)" ] }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 92, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42.0" + ] + }, + "execution_count": 92, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"42\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Leading and trailing whitespace is ignored ..." + ] + }, + { + "cell_type": "code", + "execution_count": 93, "metadata": { "slideshow": { "slide_type": "skip" @@ -2281,16 +2990,52 @@ { "data": { "text/plain": [ - "1.0" + "42.87" ] }, - "execution_count": 74, + "execution_count": 93, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "float(\"1.000\")" + "float(\" 42.87 \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... but not whitespace in between." + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "could not convert string to float: '42. 87'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfloat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"42. 87\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: could not convert string to float: '42. 87'" + ] + } + ], + "source": [ + "float(\"42. 87\")" ] }, { @@ -2306,7 +3051,7 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 95, "metadata": { "slideshow": { "slide_type": "slide" @@ -2319,7 +3064,7 @@ "0.3333333333333333" ] }, - "execution_count": 75, + "execution_count": 95, "metadata": {}, "output_type": "execute_result" } @@ -2341,7 +3086,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 96, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2354,7 +3099,7 @@ "42.0" ] }, - "execution_count": 76, + "execution_count": 96, "metadata": {}, "output_type": "execute_result" } @@ -2365,10 +3110,10 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 97, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -2378,7 +3123,7 @@ "42.0" ] }, - "execution_count": 77, + "execution_count": 97, "metadata": {}, "output_type": "execute_result" } @@ -2411,7 +3156,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 98, "metadata": { "slideshow": { "slide_type": "slide" @@ -2424,7 +3169,7 @@ "1.23" ] }, - "execution_count": 78, + "execution_count": 98, "metadata": {}, "output_type": "execute_result" } @@ -2446,7 +3191,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 99, "metadata": { "slideshow": { "slide_type": "skip" @@ -2455,10 +3200,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "invalid syntax (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23 e0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23 e0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], @@ -2468,7 +3213,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 100, "metadata": { "slideshow": { "slide_type": "skip" @@ -2477,10 +3222,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "invalid syntax (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23e 0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23e 0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], @@ -2490,7 +3235,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 101, "metadata": { "slideshow": { "slide_type": "skip" @@ -2499,10 +3244,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "invalid syntax (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23e0.0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23e0.0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], @@ -2518,32 +3263,32 @@ } }, "source": [ - "If we leave out the number to the left, Python raises a `NameError` as it unsuccessfully tries to look up a variable named `e1`." + "If we leave out the number to the left, Python raises a `NameError` as it unsuccessfully tries to look up a variable named `e0`." ] }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 102, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "skip" } }, "outputs": [ { "ename": "NameError", - "evalue": "name 'e1' is not defined", + "evalue": "name 'e0' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0me1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mNameError\u001b[0m: name 'e1' is not defined" + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0me0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mNameError\u001b[0m: name 'e0' is not defined" ] } ], "source": [ - "e1" + "e0" ] }, { @@ -2554,12 +3299,12 @@ } }, "source": [ - "So, to write $10^1$ in Python, we need to think of it as $1*10^1$ and write `1e1`." + "So, to write $10^0$ in Python, we need to think of it as $1*10^0$ and write `1e0`." ] }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 103, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2569,16 +3314,86 @@ { "data": { "text/plain": [ - "10.0" + "1.0" ] }, - "execution_count": 83, + "execution_count": 103, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "1e1" + "1e0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To express thousands of something (i.e., $10^3$), we write `1e3`." + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1000.0" + ] + }, + "execution_count": 104, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1e3 # = thousands" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Similarly, to express, for example, milliseconds (i.e., $10^{-3} s$), we write `1e-3`." + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.001" + ] + }, + "execution_count": 105, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1e-3 # = milli" ] }, { @@ -2605,7 +3420,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 106, "metadata": { "slideshow": { "slide_type": "slide" @@ -2618,18 +3433,18 @@ "nan" ] }, - "execution_count": 84, + "execution_count": 106, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "float(\"nan\") # also works as float(\"NaN\")" + "float(\"nan\") # also float(\"NaN\")" ] }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 107, "metadata": { "slideshow": { "slide_type": "skip" @@ -2642,21 +3457,21 @@ "inf" ] }, - "execution_count": 85, + "execution_count": 107, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "float(\"+inf\") # also works as float(\"+infinity\")" + "float(\"+inf\") # also float(\"+infinity\") or float(\"infinity\")" ] }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 108, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -2666,21 +3481,21 @@ "inf" ] }, - "execution_count": 86, + "execution_count": 108, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "float(\"inf\") # by omitting the plus sign we mean positive infinity" + "float(\"inf\") # same as float(\"+inf\")" ] }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 109, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -2690,7 +3505,7 @@ "-inf" ] }, - "execution_count": 87, + "execution_count": 109, "metadata": {}, "output_type": "execute_result" } @@ -2712,7 +3527,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 110, "metadata": { "slideshow": { "slide_type": "slide" @@ -2725,7 +3540,7 @@ "False" ] }, - "execution_count": 88, + "execution_count": 110, "metadata": {}, "output_type": "execute_result" } @@ -2742,17 +3557,52 @@ } }, "source": [ - "On the contrary, as two values go to infinity, there is no such concept as difference and *everything* compares equal." + "Another caveat is that any arithmetic involving a `nan` object results in `nan`. In other words, the addition below **fails silently** as no error is raised. As this also happens in accordance with the [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) standard, we *need* to be aware of that and check any data we work with for any `nan` occurrences *before* doing any calculations." ] }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 111, "metadata": { "slideshow": { "slide_type": "fragment" } }, + "outputs": [ + { + "data": { + "text/plain": [ + "nan" + ] + }, + "execution_count": 111, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 + float(\"nan\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "On the contrary, as two values go to infinity, there is no such concept as difference and *everything* compares equal." + ] + }, + { + "cell_type": "code", + "execution_count": 112, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [ { "data": { @@ -2760,7 +3610,7 @@ "True" ] }, - "execution_count": 89, + "execution_count": 112, "metadata": {}, "output_type": "execute_result" } @@ -2782,7 +3632,7 @@ }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 113, "metadata": { "slideshow": { "slide_type": "skip" @@ -2795,7 +3645,7 @@ "inf" ] }, - "execution_count": 90, + "execution_count": 113, "metadata": {}, "output_type": "execute_result" } @@ -2806,7 +3656,7 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 114, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2819,7 +3669,7 @@ "True" ] }, - "execution_count": 91, + "execution_count": 114, "metadata": {}, "output_type": "execute_result" } @@ -2841,7 +3691,7 @@ }, { "cell_type": "code", - "execution_count": 92, + "execution_count": 115, "metadata": { "slideshow": { "slide_type": "skip" @@ -2854,7 +3704,7 @@ "inf" ] }, - "execution_count": 92, + "execution_count": 115, "metadata": {}, "output_type": "execute_result" } @@ -2865,7 +3715,7 @@ }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 116, "metadata": { "slideshow": { "slide_type": "skip" @@ -2878,7 +3728,7 @@ "True" ] }, - "execution_count": 93, + "execution_count": 116, "metadata": {}, "output_type": "execute_result" } @@ -2900,7 +3750,7 @@ }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 117, "metadata": { "slideshow": { "slide_type": "skip" @@ -2913,7 +3763,7 @@ "inf" ] }, - "execution_count": 94, + "execution_count": 117, "metadata": {}, "output_type": "execute_result" } @@ -2924,7 +3774,7 @@ }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 118, "metadata": { "slideshow": { "slide_type": "skip" @@ -2937,7 +3787,7 @@ "True" ] }, - "execution_count": 95, + "execution_count": 118, "metadata": {}, "output_type": "execute_result" } @@ -2959,7 +3809,7 @@ }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 119, "metadata": { "slideshow": { "slide_type": "skip" @@ -2972,7 +3822,7 @@ "inf" ] }, - "execution_count": 96, + "execution_count": 119, "metadata": {}, "output_type": "execute_result" } @@ -2983,7 +3833,7 @@ }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 120, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2996,7 +3846,7 @@ "True" ] }, - "execution_count": 97, + "execution_count": 120, "metadata": {}, "output_type": "execute_result" } @@ -3013,12 +3863,12 @@ } }, "source": [ - "As a caveat, adding infinities of different signs is an *undefined operation* in math and results in a `nan` object. So, if we (accidentally or unknowingly) do this on a real dataset, we do *not* see any error messages, and our program may continue to run with non-meaningful results! This is an example of a piece of code **failing silently**." + "As a caveat, adding infinities of different signs is an *undefined operation* in math and results in a `nan` object. So, if we (accidentally or unknowingly) do this on a real dataset, we do *not* see any error messages, and our program may continue to run with non-meaningful results! This is another example of a piece of code **failing silently**." ] }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 121, "metadata": { "slideshow": { "slide_type": "slide" @@ -3031,7 +3881,7 @@ "nan" ] }, - "execution_count": 98, + "execution_count": 121, "metadata": {}, "output_type": "execute_result" } @@ -3042,10 +3892,10 @@ }, { "cell_type": "code", - "execution_count": 99, + "execution_count": 122, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -3055,7 +3905,7 @@ "nan" ] }, - "execution_count": 99, + "execution_count": 122, "metadata": {}, "output_type": "execute_result" } @@ -3085,71 +3935,12 @@ "source": [ "`float` objects are *inherently* imprecise, and there is *nothing* we can do about it! In particular, arithmetic operations with two `float` objects may result in \"weird\" rounding \"errors\" that are strictly deterministic and occur in accordance with the [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) standard.\n", "\n", - "For example, let's add `1e0` to `1e15` and `1e16`, respectively. In the latter case, the `1e0` somehow gets \"lost.\"" + "For example, let's add `1` to `1e15` and `1e16`, respectively. In the latter case, the `1` somehow gets \"lost.\"" ] }, { "cell_type": "code", - "execution_count": 100, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "1000000000000001.0" - ] - }, - "execution_count": 100, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "1e15 + 1e0" - ] - }, - { - "cell_type": "code", - "execution_count": 101, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "1e+16" - ] - }, - "execution_count": 101, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "1e16 + 1e0" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "Of course, we may also add the `int` object `1` to the `float` objects in the literal `e` notation with the same outcome." - ] - }, - { - "cell_type": "code", - "execution_count": 102, + "execution_count": 123, "metadata": { "slideshow": { "slide_type": "slide" @@ -3162,7 +3953,7 @@ "1000000000000001.0" ] }, - "execution_count": 102, + "execution_count": 123, "metadata": {}, "output_type": "execute_result" } @@ -3173,10 +3964,10 @@ }, { "cell_type": "code", - "execution_count": 103, + "execution_count": 124, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -3186,7 +3977,7 @@ "1e+16" ] }, - "execution_count": 103, + "execution_count": 124, "metadata": {}, "output_type": "execute_result" } @@ -3208,7 +3999,7 @@ }, { "cell_type": "code", - "execution_count": 104, + "execution_count": 125, "metadata": { "slideshow": { "slide_type": "slide" @@ -3221,10 +4012,10 @@ }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 126, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -3234,7 +4025,7 @@ "2.0000000000000004" ] }, - "execution_count": 105, + "execution_count": 126, "metadata": {}, "output_type": "execute_result" } @@ -3245,7 +4036,7 @@ }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 127, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3258,7 +4049,7 @@ "0.30000000000000004" ] }, - "execution_count": 106, + "execution_count": 127, "metadata": {}, "output_type": "execute_result" } @@ -3280,7 +4071,7 @@ }, { "cell_type": "code", - "execution_count": 107, + "execution_count": 128, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3293,7 +4084,7 @@ "False" ] }, - "execution_count": 107, + "execution_count": 128, "metadata": {}, "output_type": "execute_result" } @@ -3304,10 +4095,10 @@ }, { "cell_type": "code", - "execution_count": 108, + "execution_count": 129, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -3317,7 +4108,7 @@ "False" ] }, - "execution_count": 108, + "execution_count": 129, "metadata": {}, "output_type": "execute_result" } @@ -3339,7 +4130,7 @@ }, { "cell_type": "code", - "execution_count": 109, + "execution_count": 130, "metadata": { "slideshow": { "slide_type": "slide" @@ -3352,7 +4143,7 @@ }, { "cell_type": "code", - "execution_count": 110, + "execution_count": 131, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3365,7 +4156,7 @@ "True" ] }, - "execution_count": 110, + "execution_count": 131, "metadata": {}, "output_type": "execute_result" } @@ -3376,10 +4167,10 @@ }, { "cell_type": "code", - "execution_count": 111, + "execution_count": 132, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -3389,7 +4180,7 @@ "True" ] }, - "execution_count": 111, + "execution_count": 132, "metadata": {}, "output_type": "execute_result" } @@ -3406,14 +4197,14 @@ } }, "source": [ - "The built-in [format()](https://docs.python.org/3/library/functions.html#format) function allows us to show the **significant digits** of a `float` number as they exist in memory to arbitrary precision. To exemplify it, let's view a couple of `float` objects with `50` digits. This analysis reveals that almost no `float` number is precise! After $14$ or $15$ digits \"weird\" things happen. As we see further below, the \"random\" digits ending the `float` numbers do *not* \"physically\" exist in memory.\n", + "The built-in [format()](https://docs.python.org/3/library/functions.html#format) function allows us to show the **significant digits** of a `float` number as they exist in memory to arbitrary precision. To exemplify it, let's view a couple of `float` objects with `50` digits. This analysis reveals that almost no `float` number is precise! After 14 or 15 digits \"weird\" things happen. As we see further below, the \"random\" digits ending the `float` numbers do *not* \"physically\" exist in memory! Rather, they are \"calculated\" by the [format()](https://docs.python.org/3/library/functions.html#format) function that is forced to show `50` digits.\n", "\n", - "The [format()](https://docs.python.org/3/library/functions.html#format) function is different from the [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method on `str` objects introduced in the next chapter and both work with the so-called [format specification mini-language](https://docs.python.org/3/library/string.html#format-specification-mini-language): `\".50f\"` is the instruction to show `50` digits of a `float` number. But let's not worry too much about these details for now." + "The [format()](https://docs.python.org/3/library/functions.html#format) function is different from the [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method on `str` objects introduced in the next chapter (cf., [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text_00_lecture.ipynb#format%28%29-Method)): Yet, both work with the so-called [format specification mini-language](https://docs.python.org/3/library/string.html#format-specification-mini-language): `\".50f\"` is the instruction to show `50` digits of a `float` number." ] }, { "cell_type": "code", - "execution_count": 112, + "execution_count": 133, "metadata": { "slideshow": { "slide_type": "slide" @@ -3426,7 +4217,7 @@ "'0.10000000000000000555111512312578270211815834045410'" ] }, - "execution_count": 112, + "execution_count": 133, "metadata": {}, "output_type": "execute_result" } @@ -3437,10 +4228,10 @@ }, { "cell_type": "code", - "execution_count": 113, + "execution_count": 134, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -3450,7 +4241,7 @@ "'0.20000000000000001110223024625156540423631668090820'" ] }, - "execution_count": 113, + "execution_count": 134, "metadata": {}, "output_type": "execute_result" } @@ -3461,10 +4252,10 @@ }, { "cell_type": "code", - "execution_count": 114, + "execution_count": 135, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -3474,7 +4265,7 @@ "'0.29999999999999998889776975374843459576368331909180'" ] }, - "execution_count": 114, + "execution_count": 135, "metadata": {}, "output_type": "execute_result" } @@ -3485,7 +4276,7 @@ }, { "cell_type": "code", - "execution_count": 115, + "execution_count": 136, "metadata": { "slideshow": { "slide_type": "slide" @@ -3498,7 +4289,7 @@ "'0.33333333333333331482961625624739099293947219848633'" ] }, - "execution_count": 115, + "execution_count": 136, "metadata": {}, "output_type": "execute_result" } @@ -3524,7 +4315,7 @@ }, { "cell_type": "code", - "execution_count": 116, + "execution_count": 137, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3537,10 +4328,10 @@ }, { "cell_type": "code", - "execution_count": 117, + "execution_count": 138, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -3550,7 +4341,7 @@ "0.33333" ] }, - "execution_count": 117, + "execution_count": 138, "metadata": {}, "output_type": "execute_result" } @@ -3561,10 +4352,10 @@ }, { "cell_type": "code", - "execution_count": 118, + "execution_count": 139, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -3574,7 +4365,7 @@ "'0.33333000000000001517008740847813896834850311279297'" ] }, - "execution_count": 118, + "execution_count": 139, "metadata": {}, "output_type": "execute_result" } @@ -3596,7 +4387,7 @@ }, { "cell_type": "code", - "execution_count": 119, + "execution_count": 140, "metadata": { "slideshow": { "slide_type": "slide" @@ -3609,7 +4400,7 @@ "'0.12500000000000000000000000000000000000000000000000'" ] }, - "execution_count": 119, + "execution_count": 140, "metadata": {}, "output_type": "execute_result" } @@ -3620,10 +4411,10 @@ }, { "cell_type": "code", - "execution_count": 120, + "execution_count": 141, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -3633,7 +4424,7 @@ "'0.25000000000000000000000000000000000000000000000000'" ] }, - "execution_count": 120, + "execution_count": 141, "metadata": {}, "output_type": "execute_result" } @@ -3644,10 +4435,10 @@ }, { "cell_type": "code", - "execution_count": 121, + "execution_count": 142, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -3657,7 +4448,7 @@ "True" ] }, - "execution_count": 121, + "execution_count": 142, "metadata": {}, "output_type": "execute_result" } @@ -3685,10 +4476,28 @@ } }, "source": [ - "To understand these subtleties, we need to look at the **[binary representation of floats](https://en.wikipedia.org/wiki/Double-precision_floating-point_format)** and review the basics of the **[IEEE 754](https://en.wikipedia.org/wiki/IEEE_754)** standard. On modern machines, floats are modeled in so-called double precision with $64$ bits that are grouped as in the figure below. The first bit determines the sign ($0$ for plus, $1$ for minus), the next $11$ bits represent an $exponent$ term, and the last $52$ bits resemble the actual significant digits, the so-called $fraction$ part. The three groups are put together like so:\n", - "\n", - "$$float = (-1)^{sign} * 1.fraction * 2^{exponent-1023}$$\n", - "\n", + "To understand these subtleties, we need to look at the **[binary representation of floats](https://en.wikipedia.org/wiki/Double-precision_floating-point_format)** and review the basics of the **[IEEE 754](https://en.wikipedia.org/wiki/IEEE_754)** standard. On modern machines, floats are modeled in so-called double precision with $64$ bits that are grouped as in the figure below. The first bit determines the sign ($0$ for plus, $1$ for minus), the next $11$ bits represent an $exponent$ term, and the last $52$ bits resemble the actual significant digits, the so-called $fraction$ part. The three groups are put together like so:" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "$$float = (-1)^{sign} * 1.fraction * 2^{exponent-1023}$$" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ "A $1.$ is implicitly prepended as the first digit, and both, $fraction$ and $exponent$, are stored in base $2$ representation (i.e., they both are interpreted like integers above). As $exponent$ is consequently non-negative, between $0_{10}$ and $2047_{10}$ to be precise, the $-1023$, called the exponent bias, centers the entire $2^{exponent-1023}$ term around $1$ and allows the period within the $1.fraction$ part be shifted into either direction by the same amount. Floating-point numbers received their name as the period, formally called the **[radix point](https://en.wikipedia.org/wiki/Radix_point)**, \"floats\" along the significant digits. As an aside, an $exponent$ of all $0$s or all $1$s is used to model the special values `nan` or `inf`.\n", "\n", "As the standard defines the exponent part to come as a power of $2$, we now see why `0.125` is a *precise* float: It can be represented as a power of $2$, i.e., $0.125 = (-1)^0 * 1.0 * 2^{1020-1023} = 2^{-3} = \\frac{1}{8}$. In other words, the floating-point representation of $0.125_{10}$ is $0_2$, $1111111100_2 = 1020_{10}$, and $0_2$ for the three groups, respectively." @@ -3698,7 +4507,7 @@ "cell_type": "markdown", "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "-" } }, "source": [ @@ -3726,7 +4535,7 @@ }, { "cell_type": "code", - "execution_count": 122, + "execution_count": 143, "metadata": { "slideshow": { "slide_type": "slide" @@ -3739,10 +4548,10 @@ }, { "cell_type": "code", - "execution_count": 123, + "execution_count": 144, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -3752,13 +4561,13 @@ "'0x1.0000000000000p-3'" ] }, - "execution_count": 123, + "execution_count": 144, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "one_eighth.hex() # this basically says 2 ** (-3)" + "one_eighth.hex()" ] }, { @@ -3774,10 +4583,10 @@ }, { "cell_type": "code", - "execution_count": 124, + "execution_count": 145, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -3787,7 +4596,7 @@ "(1, 8)" ] }, - "execution_count": 124, + "execution_count": 145, "metadata": {}, "output_type": "execute_result" } @@ -3798,10 +4607,10 @@ }, { "cell_type": "code", - "execution_count": 125, + "execution_count": 146, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "slide" } }, "outputs": [ @@ -3811,7 +4620,7 @@ "'0x1.555475a31a4bep-2'" ] }, - "execution_count": 125, + "execution_count": 146, "metadata": {}, "output_type": "execute_result" } @@ -3822,10 +4631,10 @@ }, { "cell_type": "code", - "execution_count": 126, + "execution_count": 147, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -3835,7 +4644,7 @@ "(3002369727582815, 9007199254740992)" ] }, - "execution_count": 126, + "execution_count": 147, "metadata": {}, "output_type": "execute_result" } @@ -3857,10 +4666,10 @@ }, { "cell_type": "code", - "execution_count": 127, + "execution_count": 148, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "skip" } }, "outputs": [], @@ -3870,10 +4679,10 @@ }, { "cell_type": "code", - "execution_count": 128, + "execution_count": 149, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ @@ -3883,7 +4692,7 @@ "'0x0.0p+0'" ] }, - "execution_count": 128, + "execution_count": 149, "metadata": {}, "output_type": "execute_result" } @@ -3894,10 +4703,10 @@ }, { "cell_type": "code", - "execution_count": 129, + "execution_count": 150, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ @@ -3907,7 +4716,7 @@ "(0, 1)" ] }, - "execution_count": 129, + "execution_count": 150, "metadata": {}, "output_type": "execute_result" } @@ -3929,7 +4738,7 @@ }, { "cell_type": "code", - "execution_count": 130, + "execution_count": 151, "metadata": { "slideshow": { "slide_type": "skip" @@ -3942,7 +4751,7 @@ "False" ] }, - "execution_count": 130, + "execution_count": 151, "metadata": {}, "output_type": "execute_result" } @@ -3953,7 +4762,7 @@ }, { "cell_type": "code", - "execution_count": 131, + "execution_count": 152, "metadata": { "slideshow": { "slide_type": "skip" @@ -3966,7 +4775,7 @@ "True" ] }, - "execution_count": 131, + "execution_count": 152, "metadata": {}, "output_type": "execute_result" } @@ -3990,7 +4799,7 @@ }, { "cell_type": "code", - "execution_count": 132, + "execution_count": 153, "metadata": { "slideshow": { "slide_type": "skip" @@ -4003,7 +4812,7 @@ }, { "cell_type": "code", - "execution_count": 133, + "execution_count": 154, "metadata": { "slideshow": { "slide_type": "skip" @@ -4016,7 +4825,7 @@ "sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)" ] }, - "execution_count": 133, + "execution_count": 154, "metadata": {}, "output_type": "execute_result" } @@ -4044,14 +4853,14 @@ } }, "source": [ - "The [decimal](https://docs.python.org/3/library/decimal.html) module in the [standard library](https://docs.python.org/3/library/index.html) provides a [Decimal](https://docs.python.org/3/library/decimal.html#decimal.Decimal) type that may be used to represent any real number to a user-defined level of precision: \"User-defined\" does *not* mean an infinite or \"exact\" precision! The `Decimal` type merely allows us to work with a number of bits *different* from the $64$ as specified for the `float` type and also to customize the rounding rules and some other settings.\n", + "The [decimal](https://docs.python.org/3/library/decimal.html) module in the [standard library](https://docs.python.org/3/library/index.html) provides a [Decimal](https://docs.python.org/3/library/decimal.html#decimal.Decimal) type that may be used to represent any real number to a user-defined level of precision: \"User-defined\" does *not* mean an infinite or exact precision! The `Decimal` type merely allows us to work with a number of bits *different* from the $64$ as specified for the `float` type and also to customize the rounding rules and some other settings.\n", "\n", "We import the `Decimal` type and also the [getcontext()](https://docs.python.org/3/library/decimal.html#decimal.getcontext) function from the [decimal](https://docs.python.org/3/library/decimal.html) module." ] }, { "cell_type": "code", - "execution_count": 134, + "execution_count": 155, "metadata": { "slideshow": { "slide_type": "slide" @@ -4075,7 +4884,7 @@ }, { "cell_type": "code", - "execution_count": 135, + "execution_count": 156, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4088,7 +4897,7 @@ "Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])" ] }, - "execution_count": 135, + "execution_count": 156, "metadata": {}, "output_type": "execute_result" } @@ -4110,7 +4919,7 @@ }, { "cell_type": "code", - "execution_count": 136, + "execution_count": 157, "metadata": { "slideshow": { "slide_type": "slide" @@ -4123,7 +4932,7 @@ "Decimal('42')" ] }, - "execution_count": 136, + "execution_count": 157, "metadata": {}, "output_type": "execute_result" } @@ -4134,10 +4943,10 @@ }, { "cell_type": "code", - "execution_count": 137, + "execution_count": 158, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -4147,7 +4956,7 @@ "Decimal('0.1')" ] }, - "execution_count": 137, + "execution_count": 158, "metadata": {}, "output_type": "execute_result" } @@ -4158,26 +4967,26 @@ }, { "cell_type": "code", - "execution_count": 138, + "execution_count": 159, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "Decimal('1E+5')" + "Decimal('0.001')" ] }, - "execution_count": 138, + "execution_count": 159, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "Decimal(\"1e5\")" + "Decimal(\"1e-3\")" ] }, { @@ -4193,7 +5002,7 @@ }, { "cell_type": "code", - "execution_count": 139, + "execution_count": 160, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4206,13 +5015,13 @@ "Decimal('0.1000000000000000055511151231257827021181583404541015625')" ] }, - "execution_count": 139, + "execution_count": 160, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "Decimal(0.1) # do not create a Decimal from a float" + "Decimal(0.1) # do not do this" ] }, { @@ -4228,7 +5037,7 @@ }, { "cell_type": "code", - "execution_count": 140, + "execution_count": 161, "metadata": { "slideshow": { "slide_type": "slide" @@ -4241,7 +5050,7 @@ "Decimal('0.3')" ] }, - "execution_count": 140, + "execution_count": 161, "metadata": {}, "output_type": "execute_result" } @@ -4252,10 +5061,10 @@ }, { "cell_type": "code", - "execution_count": 141, + "execution_count": 162, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -4265,7 +5074,7 @@ "True" ] }, - "execution_count": 141, + "execution_count": 162, "metadata": {}, "output_type": "execute_result" } @@ -4287,7 +5096,7 @@ }, { "cell_type": "code", - "execution_count": 142, + "execution_count": 163, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4300,7 +5109,7 @@ "Decimal('0.30000')" ] }, - "execution_count": 142, + "execution_count": 163, "metadata": {}, "output_type": "execute_result" } @@ -4311,10 +5120,10 @@ }, { "cell_type": "code", - "execution_count": 143, + "execution_count": 164, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ @@ -4324,7 +5133,7 @@ "True" ] }, - "execution_count": 143, + "execution_count": 164, "metadata": {}, "output_type": "execute_result" } @@ -4346,7 +5155,7 @@ }, { "cell_type": "code", - "execution_count": 144, + "execution_count": 165, "metadata": { "slideshow": { "slide_type": "slide" @@ -4359,42 +5168,42 @@ "Decimal('42')" ] }, - "execution_count": 144, + "execution_count": 165, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "42 + Decimal(42) - 42" + "21 + Decimal(21)" ] }, { "cell_type": "code", - "execution_count": 145, + "execution_count": 166, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "Decimal('42')" + "Decimal('42.0')" ] }, - "execution_count": 145, + "execution_count": 166, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "10 * Decimal(42) / 10" + "10 * Decimal(\"4.2\")" ] }, { "cell_type": "code", - "execution_count": 146, + "execution_count": 167, "metadata": { "slideshow": { "slide_type": "slide" @@ -4407,7 +5216,7 @@ "Decimal('0.1')" ] }, - "execution_count": 146, + "execution_count": 167, "metadata": {}, "output_type": "execute_result" } @@ -4429,7 +5238,7 @@ }, { "cell_type": "code", - "execution_count": 147, + "execution_count": 168, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4442,7 +5251,7 @@ "'0.10000000000000000000000000000000000000000000000000'" ] }, - "execution_count": 147, + "execution_count": 168, "metadata": {}, "output_type": "execute_result" } @@ -4453,10 +5262,10 @@ }, { "cell_type": "code", - "execution_count": 148, + "execution_count": 169, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -4466,7 +5275,7 @@ "'0.10000000000000000555111512312578270211815834045410'" ] }, - "execution_count": 148, + "execution_count": 169, "metadata": {}, "output_type": "execute_result" } @@ -4488,7 +5297,7 @@ }, { "cell_type": "code", - "execution_count": 149, + "execution_count": 170, "metadata": { "slideshow": { "slide_type": "slide" @@ -4502,7 +5311,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m1.0\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m42\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m1.0\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m42\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for *: 'float' and 'decimal.Decimal'" ] } @@ -4519,15 +5328,15 @@ } }, "source": [ - "To preserve the precision for more advanced mathematical functions, `Decimal` objects come with many **methods bound** on them. For example, [ln()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.ln) and [log10()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.log10) take the logarithm while [sqrt()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.sqrt) calculates the square root. In general, the functions in the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html) should only be used with `float` objects as they do *not* preserve precision." + "To preserve the precision for more advanced mathematical functions, `Decimal` objects come with many **methods bound** on them. For example, [ln()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.ln) and [log10()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.log10) take the logarithm while [sqrt()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.sqrt) calculates the square root. The methods always return a *new* `Decimal` object. We must never use the functions in the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html) with `Decimal` objects as they do *not* preserve precision." ] }, { "cell_type": "code", - "execution_count": 150, + "execution_count": 171, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "skip" } }, "outputs": [ @@ -4537,7 +5346,7 @@ "Decimal('2')" ] }, - "execution_count": 150, + "execution_count": 171, "metadata": {}, "output_type": "execute_result" } @@ -4548,10 +5357,10 @@ }, { "cell_type": "code", - "execution_count": 151, + "execution_count": 172, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "slide" } }, "outputs": [ @@ -4561,7 +5370,7 @@ "Decimal('1.414213562373095048801688724')" ] }, - "execution_count": 151, + "execution_count": 172, "metadata": {}, "output_type": "execute_result" } @@ -4585,7 +5394,7 @@ }, { "cell_type": "code", - "execution_count": 152, + "execution_count": 173, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4598,7 +5407,7 @@ "Decimal('1.999999999999999999999999999')" ] }, - "execution_count": 152, + "execution_count": 173, "metadata": {}, "output_type": "execute_result" } @@ -4617,17 +5426,17 @@ } }, "source": [ - "The [quantize()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize) method allows us to [quantize](https://www.dictionary.com/browse/quantize) (i.e., \"round\") a `Decimal` number at any precision that is *smaller* than the set precision. It looks at the number of decimals (i.e., to the right of the period) of the numeric argument we pass in.\n", + "However, the [quantize()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize) method allows us to [quantize](https://www.dictionary.com/browse/quantize) (i.e., \"round\") a `Decimal` number at any precision that is *smaller* than the set precision. It takes the number of decimals to the right of the period of the `Decimal` argument we pass in and rounds accordingly.\n", "\n", - "For example, as the overall imprecise value of `two` still has an internal precision of `28` digits, we can correctly round it to *four* decimals (i.e., `Decimal(\"0.0001\")` has four decimals)." + "For example, as the overall imprecise value of `two` still has an internal precision of `28` digits, we can correctly round it to *four* decimals (i.e., `Decimal(\"0.0000\")` has four decimals)." ] }, { "cell_type": "code", - "execution_count": 153, + "execution_count": 174, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "slide" } }, "outputs": [ @@ -4637,26 +5446,34 @@ "Decimal('2.0000')" ] }, - "execution_count": 153, + "execution_count": 174, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "two.quantize(Decimal(\"0.0001\"))" + "two.quantize(Decimal(\"0.0000\"))" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "We can never round a `Decimal` number and obtain a greater precision than before: The `InvalidOperation` exception tells us that *loudly*." ] }, { "cell_type": "code", - "execution_count": 154, - "metadata": {}, + "execution_count": 175, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "ename": "InvalidOperation", @@ -4665,13 +5482,13 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtwo\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquantize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"0.1\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m**\u001b[0m \u001b[0;36m28\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtwo\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquantize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"1e-28\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m: []" ] } ], "source": [ - "two.quantize(Decimal(\"0.1\") ** 28)" + "two.quantize(Decimal(\"1e-28\"))" ] }, { @@ -4687,7 +5504,7 @@ }, { "cell_type": "code", - "execution_count": 155, + "execution_count": 176, "metadata": { "slideshow": { "slide_type": "skip" @@ -4700,13 +5517,13 @@ "True" ] }, - "execution_count": 155, + "execution_count": 176, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "two.quantize(Decimal(\"0.0001\")) == 2" + "two.quantize(Decimal(\"0.0000\")) == 2" ] }, { @@ -4722,10 +5539,10 @@ }, { "cell_type": "code", - "execution_count": 156, + "execution_count": 177, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "skip" } }, "outputs": [ @@ -4735,13 +5552,13 @@ "True" ] }, - "execution_count": 156, + "execution_count": 177, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "(Decimal(2).sqrt() ** 2).quantize(Decimal(\"0.0001\")) == 2" + "(Decimal(2).sqrt() ** 2).quantize(Decimal(\"0.0000\")) == 2" ] }, { @@ -4757,7 +5574,7 @@ }, { "cell_type": "code", - "execution_count": 157, + "execution_count": 178, "metadata": { "slideshow": { "slide_type": "skip" @@ -4770,7 +5587,7 @@ "Decimal('NaN')" ] }, - "execution_count": 157, + "execution_count": 178, "metadata": {}, "output_type": "execute_result" } @@ -4779,9 +5596,20 @@ "Decimal(\"nan\")" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`Decimal(\"nan\")`s never compare equal to anything, not even to themselves." + ] + }, { "cell_type": "code", - "execution_count": 158, + "execution_count": 179, "metadata": { "slideshow": { "slide_type": "skip" @@ -4794,18 +5622,29 @@ "False" ] }, - "execution_count": 158, + "execution_count": 179, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "Decimal(\"nan\") == Decimal(\"nan\") # nan's never compare equal to anything, not even to themselves" + "Decimal(\"nan\") == Decimal(\"nan\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Infinity is larger than any concrete number." ] }, { "cell_type": "code", - "execution_count": 159, + "execution_count": 180, "metadata": { "slideshow": { "slide_type": "skip" @@ -4818,7 +5657,7 @@ "Decimal('Infinity')" ] }, - "execution_count": 159, + "execution_count": 180, "metadata": {}, "output_type": "execute_result" } @@ -4829,7 +5668,7 @@ }, { "cell_type": "code", - "execution_count": 160, + "execution_count": 181, "metadata": { "slideshow": { "slide_type": "skip" @@ -4842,7 +5681,7 @@ "Decimal('-Infinity')" ] }, - "execution_count": 160, + "execution_count": 181, "metadata": {}, "output_type": "execute_result" } @@ -4853,7 +5692,7 @@ }, { "cell_type": "code", - "execution_count": 161, + "execution_count": 182, "metadata": { "slideshow": { "slide_type": "skip" @@ -4866,18 +5705,18 @@ "Decimal('Infinity')" ] }, - "execution_count": 161, + "execution_count": 182, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "Decimal(\"inf\") + 42 # Infinity is infinity, concrete numbers loose its meaning" + "Decimal(\"inf\") + 42" ] }, { "cell_type": "code", - "execution_count": 162, + "execution_count": 183, "metadata": { "slideshow": { "slide_type": "skip" @@ -4890,7 +5729,7 @@ "True" ] }, - "execution_count": 162, + "execution_count": 183, "metadata": {}, "output_type": "execute_result" } @@ -4907,12 +5746,12 @@ } }, "source": [ - "As with `float` objects, we cannot add infinities of different signs: Now get a module-specific `InvalidOperation` exception instead of a `nan` value. Here, **failing loudly** is a good thing as it prevents us from working with invalid results." + "As with `float` objects, we cannot add infinities of different signs: Now, get a module-specific `InvalidOperation` exception instead of a `nan` value. Here, **failing loudly** is a good thing as it prevents us from working with invalid results." ] }, { "cell_type": "code", - "execution_count": 163, + "execution_count": 184, "metadata": { "slideshow": { "slide_type": "skip" @@ -4926,7 +5765,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"-inf\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"-inf\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m: []" ] } @@ -4937,7 +5776,7 @@ }, { "cell_type": "code", - "execution_count": 164, + "execution_count": 185, "metadata": { "slideshow": { "slide_type": "skip" @@ -4951,7 +5790,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m: []" ] } @@ -4997,7 +5836,7 @@ }, { "cell_type": "code", - "execution_count": 165, + "execution_count": 186, "metadata": { "slideshow": { "slide_type": "slide" @@ -5021,7 +5860,7 @@ }, { "cell_type": "code", - "execution_count": 166, + "execution_count": 187, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5034,21 +5873,21 @@ "Fraction(1, 3)" ] }, - "execution_count": 166, + "execution_count": 187, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "Fraction(1, 3) # this is now 1/3 with \"full\" precision" + "Fraction(1, 3) # 1/3 with \"full\" precision" ] }, { "cell_type": "code", - "execution_count": 167, + "execution_count": 188, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -5058,21 +5897,21 @@ "Fraction(1, 3)" ] }, - "execution_count": 167, + "execution_count": 188, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "Fraction(\"1/3\") # this is 1/3 with \"full\" precision again" + "Fraction(\"1/3\") # 1/3 with \"full\" precision" ] }, { "cell_type": "code", - "execution_count": 168, + "execution_count": 189, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -5082,21 +5921,21 @@ "Fraction(3333333333, 10000000000)" ] }, - "execution_count": 168, + "execution_count": 189, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "Fraction(\"0.3333333333\") # this is 1/3 with a precision of 10 significant digits" + "Fraction(\"0.3333333333\") # 1/3 with a precision of 10 significant digits" ] }, { "cell_type": "code", - "execution_count": 169, + "execution_count": 190, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ @@ -5106,13 +5945,13 @@ "Fraction(3333333333, 10000000000)" ] }, - "execution_count": 169, + "execution_count": 190, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "Fraction(\"3333333333e-10\") # the same in scientific notation" + "Fraction(\"3333333333e-10\") # scientific notation is also allowed" ] }, { @@ -5128,7 +5967,7 @@ }, { "cell_type": "code", - "execution_count": 170, + "execution_count": 191, "metadata": { "slideshow": { "slide_type": "slide" @@ -5141,7 +5980,7 @@ "Fraction(3, 2)" ] }, - "execution_count": 170, + "execution_count": 191, "metadata": {}, "output_type": "execute_result" } @@ -5152,10 +5991,10 @@ }, { "cell_type": "code", - "execution_count": 171, + "execution_count": 192, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -5165,7 +6004,7 @@ "Fraction(3, 2)" ] }, - "execution_count": 171, + "execution_count": 192, "metadata": {}, "output_type": "execute_result" } @@ -5187,7 +6026,7 @@ }, { "cell_type": "code", - "execution_count": 172, + "execution_count": 193, "metadata": { "slideshow": { "slide_type": "slide" @@ -5200,7 +6039,7 @@ "Fraction(1, 10)" ] }, - "execution_count": 172, + "execution_count": 193, "metadata": {}, "output_type": "execute_result" } @@ -5222,10 +6061,10 @@ }, { "cell_type": "code", - "execution_count": 173, + "execution_count": 194, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -5235,7 +6074,7 @@ "Fraction(3602879701896397, 36028797018963968)" ] }, - "execution_count": 173, + "execution_count": 194, "metadata": {}, "output_type": "execute_result" } @@ -5257,7 +6096,7 @@ }, { "cell_type": "code", - "execution_count": 174, + "execution_count": 195, "metadata": { "slideshow": { "slide_type": "slide" @@ -5270,7 +6109,7 @@ "Fraction(7, 4)" ] }, - "execution_count": 174, + "execution_count": 195, "metadata": {}, "output_type": "execute_result" } @@ -5281,10 +6120,10 @@ }, { "cell_type": "code", - "execution_count": 175, + "execution_count": 196, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -5294,7 +6133,7 @@ "Fraction(1, 2)" ] }, - "execution_count": 175, + "execution_count": 196, "metadata": {}, "output_type": "execute_result" } @@ -5305,10 +6144,10 @@ }, { "cell_type": "code", - "execution_count": 176, + "execution_count": 197, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -5318,7 +6157,7 @@ "Fraction(1, 1)" ] }, - "execution_count": 176, + "execution_count": 197, "metadata": {}, "output_type": "execute_result" } @@ -5329,10 +6168,10 @@ }, { "cell_type": "code", - "execution_count": 177, + "execution_count": 198, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -5342,7 +6181,7 @@ "Fraction(1, 1)" ] }, - "execution_count": 177, + "execution_count": 198, "metadata": {}, "output_type": "execute_result" } @@ -5364,7 +6203,7 @@ }, { "cell_type": "code", - "execution_count": 178, + "execution_count": 199, "metadata": { "slideshow": { "slide_type": "slide" @@ -5377,18 +6216,18 @@ "0.1" ] }, - "execution_count": 178, + "execution_count": 199, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "10.0 * Fraction(1, 100)" + "10.0 * Fraction(1, 100) # do not do this" ] }, { "cell_type": "code", - "execution_count": 179, + "execution_count": 200, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5401,7 +6240,7 @@ "'0.10000000000000000555111512312578270211815834045410'" ] }, - "execution_count": 179, + "execution_count": 200, "metadata": {}, "output_type": "execute_result" } @@ -5432,6 +6271,17 @@ "## The `complex` Type" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "**What is the solution to $x^2 = -1$ ?**" + ] + }, { "cell_type": "markdown", "metadata": { @@ -5453,7 +6303,7 @@ "cell_type": "markdown", "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "-" } }, "source": [ @@ -5475,7 +6325,7 @@ }, { "cell_type": "code", - "execution_count": 180, + "execution_count": 201, "metadata": { "slideshow": { "slide_type": "slide" @@ -5488,7 +6338,7 @@ }, { "cell_type": "code", - "execution_count": 181, + "execution_count": 202, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5498,10 +6348,10 @@ { "data": { "text/plain": [ - "140166567772848" + "139663415663408" ] }, - "execution_count": 181, + "execution_count": 202, "metadata": {}, "output_type": "execute_result" } @@ -5512,10 +6362,10 @@ }, { "cell_type": "code", - "execution_count": 182, + "execution_count": 203, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -5525,7 +6375,7 @@ "complex" ] }, - "execution_count": 182, + "execution_count": 203, "metadata": {}, "output_type": "execute_result" } @@ -5536,10 +6386,10 @@ }, { "cell_type": "code", - "execution_count": 183, + "execution_count": 204, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -5549,7 +6399,7 @@ "1j" ] }, - "execution_count": 183, + "execution_count": 204, "metadata": {}, "output_type": "execute_result" } @@ -5571,7 +6421,7 @@ }, { "cell_type": "code", - "execution_count": 184, + "execution_count": 205, "metadata": { "slideshow": { "slide_type": "slide" @@ -5584,7 +6434,7 @@ "True" ] }, - "execution_count": 184, + "execution_count": 205, "metadata": {}, "output_type": "execute_result" } @@ -5606,7 +6456,7 @@ }, { "cell_type": "code", - "execution_count": 185, + "execution_count": 206, "metadata": { "slideshow": { "slide_type": "slide" @@ -5619,7 +6469,7 @@ "(2+0.5j)" ] }, - "execution_count": 185, + "execution_count": 206, "metadata": {}, "output_type": "execute_result" } @@ -5641,10 +6491,10 @@ }, { "cell_type": "code", - "execution_count": 186, + "execution_count": 207, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -5654,21 +6504,32 @@ "(2+0.5j)" ] }, - "execution_count": 186, + "execution_count": 207, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "complex(2, 0.5) # an integer and a float work just fine as arguments" + "complex(2, 0.5)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "By omitting the second argument, we set the imaginary part to $0$." ] }, { "cell_type": "code", - "execution_count": 187, + "execution_count": 208, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ @@ -5678,18 +6539,29 @@ "(2+0j)" ] }, - "execution_count": 187, + "execution_count": 208, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "complex(2) # omitting the second argument sets the imaginary part to 0" + "complex(2) " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + " The arguments to [complex()](https://docs.python.org/3/library/functions.html#complex) may be any numeric type." ] }, { "cell_type": "code", - "execution_count": 188, + "execution_count": 209, "metadata": { "slideshow": { "slide_type": "skip" @@ -5702,18 +6574,18 @@ "(2+0.5j)" ] }, - "execution_count": 188, + "execution_count": 209, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "complex(Decimal(\"2.0\"), Fraction(1, 2)) # the arguments may be any numeric type" + "complex(Decimal(\"2.0\"), Fraction(1, 2))" ] }, { "cell_type": "code", - "execution_count": 189, + "execution_count": 210, "metadata": { "slideshow": { "slide_type": "skip" @@ -5726,7 +6598,7 @@ "(2+0.5j)" ] }, - "execution_count": 189, + "execution_count": 210, "metadata": {}, "output_type": "execute_result" } @@ -5748,7 +6620,7 @@ }, { "cell_type": "code", - "execution_count": 190, + "execution_count": 211, "metadata": { "slideshow": { "slide_type": "slide" @@ -5762,7 +6634,7 @@ }, { "cell_type": "code", - "execution_count": 191, + "execution_count": 212, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5775,7 +6647,7 @@ "(4+6j)" ] }, - "execution_count": 191, + "execution_count": 212, "metadata": {}, "output_type": "execute_result" } @@ -5786,10 +6658,10 @@ }, { "cell_type": "code", - "execution_count": 192, + "execution_count": 213, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -5799,7 +6671,7 @@ "(-2-2j)" ] }, - "execution_count": 192, + "execution_count": 213, "metadata": {}, "output_type": "execute_result" } @@ -5810,7 +6682,7 @@ }, { "cell_type": "code", - "execution_count": 193, + "execution_count": 214, "metadata": { "slideshow": { "slide_type": "skip" @@ -5820,21 +6692,21 @@ { "data": { "text/plain": [ - "(40+2j)" + "(2+2j)" ] }, - "execution_count": 193, + "execution_count": 214, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "c1 + 39" + "c1 + 1" ] }, { "cell_type": "code", - "execution_count": 194, + "execution_count": 215, "metadata": { "slideshow": { "slide_type": "skip" @@ -5844,21 +6716,21 @@ { "data": { "text/plain": [ - "-4j" + "(0.5-4j)" ] }, - "execution_count": 194, + "execution_count": 215, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "3.0 - c2" + "3.5 - c2" ] }, { "cell_type": "code", - "execution_count": 195, + "execution_count": 216, "metadata": { "slideshow": { "slide_type": "skip" @@ -5871,7 +6743,7 @@ "(5+10j)" ] }, - "execution_count": 195, + "execution_count": 216, "metadata": {}, "output_type": "execute_result" } @@ -5882,7 +6754,7 @@ }, { "cell_type": "code", - "execution_count": 196, + "execution_count": 217, "metadata": { "slideshow": { "slide_type": "skip" @@ -5895,7 +6767,7 @@ "(0.5+0.6666666666666666j)" ] }, - "execution_count": 196, + "execution_count": 217, "metadata": {}, "output_type": "execute_result" } @@ -5906,7 +6778,7 @@ }, { "cell_type": "code", - "execution_count": 197, + "execution_count": 218, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5919,7 +6791,7 @@ "(-5+10j)" ] }, - "execution_count": 197, + "execution_count": 218, "metadata": {}, "output_type": "execute_result" } @@ -5930,10 +6802,10 @@ }, { "cell_type": "code", - "execution_count": 198, + "execution_count": 219, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -5943,7 +6815,7 @@ "(0.44+0.08j)" ] }, - "execution_count": 198, + "execution_count": 219, "metadata": {}, "output_type": "execute_result" } @@ -5965,7 +6837,7 @@ }, { "cell_type": "code", - "execution_count": 199, + "execution_count": 220, "metadata": { "slideshow": { "slide_type": "slide" @@ -5978,7 +6850,7 @@ "5.0" ] }, - "execution_count": 199, + "execution_count": 220, "metadata": {}, "output_type": "execute_result" } @@ -6000,7 +6872,7 @@ }, { "cell_type": "code", - "execution_count": 200, + "execution_count": 221, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6013,7 +6885,7 @@ "1.0" ] }, - "execution_count": 200, + "execution_count": 221, "metadata": {}, "output_type": "execute_result" } @@ -6024,10 +6896,10 @@ }, { "cell_type": "code", - "execution_count": 201, + "execution_count": 222, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -6037,7 +6909,7 @@ "2.0" ] }, - "execution_count": 201, + "execution_count": 222, "metadata": {}, "output_type": "execute_result" } @@ -6059,7 +6931,7 @@ }, { "cell_type": "code", - "execution_count": 202, + "execution_count": 223, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6072,7 +6944,7 @@ "(1-2j)" ] }, - "execution_count": 202, + "execution_count": 223, "metadata": {}, "output_type": "execute_result" } @@ -6160,7 +7032,7 @@ }, { "cell_type": "code", - "execution_count": 203, + "execution_count": 224, "metadata": { "slideshow": { "slide_type": "slide" @@ -6173,10 +7045,10 @@ }, { "cell_type": "code", - "execution_count": 204, + "execution_count": 225, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "slide" } }, "outputs": [ @@ -6201,7 +7073,7 @@ " 'abstractmethod']" ] }, - "execution_count": 204, + "execution_count": 225, "metadata": {}, "output_type": "execute_result" } @@ -6227,7 +7099,7 @@ }, { "cell_type": "code", - "execution_count": 205, + "execution_count": 226, "metadata": { "scrolled": true, "slideshow": { @@ -6338,7 +7210,7 @@ }, { "cell_type": "code", - "execution_count": 206, + "execution_count": 227, "metadata": { "slideshow": { "slide_type": "skip" @@ -6490,6 +7362,17 @@ "help(complex)" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Duck Typing" + ] + }, { "cell_type": "markdown", "metadata": { @@ -6498,14 +7381,16 @@ } }, "source": [ - "The primary purpose of ABCs is to classify the *concrete* data types and standardize how they behave.\n", + "The primary purpose of ABCs is to classify the *concrete* data types and standardize how they behave. This guides us as the programmers in what kind of behavior we should expect from objects of a given data type. In this context, ABCs are not reflected in code but only in our heads.\n", "\n", - "For, example, as all numeric data types are `Complex` numbers in the abstract sense, they all work with the built-in [abs()](https://docs.python.org/3/library/functions.html#abs) function (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Complex)). While it is intuitively clear what the [absolute value](https://en.wikipedia.org/wiki/Absolute_value) (i.e., \"distance\" to $0$) of an integer, a fraction, or any real number is, [abs()](https://docs.python.org/3/library/functions.html#abs) calculates the equivalent of that for complex numbers. That concept is called the [magnitude](https://en.wikipedia.org/wiki/Magnitude_%28mathematics%29) of a number, and is really a *generalization* of the absolute value." + "For, example, as all numeric data types are `Complex` numbers in the abstract sense, they all work with the built-in [abs()](https://docs.python.org/3/library/functions.html#abs) function (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Complex)). While it is intuitively clear what the [absolute value](https://en.wikipedia.org/wiki/Absolute_value) (i.e., \"distance\" to $0$) of an integer, a fraction, or any real number is, [abs()](https://docs.python.org/3/library/functions.html#abs) calculates the equivalent of that for complex numbers. That concept is called the [magnitude](https://en.wikipedia.org/wiki/Magnitude_%28mathematics%29) of a number, and is really a *generalization* of the absolute value.\n", + "\n", + "Relating back to the concept of **duck typing** mentioned in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_lecture.ipynb#Type-Checking-&-Input-Validation), `int`, `float`, and `complex` objects \"walk\" and \"quack\" alike in context of the [abs()](https://docs.python.org/3/library/functions.html#abs) function." ] }, { "cell_type": "code", - "execution_count": 207, + "execution_count": 228, "metadata": { "slideshow": { "slide_type": "slide" @@ -6515,48 +7400,48 @@ { "data": { "text/plain": [ - "42" + "1" ] }, - "execution_count": 207, + "execution_count": 228, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "abs(-42)" + "abs(-1)" ] }, { "cell_type": "code", - "execution_count": 208, + "execution_count": 229, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "Decimal('0.1')" + "42.87" ] }, - "execution_count": 208, + "execution_count": 229, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "abs(Decimal(\"-0.1\"))" + "abs(-42.87)" ] }, { "cell_type": "code", - "execution_count": 209, + "execution_count": 230, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -6566,7 +7451,7 @@ "5.0" ] }, - "execution_count": 209, + "execution_count": 230, "metadata": {}, "output_type": "execute_result" } @@ -6588,7 +7473,31 @@ }, { "cell_type": "code", - "execution_count": 210, + "execution_count": 231, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "100" + ] + }, + "execution_count": 231, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "round(123, -2)" + ] + }, + { + "cell_type": "code", + "execution_count": 232, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6601,7 +7510,7 @@ "42" ] }, - "execution_count": 210, + "execution_count": 232, "metadata": {}, "output_type": "execute_result" } @@ -6610,30 +7519,6 @@ "round(42.1)" ] }, - { - "cell_type": "code", - "execution_count": 211, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 211, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "round(Decimal(\"0.1\"))" - ] - }, { "cell_type": "markdown", "metadata": { @@ -6642,15 +7527,15 @@ } }, "source": [ - "`Complex` numbers are two-dimensional. So, rounding makes no sense and raises a `TypeError`." + "`Complex` numbers are two-dimensional. So, rounding makes no sense here and leads to a `TypeError`. So, in the context of the [round()](https://docs.python.org/3/library/functions.html#round) function, `int` and `float` objects \"walk\" and \"quack\" alike whereas `complex` objects do not." ] }, { "cell_type": "code", - "execution_count": 212, + "execution_count": 233, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -6661,311 +7546,13 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mround\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m3j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mround\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m2j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: type complex doesn't define __round__ method" ] } ], "source": [ - "round(4 + 3j)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "Knowing what ABCs a numeric type adheres to, is not only important in the context of built-ins. The [trunc()](https://docs.python.org/3/library/math.html#math.trunc) function from the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html), for example, only works with `Real` types (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Real))." - ] - }, - { - "cell_type": "code", - "execution_count": 213, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [], - "source": [ - "import math" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "[trunc()](https://docs.python.org/3/library/math.html#math.trunc) cuts off a number's decimals." - ] - }, - { - "cell_type": "code", - "execution_count": 214, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 214, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "math.trunc(9 / 10)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "A `Complex` number leads to a `TypeError`." - ] - }, - { - "cell_type": "code", - "execution_count": 215, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [ - { - "ename": "TypeError", - "evalue": "type complex doesn't define __trunc__ method", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0.9\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m: type complex doesn't define __trunc__ method" - ] - } - ], - "source": [ - "math.trunc(0.9 + 1j)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "Another way to use ABCs is in place of a *concrete* data type.\n", - "\n", - "For example, we may pass them as arguments to the built-in [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function and check in which of the five mathematical sets the object `1 / 10` is." - ] - }, - { - "cell_type": "code", - "execution_count": 216, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 216, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "isinstance(1 / 10, float) # we know that from before" - ] - }, - { - "cell_type": "code", - "execution_count": 217, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 217, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "isinstance(1 / 10, numbers.Number) # a float object is a Number in the abstract sense" - ] - }, - { - "cell_type": "code", - "execution_count": 218, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 218, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "isinstance(1 / 10, numbers.Complex) # float objects are always also Complex numbers" - ] - }, - { - "cell_type": "code", - "execution_count": 219, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 219, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "isinstance(1 / 10, numbers.Real) # a float object's purpose is to model a Real number" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "Due to the `float` type's inherent imprecision, `1 / 10` is *not* a rational number." - ] - }, - { - "cell_type": "code", - "execution_count": 220, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 220, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "isinstance(1 / 10, numbers.Rational) # the type of `1 / 10` is what is important, not its value" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "The `Fraction` type qualifies as a rational number; however, the `Decimal` type does not." - ] - }, - { - "cell_type": "code", - "execution_count": 221, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 221, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "isinstance(Fraction(1, 10), numbers.Rational)" - ] - }, - { - "cell_type": "code", - "execution_count": 222, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 222, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "isinstance(Decimal(\"0.1\"), numbers.Rational)" + "round(1 + 2j)" ] }, { @@ -6987,7 +7574,197 @@ } }, "source": [ - "Replacing *concrete* data types with ABCs is particularly valuable in the context of input validation: The revised version of the `factorial()` function below allows its user to take advantage of *duck typing*: If a real but non-integer argument `n` is passed in, `factorial()` tries to cast `n` as an `int` object with [math.trunc()](https://docs.python.org/3/library/math.html#math.trunc).\n", + "Another way to use ABCs is in place of a *concrete* data type.\n", + "\n", + "For example, we may pass them as arguments to the built-in [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function and check in which of the five mathematical sets the object `1 / 10` is." + ] + }, + { + "cell_type": "code", + "execution_count": 234, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 234, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(1 / 10, float)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A `float` object is a generic `Number` in the abstract sense but may also be seen as a `Complex` or `Real` number." + ] + }, + { + "cell_type": "code", + "execution_count": 235, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 235, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(1 / 10, numbers.Number)" + ] + }, + { + "cell_type": "code", + "execution_count": 236, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 236, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(1 / 10, numbers.Complex)" + ] + }, + { + "cell_type": "code", + "execution_count": 237, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 237, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(1 / 10, numbers.Real)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Due to the `float` type's inherent imprecision, `1 / 10` is *not* a `Rational` number." + ] + }, + { + "cell_type": "code", + "execution_count": 238, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 238, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(1 / 10, numbers.Rational)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, if we model `1 / 10` as a `Fraction`, it is recognized as a `Rational` number." + ] + }, + { + "cell_type": "code", + "execution_count": 239, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 239, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(Fraction(\"1/10\"), numbers.Rational)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Replacing *concrete* data types with ABCs is particularly valuable in the context of input validation: The revised version of the `factorial()` function below allows its user to take advantage of *duck typing*: If a real but non-integer argument `n` is passed in, `factorial()` tries to cast `n` as an `int` object with the [int()](https://docs.python.org/3/library/functions.html#int) built-in.\n", "\n", "Two popular and distinguished Pythonistas, [Luciano Ramalho](https://github.com/ramalho) and [Alex Martelli](https://en.wikipedia.org/wiki/Alex_Martelli), coin the term **goose typing** to specifically mean using the built-in [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function with an ABC (cf., Chapter 11 in this [book](https://www.amazon.com/Fluent-Python-Concise-Effective-Programming/dp/1491946008) or this [summary](https://dgkim5360.github.io/blog/python/2017/07/duck-typing-vs-goose-typing-pythonic-interfaces/) thereof)." ] @@ -7005,7 +7782,7 @@ }, { "cell_type": "code", - "execution_count": 223, + "execution_count": 240, "metadata": { "slideshow": { "slide_type": "slide" @@ -7018,7 +7795,8 @@ "\n", " Args:\n", " n (int): number to calculate the factorial for; must be positive\n", - " strict (bool): if n must not contain decimals; defaults to True\n", + " strict (bool): if n must not contain decimals; defaults to True;\n", + " if set to False, the decimals in n are ignored\n", "\n", " Returns:\n", " factorial (int)\n", @@ -7029,15 +7807,15 @@ " \"\"\"\n", " if not isinstance(n, numbers.Integral):\n", " if isinstance(n, numbers.Real):\n", - " if n != math.trunc(n) and strict:\n", - " raise TypeError(\"n is not an integer-like value; it has decimals\")\n", - " n = math.trunc(n)\n", + " if n != int(n) and strict:\n", + " raise TypeError(\"n is not integer-like; it has decimals\")\n", + " n = int(n)\n", " else:\n", " raise TypeError(\"Factorial is only defined for integers\")\n", "\n", " if n < 0:\n", " raise ValueError(\"Factorial is not defined for negative integers\")\n", - " elif n == 0: # = base case\n", + " elif n == 0:\n", " return 1\n", " return n * factorial(n - 1)" ] @@ -7055,7 +7833,7 @@ }, { "cell_type": "code", - "execution_count": 224, + "execution_count": 241, "metadata": { "slideshow": { "slide_type": "slide" @@ -7068,7 +7846,7 @@ "1" ] }, - "execution_count": 224, + "execution_count": 241, "metadata": {}, "output_type": "execute_result" } @@ -7079,31 +7857,7 @@ }, { "cell_type": "code", - "execution_count": 225, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "6" - ] - }, - "execution_count": 225, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "factorial(3)" - ] - }, - { - "cell_type": "code", - "execution_count": 226, + "execution_count": 242, "metadata": { "slideshow": { "slide_type": "fragment" @@ -7116,7 +7870,31 @@ "6" ] }, - "execution_count": 226, + "execution_count": 242, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(3)" + ] + }, + { + "cell_type": "code", + "execution_count": 243, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 243, "metadata": {}, "output_type": "execute_result" } @@ -7138,7 +7916,7 @@ }, { "cell_type": "code", - "execution_count": 227, + "execution_count": 244, "metadata": { "slideshow": { "slide_type": "slide" @@ -7147,14 +7925,14 @@ "outputs": [ { "ename": "TypeError", - "evalue": "n is not an integer-like value; it has decimals", + "evalue": "n is not integer-like; it has decimals", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mReal\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mstrict\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 18\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"n is not an integer-like value; it has decimals\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 19\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mTypeError\u001b[0m: n is not an integer-like value; it has decimals" + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mReal\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mstrict\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 19\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"n is not integer-like; it has decimals\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 20\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 21\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: n is not integer-like; it has decimals" ] } ], @@ -7175,7 +7953,7 @@ }, { "cell_type": "code", - "execution_count": 228, + "execution_count": 245, "metadata": { "slideshow": { "slide_type": "fragment" @@ -7188,7 +7966,7 @@ "6" ] }, - "execution_count": 228, + "execution_count": 245, "metadata": {}, "output_type": "execute_result" } @@ -7210,7 +7988,7 @@ }, { "cell_type": "code", - "execution_count": 229, + "execution_count": 246, "metadata": { "slideshow": { "slide_type": "slide" @@ -7224,8 +8002,8 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m2j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 21\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 22\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m2j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 21\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 22\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 23\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 24\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mTypeError\u001b[0m: Factorial is only defined for integers" ] } @@ -7269,6 +8047,242 @@ "\n", "The **numerical tower** is Python's way of implementing various **abstract** ideas of what numbers are in mathematics." ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## Further Resources" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The two videos below show how addition and multiplication works with numbers in their binary representations. Subtraction is a bit more involved as we need to understand how negative numbers are represented in binary with the concept of [Two's Complement](https://en.wikipedia.org/wiki/Two%27s_complement) first. A video on that is shown further below. Division in binary is actually also quite simple." + ] + }, + { + "cell_type": "code", + "execution_count": 247, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAUDBAcICAgICAgJCAgIBwgICAgICAgICAgICAgICAgICAgIChALCAgOCQgIDRUODhERExMTCAsXGBYSGBASExIBBQUFBwYHDwkJDxcVEhUXFRMVFxUYFRUVFRUVFRcYFRUVGBUXFRIVFhUXFxcVFRUXGBYVFxUVFRUVFRYVFRUSFf/AABEIAWgB4AMBIgACEQEDEQH/xAAdAAEAAQUBAQEAAAAAAAAAAAAAAQQFBgcIAwIJ/8QAVxAAAQQBAwIDBAQHCQsJCQEAAQACAwQRBQYSEyEHCDEUIkFRCTJhcRUjNkJSgZEzYnJzdHWhtLUWJDQ1Y3aCorGzwjdDRFNVh5LBxCaDk5SjssPR1Bj/xAAbAQEBAQEAAwEAAAAAAAAAAAAAAQIDBAYHBf/EAC8RAQEAAgAEBAIJBQAAAAAAAAABAhEDEiExBAVBUTJxBiJhgZGhsdHwFCNSweH/2gAMAwEAAhEDEQA/AOMkREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCUREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQFUdEdHqd89Thj4Y48h/5qnVZAc1pR+hLE79ThI0/08VrGb/NvCb38qoyiIssCIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAAiq6E7mnixjHSPcA1zm8iM9sNDuwz88Jq3DrP444h2PdxjIADsY+HIFb5fq7b5fq7UiKqsRBjGD1e4c3fvWn6jcD4kd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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 247, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from IPython.display import YouTubeVideo\n", + "YouTubeVideo(\"RgklPQ8rbkg\", width=\"60%\")" + ] + }, + { + "cell_type": "code", + "execution_count": 248, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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OVfTdF7GINmCsLIdz60jfXrt2239Ctd+POs6+odQ5LMVopYIMi+u6OKxw6rRDUr1zz6bnN3LoXHsT2IWDogKw3kCw2Rn1W21VeY6uMpWHZN227ZYrDDFDVP9Z04jlH/lXH4KvKs95ZPHrS2kMNNWlo5Szk71mSzcmhhpCu4tHTq145X2GydBsY5buYSHTS7AjYIOx+US197TkaWnoZN4sQz2y+1pBBu2WbV2P+IdHVcXf/en12G1UF2+s9QWMrkbuStO5T5K3NZl7ucGmZ5eI2cu4jY0hjR8GsaPguoQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQERAgKeP+Sy/Rfh7kMlxk4+zVj62ZmnZw/8GPs6X8vZvr3W7dKaKx2ObtDCJZXNIfYsNbJK7f6TR22Yz7mgffv6rkw4WWXd571T8S8n6fej+LP2x/zfl979FYiEW+daeFVO2HTUS2pOdz0tj7LIfzW7mH8rdx/VWmtQ4C3Ql6NuF0TvxSdiyQD1dHIPde309D8e+ymeGWHl2/S/W+U9Sx/c5fF88b5/3PrHVoiLD9YRNvgiAiIgIiICIiAiljSewBJAJ2Hc7NBc4/kABJ/IoQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQF2mko2vv0WPaHMkvVWva4btc107A5rgexBBIXVrttG/6xx/6QqfvEas8uHmLrg5We1/st3SwV+aB1iCpPLXjcWOlhifI1rmtaSHCMEtADm99tgCtweXGTHCrZG8TbxsHq9QsEprcGdPhv3MXLqb/AH+vwXy8teajDLePc4CTqe1wNP47XNZFKG/e0xxn/wBQ/Yvp49aHidGMnViAmEscdqONoAm672xRy8f+96jmNJ+IfufTv2s8t3or516T6f8AsnLY+q8v8dkvVjfvq+886s8ffX/ihBWnzdmPFx9VryzeOmwyNfOGjrGJkQPL3vXj+NyWqPHn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P+1d9y1iiDYevPGPUeTyd2+zK5GnHdsySQ062QtRQ1od+MMDGRPazZkbWN3AHIgk9ys88G/MOcfi8th9RxXc7SzLXNYX2zJYhbNC6CzH1bRcem5gic0N24ua8/jdtAIgsR4L+YytQxDdO6lxLc5iIhtX3EUk0UbX9SOB8NkdOeNj+7HcmuZsANwG8fj4ueYKjbixVHTeDrYijgclBlazpoIOt7ZXkEsfSjg9ytGX7l7g5z5OwJaAQ6vqILW6k8yekLtirnZdKST6moQMZVmsWR7DDLG4vik3Y/eYRyOc9pdCHj0Dm9iNeeIXjzYt6vrarxVd1OWnWrwitacJWyhkckdiKXp7coZGyvZ22O2xGx220qiC3eT802k5Hty7dItdqSOJoit2BUfHFM1vBj/bA3rvawdgem12w4gt9Rqfxh8cbGX1Hi9SY6F+PuYihUha17hI02IJrM0rgB613+0uj4nuW8t/VacRBbex5mdF5Aw5HM6ObZzdZjQJmspzQufF3jPXn2kDAe4D2PLN+xO251d4deM9TG6ys6okxTYoLnte+Mxzw1sJsxhu7HSjZzi8F7js0F0jiA0bNGmUQbdwHi5Wra7fq51OZ1d967Z9hbIzrhtqpPWa3qEcOQMwcfzSFOV8Xa02vG6vFOYVm3q1n2EyM6/CCpFVLee3DkTGXf37LUKINr+K+uJdW6vgyOJglp2b1jGVcfFK9jpW24zDDA/k0cRvNxI9fRWT+UH12+nh6OAZIDZzUjbF/h2HsdQtIHEndrZbfEj17VnhVA8JdZu09l6uXZUhuS0Oq6GCy57Y+pJE+Jsh6Z35N6hI+8D7iPv4yeId3VGWmy1xjInyxwww1oi50VeGFnFscZf7xBeZJDv+NK78iDDUREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEUbpuglFG6boJRRum6CUUbpuglFG6boJRRum6CUUbpuglFG6boJRRum6CUUbpuglFG6boJRRum6CUUbpuglFG6boJRRum6CUUbpuglFG6boJRRum6CUUbpuglFG6boJRRum6CUUbpuglFG6boJRRum6CUUbpuglFG6boJRRum6CUUbpuglFG6boJRRum6CEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQf/9k=\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 248, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"xHWKYFhhtJQ\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The video below explains the idea behind [Two's Complement](https://en.wikipedia.org/wiki/Two%27s_complement). This is how most modern programming languages implement negative integers. The video also shows how subtraction in binary works." + ] + }, + { + "cell_type": "code", + "execution_count": 249, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 249, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"4qH4unVtJkE\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This video by the YouTube channel [Computerphile](https://www.youtube.com/channel/UC9-y-6csu5WGm29I7JiwpnA) explains floating point numbers in an intuitive way with some numeric examples." + ] + }, + { + "cell_type": "code", + "execution_count": 250, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 250, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"PZRI1IfStY0\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Below is a short introduction to [complex numbers](https://en.wikipedia.org/wiki/Complex_number) by [MIT](https://www.mit.edu) professor [Gilbert Strang](https://en.wikipedia.org/wiki/Gilbert_Strang) aimed at high school students." + ] + }, + { + "cell_type": "code", + "execution_count": 251, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 251, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"Jkv-55ndVYY\", width=\"60%\")" + ] } ], "metadata": { diff --git a/05_numbers_02_exercises.ipynb b/05_numbers_02_exercises.ipynb index ab2499d..ff5e9a8 100644 --- a/05_numbers_02_exercises.ipynb +++ b/05_numbers_02_exercises.ipynb @@ -19,7 +19,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Read [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers_00_lecture.ipynb) of the book. Then, work through the exercises below." + "Read [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers_00_lecture.ipynb) of the book. Then, work through the exercises below. The `...` indicate where you need to fill in your answers. You should not need to create any additional code cells." ] }, { @@ -33,20 +33,20 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q1** in [Chapter 2's Exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions_02_exercises.ipynb#Volume-of-a-Sphere) section already revealed that we must consider the effects of the `float` type's imprecision.\n", + "The \"*Volume of a Sphere*\" problem in [Chapter 2's Exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions_02_exercises.ipynb#Volume-of-a-Sphere) section revealed that we must consider the effects of the `float` type's imprecision.\n", "\n", "This becomes even more important when we deal with numeric data modeling accounting or finance data (cf., [this comment](https://stackoverflow.com/a/24976426) on \"falsehoods programmers believe about money\").\n", "\n", "In addition to the *inherent imprecision* of numbers in general, the topic of **[rounding numbers](https://en.wikipedia.org/wiki/Rounding)** is also not as trivial as we might expect! [This article](https://realpython.com/python-rounding/) summarizes everything the data science practitioner needs to know.\n", "\n", - "In this exercise, we revisit **Q1** from [Chapter 3's Exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_02_exercises.ipynb#Discounting-Customer-Orders) section, and make the `discounted_price()` function work *correctly* for real-life sales data." + "In this exercise, we revisit the \"*Discounting Customer Orders*\" problem from [Chapter 3's Exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_02_exercises.ipynb#Discounting-Customer-Orders) section and make the `discounted_price()` function work *correctly* for real-life sales data." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "**Q1.1**: Execute the code cells below! What results would you have *expected*, and why?" + "**Q1**: Execute the code cells below! What results would you have *expected*, and why?" ] }, { @@ -87,7 +87,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q1.2**: The built-in [round()](https://docs.python.org/3/library/functions.html#round) function implements the \"**[round half to even](https://en.wikipedia.org/wiki/Rounding#Round_half_to_even)**\" strategy. Describe in one or two sentences what that means!" + "**Q2**: The built-in [round()](https://docs.python.org/3/library/functions.html#round) function implements the \"**[round half to even](https://en.wikipedia.org/wiki/Rounding#Round_half_to_even)**\" strategy. Describe in one or two sentences what that means!" ] }, { @@ -101,7 +101,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q1.3**: For the revised `discounted_price()` function, we have to tackle *two* issues: First, we have to replace the built-in `float` type with a data type that allows us to control the precision. Second, the discounted price should be rounded according to a more human-friendly rounding strategy, namely \"**[round half away from zero](https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero)**.\"\n", + "**Q3**: For the revised `discounted_price()` function, we have to tackle *two* issues: First, we have to replace the built-in `float` type with a data type that allows us to control the precision. Second, the discounted price should be rounded according to a more human-friendly rounding strategy, namely \"**[round half away from zero](https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero)**.\"\n", "\n", "Describe in one or two sentences how \"**[round half away from zero](https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero)**\" is more in line with how humans think of rounding!" ] @@ -117,7 +117,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q1.4**: We use the `Decimal` type from the [decimal](https://docs.python.org/3/library/decimal.html) module in the [standard library](https://docs.python.org/3/library/index.html) to tackle *both* issues simultaneously.\n", + "**Q4**: We use the `Decimal` type from the [decimal](https://docs.python.org/3/library/decimal.html) module in the [standard library](https://docs.python.org/3/library/index.html) to tackle *both* issues simultaneously.\n", "\n", "Assign `euro` a numeric object such that both `Decimal(\"1.5\")` and `Decimal(\"2.5\")` are rounded to `Decimal(\"2\")` (i.e., no decimal) with the [quantize()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize) method!" ] @@ -162,7 +162,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q1.5**: Obviously, the two preceding code cells still [round half to even](https://en.wikipedia.org/wiki/Rounding#Round_half_to_even).\n", + "**Q5**: Obviously, the two preceding code cells still [round half to even](https://en.wikipedia.org/wiki/Rounding#Round_half_to_even).\n", "\n", "The [decimal](https://docs.python.org/3/library/decimal.html) module defines a `ROUND_HALF_UP` flag that we can pass as the second argument to the [quantize()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize) method. Then, it [rounds half away from zero](https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero).\n", "\n", @@ -200,7 +200,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q1.6**: Instead of `euro`, define `cents` such that rounding occurs to *two* decimals! `Decimal(\"2.675\")` should now be rounded to `Decimal(\"2.68\")`. Do *not* forget to include the `ROUND_HALF_UP` flag!" + "**Q6**: Instead of `euro`, define `cents` such that rounding occurs to *two* decimals! `Decimal(\"2.675\")` should now be rounded to `Decimal(\"2.68\")`. Do *not* forget to include the `ROUND_HALF_UP` flag!" ] }, { @@ -225,7 +225,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q1.7**: Rewrite the function `discounted_price()` from [Chapter 3's Exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_02_exercises.ipynb#Discounting-Customer-Orders) section!\n", + "**Q7**: Rewrite the function `discounted_price()` from [Chapter 3's Exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_02_exercises.ipynb#Discounting-Customer-Orders) section!\n", "\n", "It takes the *positional* arguments `unit_price` and `quantity` and implements a discount scheme for a line item in a customer order as follows:\n", "\n", @@ -264,7 +264,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q1.8**: Execute the code cells below and verify the final price for the following four test cases:\n", + "**Q8**: Execute the code cells below and verify the final price for the following four test cases:\n", "\n", "- $7$ smartphones @ $99.00$ USD\n", "- $3$ workstations @ $999.00$ USD\n", diff --git a/06_text_00_lecture.ipynb b/06_text_00_lecture.ipynb index fc0eeb2..3c23901 100644 --- a/06_text_00_lecture.ipynb +++ b/06_text_00_lecture.ipynb @@ -19,7 +19,7 @@ } }, "source": [ - "In this chapter, we continue the study of the built-in data types. Building on our knowledge of numbers, the next layer consists of textual data that are modeled primarily with the `str` type in Python. `str` objects are naturally more \"complex\" than numeric objects as any text consists of an arbitrary and possibly large number of individual characters that may be chosen from any alphabet in the history of humankind. Luckily, Python abstracts away most of this complexity." + "In this chapter, we continue the study of the built-in data types. The next layer on top of numbers consists of **textual data** that are modeled primarily with the `str` type in Python. `str` objects are more complex than the numeric objects in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers_00_lecture.ipynb) as they *consist* of an *arbitrary* and possibly large number of *individual* characters that may be chosen from *any* alphabet in the history of humankind. Luckily, Python abstracts away most of this complexity from us. However, after looking at the `str` type in great detail, we briefly introduce the `bytes` type at the end of this chapter, and learn how characters are modeled in memory." ] }, { @@ -41,7 +41,7 @@ } }, "source": [ - "The `str` type is the default way of modeling **textual data**. To create a `str` object, we use a **literal notation** and type the text between enclosing **double quotes** `\"`." + "To create a `str` object, we use the *literal* notation and type the text between enclosing **double quotes** `\"`." ] }, { @@ -54,7 +54,7 @@ }, "outputs": [], "source": [ - "text = \"Lorem ipsum dolor sit amet, ...\"" + "text = \"Lorem ipsum dolor sit amet.\"" ] }, { @@ -65,7 +65,7 @@ } }, "source": [ - "Like everything in Python, `text` is an object." + "Like everything in Python, `text` is an object with an *identity*, a *type*, and a *value*." ] }, { @@ -80,7 +80,7 @@ { "data": { "text/plain": [ - "140483431254256" + "140461336295424" ] }, "execution_count": 2, @@ -97,7 +97,7 @@ "execution_count": 3, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -124,9 +124,9 @@ } }, "source": [ - "A `str` object evaluates to itself in a literal notation with enclosing **single quotes** `'` by default. In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_lecture.ipynb#Value-/-\"Meaning\"), we already specified the double quotes `\"` convention we stick to in this book. Yet, single quotes `'` and double quotes `\"` are *perfect* substitutes for all `str` objects that do *not* contain any of the two symbols in it. We could use the reverse convention, as well.\n", + "As seen before, a `str` object evaluates to itself in a literal notation with enclosing **single quotes** `'`.\n", "\n", - "As [this discussion](https://stackoverflow.com/questions/56011/single-quotes-vs-double-quotes-in-python) shows, many programmers have *strong* opinions about that and make up *new* conventions for their projects. Consequently, the discussion was \"closed as not constructive\" by the moderators." + "In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_lecture.ipynb#Value-/-\"Meaning\"), we specify the double quotes `\"` convention this book follows. Yet, single quotes `'` and double quotes `\"` are *perfect* substitutes. We could use the reverse convention, as well. As [this discussion](https://stackoverflow.com/questions/56011/single-quotes-vs-double-quotes-in-python) shows, many programmers have *strong* opinions about such conventions. Consequently, the discussion was \"closed as not constructive\" by the moderators." ] }, { @@ -134,14 +134,14 @@ "execution_count": 4, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "'Lorem ipsum dolor sit amet, ...'" + "'Lorem ipsum dolor sit amet.'" ] }, "execution_count": 4, @@ -161,13 +161,11 @@ } }, "source": [ - "As the single quote `'` is often used in the English language as a shortener, we could make an argument in favor of using the double quotes `\"`: There are possibly fewer situations like in the two code cells below, in which we must revert to using a `\\` to **escape** a single quote `'` in a text (cf., the [Special Characters](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text_00_lecture.ipynb#Special-Characters) section further below). However, double quotes `\"` are often used as well. So, this argument is somewhat not convincing.\n", + "As the single quote `'` is often used in the English language as a shortener, we could make an argument in favor of using the double quotes `\"`: There are possibly fewer situations like the two code cells below, where we must **escape** the kind of quote used as the `str` object's delimiter with a backslash `\"\\\"` inside the text (cf., also the \"*Unicode & (Special) Characters*\" section further below). However, double quotes `\"` are often used as well, for example, to indicate a quote like the one by [Albert Einstein](https://de.wikipedia.org/wiki/Albert_Einstein) below. So, such arguments are not convincing.\n", "\n", - "Many proponents of the single quote `'` usage claim that double quotes `\"` make more **visual noise** on the screen. This argument is also not convincing. On the contrary, one could claim that *two* single quotes `''` look so similar to *one* double quote `\"` that it might not be apparent right away what we are looking at. By sticking to double quotes `\"`, we avoid such danger of confusion.\n", + "Many proponents of the single quote `'` usage claim that double quotes `\"` cause more **visual noise** on the screen. However, this argument is also not convincing as, for example, one could claim that *two* single quotes `''` look so similar to *one* double quote `\"` that a reader may confuse an *empty* `str` object with a missing closing quote `\"`. With the double quotes `\"` convention we at least avoid such confusion (i.e., empty `str` objects are written as `\"\"`).\n", "\n", - "This discussion is an exellent example of a [flame war](https://en.wikipedia.org/wiki/Flaming_%28Internet%29#Flame_war) in the programming world that leads to *no* result.\n", - "\n", - "An *important* fact to know is that enclosing quotes of either kind are *not* part of the `str` object's *value*! They are merely *syntax* to make the text in a code cell a *literal* that Python converts into a `str` object upon reading." + "This discussion is an excellent example of a [flame war](https://en.wikipedia.org/wiki/Flaming_%28Internet%29#Flame_war) in the programming world: Everyone has an opinion and the discussion leads to *no* result." ] }, { @@ -182,7 +180,7 @@ { "data": { "text/plain": [ - "'It\\'s cool that \"strings\" are versatile'" + "'Einstein said, \"If you can\\'t explain it, you don\\'t understand it.\"'" ] }, "execution_count": 5, @@ -191,7 +189,7 @@ } ], "source": [ - "\"It's cool that \\\"strings\\\" are versatile\"" + "\"Einstein said, \\\"If you can't explain it, you don't understand it.\\\"\"" ] }, { @@ -199,14 +197,14 @@ "execution_count": 6, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "'It\\'s cool that \"strings\" are versatile'" + "'Einstein said, \"If you can\\'t explain it, you don\\'t understand it.\"'" ] }, "execution_count": 6, @@ -215,7 +213,7 @@ } ], "source": [ - "'It\\'s cool that \"strings\" are versatile'" + "'Einstein said, \"If you can\\'t explain it, you don\\'t understand it.\"'" ] }, { @@ -226,12 +224,67 @@ } }, "source": [ - "We can always use the [str()](https://docs.python.org/3/library/stdtypes.html#str) built-in to cast non-`str` objects as a `str`." + "An *important* fact to know is that enclosing quotes of either kind are *not* part of the `str` object's *value*! They are merely *syntax* indicating the literal notation.\n", + "\n", + "So, printing out the sentence with the built-in [print()](https://docs.python.org/3/library/functions.html#print) function does the same in both cases." ] }, { "cell_type": "code", "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Einstein said, \"If you can't explain it, you don't understand it.\"\n" + ] + } + ], + "source": [ + "print(\"Einstein said, \\\"If you can't explain it, you don't understand it.\\\"\")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Einstein said, \"If you can't explain it, you don't understand it.\"\n" + ] + } + ], + "source": [ + "print('Einstein said, \"If you can\\'t explain it, you don\\'t understand it.\"')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As an alternative to the literal notation, we may use the built-in [str()](https://docs.python.org/3/library/stdtypes.html#str) constructor to cast non-`str` objects as `str` ones. As Chapter 10 reveals, basically any object in Python has a **text representation**. Because of that we may also pass `list` objects, the boolean `True` and `False`, or `None` to [str()](https://docs.python.org/3/library/stdtypes.html#str)." + ] + }, + { + "cell_type": "code", + "execution_count": 9, "metadata": { "slideshow": { "slide_type": "slide" @@ -241,16 +294,147 @@ { "data": { "text/plain": [ - "'123'" + "'42'" ] }, - "execution_count": 7, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "str(123)" + "str(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'42.87'" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "str(42.87)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'[1, 2, 3]'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "str([1, 2, 3])" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'True'" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "str(True)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'False'" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "str(False)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'None'" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "str(None)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### User Input" ] }, { @@ -261,12 +445,103 @@ } }, "source": [ - "Another common situation where we obtain `str` objects is when reading the contents of a file with the [open()](https://docs.python.org/3/library/functions.html#open) built-in. In its simplest form, to open a [text file](https://en.wikipedia.org/wiki/Text_file) file in read-only mode, we pass in its path (i.e., \"filename\") as a `str` object." + "As shown in the \"*Guessing a Coin Toss*\" example in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_lecture.ipynb#Example:-Guessing-a-Coin-Toss), the built-in [input()](https://docs.python.org/3/library/functions.html#input) function displays a prompt to the user and returns whatever is entered as a `str` object. [input()](https://docs.python.org/3/library/functions.html#input) is in particular valuable when writing command-line tools." ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Whatever you enter is put in a new string: I will be a string\n" + ] + } + ], + "source": [ + "user_input = input(\"Whatever you enter is put in a new string: \")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "str" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(user_input)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'I will be a string'" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "user_input" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Reading Files" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A more common situation where we obtain `str` objects is when reading the contents of a file with the [open()](https://docs.python.org/3/library/functions.html#open) built-in. In its simplest usage form, to open a [text file](https://en.wikipedia.org/wiki/Text_file) file, we pass in its path (i.e., \"filename\") as a `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 18, "metadata": { "slideshow": { "slide_type": "slide" @@ -285,12 +560,36 @@ } }, "source": [ - "[open()](https://docs.python.org/3/library/functions.html#open) returns a **[proxy](https://en.wikipedia.org/wiki/Proxy_pattern)** object of type `TextIOWrapper` that allows us to interact with the file on disk." + "[open()](https://docs.python.org/3/library/functions.html#open) returns a **[proxy](https://en.wikipedia.org/wiki/Proxy_pattern)** object of type `TextIOWrapper` that allows us to interact with the file on disk. `mode='r'` shows that we opened the file in read-only mode and `encoding='UTF-8'` is explained in detail in the \"*The `bytes` Type*\" section at the end of this chapter." ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "_io.TextIOWrapper" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(file)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, "metadata": { "slideshow": { "slide_type": "fragment" @@ -303,7 +602,7 @@ "<_io.TextIOWrapper name='lorem_ipsum.txt' mode='r' encoding='UTF-8'>" ] }, - "execution_count": 9, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -312,30 +611,6 @@ "file" ] }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "_io.TextIOWrapper" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(file)" - ] - }, { "cell_type": "markdown", "metadata": { @@ -344,12 +619,121 @@ } }, "source": [ - "While `file` provides, for example, the [read()](https://docs.python.org/3/library/io.html#io.TextIOBase.read), [readline()](https://docs.python.org/3/library/io.html#io.TextIOBase.readline), and [readlines()](https://docs.python.org/3/library/io.html#io.IOBase.readlines) methods to access its contents, it is also *iterable*, and we may loop over the individual lines with a `for` statement." + "`TextIOWrapper` objects come with plenty of type-specific methods and attributes." ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.readable()" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.writable()" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'lorem_ipsum.txt'" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.name" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'UTF-8'" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.encoding" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "So far, we have not yet read anything from the file (i.e., from disk)! That is intentional as, for example, the file could contain more data than could fit into our computer's memory. Therefore, we have to explicitly instruct the `file` object to read some of or all the data in the file.\n", + "\n", + "One way to do that, is to simply loop over the `file` object with the `for` statement as shown next: In each iteration, `line` is assigned the next line in the file. Because we may loop over `TextIOWrapper` objects, they are *iterables*." + ] + }, + { + "cell_type": "code", + "execution_count": 25, "metadata": { "slideshow": { "slide_type": "slide" @@ -388,12 +772,12 @@ } }, "source": [ - "Once we looped over `file` the first time, it is **exhausted**: That means we do not see any output if we loop over it another time." + "Once we looped over the `file` object, it is **exhausted**: We can *not* loop over it a second time. So, the built-in [print()](https://docs.python.org/3/library/functions.html#print) function is *never* called in the code cell below!" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 26, "metadata": { "slideshow": { "slide_type": "skip" @@ -413,12 +797,12 @@ } }, "source": [ - "After the `for`-loop, `line` is still set to the last line in the file, and we verify that it is indeed a `str` object." + "After the `for`-loop, the `line` variable is still set and references the *last* line in the file. We verify that it is indeed a `str` object." ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 27, "metadata": { "slideshow": { "slide_type": "slide" @@ -431,7 +815,7 @@ "'the 1960s with the release of Letraset sheets.\\n'" ] }, - "execution_count": 13, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -442,10 +826,10 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 28, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -455,7 +839,7 @@ "str" ] }, - "execution_count": 14, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } @@ -472,14 +856,14 @@ } }, "source": [ - "An important fact is that `file` is still associated with an *open* **[file descriptor](https://en.wikipedia.org/wiki/File_descriptor)**. Without going into any technical details, we note that an operating system can only handle a limited number of \"open files\" at the same time, and, therefore, we should always *close* the file once we are done processing it.\n", + "An *important* observation is that the `file` object is still associated with an *open* **[file descriptor](https://en.wikipedia.org/wiki/File_descriptor)**. Without going into any technical details, we note that an operating system can only handle a *limited* number of \"open files\" at the same time, and, therefore, we should always *close* the file once we are done processing it.\n", "\n", - "`file` has a `closed` attribute on it that shows us if a file descriptor is open or closed, and with the [close()](https://docs.python.org/3/library/io.html#io.IOBase.close) method, we can \"manually\" close it." + "`TextIOWrapper` objects have a `closed` attribute on them that indicates if the associated file descriptor is still open or has been closed. We can \"manually\" close any `TextIOWrapper` object with the [close()](https://docs.python.org/3/library/io.html#io.IOBase.close) method." ] }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 29, "metadata": { "slideshow": { "slide_type": "slide" @@ -492,7 +876,7 @@ "False" ] }, - "execution_count": 15, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -503,7 +887,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 30, "metadata": { "slideshow": { "slide_type": "fragment" @@ -516,10 +900,10 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 31, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -529,7 +913,7 @@ "True" ] }, - "execution_count": 17, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } @@ -546,12 +930,12 @@ } }, "source": [ - "The more Pythonic way is to open a file with the `with` statement (cf., [reference](https://docs.python.org/3/reference/compound_stmts.html#the-with-statement)): The indented code block is said to be executed in the **context** of the header line that acts as a **[context manager](https://docs.python.org/3/reference/datamodel.html?highlight=context%20manager#with-statement-context-managers)**. Such objects may have many different purposes. Here, the context manager created with `with open(...) as file:` mainly ensures that the file descriptor gets automatically closed after the last line in the code block is executed." + "The more Pythonic way is to use [open()](https://docs.python.org/3/library/functions.html#open) within the compound `with` statement (cf., [reference](https://docs.python.org/3/reference/compound_stmts.html#the-with-statement)): In the example below, the indented code block is said to be executed within the **context** of the `file` object that now plays the role of a **[context manager](https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers)**. Many different kinds of context managers exist in Python with different applications and purposes. Context managers returned from [open()](https://docs.python.org/3/library/functions.html#open) mainly ensure that file descriptors get automatically closed after the last line in the code block is executed." ] }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 32, "metadata": { "slideshow": { "slide_type": "slide" @@ -585,7 +969,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 33, "metadata": { "slideshow": { "slide_type": "fragment" @@ -598,7 +982,7 @@ "True" ] }, - "execution_count": 19, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" } @@ -615,12 +999,12 @@ } }, "source": [ - "To use constructs familiar from [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_00_lecture.ipynb#The-try-Statement) to explain what `with open(...) as file:` does, below is a formulation with a `try` statement *equivalent* to the `with` statement above. The `finally`-branch is *always* executed, even if an exception is raised in the `for`-loop. So, `file` is sure to be closed too, with a somewhat less expressive formulation." + "Using syntax familiar from [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_00_lecture.ipynb#The-try-Statement) to explain what the `with open(...) as file:` does above, we provide an alternative formulation with a `try` statement below: The `finally`-branch is *always* executed, even if an exception is raised inside the `for`-loop. Therefore, `file` is sure to be closed too. However, this formulation is somewhat less expressive." ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 34, "metadata": { "slideshow": { "slide_type": "skip" @@ -657,7 +1041,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 35, "metadata": { "slideshow": { "slide_type": "skip" @@ -670,7 +1054,7 @@ "True" ] }, - "execution_count": 21, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } @@ -687,17 +1071,258 @@ } }, "source": [ - "A subtlety to notice is that there is an empty line printed between each `line`. That is because each `line` ends with a `\"\\n\"` that results in a line break and that is explained further below. To print the text without empty lines in between, we pass a `end=\"\"` argument to the [print()](https://docs.python.org/3/library/functions.html#print) function." + "As an alternative to reading the contents of a file by looping over a `TextIOWrapper` object, we may also call one of the methods they come with.\n", + "\n", + "For example, the [read()](https://docs.python.org/3/library/io.html#io.TextIOBase.read) method takes a single `size` argument of type `int` and returns a `str` object with the specified number of characters." ] }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 36, "metadata": { "slideshow": { "slide_type": "slide" } }, + "outputs": [], + "source": [ + "file = open(\"lorem_ipsum.txt\")" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem Ipsum'" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.read(11)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When we call [read()](https://docs.python.org/3/library/io.html#io.TextIOBase.read) again, the returned `str` object begins where the previous one left off. This is because `TextIOWrapper` objects like `file` simply store a position at which the associated file on disk is being read. In other words, `file` is like a **cursor** pointing into a file." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "' is simply '" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.read(11)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "On the contrary, the [readline()](https://docs.python.org/3/library/io.html#io.TextIOBase.readline) method keeps reading until it hits a **newline character**. These are shown in `str` objects as `\"\\n\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'dummy text of the printing and typesetting industry.\\n'" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.readline()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When we call [readline()](https://docs.python.org/3/library/io.html#io.TextIOBase.readline) again, we obtain the next line." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Lorem Ipsum has been the industry's standard dummy text ever since the 1500s\\n\"" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.readline()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Lastly, the [readlines()](https://docs.python.org/3/library/io.html#io.IOBase.readlines) method returns a `list` object that holds *all* lines in the `file` from the current position to the end of the file. The latter position is often abbreviated as **EOF** in the documentation. Let's always remember that [readlines()](https://docs.python.org/3/library/io.html#io.IOBase.readlines) has the potential to crash a computer with a `MemoryError`." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['when an unknown printer took a galley of type and scrambled it to make a type\\n',\n", + " 'specimen book. It has survived not only five centuries but also the leap into\\n',\n", + " 'electronic typesetting, remaining essentially unchanged. It was popularised in\\n',\n", + " 'the 1960s with the release of Letraset sheets.\\n']" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.readlines()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Calling [readlines()](https://docs.python.org/3/library/io.html#io.IOBase.readlines) a second time, is as pointless as looping over `file` a second time." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.readlines()" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "file.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Because every `str` object created by reading the contents of a file in any of the ways shown in this section ends with a `\"\\n\"`, we see empty lines printed between each `line` in the `for`-loops above. To print the entire text without empty lines in between, we pass a `end=\"\"` argument to the [print()](https://docs.python.org/3/library/functions.html#print) function." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "outputs": [ { "name": "stdout", @@ -726,7 +1351,7 @@ } }, "source": [ - "## A \"String\" of Characters" + "### A String of Characters" ] }, { @@ -739,21 +1364,23 @@ "source": [ "A **sequence** is yet another *abstract* concept (cf., the \"*Containers vs. Iterables*\" section in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_lecture.ipynb#Containers-vs.-Iterables)).\n", "\n", - "It unifies *four* [orthogonal](https://en.wikipedia.org/wiki/Orthogonality) (i.e., \"independent\") behaviors into one idea: Any data type, such as `str`, is considered a sequence if it simultaneously\n", + "It unifies *four* [orthogonal](https://en.wikipedia.org/wiki/Orthogonality) (i.e., \"independent\") concepts into one bigger idea: Any data type, such as `str`, is considered a sequence if it\n", "\n", - "1. **contains** other \"things,\"\n", - "2. is **iterable**, and \n", - "3. comes with a *predictable* **order** of its\n", - "4. **finite** number of \"things.\"\n", + "1. **contains**\n", + "2. a **finite** number of other \"things\" that\n", + "3. can be **iterated** over\n", + "4. in a *predictable* **order**.\n", "\n", - "[Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_lecture.ipynb#Collections-vs.-Sequences) formalizes sequences in great detail. Here, we keep our focus on the `str` type that historically received its name as it models a \"**[string of characters](https://en.wikipedia.org/wiki/String_%28computer_science%29)**,\" and a \"string\" is more formally called a sequence in the computer science literature.\n", + "[Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_lecture.ipynb#Collections-vs.-Sequences) formalizes these concepts in great detail. Here, we keep our focus on the `str` type that historically received its name as it models a **[string of characters](https://en.wikipedia.org/wiki/String_%28computer_science%29)**, and *string* is simply another term for *sequence* in the computer science literature.\n", "\n", - "Behaving like a sequence, `str` objects may be treated like `list` objects in many cases. For example, the built-in [len()](https://docs.python.org/3/library/functions.html#len) function tells us how many elements (i.e., characters) make up `text`. [len()](https://docs.python.org/3/library/functions.html#len) would not work with an *infinite* object: As anything modeled in a program must fit into a computer's finite memory at runtime, there cannot exist objects containing a truly infinite number of elements; however, [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_lecture.ipynb#Iterators-vs.-Iterables) introduces iterable data types that can be used to model an *infinite* series of elements and that, consequently, have no concept of \"length.\"" + "Another example of a sequence is the `list` type. Because of that, `str` objects may be treated like `list` objects in many situations.\n", + "\n", + "Below, the built-in [len()](https://docs.python.org/3/library/functions.html#len) function tells us how many characters make up `text`. [len()](https://docs.python.org/3/library/functions.html#len) would not work with an \"infinite\" object. As anything modeled in a program must fit into a computer's finite memory, there cannot exist truly infinite objects; however, [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_lecture.ipynb#Iterators-vs.-Iterables) introduces specialized iterable data types that can be used to model an *infinite* series of \"things\" and that, consequently, have no concept of \"length.\"" ] }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 45, "metadata": { "slideshow": { "slide_type": "slide" @@ -763,10 +1390,34 @@ { "data": { "text/plain": [ - "31" + "'Lorem ipsum dolor sit amet.'" ] }, - "execution_count": 23, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "27" + ] + }, + "execution_count": 46, "metadata": {}, "output_type": "execute_result" } @@ -783,12 +1434,45 @@ } }, "source": [ - "Being iterable, we may loop over `text` and do something with the individual characters, for example, print them out with extra space in between them." + "Being iterable, we may loop over `text` and do something with the individual characters, for example, print them out with extra space in between them. If it were not for the appropriately chosen name of the `text` variable, we could not tell what *concrete* type of object the `for` statement is looping over." ] }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "L o r e m i p s u m d o l o r s i t a m e t . " + ] + } + ], + "source": [ + "for character in text:\n", + " print(character, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With the [reversed()](https://docs.python.org/3/library/functions.html#reversed) built-in, we may loop over `text` in reversed order. Reversing `text` only works as it has a forward order to begin with." + ] + }, + { + "cell_type": "code", + "execution_count": 48, "metadata": { "slideshow": { "slide_type": "fragment" @@ -799,13 +1483,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "L o r e m i p s u m d o l o r s i t a m e t , . . . " + ". t e m a t i s r o l o d m u s p i m e r o L " ] } ], "source": [ - "for letter in text:\n", - " print(letter, end=\" \")" + "for character in reversed(text):\n", + " print(character, end=\" \")" ] }, { @@ -818,12 +1502,36 @@ "source": [ "Being a container, we may check if a given `str` object is contained in `text` with the `in` operator.\n", "\n", - "The `in` operator has *two* usages: First, it checks if a *single* character is contained in a `str` object. Second, it may also check if a shorter `str` object, then called a **substring**, is contained in a longer one." + "The `in` operator has *two* distinct usages: First, it checks if a *single* character is contained in a `str` object. Second, it may also check if a shorter `str` object, then called a **substring**, is contained in a longer one." ] }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"L\" in text" + ] + }, + { + "cell_type": "code", + "execution_count": 50, "metadata": { "slideshow": { "slide_type": "fragment" @@ -836,31 +1544,7 @@ "True" ] }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "\"L\" in text" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 26, + "execution_count": 50, "metadata": {}, "output_type": "execute_result" } @@ -871,10 +1555,10 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 51, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -884,7 +1568,7 @@ "False" ] }, - "execution_count": 27, + "execution_count": 51, "metadata": {}, "output_type": "execute_result" } @@ -901,7 +1585,7 @@ } }, "source": [ - "## Indexing" + "### Indexing" ] }, { @@ -912,12 +1596,12 @@ } }, "source": [ - "As `str` objects have the additional property of being *ordered*, we may **index** into them to obtain individual characters with the **indexing operator** `[]`. This is analogous to how we obtained individual elements of a `list` object in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_lecture.ipynb#Who-am-I?-And-how-many?)." + "As `str` objects are *ordered* and *finite*, we may **index** into them to obtain individual characters with the **indexing operator** `[]`. This is analogous to how we obtained individual elements of a `list` object in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_lecture.ipynb#Who-am-I?-And-how-many?)." ] }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 52, "metadata": { "slideshow": { "slide_type": "slide" @@ -930,7 +1614,7 @@ "'L'" ] }, - "execution_count": 28, + "execution_count": 52, "metadata": {}, "output_type": "execute_result" } @@ -941,10 +1625,10 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 53, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -954,7 +1638,7 @@ "'o'" ] }, - "execution_count": 29, + "execution_count": 53, "metadata": {}, "output_type": "execute_result" } @@ -971,12 +1655,12 @@ } }, "source": [ - "The index must be of type `int` or we get a `TypeError`." + "The index must be of type `int`; othewise, we get a `TypeError`." ] }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 54, "metadata": { "slideshow": { "slide_type": "skip" @@ -990,7 +1674,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: string indices must be integers" ] } @@ -1007,12 +1691,12 @@ } }, "source": [ - "The last index is one less than the above \"length\" of the `str` object as we start counting at 0." + "The last index is one less than the above \"length\" of the `str` object as we start counting at `0`." ] }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 55, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1025,13 +1709,13 @@ "'.'" ] }, - "execution_count": 31, + "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "text[30] # = len(text) - 1" + "text[26] # == text[len(text) - 1]" ] }, { @@ -1042,15 +1726,15 @@ } }, "source": [ - "An `IndexError` is raised whenever the index is too large." + "An `IndexError` is raised whenever the index is out of range." ] }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 56, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -1061,13 +1745,13 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m31\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;31m# = len(text)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m27\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;31m# == text[len(text)]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mIndexError\u001b[0m: string index out of range" ] } ], "source": [ - "text[31] # = len(text)" + "text[27] # == text[len(text)]" ] }, { @@ -1078,7 +1762,7 @@ } }, "source": [ - "We may use *negative* indexes to start counting from the end of the `str` object, as shown in the figure below. That only works because sequences are *finite*." + "We may use *negative* indexes to start counting from the end of the `str` object, as shown in the figure below. Note how this only works because sequences are *finite*." ] }, { @@ -1089,18 +1773,18 @@ } }, "source": [ - "| Slot | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12| 13| 14| 15| 16| 17| 18| 19| 20| 21| 22| 23| 24| 25| 26| 27| 28| 29| 30|\n", - "|:---------:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|\n", - "|**Reverse**|-31|-30|-29|-28|-27|-26|-25|-24|-23|-22|-21|-20|-19|-18|-17|-16|-15|-14|-13|-12|-11|-10|-9 |-8 |-7 |-6 |-5 |-4 |-3 |-2 |-1 |\n", - "| **Char** |`L`|`o`|`r`|`e`|`m`|` `|`i`|`p`|`s`|`u`|`m`|` `|`d`|`o`|`l`|`o`|`r`|` `|`s`|`i`|`t`|` `|`a`|`m`|`e`|`t`|`,`|` `|`.`|`.`|`.`|" + "| Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12| 13| 14| 15| 16| 17| 18| 19| 20| 21| 22| 23| 24| 25| 26|\n", + "|:---------:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|\n", + "|**Reverse**|-27|-26|-25|-24|-23|-22|-21|-20|-19|-18|-17|-16|-15|-14|-13|-12|-11|-10|-9 |-8 |-7 |-6 |-5 |-4 |-3 |-2 |-1 |\n", + "| **Character** |`L`|`o`|`r`|`e`|`m`|` `|`i`|`p`|`s`|`u`|`m`|` `|`d`|`o`|`l`|`o`|`r`|` `|`s`|`i`|`t`|` `|`a`|`m`|`e`|`t`|`.`|" ] }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 57, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -1110,7 +1794,7 @@ "'.'" ] }, - "execution_count": 33, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" } @@ -1121,7 +1805,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 58, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1134,13 +1818,13 @@ "'L'" ] }, - "execution_count": 34, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "text[-31] # = -len(text)" + "text[-27] # == text[-len(text)]" ] }, { @@ -1151,15 +1835,15 @@ } }, "source": [ - "One reason why programmers like to start counting at 0 is that a positive index and its *corresponding* negative index always add up to the length of the sequence. Here, `6` and `25` add to `31`." + "One reason why programmers like to start counting at `0` is that a positive index and its *corresponding* negative index always add up to the length of the sequence. Here, `6` and `21` add to `27`." ] }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 59, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "skip" } }, "outputs": [ @@ -1169,7 +1853,7 @@ "'i'" ] }, - "execution_count": 35, + "execution_count": 59, "metadata": {}, "output_type": "execute_result" } @@ -1180,10 +1864,10 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 60, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ @@ -1193,13 +1877,13 @@ "'i'" ] }, - "execution_count": 36, + "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "text[-25]" + "text[-21]" ] }, { @@ -1210,7 +1894,7 @@ } }, "source": [ - "## Slicing" + "### Slicing" ] }, { @@ -1223,14 +1907,14 @@ "source": [ "A **slice** is a substring of a `str` object.\n", "\n", - "The **slicing operator** is a generalization of the indexing operator: We can put one, two, or three integers within the brackets, separated by colons `:`. The three integers are then referred to as the *start*, *end*, and *step* values.\n", + "The **slicing operator** is a generalization of the indexing operator: We put one, two, or three integers within the brackets `[]`, separated by colons `:`. The three integers are then referred to as the *start*, *stop*, and *step* values.\n", "\n", - "Let's start with two integers, *start* and *end*." + "Let's start with two integers, *start* and *stop*. Whereas the character at the *start* position is included in the returned `str` object, the one at the *stop* position is not. If both *start* and *stop* are positive, the difference \"*stop* minus *start*\" tells us how many characters the resulting slice has. So, below, `5 - 0 == 5` implies that `\"Lorem\"` consists of `5` characters. So, colloquially speaking, `text[0:5]` means \"taking the first `5 - 0 == 5` characters of `text`.\"" ] }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 61, "metadata": { "slideshow": { "slide_type": "slide" @@ -1243,7 +1927,7 @@ "'Lorem'" ] }, - "execution_count": 37, + "execution_count": 61, "metadata": {}, "output_type": "execute_result" } @@ -1252,22 +1936,9 @@ "text[0:5]" ] }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "Whereas the *start* is always included in the result, the *end* is not. Counter-intuitive at first, this makes working with individual slices easier as they \"add\" up to the original `str` object again (cf., the \"*String Operations*\" sub-section below regarding the overloaded `+` operator). Because the *end* is *not* included, we end the second slice below with `len(text)` or `31` below.\n", - "\n", - "Not including the *end* has another advantage: The difference \"*end* minus *start*\" tells us how many elements the resulting slice has. Above, for example, `5 - 0` implies that `\"Lorem\"` consists of `5` characters. So, colloquially, `0:5` means \"taking the first five characters.\" That rule only works if both *start* and *end* are *positive*." - ] - }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 62, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1277,16 +1948,16 @@ { "data": { "text/plain": [ - "'Lorem ipsum dolor sit amet, ...'" + "'dolor sit amet.'" ] }, - "execution_count": 38, + "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "text[0:5] + text[5:len(text)]" + "text[12:len(text)]" ] }, { @@ -1297,12 +1968,12 @@ } }, "source": [ - "By combining a *positive* start with a *negative* end index, we specify both ends of the slice *relative* to the ends of the entire `str` object. So, colloquially, `6:-5` below means \"drop the first six and last five characters.\" The length of the resulting slice can *not* be calculated from the indexes and depends only on the length of the original `str` object." + "If left out, *start* defaults to `0` and *stop* to the length of the `str` object (i.e., the end)." ] }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 63, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1312,16 +1983,145 @@ { "data": { "text/plain": [ - "'ipsum dolor sit amet'" + "'Lorem'" ] }, - "execution_count": 39, + "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "text[6:-5]" + "text[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'dolor sit amet.'" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[12:]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Not including the character at the *stop* position makes working with individual slices easier as they add up to the original `str` object again (cf., the \"*String Operations*\" section below regarding the overloaded `+` operator)." + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum dolor sit amet.'" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[:5] + text[5:]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Slicing and indexing makes it easy to obtain shorter versions of the original `str` object. A common application would be to **parse** out meaningful substrings from raw text data." + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum sit amet.'" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[:11] + text[-10:]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "By combining a positive *start* with a negative *stop* index, we specify both ends of the slice *relative* to the ends of the entire `str` object. So, colloquially speaking, `6:-10` below means \"drop the first six and last ten characters.\" The length of the resulting slice can then *not* be calculated from the indexes and depends only on the length of the original `str` object!" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'ipsum dolor'" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[6:-10]" ] }, { @@ -1337,7 +2137,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 68, "metadata": { "slideshow": { "slide_type": "skip" @@ -1347,16 +2147,16 @@ { "data": { "text/plain": [ - "'Lorem ipsum dolor sit amet, ...'" + "'Lorem ipsum dolor sit amet.'" ] }, - "execution_count": 40, + "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "text[0:999]" + "text[-999:999]" ] }, { @@ -1367,12 +2167,12 @@ } }, "source": [ - "If left out, *start* defaults to `0` and *end* to the \"length\" of the `str` object. Here, we take a \"full\" slice that is essentially a copy of the original `str` object." + "By leaving out both *start* and *stop*, we take a \"full\" slice that is essentially a *copy* of the original `str` object." ] }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 69, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1382,10 +2182,10 @@ { "data": { "text/plain": [ - "'Lorem ipsum dolor sit amet, ...'" + "'Lorem ipsum dolor sit amet.'" ] }, - "execution_count": 41, + "execution_count": 69, "metadata": {}, "output_type": "execute_result" } @@ -1402,47 +2202,12 @@ } }, "source": [ - "Slicing (and indexing) makes it easy to obtain shorter versions of the original `str` object." + "A *step* value of `i` can be used to obtain only every `i`th character." ] }, { "cell_type": "code", - "execution_count": 42, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "'Lorem ipsum dolor sit amet.'" - ] - }, - "execution_count": 42, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "text[:26] + text[30]" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "A *step* value of $i$ can be used to obtain only every $i$th letter." - ] - }, - { - "cell_type": "code", - "execution_count": 43, + "execution_count": 70, "metadata": { "slideshow": { "slide_type": "slide" @@ -1452,10 +2217,10 @@ { "data": { "text/plain": [ - "'Lrmismdlrstae,..'" + "'Lrmismdlrstae.'" ] }, - "execution_count": 43, + "execution_count": 70, "metadata": {}, "output_type": "execute_result" } @@ -1477,7 +2242,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 71, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1487,10 +2252,10 @@ { "data": { "text/plain": [ - "'... ,tema tis rolod muspi meroL'" + "'.tema tis rolod muspi meroL'" ] }, - "execution_count": 44, + "execution_count": 71, "metadata": {}, "output_type": "execute_result" } @@ -1507,7 +2272,7 @@ } }, "source": [ - "## Immutability" + "### Immutability" ] }, { @@ -1518,16 +2283,16 @@ } }, "source": [ - "Whereas elements of a `list` object *may* be *re-assigned*, as shortly hinted at in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_lecture.ipynb#Who-am-I?-And-how-many?), this is *not* allowed for `str` objects. Once created, they *cannot* be *changed*. Formally, we say that they are **immutable**. In that regard, `str` objects and all the numeric types in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers_00_lecture.ipynb) are alike.\n", + "Whereas elements of a `list` object *may* be *re-assigned*, as shortly hinted at in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_lecture.ipynb#Who-am-I?-And-how-many?), this is *not* allowed for the individual characters of `str` objects. Once created, they can *not* be changed. Formally, we say that `str` objects are **immutable**. In that regard, they are like the numeric types in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers_00_lecture.ipynb).\n", "\n", - "On the contrary, objects that may be changed after creation, are called **mutable**. We already saw in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_lecture.ipynb#Who-am-I?-And-how-many?) how mutable objects are more difficult to reason about for a beginner, in particular, if more than *one* variable references it. Yet, mutability does have its place in a programmer's toolbox, and we revisit this idea in the next chapters.\n", + "On the contrary, objects that may be changed after creation, are called **mutable**. We already saw in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_lecture.ipynb#Who-am-I?-And-how-many?) how mutable objects are more difficult to reason about for a beginner, in particular, if more than one variable references it. Yet, mutability does have its place in a programmer's toolbox, and we revisit this idea in the next chapters.\n", "\n", "The `TypeError` indicates that `str` objects are *immutable*: Assignment to an index or a slice are *not* supported." ] }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 72, "metadata": { "slideshow": { "slide_type": "slide" @@ -1541,21 +2306,21 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"Z\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"X\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: 'str' object does not support item assignment" ] } ], "source": [ - "text[0] = \"Z\"" + "text[0] = \"X\"" ] }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 73, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -1566,7 +2331,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"random\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"random\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: 'str' object does not support item assignment" ] } @@ -1583,7 +2348,7 @@ } }, "source": [ - "## String Methods" + "### String Methods" ] }, { @@ -1596,12 +2361,12 @@ "source": [ "Objects of type `str` come with many **methods** bound on them (cf., the [documentation](https://docs.python.org/3/library/stdtypes.html#string-methods) for a full list). As seen before, they work like *normal* functions and are accessed via the **dot operator** `.`. Calling a method is also referred to as **method invocation**.\n", "\n", - "The [find()](https://docs.python.org/3/library/stdtypes.html#str.find) method returns the index of the first occurrence of a character or a substring. If no match is found, it returns `-1`." + "The [find()](https://docs.python.org/3/library/stdtypes.html#str.find) method returns the index of the first occurrence of a character or a substring. If no match is found, it returns `-1`. A mirrored version searching from the right called [rfind()](https://docs.python.org/3/library/stdtypes.html#str.rfind) exists as well. The [index()](https://docs.python.org/3/library/stdtypes.html#str.index) and [rindex()](https://docs.python.org/3/library/stdtypes.html#str.rindex) methods work in the same way but raise a `ValueError` if no match is found. So, we can control if a search fails *silently* or *loudly*." ] }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 74, "metadata": { "slideshow": { "slide_type": "slide" @@ -1611,10 +2376,10 @@ { "data": { "text/plain": [ - "'Lorem ipsum dolor sit amet, ...'" + "'Lorem ipsum dolor sit amet.'" ] }, - "execution_count": 47, + "execution_count": 74, "metadata": {}, "output_type": "execute_result" } @@ -1625,10 +2390,10 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 75, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -1638,7 +2403,7 @@ "22" ] }, - "execution_count": 48, + "execution_count": 75, "metadata": {}, "output_type": "execute_result" } @@ -1649,10 +2414,10 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 76, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -1662,18 +2427,18 @@ "-1" ] }, - "execution_count": 49, + "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "text.find(\"z\")" + "text.find(\"b\")" ] }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 77, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1686,7 +2451,7 @@ "12" ] }, - "execution_count": 50, + "execution_count": 77, "metadata": {}, "output_type": "execute_result" } @@ -1708,10 +2473,10 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 78, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "fragment" } }, "outputs": [ @@ -1721,7 +2486,7 @@ "1" ] }, - "execution_count": 51, + "execution_count": 78, "metadata": {}, "output_type": "execute_result" } @@ -1732,10 +2497,10 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 79, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "skip" } }, "outputs": [ @@ -1745,18 +2510,18 @@ "13" ] }, - "execution_count": 52, + "execution_count": 79, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "text.find(\"o\", 2) # 2 not 1 as otherwise the same \"o\" is found again" + "text.find(\"o\", 2)" ] }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 80, "metadata": { "slideshow": { "slide_type": "skip" @@ -1769,13 +2534,13 @@ "-1" ] }, - "execution_count": 53, + "execution_count": 80, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "text.find(\"o\", 2, 12) # \"o\" does not occur in the specified slice" + "text.find(\"o\", 2, 12)" ] }, { @@ -1786,17 +2551,41 @@ } }, "source": [ - "[count()](https://docs.python.org/3/library/stdtypes.html#str.count) does what we expect." + "The [count()](https://docs.python.org/3/library/stdtypes.html#str.count) method does what we expect." ] }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 81, "metadata": { "slideshow": { "slide_type": "slide" } }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum dolor sit amet.'" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -1804,7 +2593,7 @@ "1" ] }, - "execution_count": 54, + "execution_count": 82, "metadata": {}, "output_type": "execute_result" } @@ -1826,7 +2615,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 83, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1839,7 +2628,7 @@ "2" ] }, - "execution_count": 55, + "execution_count": 83, "metadata": {}, "output_type": "execute_result" } @@ -1856,12 +2645,12 @@ } }, "source": [ - "Alternatively, we may use the [upper()](https://docs.python.org/3/library/stdtypes.html#str.upper) method and search for `\"O\"`s." + "Alternatively, we can use the [upper()](https://docs.python.org/3/library/stdtypes.html#str.upper) method and search for `\"L\"`s." ] }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 84, "metadata": { "slideshow": { "slide_type": "skip" @@ -1874,7 +2663,7 @@ "2" ] }, - "execution_count": 56, + "execution_count": 84, "metadata": {}, "output_type": "execute_result" } @@ -1896,7 +2685,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 85, "metadata": { "slideshow": { "slide_type": "slide" @@ -1909,20 +2698,20 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 86, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ - "140483525349552" + "140461430391152" ] }, - "execution_count": 58, + "execution_count": 86, "metadata": {}, "output_type": "execute_result" } @@ -1933,7 +2722,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 87, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1946,20 +2735,20 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 88, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ - "140483422704176" + "140461335949424" ] }, - "execution_count": 60, + "execution_count": 88, "metadata": {}, "output_type": "execute_result" } @@ -1981,7 +2770,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 89, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1994,7 +2783,7 @@ "False" ] }, - "execution_count": 61, + "execution_count": 89, "metadata": {}, "output_type": "execute_result" } @@ -2005,10 +2794,10 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 90, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -2018,7 +2807,7 @@ "True" ] }, - "execution_count": 62, + "execution_count": 90, "metadata": {}, "output_type": "execute_result" } @@ -2035,36 +2824,103 @@ } }, "source": [ - "Another popular string method is [split()](https://docs.python.org/3/library/stdtypes.html#str.split): It separates a longer `str` object into smaller ones contained in a `list` object. By default, groups of contiguous whitespace are used as the *separator*.\n", - "\n", - "As an example, we use [split()](https://docs.python.org/3/library/stdtypes.html#str.split) to print out the individual words in `text` on separate lines." + "Besides [upper()](https://docs.python.org/3/library/stdtypes.html#str.upper) and [lower()](https://docs.python.org/3/library/stdtypes.html#str.lower) there exist also [title()](https://docs.python.org/3/library/stdtypes.html#str.title) and [swapcase()](https://docs.python.org/3/library/stdtypes.html#str.swapcase) methods." ] }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 91, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "skip" } }, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Lorem\n", - "ipsum\n", - "dolor\n", - "sit\n", - "amet,\n", - "...\n" - ] + "data": { + "text/plain": [ + "'lorem ipsum dolor sit amet.'" + ] + }, + "execution_count": 91, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "for word in text.split():\n", - " print(word)" + "text.lower()" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'LOREM IPSUM DOLOR SIT AMET.'" + ] + }, + "execution_count": 92, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.upper()" + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem Ipsum Dolor Sit Amet.'" + ] + }, + "execution_count": 93, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.title()" + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'lOREM IPSUM DOLOR SIT AMET.'" + ] + }, + "execution_count": 94, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.swapcase()" ] }, { @@ -2075,12 +2931,71 @@ } }, "source": [ - "The opposite of splitting is done with the [join()](https://docs.python.org/3/library/stdtypes.html#str.join) method. It is typically invoked on a `str` object that represents a separator (e.g., `\" \"` or `\", \"`) and connects the elements of an *iterable* argument passed in (e.g., `words` below) into one *new* `str` object." + "Another popular string method is [split()](https://docs.python.org/3/library/stdtypes.html#str.split): It separates a longer `str` object into smaller ones collected in a `list` object. By default, groups of contiguous whitespace characters are used as the *separator*.\n", + "\n", + "As an example, we use [split()](https://docs.python.org/3/library/stdtypes.html#str.split) to print out the individual words in `text` with more whitespace in between them." ] }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 95, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Lorem', 'ipsum', 'dolor', 'sit', 'amet.']" + ] + }, + "execution_count": 95, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.split()" + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lorem ipsum dolor sit amet. " + ] + } + ], + "source": [ + "for word in text.split():\n", + " print(word, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The opposite of splitting is done with the [join()](https://docs.python.org/3/library/stdtypes.html#str.join) method. It is typically invoked on a `str` object that represents a separator (e.g., `\" \"` or `\", \"`) and connects the elements provided by an *iterable* argument (e.g., `words` below) into one *new* `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 97, "metadata": { "slideshow": { "slide_type": "slide" @@ -2088,15 +3003,15 @@ }, "outputs": [], "source": [ - "words = [\"This\", \"will\", \"become\", \"a\", \"sentence\"]" + "words = [\"This\", \"will\", \"become\", \"a\", \"sentence.\"]" ] }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 98, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [], @@ -2106,20 +3021,20 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 99, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "'This will become a sentence'" + "'This will become a sentence.'" ] }, - "execution_count": 66, + "execution_count": 99, "metadata": {}, "output_type": "execute_result" } @@ -2136,12 +3051,12 @@ } }, "source": [ - "As the `str` object `\"abcde\"` below is an *iterable* itself, its elements (i.e., characters) are joined together with a space `\" \"` in between." + "As the `str` object `\"abcde\"` below is an *iterable* itself, its characters (!) are joined together with a space `\" \"` in between." ] }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 100, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2154,7 +3069,7 @@ "'a b c d e'" ] }, - "execution_count": 67, + "execution_count": 100, "metadata": {}, "output_type": "execute_result" } @@ -2176,7 +3091,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 101, "metadata": { "slideshow": { "slide_type": "slide" @@ -2186,10 +3101,10 @@ { "data": { "text/plain": [ - "'This is a sentence'" + "'This is a sentence.'" ] }, - "execution_count": 68, + "execution_count": 101, "metadata": {}, "output_type": "execute_result" } @@ -2198,6 +3113,242 @@ "sentence.replace(\"will become\", \"is\")" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Note how `sentence` itself remains unchanged. Bound to an immutable object, [replace()](https://docs.python.org/3/library/stdtypes.html#str.replace) must create *new* objects." + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'This will become a sentence.'" + ] + }, + "execution_count": 102, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sentence" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As seen previously, the [strip()](https://docs.python.org/3/library/stdtypes.html#str.strip) method is often helpful in cleaning text data from unreliable sources like user input from unnecessary leading and trailing whitespace. The [lstrip()](https://docs.python.org/3/library/stdtypes.html#str.lstrip) and [rstrip()](https://docs.python.org/3/library/stdtypes.html#str.rstrip) methods are specialized versions of it." + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'text with whitespace'" + ] + }, + "execution_count": 103, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\" text with whitespace \".strip()" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'text with whitespace '" + ] + }, + "execution_count": 104, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\" text with whitespace \".lstrip()" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "' text with whitespace'" + ] + }, + "execution_count": 105, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\" text with whitespace \".rstrip()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When justifying a `str` object for output, the [ljust()](https://docs.python.org/3/library/stdtypes.html#str.ljust) and [rjust()](https://docs.python.org/3/library/stdtypes.html#str.rjust) methods may be helpful." + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'This will become a sentence. '" + ] + }, + "execution_count": 106, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sentence.ljust(40)" + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "' This will become a sentence.'" + ] + }, + "execution_count": 107, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sentence.rjust(40)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Similarly, the [zfill()](https://docs.python.org/3/library/stdtypes.html#str.zfill) method can be used to pad a `str` representation of a number with leading `0`s for justified output." + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0000042.87'" + ] + }, + "execution_count": 108, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"42.87\".zfill(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'-000042.87'" + ] + }, + "execution_count": 109, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"-42.87\".zfill(10)" + ] + }, { "cell_type": "markdown", "metadata": { @@ -2206,7 +3357,7 @@ } }, "source": [ - "## String Operations" + "### String Operations" ] }, { @@ -2222,7 +3373,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 110, "metadata": { "slideshow": { "slide_type": "slide" @@ -2235,7 +3386,7 @@ "'Hello Lore'" ] }, - "execution_count": 69, + "execution_count": 110, "metadata": {}, "output_type": "execute_result" } @@ -2246,26 +3397,26 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 111, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "'Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum '" + "'Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum ...'" ] }, - "execution_count": 70, + "execution_count": 111, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "5 * text[:12]" + "5 * text[:12] + \"...\"" ] }, { @@ -2276,7 +3427,7 @@ } }, "source": [ - "### String Comparison" + "#### String Comparison" ] }, { @@ -2287,32 +3438,17 @@ } }, "source": [ - "The *relational* operators also work with `str` objects, another example of operator overloading. Comparison is done one character at a time until the first pair differs or one operand ends. However, `str` objects are sorted in a \"weird\" way. The reason for this is that computers store characters internally as numbers (i.e., $0$s and $1$s). Depending on the character encoding, these numbers vary. Commonly, characters and symbols used in American English are encoded with the numbers 0 through 127, the so-called [ASCII standard](https://en.wikipedia.org/wiki/ASCII). However, Python works with the more general [Unicode/UTF-8 standard](https://en.wikipedia.org/wiki/UTF-8) that understands every language ever used by humans, even emojis." + "The *relational* operators also work with `str` objects, another example of operator overloading. Comparison is done one character at a time in a pairwise fashion until the first pair differs or one operand ends. However, `str` objects are sorted in a \"weird\" way. For example, all upper case characters come before all lower case characters. The reason for that is given in the \"*Characters are Numbers with a Convention*\" sub-section further below." ] }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 112, "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [], - "source": [ - "A = \"Apple\" # ignore snake_case for variable names in this example\n", - "a = \"apple\"\n", - "B = \"Banana\"" - ] - }, - { - "cell_type": "code", - "execution_count": 72, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, "outputs": [ { "data": { @@ -2320,21 +3456,21 @@ "True" ] }, - "execution_count": 72, + "execution_count": 112, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "A < B" + "\"Apple\" < \"Banana\"" ] }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 113, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -2344,29 +3480,18 @@ "False" ] }, - "execution_count": 73, + "execution_count": 113, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "a < B" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "One way to fix this is to only compare lower-cased `str` objects." + "\"apple\" < \"Banana\" # upper case letter come before lower case ones" ] }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 114, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2379,13 +3504,13 @@ "True" ] }, - "execution_count": 74, + "execution_count": 114, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "a < B.lower()" + "\"apple\" < \"Banana\".lower()" ] }, { @@ -2396,57 +3521,31 @@ } }, "source": [ - "To provide a simple intuition for the \"weird\" sorting above, let's think of the alphabet as being represented by the numbers as listed below. Then `\"Banana\"` is clearly \"smaller\" than `\"apple\"`. In general, all the upper case letters are \"smaller\" than all the lower case letters." + "Below is an example with typical German last names that shows how characters other than the first decide the ordering." ] }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 115, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "fragment" } }, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "A -> 65 \t a -> 97\n", - "B -> 66 \t b -> 98\n", - "C -> 67 \t c -> 99\n", - "D -> 68 \t d -> 100\n", - "E -> 69 \t e -> 101\n", - "F -> 70 \t f -> 102\n", - "G -> 71 \t g -> 103\n", - "H -> 72 \t h -> 104\n", - "I -> 73 \t i -> 105\n", - "J -> 74 \t j -> 106\n", - "K -> 75 \t k -> 107\n", - "L -> 76 \t l -> 108\n", - "M -> 77 \t m -> 109\n", - "N -> 78 \t n -> 110\n", - "O -> 79 \t o -> 111\n", - "P -> 80 \t p -> 112\n", - "Q -> 81 \t q -> 113\n", - "R -> 82 \t r -> 114\n", - "S -> 83 \t s -> 115\n", - "T -> 84 \t t -> 116\n", - "U -> 85 \t u -> 117\n", - "V -> 86 \t v -> 118\n", - "W -> 87 \t w -> 119\n", - "X -> 88 \t x -> 120\n", - "Y -> 89 \t y -> 121\n", - "Z -> 90 \t z -> 122\n" - ] + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 115, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "for lower_i in range(65, 91):\n", - " upper_i = lower_i + 32 # all the upper case characters are offset by 32\n", - " lower_char = chr(lower_i) # from their lower case counterpart\n", - " upper_char = chr(upper_i)\n", - " print(f\"{lower_char} -> {lower_i} \\t {upper_char} -> {upper_i}\")" + "\"Mai\" < \"Maier\" < \"Mayer\" < \"Meier\" < \"Meyer\"" ] }, { @@ -2457,7 +3556,7 @@ } }, "source": [ - "## String Interpolation" + "### String Interpolation" ] }, { @@ -2468,9 +3567,7 @@ } }, "source": [ - "The previous code cell shows an example of a so-called **f-string**, as introduced by [PEP 498](https://www.python.org/dev/peps/pep-0498/) only in 2016, that is passed as the argument to the [print()](https://docs.python.org/3/library/functions.html#print) function.\n", - "\n", - "The \"f\" stands for \"formatted\", and we can think of the `str` object as a text \"draft\" that is filled in with values determined at runtime. This concept is formally called **string interpolation**, and there are three ways to achieve that in Python." + "Often, we want to use `str` objects as drafts in the source code that are filled in with concrete text only at runtime. This approach is called **string interpolation**. There are three ways to do that in Python." ] }, { @@ -2481,7 +3578,7 @@ } }, "source": [ - "### f-strings" + "#### f-strings" ] }, { @@ -2492,12 +3589,12 @@ } }, "source": [ - "f-strings, formally called **[formatted string literals](https://docs.python.org/3/reference/lexical_analysis.html#formatted-string-literals)**, are the least recently added and most readable way: We prepend a `str` in literal notation with an `f`, and put variables, or more generally, expressions, within curly braces. These are then filled in when a `str` object is evaluated." + "**[Formatted string literals](https://docs.python.org/3/reference/lexical_analysis.html#formatted-string-literals)**, of **f-strings** for short, are the least recently added (cf., [PEP 498](https://www.python.org/dev/peps/pep-0498/) in 2016) and most readable way: We simply prepend a `str` in its literal notation with an `f`, and put variables, or more generally, expressions, within curly braces `{}`. These are then filled in when the string literal is evaluated." ] }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 116, "metadata": { "slideshow": { "slide_type": "slide" @@ -2511,10 +3608,10 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 117, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -2524,7 +3621,7 @@ "'Hello Alexander! Good morning.'" ] }, - "execution_count": 77, + "execution_count": 117, "metadata": {}, "output_type": "execute_result" } @@ -2541,15 +3638,15 @@ } }, "source": [ - "Separated by a colon `:`, various formatting options are available. In the beginning, the ability to round may be particularly useful: This can be achieved by adding `:.2f` to the variable name inside the curly braces, which casts the number as a `float` and rounds it to two digits. The `:.2f` is a so-called format specifier, and there exists a whole **[format specification mini-language](https://docs.python.org/3/library/string.html#formatspec)** to govern how specifiers work." + "Separated by a colon `:`, various formatting options are available. In the beginning, the ability to round numbers for output may be particularly useful: This can be achieved by adding `:.2f` to the variable name inside the curly braces, which casts the number as a `float` and rounds it to two digits. The `:.2f` is a so-called format specifier, and there exists a whole **[format specification mini-language](https://docs.python.org/3/library/string.html#formatspec)** to govern how specifiers work." ] }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 118, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "fragment" } }, "outputs": [], @@ -2559,10 +3656,10 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 119, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -2572,7 +3669,7 @@ "'Pi is 3.14'" ] }, - "execution_count": 79, + "execution_count": 119, "metadata": {}, "output_type": "execute_result" } @@ -2581,30 +3678,6 @@ "f\"Pi is {pi:.2f}\"" ] }, - { - "cell_type": "code", - "execution_count": 80, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "'Pi is 3.142'" - ] - }, - "execution_count": 80, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "f\"Pi is {pi:.3f}\"" - ] - }, { "cell_type": "markdown", "metadata": { @@ -2613,7 +3686,7 @@ } }, "source": [ - "### [format()](https://docs.python.org/3/library/stdtypes.html#str.format) Method" + "#### [format()](https://docs.python.org/3/library/stdtypes.html#str.format) Method" ] }, { @@ -2624,12 +3697,12 @@ } }, "source": [ - "`str` objects also provide a [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method that accepts an arbitrary number of *positional* arguments that are inserted into the `str` object in the same order replacing empty curly brackets. String interpolation with the [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method is a more traditional and probably the most common way one as of today. While f-strings are the recommended way going forward, usage of the [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method is likely not declining any time soon." + "`str` objects also provide a [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method that accepts an arbitrary number of *positional* arguments that are inserted into the `str` object in the same order replacing empty curly brackets `{}`. String interpolation with the [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method is a more traditional and probably the most common way as of today. While f-strings are the recommended way going forward, usage of the [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method is likely not declining any time soon." ] }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 120, "metadata": { "slideshow": { "slide_type": "slide" @@ -2642,7 +3715,7 @@ "'Hello Alexander! Good morning.'" ] }, - "execution_count": 81, + "execution_count": 120, "metadata": {}, "output_type": "execute_result" } @@ -2664,7 +3737,7 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 121, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2677,7 +3750,7 @@ "'Good morning, Alexander'" ] }, - "execution_count": 82, + "execution_count": 121, "metadata": {}, "output_type": "execute_result" } @@ -2699,7 +3772,7 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 122, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2712,7 +3785,7 @@ "'Hello Alexander! Good morning.'" ] }, - "execution_count": 83, + "execution_count": 122, "metadata": {}, "output_type": "execute_result" } @@ -2734,7 +3807,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 123, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2747,7 +3820,7 @@ "'Pi is 3.14'" ] }, - "execution_count": 84, + "execution_count": 123, "metadata": {}, "output_type": "execute_result" } @@ -2760,11 +3833,11 @@ "cell_type": "markdown", "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "slide" } }, "source": [ - "### `%` Operator" + "#### `%` Operator" ] }, { @@ -2782,10 +3855,10 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 124, "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "slide" } }, "outputs": [ @@ -2795,7 +3868,7 @@ "'Pi is 3.14'" ] }, - "execution_count": 85, + "execution_count": 124, "metadata": {}, "output_type": "execute_result" } @@ -2817,10 +3890,10 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 125, "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "fragment" } }, "outputs": [ @@ -2830,7 +3903,7 @@ "'Hello Alexander! Good morning.'" ] }, - "execution_count": 86, + "execution_count": 125, "metadata": {}, "output_type": "execute_result" } @@ -2847,7 +3920,7 @@ } }, "source": [ - "## Special Characters" + "### Unicode & (Special) Characters" ] }, { @@ -2858,19 +3931,43 @@ } }, "source": [ - "Some symbols have a special meaning within `str` objects. Popular examples are the newline `\\n` and tab `\\t` \"characters.\" The backslash symbol `\\` is also referred to as an **escape character** in this context, indicating that the following character has a meaning other than its literal meaning.\n", + "As previously seen, some characters have a special meaning when following the **escape character** `\"\\\"`. Besides escaping the kind of quote used as the `str` object's delimiter, `'` or `\"`, most of these **escape sequences** (i.e., `\"\\\"` with the subsequent character), act as a **control character** that moves the \"cursor\" in the output *without* generating any pixel on the screen. Because of that, we only see the effect of such escape sequences when used with the [print()](https://docs.python.org/3/library/functions.html#print) function. The [documentation](https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals) lists all available escape sequences, of which we show the most important ones below.\n", "\n", - "The built-in [print()](https://docs.python.org/3/library/functions.html#print) function then \"prints\" out these special characters accordingly." + "The most common escape sequence is `\"\\n\"` that \"prints\" a [newline character](https://en.wikipedia.org/wiki/Newline) that is also called the line feed character or LF for short." ] }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 126, "metadata": { "slideshow": { "slide_type": "slide" } }, + "outputs": [ + { + "data": { + "text/plain": [ + "'This is a sentence\\nthat is printed\\non three lines.'" + ] + }, + "execution_count": 126, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"This is a sentence\\nthat is printed\\non three lines.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 127, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "name": "stdout", @@ -2887,11 +3984,22 @@ ] }, { - "cell_type": "code", - "execution_count": 88, + "cell_type": "markdown", "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "skip" + } + }, + "source": [ + "`\"\\b\"` is the [backspace character](https://en.wikipedia.org/wiki/Backspace), or BS for short, that moves the cursor back by one character." + ] + }, + { + "cell_type": "code", + "execution_count": 128, + "metadata": { + "slideshow": { + "slide_type": "slide" } }, "outputs": [ @@ -2899,12 +4007,33 @@ "name": "stdout", "output_type": "stream", "text": [ - "Words\taligned\twith\ttabs.\n" + "ABX\n" ] } ], "source": [ - "print(\"Words\\taligned\\twith\\ttabs.\")" + "print(\"ABC\\bX\")" + ] + }, + { + "cell_type": "code", + "execution_count": 129, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ABXY\n" + ] + } + ], + "source": [ + "print(\"ABC\\bXY\")" ] }, { @@ -2915,12 +4044,12 @@ } }, "source": [ - "As emojis are important as well, they can be inserted with the corresponding **unicode code point** number starting with `\\U`. See this [list](https://en.wikipedia.org/wiki/List_of_Unicode_characters) of unicode characters for an overview." + "Similarly, `\"\\r\"` is the [carriage return character](https://en.wikipedia.org/wiki/Carriage_return), or CR for short, that moves the cursor back to the beginning of the line." ] }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 130, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2931,12 +4060,33 @@ "name": "stdout", "output_type": "stream", "text": [ - "😄\n" + "XBC\n" ] } ], "source": [ - "print(\"\\U0001f604\")" + "print(\"ABC\\rX\")" + ] + }, + { + "cell_type": "code", + "execution_count": 131, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "XYC\n" + ] + } + ], + "source": [ + "print(\"ABC\\rXY\")" ] }, { @@ -2947,12 +4097,12 @@ } }, "source": [ - "Outside the [print()](https://docs.python.org/3/library/functions.html#print) function, the special characters are not treated any different from non-special ones." + "While Linux and modern MacOS systems use solely `\"\\n\"` to express a new line, Windows systems default to using `\"\\r\\n\"`. This may lead to \"weird\" bugs on software projects where people using both kind of operating systems collaborate." ] }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 132, "metadata": { "slideshow": { "slide_type": "skip" @@ -2960,18 +4110,50 @@ }, "outputs": [ { - "data": { - "text/plain": [ - "'This is a sentence\\nthat is printed\\non three lines.'" - ] - }, - "execution_count": 90, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "This is a sentence\n", + "that is printed\n", + "on three lines.\n" + ] } ], "source": [ - "\"This is a sentence\\nthat is printed\\non three lines.\"" + "print(\"This is a sentence\\r\\nthat is printed\\r\\non three lines.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`\"\\t\"` makes the cursor \"jump\" in equidistant tab stops. That may be useful for formatting a program with lengthy and tabular results." + ] + }, + { + "cell_type": "code", + "execution_count": 133, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Jump\tfrom\ttab\tstop\tto\ttab\tstop.\n", + "The\tsecond\tline\tdoes\tso\ttoo.\n" + ] + } + ], + "source": [ + "print(\"Jump\\tfrom\\ttab\\tstop\\tto\\ttab\\tstop.\\nThe\\tsecond\\tline\\tdoes\\tso\\ttoo.\")" ] }, { @@ -2982,7 +4164,7 @@ } }, "source": [ - "## Raw Strings" + "#### Raw Strings" ] }, { @@ -2993,14 +4175,12 @@ } }, "source": [ - "Sometimes we do *not* want the backslash `\\` and its following character be interpreted as special characters.\n", - "\n", - "For example, let's print a typical installation path on a Windows systems. Obviously, the newline character `\\n` does *not* makes sense here." + "Sometimes we do *not* want the backslash `\"\\\"` and its subsequent character be interpreted as an escape sequence. For example, let's print a typical installation path on a Windows systems. Obviously, the newline character `\"\\n\"` does *not* makes sense here." ] }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 134, "metadata": { "slideshow": { "slide_type": "slide" @@ -3028,12 +4208,12 @@ } }, "source": [ - "Some `str` objects even produce a `SyntaxError` because the `\\U` *cannot* be interpreted as a unicode code point." + "Some `str` objects even produce a `SyntaxError` because the `\"\\U\"` can *not* be interpreted as a Unicode code point (cf., next section)." ] }, { "cell_type": "code", - "execution_count": 92, + "execution_count": 135, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3042,15 +4222,15 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "(unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape (, line 1)", + "evalue": "(unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print(\"C:\\Users\\Administrator\\Desktop\\Project_Folder\")\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print(\"C:\\Users\\Administrator\\Desktop\\Project\")\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape\n" ] } ], "source": [ - "print(\"C:\\Users\\Administrator\\Desktop\\Project_Folder\")" + "print(\"C:\\Users\\Administrator\\Desktop\\Project\")" ] }, { @@ -3061,12 +4241,12 @@ } }, "source": [ - "A simple solution would be to escape the escape character with a *second* backslash `\\`." + "A simple solution would be to escape the escape character with a *second* backslash `\"\\\"`." ] }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 136, "metadata": { "slideshow": { "slide_type": "slide" @@ -3087,10 +4267,10 @@ }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 137, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -3098,12 +4278,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "C:\\Users\\Administrator\\Desktop\\Project_Folder\n" + "C:\\Users\\Administrator\\Desktop\\Project\n" ] } ], "source": [ - "print(\"C:\\\\Users\\\\Administrator\\\\Desktop\\\\Project_Folder\")" + "print(\"C:\\\\Users\\\\Administrator\\\\Desktop\\\\Project\")" ] }, { @@ -3114,12 +4294,12 @@ } }, "source": [ - "However, this is tedious to remember and type. Luckily, Python allows treating any string literal as \"raw,\" and this is indicated in the string literal by the prefix `r`." + "However, this is tedious to remember and type. For such use cases, Python allows to prefix any string literal with a `r`. The literal is then interpreted in a \"raw\" way." ] }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 138, "metadata": { "slideshow": { "slide_type": "slide" @@ -3140,10 +4320,10 @@ }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 139, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -3151,12 +4331,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "C:\\Users\\Administrator\\Desktop\\Project_Folder\n" + "C:\\Users\\Administrator\\Desktop\\Project\n" ] } ], "source": [ - "print(r\"C:\\Users\\Administrator\\Desktop\\Project_Folder\")" + "print(r\"C:\\Users\\Administrator\\Desktop\\Project\")" ] }, { @@ -3167,7 +4347,7 @@ } }, "source": [ - "## Multi-line Strings" + "#### Characters are Numbers with a Convention" ] }, { @@ -3178,12 +4358,693 @@ } }, "source": [ - "Sometimes, it is convenient to split text across multiple lines in source code. For example, to make lines fit into the 79 characters requirement of [PEP 8](https://www.python.org/dev/peps/pep-0008/) or because the text naturally contains many newlines. Using double quotes `\"` around multiple lines results in a `SyntaxError`." + "So far, we used the term **character** without any further consideration. In this section, we briefly look into what characters are and how they are modeled in software.\n", + "\n", + "[Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers_00_lecture.ipynb) gives us an idea on how individual **bits** are used to express all types of numbers, from \"simple\" `int` objects to \"complex\" `float` ones. To model characters, another **layer of abstraction** is put on top of whole numbers. So, just as bits are used to express integers, they themselves are used to express characters." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "##### ASCII" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Many conventions have been developed as to what integer is associated with which character. The most basic one that was also adopted around the world is the the so-called [American Standard Code for Information Interchange](https://en.wikipedia.org/wiki/ASCII), or **ASCII** for short. It uses 7 bits of information to map the unprintable control characters as well as the printable letters of the alphabet, numbers, and common symbols to the numbers `0` through `127`.\n", + "\n", + "A mapping from characters to numbers is referred to by the technical term **encoding**. We may use the built-in [ord()](https://docs.python.org/3/library/functions.html#ord) function to **encode** any single character. The inverse to that is the built-in [chr()](https://docs.python.org/3/library/functions.html#chr) function, which **decodes** a number into a character." ] }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 140, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "65" + ] + }, + "execution_count": 140, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ord(\"A\")" + ] + }, + { + "cell_type": "code", + "execution_count": 141, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'A'" + ] + }, + "execution_count": 141, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chr(65)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Of course, unprintable escape sequences like `\"\\n\"` count as only *one* character." + ] + }, + { + "cell_type": "code", + "execution_count": 142, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10" + ] + }, + "execution_count": 142, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ord(\"\\n\")" + ] + }, + { + "cell_type": "code", + "execution_count": 143, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'\\n'" + ] + }, + "execution_count": 143, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chr(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In ASCII, the numbers `0` through `31` (and `127`) are mapped to all kinds of unprintable control characters. The decimal digits are encoded with the numbers `48` through `57`, the upper case letters with `65` through `90`, and the lower case letters with `97` through `122`. While this seems random as first, there is of course a \"sophisticated\" system behind it. That can immediately be seen when looking at the encoded numbers in their *binary* representations.\n", + "\n", + "For example, the digit `5` is mapped to the number `53` in ASCII. The binary representation of `53` is `0b_11_0101` and the least significant four bits, `0101`, mean $5$. Similarly, the letter `\"E\"` is the fifth letter in the alphabet. It is encoded with the number `69` in ASCII, which is `0b_100_0101` in binary. And, the least significant bits, `0_0101`, mean $5$. Analogously, `\"e\"` is encoded with `101` in ASCII, which is `0b_110_0101` in binary. And, the least significant bits, `0_0101`, mean $5$ again. This encoding was chosen mainly because programmers \"in the old days\" needed to implement these encodings \"by hand.\" Python abstracts that logic away from its users.\n", + "\n", + "This encoding scheme is also the cause for the \"weird\" sorting in the \"*String Comparison*\" section above, where `\"apple\"` comes *after* `\"Banana\"`. As `\"a\"` is encoded with `97` and `\"B\"` with `66`, `\"Banana\"` must of course be \"smaller\" than `\"apple\"` when comparison is done in a pairwise fashion of the individual characters." + ] + }, + { + "cell_type": "code", + "execution_count": 144, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "48 0b110000 -> 0\n", + "49 0b110001 -> 1\n", + "50 0b110010 -> 2\n", + "51 0b110011 -> 3\n", + "52 0b110100 -> 4\n", + "53 0b110101 -> 5\n", + "54 0b110110 -> 6\n", + "55 0b110111 -> 7\n", + "56 0b111000 -> 8\n", + "57 0b111001 -> 9\n" + ] + } + ], + "source": [ + "for number in range(48, 58):\n", + " print(number, bin(number), \"-> \", chr(number))" + ] + }, + { + "cell_type": "code", + "execution_count": 145, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "65 0b1000001 -> A\t66 0b1000010 -> B\t67 0b1000011 -> C\n", + "68 0b1000100 -> D\t69 0b1000101 -> E\t70 0b1000110 -> F\n", + "71 0b1000111 -> G\t72 0b1001000 -> H\t73 0b1001001 -> I\n", + "74 0b1001010 -> J\t75 0b1001011 -> K\t76 0b1001100 -> L\n", + "77 0b1001101 -> M\t78 0b1001110 -> N\t79 0b1001111 -> O\n", + "80 0b1010000 -> P\t81 0b1010001 -> Q\t82 0b1010010 -> R\n", + "83 0b1010011 -> S\t84 0b1010100 -> T\t85 0b1010101 -> U\n", + "86 0b1010110 -> V\t87 0b1010111 -> W\t88 0b1011000 -> X\n", + "89 0b1011001 -> Y\t90 0b1011010 -> Z\t" + ] + } + ], + "source": [ + "for i, number in enumerate(range(65, 91), start=1):\n", + " end = \"\\n\" if i % 3 == 0 else \"\\t\"\n", + " print(number, bin(number), \"-> \", chr(number), end=end)" + ] + }, + { + "cell_type": "code", + "execution_count": 146, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 97 0b1100001 -> a\t 98 0b1100010 -> b\t 99 0b1100011 -> c\n", + "100 0b1100100 -> d\t101 0b1100101 -> e\t102 0b1100110 -> f\n", + "103 0b1100111 -> g\t104 0b1101000 -> h\t105 0b1101001 -> i\n", + "106 0b1101010 -> j\t107 0b1101011 -> k\t108 0b1101100 -> l\n", + "109 0b1101101 -> m\t110 0b1101110 -> n\t111 0b1101111 -> o\n", + "112 0b1110000 -> p\t113 0b1110001 -> q\t114 0b1110010 -> r\n", + "115 0b1110011 -> s\t116 0b1110100 -> t\t117 0b1110101 -> u\n", + "118 0b1110110 -> v\t119 0b1110111 -> w\t120 0b1111000 -> x\n", + "121 0b1111001 -> y\t122 0b1111010 -> z\t" + ] + } + ], + "source": [ + "for i, number in enumerate(range(97, 123), start=1):\n", + " end = \"\\n\" if i % 3 == 0 else \"\\t\"\n", + " print(str(number).rjust(3), bin(number), \"-> \", chr(number), end=end)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The remaining `symbols` encoded in ASCII are encoded with the numbers still unused, which is why they are scattered." + ] + }, + { + "cell_type": "code", + "execution_count": 147, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "symbols = (\n", + " list(range(32, 48))\n", + " + list(range(58, 65))\n", + " + list(range(91, 97))\n", + " + list(range(123, 127))\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 148, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 32 0b100000 -> \t 33 0b100001 -> !\t 34 0b100010 -> \"\n", + " 35 0b100011 -> #\t 36 0b100100 -> $\t 37 0b100101 -> %\n", + " 38 0b100110 -> &\t 39 0b100111 -> '\t 40 0b101000 -> (\n", + " 41 0b101001 -> )\t 42 0b101010 -> *\t 43 0b101011 -> +\n", + " 44 0b101100 -> ,\t 45 0b101101 -> -\t 46 0b101110 -> .\n", + " 47 0b101111 -> /\t 58 0b111010 -> :\t 59 0b111011 -> ;\n", + " 60 0b111100 -> <\t 61 0b111101 -> =\t 62 0b111110 -> >\n", + " 63 0b111111 -> ?\t 64 0b1000000 -> @\t 91 0b1011011 -> [\n", + " 92 0b1011100 -> \\\t 93 0b1011101 -> ]\t 94 0b1011110 -> ^\n", + " 95 0b1011111 -> _\t 96 0b1100000 -> `\t123 0b1111011 -> {\n", + "124 0b1111100 -> |\t125 0b1111101 -> }\t126 0b1111110 -> ~\n" + ] + } + ], + "source": [ + "for i, number in enumerate(symbols, start=1):\n", + " end = \"\\n\" if i % 3 == 0 else \"\\t\"\n", + " print(str(number).rjust(3), bin(number).rjust(10), \"-> \", chr(number), end=end)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As the ASCII character set does not work for many languages other than English, various encodings were developed. Popular examples are [ISO 8859-1](https://en.wikipedia.org/wiki/ISO/IEC_8859-1) for western European letters or [Windows 1250](https://en.wikipedia.org/wiki/Windows-1250) for Latin ones. Many of these encodings use 8-bit numbers (i.e., `0` through `255`) to map the multitude of non-English letters (e.g., the German [umlauts](https://en.wikipedia.org/wiki/Umlaut_%28linguistics%29) `\"ä\"`, `\"ö\"`, `\"ü\"`, or `\"ß\"`)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "##### Unicode" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, none of these specialized encodings can map *all* characters of *all* languages around the world from *all* times in human history. To achieve that, a truly global standard called **[Unicode](https://en.wikipedia.org/wiki/Unicode)** was developed and its first version released in 1991. Since then, Unicode has been amended with many other \"characters.\" The most popular among them being [emojis](https://en.wikipedia.org/wiki/Emoji) or the [Klingon](https://en.wikipedia.org/wiki/Klingon_scripts) language (from the science fiction series [Star Trek](https://en.wikipedia.org/wiki/Star_Trek)). In Unicode, every character is given an identity referred to as the **code point**. Code points are hexadecimal numbers from `0x0000` through `0x10ffff`, written as U+0000 and U+10FFFF outside of Python. Consequently, there exist at most $1,114,112$ code points, of which only about 10% are currently in use, allowing lots of room for new characters to be invented. The first `127` code points are identical to the ASCII encoding for reasons explained in the \"*The `bytes` Type*\" section further below. There exist plenty of lists of all Unicode characters on the web (e.g., [Wikipedia](https://en.wikipedia.org/wiki/List_of_Unicode_characters)).\n", + "\n", + "All we need to know to print a character is its code point. Python uses the escape sequence `\"\\U\"` that is followed by eight hexadecimal digits. Underscore separators are unfortunately *not* allowed here.\n", + "\n", + "So, to print a smiley, we just need to look up the corresponding number (e.g., [here](https://en.wikipedia.org/wiki/Emoji#Unicode_blocks))." + ] + }, + { + "cell_type": "code", + "execution_count": 149, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'😄'" + ] + }, + "execution_count": 149, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"\\U0001f604\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Every Unicode character also has a descriptive name that we can use with the escape sequence `\"\\N\"` and within curly braces `{}`." + ] + }, + { + "cell_type": "code", + "execution_count": 150, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'😂'" + ] + }, + "execution_count": 150, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"\\N{FACE WITH TEARS OF JOY}\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Whenever the code point can be expressed with just four hexadecimal digits, we may use the escape sequence `\"\\u\"` for brevity." + ] + }, + { + "cell_type": "code", + "execution_count": 151, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'A'" + ] + }, + "execution_count": 151, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"\\U00000041\" # hex(65) == 0x41" + ] + }, + { + "cell_type": "code", + "execution_count": 152, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'A'" + ] + }, + "execution_count": 152, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"\\u0041\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Analogously, if the code point can be expressed with two hexadecimal digits, we may use the escape sequence `\"\\x\"` for even conciser code." + ] + }, + { + "cell_type": "code", + "execution_count": 153, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'A'" + ] + }, + "execution_count": 153, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"\\x41\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As the `str` type is based on Unicode, a `str` object's behavior is more in line with how humans view text and not how it is expressed in source code.\n", + "\n", + "For example, while it is obvious that `len(\"A\")` evaluates to `1`, ..." + ] + }, + { + "cell_type": "code", + "execution_count": 154, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 154, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(\"A\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... what should `len(\"\\N{SNAKE}\")` evaluate to? As the idea of a snake is expressed as *one* \"character,\" [len()](https://docs.python.org/3/library/functions.html#len) also returns `1` here." + ] + }, + { + "cell_type": "code", + "execution_count": 155, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'🐍'" + ] + }, + "execution_count": 155, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"\\N{SNAKE}\"" + ] + }, + { + "cell_type": "code", + "execution_count": 156, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 156, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(\"\\N{SNAKE}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Many of the built-in `str` methods also consider Unicode. For example, in contrast to [lower()](https://docs.python.org/3/library/stdtypes.html#str.lower), the [casefold()](https://docs.python.org/3/library/stdtypes.html#str.casefold) method knows that the German `\"ß\"` is commonly converted to `\"ss\"`. So, when searching for exact matches, normalizing text with [casefold()](https://docs.python.org/3/library/stdtypes.html#str.casefold) may yield better results than with [lower()](https://docs.python.org/3/library/stdtypes.html#str.lower)." + ] + }, + { + "cell_type": "code", + "execution_count": 157, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'straße'" + ] + }, + "execution_count": 157, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Straße\".lower()" + ] + }, + { + "cell_type": "code", + "execution_count": 158, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'strasse'" + ] + }, + "execution_count": 158, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Straße\".casefold()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Many other methods like [isdecimal()](https://docs.python.org/3/library/stdtypes.html#str.isdecimal), [isdigit()](https://docs.python.org/3/library/stdtypes.html#str.isdigit), [isnumeric()](https://docs.python.org/3/library/stdtypes.html#str.isnumeric), [isprintable()](https://docs.python.org/3/library/stdtypes.html#str.isprintable), [isidentifier()](https://docs.python.org/3/library/stdtypes.html#str.isidentifier), and many more may be worthwhile to know for the data science practitioner, especially when it comes to data cleaning." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Multi-line Strings" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Sometimes, it is convenient to split text across multiple lines in source code. For example, to make lines fit into the 79 characters requirement of [PEP 8](https://www.python.org/dev/peps/pep-0008/) or because the text consists of many lines and typing out `\"\\n\"` is tedious. However, using single double quotes `\"` around multiple lines results in a `SyntaxError`." + ] + }, + { + "cell_type": "code", + "execution_count": 159, "metadata": { "slideshow": { "slide_type": "slide" @@ -3192,10 +5053,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "EOL while scanning string literal (, line 1)", + "evalue": "EOL while scanning string literal (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m \"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m EOL while scanning string literal\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m \"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m EOL while scanning string literal\n" ] } ], @@ -3213,22 +5074,22 @@ } }, "source": [ - "However, by enclosing a string literal with either **triple-double** quotes `\"\"\"` or **triple-single** quotes `'''`, Python creates a \"plain\" `str` object. Docstrings are precisely that, and, by convention, always written in triple-double quotes `\"\"\"`." + "Instead, we may enclose a string literal with either **triple double** quotes `\"\"\"` or **triple single** quotes `'''`. Then, newline characters in the source code are converted into `\"\\n\"` characters in the resulting `str` object. Docstrings are precisely that, and, by convention, always written within triple double quotes `\"\"\"`." ] }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 160, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "slide" } }, "outputs": [], "source": [ "multi_line = \"\"\"\n", "I am a multi-line string\n", - "consisting of 4 lines.\n", + "consisting of four lines.\n", "\"\"\"" ] }, @@ -3240,25 +5101,25 @@ } }, "source": [ - "Line breaks are kept and implicitly converted into `\\n` characters." + "A caveat is that `\"\\n\"` characters are often inserted at the beginning or end of the text when we try to format the source code nicely." ] }, { "cell_type": "code", - "execution_count": 99, + "execution_count": 161, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ - "'\\nI am a multi-line string\\nconsisting of 4 lines.\\n'" + "'\\nI am a multi-line string\\nconsisting of four lines.\\n'" ] }, - "execution_count": 99, + "execution_count": 161, "metadata": {}, "output_type": "execute_result" } @@ -3267,20 +5128,9 @@ "multi_line" ] }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "The built-in [print()](https://docs.python.org/3/library/functions.html#print) function correctly prints out the `\\n` characters." - ] - }, { "cell_type": "code", - "execution_count": 100, + "execution_count": 162, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3293,7 +5143,7 @@ "text": [ "\n", "I am a multi-line string\n", - "consisting of 4 lines.\n", + "consisting of four lines.\n", "\n" ] } @@ -3310,15 +5160,15 @@ } }, "source": [ - "Using the [split()](https://docs.python.org/3/library/stdtypes.html#str.split) method with the optional *sep* argument, we confirm that `multi_line` consists of *four* lines with the first and last line breaks being the first and last characters in the `str` object." + "Using the [split()](https://docs.python.org/3/library/stdtypes.html#str.split) method with the optional `sep` argument, we confirm that `multi_line` consists of *four* lines with the first and last line being empty." ] }, { "cell_type": "code", - "execution_count": 101, + "execution_count": 163, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "fragment" } }, "outputs": [ @@ -3326,15 +5176,15 @@ "name": "stdout", "output_type": "stream", "text": [ - "0 \n", - "1 I am a multi-line string\n", - "2 consisting of 4 lines.\n", - "3 \n" + "1 \n", + "2 I am a multi-line string\n", + "3 consisting of four lines.\n", + "4 \n" ] } ], "source": [ - "for i, line in enumerate(multi_line.split(\"\\n\")):\n", + "for i, line in enumerate(multi_line.split(\"\\n\"), start=1):\n", " print(i, line)" ] }, @@ -3346,12 +5196,75 @@ } }, "source": [ - "The next code cell puts several constructs from this chapter together to create a multi-line `str` object `content`: The `with` statement provides a context that ensures `file` is not left open. Then, the [readlines()](https://docs.python.org/3/library/io.html#io.IOBase.readlines) method returns the contents of `file` as a `list` object holding as many `str` objects as there are lines in the file on disk. Lastly, we concatenate these together with the [join()](https://docs.python.org/3/library/stdtypes.html#str.join) method to obtain `content`. We do so on an empty `str` object `\"\"` as each line already ends with a `\"\\n\"`." + "To mitigate that, we often see the [strip()](https://docs.python.org/3/library/stdtypes.html#bytes.strip) method in source code." ] }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 164, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "multi_line = \"\"\"\n", + "I am a multi-line string\n", + "consisting of two lines.\n", + "\"\"\".strip()" + ] + }, + { + "cell_type": "code", + "execution_count": 165, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 I am a multi-line string\n", + "2 consisting of two lines.\n" + ] + } + ], + "source": [ + "for i, line in enumerate(multi_line.split(\"\\n\"), start=1):\n", + " print(i, line)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The `bytes` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To end this chapter, we want to briefly look at the `bytes` data type, which conceptually is a sequence of bytes. That data format is probably one of the most generic ways of exchanging data between any two programs or computers (e.g., a web browser obtains its data from a web server in this format).\n", + "\n", + "Let's open a binary file in read-only mode (i.e., `mode=\"rb\"`) and read in all of its contents." + ] + }, + { + "cell_type": "code", + "execution_count": 166, "metadata": { "slideshow": { "slide_type": "slide" @@ -3359,40 +5272,619 @@ }, "outputs": [], "source": [ - "with open(\"lorem_ipsum.txt\") as file:\n", - " content = \"\".join(file.readlines())" + "with open(\"full_house.bin\", mode=\"rb\") as binary_file:\n", + " data = binary_file.read()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`data` is an object of type `bytes`." ] }, { "cell_type": "code", - "execution_count": 103, + "execution_count": 167, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "\"Lorem Ipsum is simply dummy text of the printing and typesetting industry.\\nLorem Ipsum has been the industry's standard dummy text ever since the 1500s\\nwhen an unknown printer took a galley of type and scrambled it to make a type\\nspecimen book. It has survived not only five centuries but also the leap into\\nelectronic typesetting, remaining essentially unchanged. It was popularised in\\nthe 1960s with the release of Letraset sheets.\\n\"" + "140461335555696" ] }, - "execution_count": 103, + "execution_count": 167, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "content" + "id(data)" ] }, { "cell_type": "code", - "execution_count": 104, + "execution_count": 168, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "bytes" + ] + }, + "execution_count": 168, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(data)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "It's value is given out in the literal bytes notation with a `b` prefix (cf., the [reference](https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals)). Every byte is expressed in hexadecimal representation with the escape sequence `\"\\x\"`. This representation is commonly chosen as we can *not* tell what kind of information is hidden in the `data` by just looking at the bytes. Instead, we must be told by some other source how to **decode** the raw bytes into information we can interpret." + ] + }, + { + "cell_type": "code", + "execution_count": 169, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "b'\\xf0\\x9f\\x82\\xa7\\xf0\\x9f\\x82\\xb7\\xf0\\x9f\\x83\\x97\\xf0\\x9f\\x83\\x8e\\xf0\\x9f\\x83\\x9e'" + ] + }, + "execution_count": 169, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`bytes` objects work like `str` objects in many ways. In particular, they are *sequences* as well: The number of bytes is *finite* and we may *iterate* over them in *order*." + ] + }, + { + "cell_type": "code", + "execution_count": 170, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "20" + ] + }, + "execution_count": 170, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(data)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Consisting of 8 bits, a single byte can always be interpreted as a whole number between `0` through `255`. That is exactly what we see when we loop over the `data` ..." + ] + }, + { + "cell_type": "code", + "execution_count": 171, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "240 159 130 167 240 159 130 183 240 159 131 151 240 159 131 142 240 159 131 158 " + ] + } + ], + "source": [ + "for byte in data:\n", + " print(byte, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... or index into them." + ] + }, + { + "cell_type": "code", + "execution_count": 172, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "158" + ] + }, + "execution_count": 172, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data[-1]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Slicing returns another `bytes` object." + ] + }, + { + "cell_type": "code", + "execution_count": 173, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "b'\\xf0\\x82\\xf0\\x82\\xf0\\x83\\xf0\\x83\\xf0\\x83'" + ] + }, + "execution_count": 173, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data[::2]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Character Encodings" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Luckily, `data` consists of bytes encoded with the [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoding. That is the most common way of mapping a Unicode character's code point to a sequence of bytes.\n", + "\n", + "To obtain a `str` object out of a given `bytes` object, we decode it with the `bytes` type's [decode()](https://docs.python.org/3/library/stdtypes.html#bytes.decode) method." + ] + }, + { + "cell_type": "code", + "execution_count": 174, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "cards = data.decode()" + ] + }, + { + "cell_type": "code", + "execution_count": 175, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "str" + ] + }, + "execution_count": 175, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(cards)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "So, `data` consisted of a [full house](https://en.wikipedia.org/wiki/List_of_poker_hands#Full_house) hand in a poker game." + ] + }, + { + "cell_type": "code", + "execution_count": 176, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'🂧🂷🃗🃎🃞'" + ] + }, + "execution_count": 176, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cards" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To go the opposite direction and encode a given `str` object, we use the `str` type's [encode()](https://docs.python.org/3/library/stdtypes.html#str.encode) method." + ] + }, + { + "cell_type": "code", + "execution_count": 177, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "place = \"Café Kastanientörtchen\"" + ] + }, + { + "cell_type": "code", + "execution_count": 178, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "b'Caf\\xc3\\xa9 Kastanient\\xc3\\xb6rtchen'" + ] + }, + "execution_count": 178, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "place.encode()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "By default, [encode()](https://docs.python.org/3/library/stdtypes.html#str.encode) and [decode()](https://docs.python.org/3/library/stdtypes.html#bytes.decode) use an `encoding=\"utf-8\"` argument. We may use another encoding like, for example, `\"iso-8859-1\"`, which can deal with ASCII and western European letters." + ] + }, + { + "cell_type": "code", + "execution_count": 179, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "b'Caf\\xe9 Kastanient\\xf6rtchen'" + ] + }, + "execution_count": 179, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "place.encode(\"iso-8859-1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, we must use the *same* encoding for the decoding step as for the encoding step. Otherwise, a `UnicodeDecodeError` is raised." + ] + }, + { + "cell_type": "code", + "execution_count": 180, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "UnicodeDecodeError", + "evalue": "'utf-8' codec can't decode byte 0xe9 in position 3: invalid continuation byte", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mUnicodeDecodeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mplace\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"iso-8859-1\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mUnicodeDecodeError\u001b[0m: 'utf-8' codec can't decode byte 0xe9 in position 3: invalid continuation byte" + ] + } + ], + "source": [ + "place.encode(\"iso-8859-1\").decode()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Not all encodings map all Unicode code points. For example `\"iso-8859-1\"` does not know Czech letters. Below, [encode()](https://docs.python.org/3/library/stdtypes.html#str.encode) raises a `UnicodeEncodeError` because of that." + ] + }, + { + "cell_type": "code", + "execution_count": 181, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "UnicodeEncodeError", + "evalue": "'latin-1' codec can't encode character '\\u0159' in position 12: ordinal not in range(256)", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mUnicodeEncodeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;34m\"Dobrý den, přátelé!\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"iso-8859-1\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mUnicodeEncodeError\u001b[0m: 'latin-1' codec can't encode character '\\u0159' in position 12: ordinal not in range(256)" + ] + } + ], + "source": [ + "\"Dobrý den, přátelé!\".encode(\"iso-8859-1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Reading Files (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [open()](https://docs.python.org/3/library/functions.html#open) function takes an optional `encoding` argument as well." + ] + }, + { + "cell_type": "code", + "execution_count": 182, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "UnicodeDecodeError", + "evalue": "'utf-8' codec can't decode byte 0xe4 in position 9: invalid continuation byte", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mUnicodeDecodeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"umlauts.txt\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mfile\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfile\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreadlines\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.10/lib/python3.7/codecs.py\u001b[0m in \u001b[0;36mdecode\u001b[0;34m(self, input, final)\u001b[0m\n\u001b[1;32m 320\u001b[0m \u001b[0;31m# decode input (taking the buffer into account)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 321\u001b[0m \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbuffer\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0minput\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 322\u001b[0;31m \u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconsumed\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_buffer_decode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0merrors\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfinal\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 323\u001b[0m \u001b[0;31m# keep undecoded input until the next call\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 324\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbuffer\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mconsumed\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mUnicodeDecodeError\u001b[0m: 'utf-8' codec can't decode byte 0xe4 in position 9: invalid continuation byte" + ] + } + ], + "source": [ + "with open(\"umlauts.txt\") as file:\n", + " print(\"\".join(file.readlines()))" + ] + }, + { + "cell_type": "code", + "execution_count": 183, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lerchen-Lärchen-Ähnlichkeiten\n", + "fehlen. Dieses abzustreiten\n", + "mag im Klang der Worte liegen.\n", + "Merke, eine Lerch' kann fliegen,\n", + "Lärchen nicht, was kaum verwundert,\n", + "denn nicht eine unter hundert\n", + "ist geflügelt. Auch im Singen\n", + "sind die Bäume zu bezwingen.\n", + "Die Bätrachtung sollte reichen,\n", + "Rächtschreibfählern auszuweichen.\n", + "Leicht gälingt's, zu unterscheiden,\n", + "wär ist wär nun von dän beiden.\n" + ] + } + ], + "source": [ + "with open(\"umlauts.txt\", encoding=\"iso-8859-1\") as file:\n", + " print(\"\".join(file.readlines()))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Best Practice: Use UTF-8 explicitly" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A best practice is to *always* specify the `encoding`, especially on computers running on Windows (cf., the talk by Łukasz Langa in the \"*Further Resources*\" section below).\n", + "\n", + "Below is the first example involving [open()](https://docs.python.org/3/library/functions.html#open) one last time: It shows how *all* the contents of a text file should be read into one `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 184, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "with open(\"lorem_ipsum.txt\", encoding=\"utf-8\") as file:\n", + " content = \"\".join(file.readlines())" + ] + }, + { + "cell_type": "code", + "execution_count": 185, + "metadata": { + "slideshow": { + "slide_type": "fragment" } }, "outputs": [ @@ -3435,7 +5927,294 @@ "source": [ "Textual data is modeled with the **immutable** `str` type.\n", "\n", - "The `str` type supports *four* orthogonal **abstract concepts** that together constitute the idea of a **sequence**: Every `str` object is an iterable container of a finite number of ordered characters." + "The `str` type supports *four* orthogonal **abstract concepts** that together constitute the idea of a **sequence**: Every `str` object is an *iterable container* of a *finite* number of *ordered* characters.\n", + "\n", + "A single **character** in a `str` object follows the idea of a **Unicode** character. It is mapped to a *unique* **code point** that is encoded into **bytes** with a dedicated character encoding, for example, **UTF-8**." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## Further Resources" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We refer to the official [Unicode HOWTO](https://docs.python.org/3/howto/unicode.html) in the Python documentation. Furthermore, the [unicodedata](https://docs.python.org/3/library/unicodedata.html) module in the [standard library](https://docs.python.org/3/library/index.html) provides a lot of utility functions around the Unicode standard.\n", + "\n", + "Next is a brief summary video by the YouTube channel [Computerphile](https://www.youtube.com/channel/UC9-y-6csu5WGm29I7JiwpnA) titled \"*Characters, Symbols and the Unicode Miracle*\"." + ] + }, + { + "cell_type": "code", + "execution_count": 186, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 186, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from IPython.display import YouTubeVideo\n", + "YouTubeVideo(\"MijmeoH9LT4\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In his [PyCon Australia 2018](https://2018.pycon-au.org/) talk titled \"*Unicode and Python: The absolute minimum you need to know*\" [Raphaël Merx](https://www.linkedin.com/in/raphaelmerx/) explains some caveats and best practices regarding Unicode." + ] + }, + { + "cell_type": "code", + "execution_count": 187, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 187, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"oXVmZGN6plY\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In a similar talk at [PyCon 2017](https://us.pycon.org/2017/) titled \"*Unicode what is the big deal*\" [Łukasz Langa](https://www.linkedin.com/in/llanga/) provides further lessons learned regarding Unicode." + ] + }, + { + "cell_type": "code", + "execution_count": 188, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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BERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQDKgNktpI66CeUmNklNVV8E0TJBI6NtJW1VLG+Ro75heyn14P5RxlTxWE7H2S4UEL6Dk1vFE6quMjZYquZkrIKyrqamJraTkW7DmMmYzTqx3nOt4pNM5TlJSVtln+LF3tVtnBBbZauleySZ9nrbtQskjl3c0NJBFLrfwBa3VUU2Wkg/Geg4kZ9qqGNtQXzZ5LJDBPu4ppcVE+ndU0YjYTNUnXH8UzLhvGZHELBpdhrrNRspJeQRim2cuVjgkZPUPM0tXHQxw1ErXU43EeKPLmDURr5ypU7IV1LSzUNBMzkYqIKmnjkqqmGch8xlr6KWtiYZmxPfmRs4y7Mz2kY4rpmwttI6qVbt21W/BPHbK37lk4klIfO6mbC2lqnVZqGMdI+E0bYuUCRsbHPILeYZ5uKpV+3dthp21b5ZzSuidNyiKhrp4WMY5zH76SGAtgka5jg5j8EaTkLG6bYuubDNrhoZXy3R1c2HltwjfFG+hhpRye6MZyiCpa5jwXhp1Nc4d7nArz7P3ottscxo6+Kj1VEsc9XUQGasEzn0YkkbSO5TBTx6cF4Bc9jHkAtTMh4me1q+HQyraK9GlbRubHrFVWU1L3+qMsbUE5fpIzqGPBKr3y+U9EI9+ZC6VzmxRQwT1M8ha3U/RBTsMjmtbxLgMDgo7bK2VdVBSGBtPyimrKWrdFLNIyF25JL42zMhLuc8HafqUdfbfeKvkz3R0se5lm3tHFdK6KKdj2RiGZ1ZBStl1xvEnxBaWnWDnIC0jFPadZTkr2+W4kKvbi2xtifv5JBPTcriFPS1dS91MCGumMdPEXNY0uAdkcMjK9uu21speMs7i3korXPhp6mpjipHB5ZUTSU8ZbDE4RyaS/GdDscxURsfshVUjYRM+Bxjtk1E7dulIMr6p8zXjeNzu9DhzknOefnWH7S26qtlBU2+PcVE9bs9S0D48VgkdVQUs9E0UBZTFlZryPiiWlmA53B3DeMIN2RxnVqRjdrp6mzKra6ginFO+Z+vXBG5zaeofBFLVaOTxT1LI9zTyv3kelshB+Nj8YzPLXG0OyNzqZn4fA+F09tnhdLW1sfJY6N9JJNStoYWGnlLpIJXiZxz8dgghoWxwtJxirWO9KU23nL6BERczsEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQHIXb62j8tS9Ub7SdvraPy1L1RvtLVyvtnWg1lGCAWuq6UEEZBBnjBBB4EYPMvUvC0kr5q5HgI47EN2z3zNg9v3aLy9J1VntJ2/dovL0nVWe0uqG7MW3H8n0XVYPYT7mLb5vouqwewqnS6Hw108j0PduK+M+vmcr9v3aLy9J1VntJ2/dovL0nVWe0uqPuYtvm+i6rB7CfcxbfN9F1WD2E0yh8NdPIz3bivjPr5nK/b92i8vSdVZ7Sdv3aLy9J1VntLqj7mLb5vouqwewn3MW3zfRdVg9hNMofDXTyHduK+M+vmcr9v3aLy9J1VntL3t9bR+WpOqN9pdP3PZq3CCYigogRFIQeSwcCGH+YuDYT3rfzR/YpmF7Gvf2ErFblBYnCZt6rd7+O77m1O31tH5al6o32k7fW0flqXqjfaWrkUvRaXCuRW94YjjfM2j2+to/LUvVG+0nb62j8tS9Ub7S1c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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 188, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"7m5JA3XaZ4k\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In a \"classic\" talk from PyCon 2012 titled \"*Pragmatic Unicode, or, How do I stop the pain?*\" [Ned Batchelder](https://nedbatchelder.com/) explains among others the concept of a \"Unicode Sandwich.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 189, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 189, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"sgHbC6udIqc\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Lastly, in his entertaining talk at [PyCon.DE 2019](https://de.pycon.org/) titled \"*Your Name is Invalid!*\" [Miroslav Šedivý](https://www.linkedin.com/in/%C5%A1ediv%C3%BD/) shows how hard it actually is to write software that can process any name a human can possibly have. Miroslav also gave a lightning talk where he shows how he uses only one keyboard for the 12 (!!!) languages he speaks." + ] + }, + { + "cell_type": "code", + "execution_count": 190, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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A+uEbf2R/wCnRBBTe3iXHUso/ICtNPjquYu1DNaipuu904nymHhoRMEWhcyGtV8tu5xMzdRqdT6Tdt+Rpx3Raq4mWxFxLmsbCXZSanWrVMXFNaHfpTUkdFZfBE3l+SGyAukJja3QzhLh6jw0Oq13BnM2h1eIhvOOs23VvTeervX6dV+rOLqy7xJWgk5r2PkEkMb974xox79zfKc0DoT1CCHO0/tTuwDEd1nqYiPLcPz5rm5COnZkmliNDTGV3WMhVqcxkdx80jucdWQHZ61j43tuytgwCHDVZI5ruFxoms5KahM69msNWykEjqRpzd3qNfPtd+1keG6ENkIIU33aMM7Q2eGKZrXB7WzRska1zfouAeCA4etHUIC4vMMRc6RkpcYmFxljbtZITpqZGtAAd5wAgrnf+UJegtOsSVqDasOBFqzip8rHDObdXiLL4W981ymoX5Kd5oxcuJ3KGhbro52h23AXGluDJQUa+JZSqZTj3iWncyMMNCOtdFSPKlh2R2TbORJoV980sQDuU7yjq1TgcPU1a7utbdG4OY7u8WrHB75Q5p2+S4SSSO1Hpe4+clezaMAIIhiBZK+ZpEbAWyybg+UHTpI4Odq7zncfWgyEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERARUz8c/st7/8AhqeOf2W9/wDw1BcxFTPxz+y3v/4anjn9lvf/AMNQXMRUz8c/st7/APhqeOf2W9//AA1BcxFTPxz+y3v/AOGp45/Zb3/8NQXMRUz8c/st7/8AhqeOf2W9/wDw1BcxFTPxz+y3v/4anjn9lvf/AMNQXMRUz8c/st7/APhqeOf2W9//AA1BcxFTPxz+y3v/AOGp45/Zb3/8NQXMRUz8c/st7/8AhqeOf2W9/wDw1BcxFTPxz+y3v/4anjn9lvf/AMNQXMRUz8c/st7/APhqeOf2W9//AA1BcxFTPxz+y3v/AOGp45/Zb3/8NQXMRUz8c/st7/8AhqeOf2W9/wDw1BcxFTPxz+y3v/4anjn9lvf/AMNQXMRUz8c/st7/APhqeOf2W9//AA1BcxFTPxz+y3v/AOGp45/Zb3/8NQXMRUz8c/st7/8AhqeOf2W9/wDw1BcxFTPxz+y3v/4anjn9lvf/AMNQXMRUz8c/st7/APhqeOf2W9//AA1BcxFTPxz+y3v/AOGp45/Zb3/8NQXMRUz8c/st7/8AhqeOf2W9/wDw1BcxFTPxz+y3v/4anjn9lvf/AMNQXMRUz8c/st7/APhqeOf2W9//AA1BcxFTPxz+y3v/AOGp45/Zb3/8NQXMRUz8c/st7/8AhqeOf2W9/wDw1BcxFTPxz+y3v/4anjn9lvf/AMNQXMRUz8c/st7/APhqeOf2W9//AA1BcxFTPxz+y3v/AOGp45/Zb3/8NQXMRUz8c/st7/8AhqeOf2W9/wDw1BcxFTPxz+y3v/4anjn9lvf/AMNQUzREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERB//2Q==\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 190, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"pBuS7EUPnQA\", width=\"60%\")" + ] + }, + { + "cell_type": "code", + "execution_count": 191, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 191, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"-4QjII981sM\", width=\"60%\")" ] } ], diff --git a/full_house.bin b/full_house.bin new file mode 100644 index 0000000..4ad5e35 --- /dev/null +++ b/full_house.bin @@ -0,0 +1 @@ +🂧🂷🃗🃎🃞 \ No newline at end of file diff --git a/umlauts.txt b/umlauts.txt new file mode 100644 index 0000000..9c0f911 --- /dev/null +++ b/umlauts.txt @@ -0,0 +1,12 @@ +Lerchen-Lrchen-hnlichkeiten +fehlen. Dieses abzustreiten +mag im Klang der Worte liegen. +Merke, eine Lerch' kann fliegen, +Lrchen nicht, was kaum verwundert, +denn nicht eine unter hundert +ist geflgelt. Auch im Singen +sind die Bume zu bezwingen. +Die Btrachtung sollte reichen, +Rchtschreibfhlern auszuweichen. +Leicht glingt's, zu unterscheiden, +wr ist wr nun von dn beiden. \ No newline at end of file