From 33f27da161bf6fccf3383e41df3bdaa73f9f8a33 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Sat, 17 Oct 2020 16:51:57 +0200 Subject: [PATCH 1/8] Fix missing static files for chapter 05 --- 05_numbers/static/complex_numbers.png | Bin 0 -> 22878 bytes 05_numbers/static/floating_point.png | Bin 0 -> 15371 bytes 05_numbers/static/numbers.png | Bin 0 -> 90958 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 05_numbers/static/complex_numbers.png create mode 100644 05_numbers/static/floating_point.png create mode 100644 05_numbers/static/numbers.png diff --git a/05_numbers/static/complex_numbers.png b/05_numbers/static/complex_numbers.png new file mode 100644 index 0000000000000000000000000000000000000000..807eb4647d1136f67c673fd2b81c74d3f813e08c GIT binary patch literal 22878 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 Ok(mi|?``_8;r|0G1$p%V literal 0 HcmV?d00001 diff --git a/05_numbers/static/floating_point.png b/05_numbers/static/floating_point.png new file mode 100644 index 0000000000000000000000000000000000000000..345aabaf1b3f150cfed2bef7739003913c8fd334 GIT binary patch literal 15371 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 RA1h$c55)T&dT@Z6{$EL>4blJr literal 0 HcmV?d00001 From 5eda6e8acfb72d0ef22ee6b4b42f3c548920960b Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 11:00:39 +0200 Subject: [PATCH 2/8] Add initial version of chapter 06 --- 00_intro/00_content.ipynb | 2 +- 06_text/00_content.ipynb | 3970 +++++++++++++++++++++++++++++++++++++ 06_text/lorem_ipsum.txt | 6 + README.md | 9 + 4 files changed, 3986 insertions(+), 1 deletion(-) create mode 100644 06_text/00_content.ipynb create mode 100644 06_text/lorem_ipsum.txt diff --git a/00_intro/00_content.ipynb b/00_intro/00_content.ipynb index d3e1fae..de293c8 100644 --- a/00_intro/00_content.ipynb +++ b/00_intro/00_content.ipynb @@ -769,7 +769,7 @@ "\n", "- How is data stored in memory?\n", " - *Chapter 5*: [Numbers & Bits ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb)\n", - " - *Chapter 6*: Text & Bytes\n", + " - *Chapter 6*: [Text & Bytes ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/00_content.ipynb)\n", " - *Chapter 7*: Sequential Data\n", " - *Chapter 8*: Map, Filter, & Reduce\n", " - *Chapter 9*: Mappings & Sets\n", diff --git a/06_text/00_content.ipynb b/06_text/00_content.ipynb new file mode 100644 index 0000000..7c9fff8 --- /dev/null +++ b/06_text/00_content.ipynb @@ -0,0 +1,3970 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/00_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 6: Text & Bytes" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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/develop/05_numbers/00_content.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 to understand how characters are modeled in memory." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The `str` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To create a `str` object, we use the *literal* notation and type the text between enclosing **double quotes** `\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "text = \"Lorem ipsum dolor sit amet.\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Like everything in Python, `text` is an object with an *identity*, a *type*, and a *value*." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140667764715968" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(text)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "str" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(text)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As seen before, a `str` object evaluates to itself in a literal notation with enclosing **single quotes** `'`.\n", + "\n", + "In [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Value-/-(Semantic)-\"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." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum dolor sit amet.'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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 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 `\"` 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 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." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Einstein said, \"If you can\\'t explain it, you don\\'t understand it.\"'" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Einstein said, \\\"If you can't explain it, you don't understand it.\\\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Einstein said, \"If you can\\'t explain it, you don\\'t understand it.\"'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "'Einstein said, \"If you can\\'t explain it, you don\\'t understand it.\"'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb) 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" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'42'" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "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" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As shown in the \"*Guessing a Coin Toss*\" example in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.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": 15, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Whatever you enter is put in a new string: 123\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": [ + "'123'" + ] + }, + "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" + } + }, + "outputs": [], + "source": [ + "file = open(\"lorem_ipsum.txt\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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. `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 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/01_content.ipynb#The-bytes-Type) section at the end of this chapter." + ] + }, + { + "cell_type": "code", + "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" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "<_io.TextIOWrapper name='lorem_ipsum.txt' mode='r' encoding='UTF-8'>" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`TextIOWrapper` objects come with plenty of type-specific methods and attributes." + ] + }, + { + "cell_type": "code", + "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" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lorem Ipsum is simply dummy text of the printing and typesetting industry.\n", + "\n", + "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s\n", + "\n", + "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", + "\n" + ] + } + ], + "source": [ + "for line in file:\n", + " print(line)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 26, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "for line in file:\n", + " print(line)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 27, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'the 1960s with the release of Letraset sheets.\\n'" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "line" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "str" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(line)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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", + "`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": 29, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.closed" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "file.close()" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.closed" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 32, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lorem Ipsum is simply dummy text of the printing and typesetting industry.\n", + "\n", + "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s\n", + "\n", + "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", + "\n" + ] + } + ], + "source": [ + "with open(\"lorem_ipsum.txt\") as file:\n", + " for line in file:\n", + " print(line)" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.closed" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Using syntax familiar from [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.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": 34, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lorem Ipsum is simply dummy text of the printing and typesetting industry.\n", + "\n", + "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s\n", + "\n", + "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", + "\n" + ] + } + ], + "source": [ + "try:\n", + " file = open(\"lorem_ipsum.txt\")\n", + " for line in file:\n", + " print(line)\n", + "finally:\n", + " file.close()" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.closed" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 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", + "output_type": "stream", + "text": [ + "Lorem Ipsum is simply dummy text of the printing and typesetting industry.\n", + "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s\n", + "when an unknown printer took a galley of type and scrambled it to make a type\n", + "specimen book. It has survived not only five centuries but also the leap into\n", + "electronic typesetting, remaining essentially unchanged. It was popularised in\n", + "the 1960s with the release of Letraset sheets.\n" + ] + } + ], + "source": [ + "with open(\"lorem_ipsum.txt\") as file:\n", + " for line in file:\n", + " print(line, end=\"\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## A String of Characters" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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/develop/04_iteration/02_content.ipynb#Containers-vs.-Iterables)).\n", + "\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**\n", + "2. a **finite** number of other objects 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/develop/07_sequences/00_content.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)**. *String* is simply another term for *sequence* in the computer science literature.\n", + "\n", + "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 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.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": 45, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum dolor sit amet.'" + ] + }, + "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" + } + ], + "source": [ + "len(text)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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. 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": 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" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + ". t e m a t i s r o l o d m u s p i m e r o L " + ] + } + ], + "source": [ + "for character in reversed(text):\n", + " print(character, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Being a container, we may check if a given `str` object is contained in `text` with the `in` operator, which 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": 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" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"ipsum\" in text" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"veni, vidi, vici\" in text" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Indexing" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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/develop/01_elements/03_content.ipynb#Who-am-I?-And-how-many?)." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'L'" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'o'" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[1]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The index must be of type `int`; othewise, we get a `TypeError`." + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "string indices must be integers", + "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[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" + ] + } + ], + "source": [ + "text[1.0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 55, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'.'" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[26] # == text[len(text) - 1]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "An `IndexError` is raised whenever the index is out of range." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "IndexError", + "evalue": "string index out of range", + "output_type": "error", + "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;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[27] # == text[len(text)]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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*." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "| 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": 57, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'.'" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[-1]" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'L'" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[-27] # == text[-len(text)]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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 `21` add to `27`." + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'i'" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[6]" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'i'" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[-21]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Slicing" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A **slice** is a substring of a `str` object.\n", + "\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 *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": 61, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem'" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[0:5]" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'dolor sit amet.'" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[12:len(text)]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 63, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem'" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "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]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For convenience, the indexes do not need to lie within the range from `0` to `len(text)` when slicing. So, no `IndexError` is raised here." + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum dolor sit amet.'" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[-999:999]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 69, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum dolor sit amet.'" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[:]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A *step* value of `i` can be used to obtain only every `i`th character." + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lrmismdlrstae.'" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[::2]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A negative *step* size of `-1` reverses the order of the characters." + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'.tema tis rolod muspi meroL'" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[::-1]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Immutability" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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/develop/01_elements/03_content.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/develop/05_numbers/00_content.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/develop/01_elements/03_content.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": 72, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'str' object does not support item assignment", + "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[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] = \"X\"" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'str' object does not support item assignment", + "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[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" + ] + } + ], + "source": [ + "text[:5] = \"random\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## String Methods" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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`. 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": 74, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum dolor sit amet.'" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "22" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.find(\"a\")" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.find(\"b\")" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.find(\"dolor\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[find() ](https://docs.python.org/3/library/stdtypes.html#str.find) takes optional *start* and *end* arguments that allow us to find occurrences other than the first one." + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.find(\"o\")" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "13" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.find(\"o\", 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.find(\"o\", 2, 12)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [count() ](https://docs.python.org/3/library/stdtypes.html#str.count) method does what we expect." + ] + }, + { + "cell_type": "code", + "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": { + "text/plain": [ + "1" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.count(\"l\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As [count() ](https://docs.python.org/3/library/stdtypes.html#str.count) is *case-sensitive*, we must **chain** it with the [lower() ](https://docs.python.org/3/library/stdtypes.html#str.lower) method to get the count of all `\"L\"`s and `\"l\"`s." + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 83, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.lower().count(\"l\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 84, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.upper().count(\"L\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Because `str` objects are *immutable*, [upper() ](https://docs.python.org/3/library/stdtypes.html#str.upper) and [lower() ](https://docs.python.org/3/library/stdtypes.html#str.lower) return *new* `str` objects, even if they do *not* change the value of the original `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "example = \"random\"" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140667840152112" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(example)" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "lower = example.lower()" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140667764453680" + ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(lower)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`example` and `lower` are *different* objects with the *same* value." + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 89, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "example is lower" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 90, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "example == lower" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 91, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'lorem ipsum dolor sit amet.'" + ] + }, + "execution_count": 91, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "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()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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 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": 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" + } + }, + "outputs": [], + "source": [ + "words = [\"This\", \"will\", \"become\", \"a\", \"sentence.\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "sentence = \" \".join(words)" + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'This will become a sentence.'" + ] + }, + "execution_count": 99, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sentence" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 100, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'a b c d e'" + ] + }, + "execution_count": 100, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\" \".join(\"abcde\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [replace() ](https://docs.python.org/3/library/stdtypes.html#str.replace) method creates a *new* `str` object with parts of the original `str` object potentially replaced." + ] + }, + { + "cell_type": "code", + "execution_count": 101, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'This is a sentence.'" + ] + }, + "execution_count": 101, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "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": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## String Operations" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As mentioned in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Operator-Overloading), the `+` and `*` operators are *overloaded* and used for **string concatenation**. They always create *new* `str` objects. That has nothing to do with the `str` type's immutability, but is the default behavior of operators." + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Hello Lore'" + ] + }, + "execution_count": 110, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Hello \" + text[:4]" + ] + }, + { + "cell_type": "code", + "execution_count": 111, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum ...'" + ] + }, + "execution_count": 111, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "5 * text[:12] + \"...\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### String Comparison" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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 in the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/02_content.ipynb#Characters-are-Numbers-with-a-Convention) of this chapter." + ] + }, + { + "cell_type": "code", + "execution_count": 112, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 112, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Apple\" < \"Banana\"" + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 113, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"apple\" < \"Banana\"" + ] + }, + { + "cell_type": "code", + "execution_count": 114, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 114, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"apple\" < \"Banana\".lower()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 115, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 115, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Mai\" < \"Maier\" < \"Mayer\" < \"Meier\" < \"Meyer\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## String Interpolation" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### f-strings" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**[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": 116, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "name = \"Alexander\"\n", + "time_of_day = \"morning\"" + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Hello Alexander! Good morning.'" + ] + }, + "execution_count": 117, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "f\"Hello {name}! Good {time_of_day}.\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 118, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "pi = 3.141592653" + ] + }, + { + "cell_type": "code", + "execution_count": 119, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Pi is 3.14'" + ] + }, + "execution_count": 119, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "f\"Pi is {pi:.2f}\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### [format() ](https://docs.python.org/3/library/stdtypes.html#str.format) Method" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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 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": 120, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Hello Alexander! Good morning.'" + ] + }, + "execution_count": 120, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Hello {}! Good {}.\".format(name, time_of_day)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We may use index numbers inside the curly braces if the order is different in the `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 121, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Good morning, Alexander'" + ] + }, + "execution_count": 121, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Good {1}, {0}\".format(name, time_of_day)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [format() ](https://docs.python.org/3/library/stdtypes.html#str.format) method may alternatively be used with *keyword* arguments as well. Then, we must put the keywords' names within the curly brackets." + ] + }, + { + "cell_type": "code", + "execution_count": 122, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Hello Alexander! Good morning.'" + ] + }, + "execution_count": 122, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Hello {name}! Good {time}.\".format(name=name, time=time_of_day)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Format specifiers work as in the f-string case." + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Pi is 3.14'" + ] + }, + "execution_count": 123, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Pi is {:.2f}\".format(pi)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### `%` Operator" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `%` operator that we saw in the context of modulo division in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#%28Arithmetic%29-Operators) is overloaded with string interpolation when its first operand is a `str` object. The second operand consists of all expressions to be filled in. Format specifiers work with a `%` instead of curly braces and according to a different set of rules referred to as **[printf-style string formatting ](https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting)**. So, `{:.2f}` becomes `%.2f`.\n", + "\n", + "This way of string interpolation is the oldest and originates from the [C language ](https://en.wikipedia.org/wiki/C_%28programming_language%29). It is still widely spread, but we should use one of the other two ways instead. We show it here mainly for completeness sake." + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Pi is 3.14'" + ] + }, + "execution_count": 124, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Pi is %.2f\" % pi" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To insert more than one expression, we must list them in order and between parenthesis `(` and `)`. As [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb#The-tuple-Type) reveals, this literal syntax creates an object of type `tuple`. Also, to format an expression as text, we use the format specifier `%s`." + ] + }, + { + "cell_type": "code", + "execution_count": 125, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Hello Alexander! Good morning.'" + ] + }, + "execution_count": 125, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Hello %s! Good %s.\" % (name, time_of_day)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/06_text/lorem_ipsum.txt b/06_text/lorem_ipsum.txt new file mode 100644 index 0000000..c0e21ab --- /dev/null +++ b/06_text/lorem_ipsum.txt @@ -0,0 +1,6 @@ +Lorem Ipsum is simply dummy text of the printing and typesetting industry. +Lorem Ipsum has been the industry's standard dummy text ever since the 1500s +when an unknown printer took a galley of type and scrambled it to make a type +specimen book. It has survived not only five centuries but also the leap into +electronic typesetting, remaining essentially unchanged. It was popularised in +the 1960s with the release of Letraset sheets. diff --git a/README.md b/README.md index 29198ed..d5d2970 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,15 @@ Alternatively, the content can be viewed in a web browser - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/05_review.ipynb) - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/06_resources.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/06_resources.ipynb) + - *Chapter 6*: Text & Bytes + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_numbers/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/00_content.ipynb) + (`str` Type; + Reading Files; + Sequences; + Indexing & Slicing; + String Methods & Operations; + String Interpolation) #### Videos From c6716db0b864779b603f64b125bba09509a8de45 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 11:34:08 +0200 Subject: [PATCH 3/8] Add initial version of chapter 06's exercises --- 06_text/01_exercises.ipynb | 995 +++++++++++++++++++++++++++++++++++++ README.md | 3 + 2 files changed, 998 insertions(+) create mode 100644 06_text/01_exercises.ipynb diff --git a/06_text/01_exercises.ipynb b/06_text/01_exercises.ipynb new file mode 100644 index 0000000..0781f2b --- /dev/null +++ b/06_text/01_exercises.ipynb @@ -0,0 +1,995 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/01_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 6: Text & Bytes (Coding Exercises)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/00_content.ipynb) of Chapter 6.\n", + "\n", + "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Detecting Palindromes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[Palindromes ](https://en.wikipedia.org/wiki/Palindrome) are sequences of characters that read the same backward as forward. Examples are first names like \"Hannah\" or \"Otto,\" words like \"radar\" or \"level,\" or sentences like \"Was it a car or a cat I saw?\"\n", + "\n", + "In this exercise, you implement various functions that check if the given arguments are palindromes or not. We start with an iterative implementation and end with a recursive one.\n", + "\n", + "Conceptually, the first function, `unpythonic_palindrome()`, is similar to the \"*Is the square of a number in `[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]` greater than `100`?*\" example in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb#Example:-Is-the-square-of-a-number-in-[7,-11,-8,-5,-3,-12,-2,-6,-9,-10,-1,-4]-greater-than-100?): It assumes that the `text` argument is a palindrome (i.e., it initializes `is_palindrom` to `True`) and then checks in a `for`-loop if a pair of corresponding characters, `forward` and `backward`, contradicts that.\n", + "\n", + "**Q1**: How many iterations are needed in the `for`-loop? Take into account that `text` may contain an even or odd number of characters! Inside `unpythonic_palindrome()` below, write an expression whose result is assigned to `chars_to_check`!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer > " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: `forward_index` is the index going from left to right. How can we calculate `backward_index`, the index going from right to left, for a given `forward_index`? Write an expression whose result is assigned to `backward_index`! Then, use the indexing operator `[]` to obtain the two characters, `forward` and `backward`, from `text`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: Finish `unpythonic_palindrome()` below! Add code that adjusts `text` such that the function is case insensitive if the `ignore_case` argument is `True`! Make sure that the function returns once the first pair of corresponding characters does not match!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def unpythonic_palindrome(text, *, ignore_case=True):\n", + " \"\"\"Check if a text is a palindrome or not.\n", + "\n", + " Args:\n", + " text (str): Text to be checked; must be an individual word\n", + " ignore_case (bool): If the check is case insensitive; defaults to True\n", + "\n", + " Returns:\n", + " is_palindrome (bool)\n", + " \"\"\"\n", + " # answer to Q3\n", + " is_palindrome = ...\n", + " if ignore_case:\n", + " ...\n", + " # answer to Q1\n", + " chars_to_check = ...\n", + "\n", + " for forward_index in range(chars_to_check):\n", + " # answer to Q2\n", + " backward_index = ...\n", + " forward = ...\n", + " backward = ...\n", + "\n", + " print(forward, \"and\", backward) # added for didactical purposes\n", + "\n", + " # answer to Q3\n", + " if ...:\n", + " is_palindrome = ...\n", + " ...\n", + "\n", + " return is_palindrome" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: Ensure that `unpythonic_palindrome()` works for the provided test cases (i.e., the `assert` statements do *not* raise an `AssertionError`)! Also, for each of the test cases, provide a brief explanation of what makes them *unique*!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert unpythonic_palindrome(\"noon\") is True" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your explanation 1 >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert unpythonic_palindrome(\"Hannah\") is True" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your explanation 2 >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert unpythonic_palindrome(\"Hannah\", ignore_case=False) is False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your explanation 3 >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert unpythonic_palindrome(\"radar\") is True" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your explanation 4 >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert unpythonic_palindrome(\"Hanna\") is False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your explanation 5 >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert unpythonic_palindrome(\"Warsaw\") is False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your explanation 6 >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`unpythonic_palindrome()` is considered *not* Pythonic as it uses index variables to implement the looping logic. Instead, we should simply loop over an *iterable* object to work with its elements one by one.\n", + "\n", + "**Q5**: Copy your solutions to the previous questions into `almost_pythonic_palindrome()` below!\n", + "\n", + "**Q6**: The [reversed() ](https://docs.python.org/3/library/functions.html#reversed) and [zip() ](https://docs.python.org/3/library/functions.html#zip) built-ins allow us to loop over the same `text` argument *in parallel* in both forward *and* backward order. Finish the `for` statement's header line to do just that!\n", + "\n", + "Hint: You may need to slice the `text` argument." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def almost_pythonic_palindrome(text, *, ignore_case=True):\n", + " \"\"\"Check if a text is a palindrome or not.\n", + "\n", + " Args:\n", + " text (str): Text to be checked; must be an individual word\n", + " ignore_case (bool): If the check is case insensitive; defaults to True\n", + "\n", + " Returns:\n", + " is_palindrome (bool)\n", + " \"\"\"\n", + " # answers from above\n", + " is_palindrome = ...\n", + " if ignore_case:\n", + " ...\n", + " chars_to_check = ...\n", + "\n", + " # answer to Q6\n", + " for ... in ...:\n", + "\n", + " print(forward, \"and\", backward) # added for didactical purposes\n", + "\n", + " # answers from above\n", + " if ...:\n", + " is_palindrome = ...\n", + " ...\n", + "\n", + " return is_palindrome" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: Verify that the test cases work as before!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert almost_pythonic_palindrome(\"noon\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert almost_pythonic_palindrome(\"Hannah\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert almost_pythonic_palindrome(\"Hannah\", ignore_case=False) is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert almost_pythonic_palindrome(\"radar\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert almost_pythonic_palindrome(\"Hanna\") is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert almost_pythonic_palindrome(\"Warsaw\") is False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: `almost_pythonic_palindrome()` above may be made more Pythonic by removing the variable `is_palindrome` with the *early exit* pattern. Make the corresponding changes!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def pythonic_palindrome(text, *, ignore_case=True):\n", + " \"\"\"Check if a text is a palindrome or not.\n", + "\n", + " Args:\n", + " text (str): Text to be checked; must be an individual word\n", + " ignore_case (bool): If the check is case insensitive; defaults to True\n", + "\n", + " Returns:\n", + " is_palindrome (bool)\n", + " \"\"\"\n", + " # answers from above\n", + " if ignore_case:\n", + " ...\n", + " chars_to_check = ...\n", + "\n", + " for ... in ...:\n", + "\n", + " print(forward, \"and\", backward) # added for didactical purposes\n", + "\n", + " if ...:\n", + " # answer to Q8\n", + " ...\n", + "\n", + " # answer to Q8\n", + " return ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: Verify that the test cases still work!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert pythonic_palindrome(\"noon\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "assert pythonic_palindrome(\"Hannah\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert pythonic_palindrome(\"Hannah\", ignore_case=False) is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert pythonic_palindrome(\"radar\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert pythonic_palindrome(\"Hanna\") is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert pythonic_palindrome(\"Warsaw\") is False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: `pythonic_palindrome()` is *not* able to check numeric palindromes. In addition to the string method that implements the case insensitivity and that essentially causes the `AttributeError`, what *abstract behaviors* are numeric data types, such as the `int` type in the example below, missing that would also cause runtime errors? " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pythonic_palindrome(12321)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: Copy your code from `pythonic_palindrome()` above into `palindrome_ducks()` below and make the latter conform to *duck typing*!\n", + "\n", + "Hints: You may want to use the [str() ](https://docs.python.org/3/library/functions.html#func-str) built-in. You only need to add *one* short line of code." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def palindrome_ducks(text, *, ignore_case=True):\n", + " \"\"\"Check if a text is a palindrome or not.\n", + "\n", + " Args:\n", + " text (str): Text to be checked; must be an individual word\n", + " ignore_case (bool): If the check is case insensitive; defaults to True\n", + "\n", + " Returns:\n", + " is_palindrome (bool)\n", + " \"\"\"\n", + " # answer to Q11\n", + " ...\n", + " # answers from above\n", + " if ignore_case:\n", + " text = ...\n", + " chars_to_check = ...\n", + "\n", + " for ... in ...:\n", + " if ...:\n", + " ...\n", + "\n", + " return ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q12**: Verify that the two new test cases work as well!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert palindrome_ducks(12321) is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert palindrome_ducks(12345) is False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`palindrome_ducks()` can *not* process palindromes that consist of more than one word." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "palindrome_ducks(\"Never odd or even.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "palindrome_ducks(\"Eva, can I stab bats in a cave?\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "palindrome_ducks(\"A man, a plan, a canal - Panama.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "palindrome_ducks(\"A Santa lived as a devil at NASA!\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "palindrome_ducks(\"\"\"\n", + " Dennis, Nell, Edna, Leon, Nedra, Anita, Rolf, Nora, Alice, Carol, Leo, Jane,\n", + " Reed, Dena, Dale, Basil, Rae, Penny, Lana, Dave, Denny, Lena, Ida, Bernadette,\n", + " Ben, Ray, Lila, Nina, Jo, Ira, Mara, Sara, Mario, Jan, Ina, Lily, Arne, Bette,\n", + " Dan, Reba, Diane, Lynn, Ed, Eva, Dana, Lynne, Pearl, Isabel, Ada, Ned, Dee,\n", + " Rena, Joel, Lora, Cecil, Aaron, Flora, Tina, Arden, Noel, and Ellen sinned.\n", + "\"\"\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q13**: Implement the final iterative version `is_a_palindrome()` below. Copy your solution from `palindrome_ducks()` above and add code that removes the \"special\" characters (and symbols) from the longer example palindromes above so that they are effectively ignored! Note that this processing should only be done if the `ignore_symbols` argument is set to `True`.\n", + "\n", + "Hints: Use the [replace() ](https://docs.python.org/3/library/stdtypes.html#str.replace) method on the `str` type to achieve that. You may want to do so within another `for`-loop." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def is_a_palindrome(text, *, ignore_case=True, ignore_symbols=True):\n", + " \"\"\"Check if a text is a palindrome or not.\n", + "\n", + " Args:\n", + " text (str): Text to be checked; may be multiple words\n", + " ignore_case (bool): If the check is case insensitive; defaults to True\n", + " ignore_symbols (bool): If special characters like \".\" or \"?\" and others\n", + " are ignored; defaults to True\n", + "\n", + " Returns:\n", + " is_palindrome (bool)\n", + " \"\"\"\n", + " # answers from above\n", + " ...\n", + " if ignore_case:\n", + " ...\n", + " # answer to Q13\n", + " if ignore_symbols:\n", + " for ... in ...:\n", + " ...\n", + " # answers from above\n", + " chars_to_check = ...\n", + "\n", + " for ... in ...:\n", + " if ...:\n", + " ...\n", + "\n", + " return ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q14**: Verify that all test cases below work!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"noon\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"Hannah\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"Hannah\", ignore_case=False) is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"radar\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"Hanna\") is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"Warsaw\") is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(12321) is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(12345) is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"Never odd or even.\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"Never odd or even.\", ignore_symbols=False) is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"Eva, can I stab bats in a cave?\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"A man, a plan, a canal - Panama.\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"A Santa lived as a devil at NASA!\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"\"\"\n", + " Dennis, Nell, Edna, Leon, Nedra, Anita, Rolf, Nora, Alice, Carol, Leo, Jane,\n", + " Reed, Dena, Dale, Basil, Rae, Penny, Lana, Dave, Denny, Lena, Ida, Bernadette,\n", + " Ben, Ray, Lila, Nina, Jo, Ira, Mara, Sara, Mario, Jan, Ina, Lily, Arne, Bette,\n", + " Dan, Reba, Diane, Lynn, Ed, Eva, Dana, Lynne, Pearl, Isabel, Ada, Ned, Dee,\n", + " Rena, Joel, Lora, Cecil, Aaron, Flora, Tina, Arden, Noel, and Ellen sinned.\n", + "\"\"\") is True" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, let's look at a *recursive* formulation in `recursive_palindrome()` below.\n", + "\n", + "**Q15**: Copy the code from `is_a_palindrome()` that implements the duck typing, the case insensitivity, and the removal of special characters!\n", + "\n", + "The recursion becomes apparent if we remove the *first* and the *last* character from a given `text`: `text` can only be a palindrome if the two removed characters are the same *and* the remaining substring is a palindrome itself! So, the word `\"noon\"` has only *one* recursive call while `\"radar\"` has *two*.\n", + "\n", + "Further, `recursive_palindrome()` has *two* base cases of which only *one* is reached for a given `text`: First, if `recursive_palindrome()` is called with either an empty `\"\"` or a `text` argument with `len(text) == 1`, and, second, if the two removed characters are *not* the same.\n", + "\n", + "**Q16**: Implement the two base cases in `recursive_palindrome()`! Use the *early exit* pattern!\n", + "\n", + "**Q17**: Add the recursive call to `recursive_palindrome()` with a substring of `text`! Pass in the `ignore_case` and `ignore_symbols` arguments as `False`! This avoids unnecessary computations in the recursive calls. Why is that the case?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def recursive_palindrome(text, *, ignore_case=True, ignore_symbols=True):\n", + " \"\"\"Check if a text is a palindrome or not.\n", + "\n", + " Args:\n", + " text (str): Text to be checked; may be multiple words\n", + " ignore_case (bool): If the check is case insensitive; defaults to True\n", + " ignore_symbols (bool): If special characters like \".\" or \"?\" and others\n", + " are ignored; defaults to True\n", + "\n", + " Returns:\n", + " is_palindrome (bool)\n", + " \"\"\"\n", + " # answers from above\n", + " ...\n", + " if ignore_case:\n", + " ...\n", + " if ignore_symbols:\n", + " for ... in ...:\n", + " ...\n", + "\n", + " # answer to Q16\n", + " if ...:\n", + " ...\n", + " elif ...:\n", + " ...\n", + "\n", + " # answer to Q17\n", + " return ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q18**: Lastly, verify that `recursive_palindrome()` passes all the test cases below!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"noon\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"Hannah\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"Hannah\", ignore_case=False) is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"radar\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"Hanna\") is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"Warsaw\") is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(12321) is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(12345) is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"Never odd or even.\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"Never odd or even.\", ignore_symbols=False) is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"Eva, can I stab bats in a cave?\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"A man, a plan, a canal - Panama.\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"A Santa lived as a devil at NASA!\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"\"\"\n", + " Dennis, Nell, Edna, Leon, Nedra, Anita, Rolf, Nora, Alice, Carol, Leo, Jane,\n", + " Reed, Dena, Dale, Basil, Rae, Penny, Lana, Dave, Denny, Lena, Ida, Bernadette,\n", + " Ben, Ray, Lila, Nina, Jo, Ira, Mara, Sara, Mario, Jan, Ina, Lily, Arne, Bette,\n", + " Dan, Reba, Diane, Lynn, Ed, Eva, Dana, Lynne, Pearl, Isabel, Ada, Ned, Dee,\n", + " Rena, Joel, Lora, Cecil, Aaron, Flora, Tina, Arden, Noel, and Ellen sinned.\n", + "\"\"\") is True" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index d5d2970..430dbb9 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,9 @@ Alternatively, the content can be viewed in a web browser Indexing & Slicing; String Methods & Operations; String Interpolation) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/01_exercises.ipynb) + (Detecting Palindromes) #### Videos From 3c91b00749cfcfa6998d3f71479893573f4f8b03 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 11:40:27 +0200 Subject: [PATCH 4/8] Add initial version of chapter 06, part 2 --- 06_text/02_content.ipynb | 2100 ++++++++++++++++++++++++++++++++++++++ 06_text/full_house.bin | 1 + 06_text/umlauts.txt | 12 + README.md | 7 + 4 files changed, 2120 insertions(+) create mode 100644 06_text/02_content.ipynb create mode 100644 06_text/full_house.bin create mode 100644 06_text/umlauts.txt diff --git a/06_text/02_content.ipynb b/06_text/02_content.ipynb new file mode 100644 index 0000000..6ff2407 --- /dev/null +++ b/06_text/02_content.ipynb @@ -0,0 +1,2100 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/01_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 6: Text & Bytes (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In this second part of the chapter, we look in more detail at how `str` objects work in memory, in particular how the $0$s and $1$s in the memory translate into characters." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Special Characters" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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 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": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'This is a sentence\\nthat is printed\\non three lines.'" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"This is a sentence\\nthat is printed\\non three lines.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This is a sentence\n", + "that is printed\n", + "on three lines.\n" + ] + } + ], + "source": [ + "print(\"This is a sentence\\nthat is printed\\non three lines.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "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": 3, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ABX\n" + ] + } + ], + "source": [ + "print(\"ABC\\bX\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ABXY\n" + ] + } + ], + "source": [ + "print(\"ABC\\bXY\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 5, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "XBC\n" + ] + } + ], + "source": [ + "print(\"ABC\\rX\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "XYC\n" + ] + } + ], + "source": [ + "print(\"ABC\\rXY\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 7, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This is a sentence\n", + "that is printed\n", + "on three lines.\n" + ] + } + ], + "source": [ + "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": 8, + "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.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Raw Strings" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 9, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C:\\Programs\n", + "ew_application\n" + ] + } + ], + "source": [ + "print(\"C:\\Programs\\new_application\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 10, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "SyntaxError", + "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\")\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\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A simple solution would be to escape the escape character with a *second* backslash `\"\\\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C:\\Programs\\new_application\n" + ] + } + ], + "source": [ + "print(\"C:\\\\Programs\\\\new_application\")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C:\\Users\\Administrator\\Desktop\\Project\n" + ] + } + ], + "source": [ + "print(\"C:\\\\Users\\\\Administrator\\\\Desktop\\\\Project\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 13, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C:\\Programs\\new_application\n" + ] + } + ], + "source": [ + "print(r\"C:\\Programs\\new_application\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C:\\Users\\Administrator\\Desktop\\Project\n" + ] + } + ], + "source": [ + "print(r\"C:\\Users\\Administrator\\Desktop\\Project\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Characters are Numbers with a Convention" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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/develop/05_numbers/00_content.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": 15, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "65" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ord(\"A\")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'A'" + ] + }, + "execution_count": 16, + "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": 17, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ord(\"\\n\")" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'\\n'" + ] + }, + "execution_count": 18, + "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 in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/01_content.ipynb#String-Comparison) of this chapter, 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": 19, + "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": 20, + "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": 21, + "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": 22, + "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": 23, + "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": 24, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'😄'" + ] + }, + "execution_count": 24, + "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": 25, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'😂'" + ] + }, + "execution_count": 25, + "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": 26, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'A'" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"\\U00000041\" # hex(65) == 0x41" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'A'" + ] + }, + "execution_count": 27, + "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": 28, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'A'" + ] + }, + "execution_count": 28, + "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": 29, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 29, + "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": 30, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'ðŸ'" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"\\N{SNAKE}\"" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 31, + "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": 32, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'straße'" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Straße\".lower()" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'strasse'" + ] + }, + "execution_count": 33, + "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": 34, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "SyntaxError", + "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" + ] + } + ], + "source": [ + "\"\n", + "Do not break the lines like this\n", + "\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 35, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "multi_line = \"\"\"\n", + "I am a multi-line string\n", + "consisting of four lines.\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 36, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'\\nI am a multi-line string\\nconsisting of four lines.\\n'" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "multi_line" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "I am a multi-line string\n", + "consisting of four lines.\n", + "\n" + ] + } + ], + "source": [ + "print(multi_line)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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 being empty." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "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\"), start=1):\n", + " print(i, line)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 39, + "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": 40, + "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": 41, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "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": 42, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139880714782512" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(data)" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "bytes" + ] + }, + "execution_count": 43, + "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": 44, + "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": 44, + "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": 45, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "20" + ] + }, + "execution_count": 45, + "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": 46, + "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": 47, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "158" + ] + }, + "execution_count": 47, + "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": 48, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "b'\\xf0\\x82\\xf0\\x82\\xf0\\x83\\xf0\\x83\\xf0\\x83'" + ] + }, + "execution_count": 48, + "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": 49, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "cards = data.decode()" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "str" + ] + }, + "execution_count": 50, + "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": 51, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'🂧🂷🃗🃎🃞'" + ] + }, + "execution_count": 51, + "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": 52, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "place = \"Café Kastanientörtchen\"" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "b'Caf\\xc3\\xa9 Kastanient\\xc3\\xb6rtchen'" + ] + }, + "execution_count": 53, + "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": 54, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "b'Caf\\xe9 Kastanient\\xf6rtchen'" + ] + }, + "execution_count": 54, + "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": 55, + "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": 56, + "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": 57, + "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/3.8.6/lib/python3.8/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": 58, + "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 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/05_resources.ipynb#Unicode)) section at the end of this chapter.\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": 59, + "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": 60, + "metadata": { + "slideshow": { + "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\"" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "content" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lorem Ipsum is simply dummy text of the printing and typesetting industry.\n", + "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s\n", + "when an unknown printer took a galley of type and scrambled it to make a type\n", + "specimen book. It has survived not only five centuries but also the leap into\n", + "electronic typesetting, remaining essentially unchanged. It was popularised in\n", + "the 1960s with the release of Letraset sheets.\n", + "\n" + ] + } + ], + "source": [ + "print(content)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/06_text/full_house.bin b/06_text/full_house.bin new file mode 100644 index 0000000..4ad5e35 --- /dev/null +++ b/06_text/full_house.bin @@ -0,0 +1 @@ +🂧🂷🃗🃎🃞 \ No newline at end of file diff --git a/06_text/umlauts.txt b/06_text/umlauts.txt new file mode 100644 index 0000000..9c0f911 --- /dev/null +++ b/06_text/umlauts.txt @@ -0,0 +1,12 @@ +Lerchen-Lärchen-Ähnlichkeiten +fehlen. Dieses abzustreiten +mag im Klang der Worte liegen. +Merke, eine Lerch' kann fliegen, +Lärchen nicht, was kaum verwundert, +denn nicht eine unter hundert +ist geflügelt. Auch im Singen +sind die Bäume zu bezwingen. +Die Bätrachtung sollte reichen, +Rächtschreibfählern auszuweichen. +Leicht gälingt's, zu unterscheiden, +wär ist wär nun von dän beiden. \ No newline at end of file diff --git a/README.md b/README.md index 430dbb9..fd12c45 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,13 @@ Alternatively, the content can be viewed in a web browser - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/01_exercises.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/01_exercises.ipynb) (Detecting Palindromes) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/02_content.ipynb) + (Special Characters; + ASCII & Unicode; + Multi-line Strings; + `bytes` Type; + Character Encodings) #### Videos From 163f2cd98d310b1d9a4c2397b2a85822b8fe9950 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 11:41:41 +0200 Subject: [PATCH 5/8] Add initial version of chapter 06's summary --- 06_text/03_summary.ipynb | 75 ++++++++++++++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 76 insertions(+) create mode 100644 06_text/03_summary.ipynb diff --git a/06_text/03_summary.ipynb b/06_text/03_summary.ipynb new file mode 100644 index 0000000..bcb6506 --- /dev/null +++ b/06_text/03_summary.ipynb @@ -0,0 +1,75 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 6: Text & Bytes (TL;DR)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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.\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**." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index fd12c45..cccdf7b 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,7 @@ Alternatively, the content can be viewed in a web browser Multi-line Strings; `bytes` Type; Character Encodings) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/03_summary.ipynb) #### Videos From a328d6c9e098c29a0255f0ddb84e26d3883603fe Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 11:43:42 +0200 Subject: [PATCH 6/8] Add initial version of chapter 06's review --- 06_text/04_review.ipynb | 199 ++++++++++++++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 200 insertions(+) create mode 100644 06_text/04_review.ipynb diff --git a/06_text/04_review.ipynb b/06_text/04_review.ipynb new file mode 100644 index 0000000..9804bfc --- /dev/null +++ b/06_text/04_review.ipynb @@ -0,0 +1,199 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 6: Text & Bytes (Review Questions)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/00_content.ipynb) and [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/01_content.ipynb) part of Chapter 6.\n", + "\n", + "Be concise in your answers! Most questions can be answered in *one* sentence." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Essay Questions " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Answer the following questions *briefly*!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: In what sense is a **\"string\" of characters** a **sequence**? What is a **sequence** after all? A *concrete* **data type**, or something else?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: What is a direct consequence of the `str` type's property of being an **ordered** sequence? What operations could we *not* do with it if it were *unordered*?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: What does it mean for an object to be **immutable**? Discuss how we can verify the `str` type's immutability by comparing the two variables `example` and `full_slice` below. Are they pointing to the *same* object in memory?\n", + "```python\n", + "example = \"text\"\n", + "full_slice = example[:]\n", + "```\n", + "Hint: Find out what `[:]` does first!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: Describe in your own words what we mean by **string interpolation**!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## True / False Questions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Motivate your answer with *one short* sentence!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: **Triple-double** quotes `\"\"\"` and **triple-single** quotes `'''` create a *new* object of type `text` that models so-called **multi-line strings**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: A **substring** is a string that *subsumes* another string." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: Indexing into a `str` object with a *negative* index **fails silently**: It does *neither* raise an error *nor* do anything useful." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: We *cannot* assign a *different* character to an index or slice of a `str` object because it is **immutable**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index cccdf7b..bace325 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,7 @@ Alternatively, the content can be viewed in a web browser `bytes` Type; Character Encodings) - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/03_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/04_review.ipynb) #### Videos From 1ef4b283ac477fce88ae23ef6f1db53b032ffb1d Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 11:45:56 +0200 Subject: [PATCH 7/8] Add initial version of chapter 06's further resources --- 06_text/05_resources.ipynb | 359 +++++++++++++++++++++++++++++++++++++ README.md | 2 + 2 files changed, 361 insertions(+) create mode 100644 06_text/05_resources.ipynb diff --git a/06_text/05_resources.ipynb b/06_text/05_resources.ipynb new file mode 100644 index 0000000..a5517f1 --- /dev/null +++ b/06_text/05_resources.ipynb @@ -0,0 +1,359 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/05_resources.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 6: Text & Bytes (Further Resources)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Unicode" + ] + }, + { + "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": 1, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 1, + "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": 2, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "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": 3, + "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": 3, + "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": 4, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"sgHbC6udIqc\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Character Encodings" + ] + }, + { + "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": 5, + "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": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"pBuS7EUPnQA\", width=\"60%\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEABALDBoYFhoaGBoeHRsfIiUmHyAiIiUlJSUnLicyMC0nLS01PVBCNThNOSstRWFFS1NWW11bMkFlbWRYbFBZW1cBERISGRYZMBsbMFc9NThXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXXVdXXVdXV15XV1dXV1dXV1dXV1dXV1dXV//AABEIAWgB4AMBIgACEQEDEQH/xAAbAAEAAQUBAAAAAAAAAAAAAAAABQECAwQGB//EAEkQAAIBAgIECAoIBAYCAwEBAAABAgMRBCEFEjFBE1FTYXGRktIGFBYXIjJSgbHRFTNCc5OhssEjNFRyJENiouHwB4KzwvHDRP/EABkBAQADAQEAAAAAAAAAAAAAAAABAgMEBf/EACURAQACAgIDAAICAwEAAAAAAAABAgMREiEEMUEiMhNRYaHBcf/aAAwDAQACEQMRAD8A8/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2Hm4xvK4ftVO4PNxjeVw/aqdwDjwdh5uMbyuH7VTuDzcY3lcP2qncA48HYebjG8rh+1U7hTzcY3lcP2qncA5AHWv/AMeYzlcP2qncNafgViVUdN1KN0r31p2/SUnJWPcomdObB1EPATEv/Ow66ZVF/wDQzr/xzjOVw3bqdwmtot6lLkAdh5uMbyuH7VTuDzcY3lcP2qncLDjwdh5uMbyuH7VTuDzcY3lcP2qncA48HXv/AMc43lcP2qncNVeBGK5Sh2p90DmgdN5D4rlKHan3R5D4rlKHan3QOZB03kNiuUodqfdHkNiuUodqfdA5kHTeQ2K5Sh2p90eQ2K5Sh2p90DmQdN5DYrlKHan3R5DYrlKHan3QOZB03kNiuUodqfdHkNiuUodqfdA5kHTeQ2K5Sh2p90eQ2K5Sh2p90DmQdN5DYrlKHan3R5DYrlKHan3QOZB03kNiuUodqfdHkNiuUodqfdA5kHTeQ2K5Sh2p90eQ2K5Sh2p90DmQdN5DYrlKHan3R5DYrlKHan3QOZB03kNiuUodqfdHkNiuUodqfdA5kHTeQ2K5Sh2p90eQ2K5Sh2p90DmQdN5DYrlKHan3R5DYrlKHan3QOZB03kNiuUodqfdHkNiuUodqfdA5kHTeQ2K5Sh2p90eQ2K5Sh2p90DmQdN5DYrlKHan3R5DYrlKHan3QOZB03kNiuUodqfdHkNiuUodqfdA5kHTeQ2K5Sh2p90eQ2K5Sh2p90DmQdN5DYrlKHan3R5DYrlKHan3QOZB03kNiuUodqfdHkNiuUodqfdA5kHTeQ2K5Sh2p9006vgzXhNwcqV00n6T325udAQoOm8h8VylDtT7pixXgdiaVKdSU6LjCLk0pTvZK+XogevgAAUKmOvWVODlLYkJnQtr11BZ7dyIPFY6pTr06spN0vVlHck99v+7Asbrybnk3s4ugyyocInC19Y4rZZtPTpjHFf2Sb/LcRWJX+Jl/YviSeAwsoUoQqS1nFWuuLd+RkqYSm25OOdrXu9ha+G1o6ct679IgzYetOLWr1biyjOnVyT1J8TzT6GblChqZz2nNTHflHEtS1Z1LdhK6LjS4V3vxbjchK6TPU1MR2RO1wACVstj6CEiTctj6CEQFwAAAwwxVOVSVOM05x9aK3dPPmsuczAVBQAVKAAAYqeJhLV1Zp60daNt8fa6M0ZQBUoVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKFQAAAAAAChrVMBSlJycXdu79KSW7cnbcjaAFDR05/JYn7qf6Wb5oac/ksT91P9LA6kAAUIzTGJS1aUacq1R3kqadlZfak+Ikzi/CPFzp46UoTcJKEY3Ttlt/czyTqrp8bH/JfTZwWIp4jWUqaoyTjGLUm05SvaLT6GTuDpcBRcp3uouUuNJK9kcVga6lOjSyzrxk3fNvJL9+s7/EQ1qc42veLVuO62GeKsTO2vmV4TqPUovRHhNh8ZNwpa989scrLe2r299rkhisbCkvSefEtp5X4K6brYSpKlTpxnKpdKMm1aaXTbdY62jio1Z6kmlVurtbLvZrLd0o2vF+O6OKtqRaIu2sPS/iQcXrR1o5rdnvW46GvTuudHK4fHJVqcaS2zinOW1pvOy3f92HXGeDHNI7aZskZJ6RNXFQh60lfiWbM2jMcqkpQSasrq/wCZG1MJCM5K32n8Ta0ZFRqqySumd1qxxefW9uSZABzutbLY+ghIk3LY+ghIgXBAAQ1GtKGCTg9WU67i5WTs54hxk896u9vMWTxFVuVPhprUliPTShrS1IRlG/o2y17bFexIvRdBuo+DX8TOecrN3Tuleyd0ndZ5F8MDSiklDYp7236frNu923xsCKjiayWrw03rPCek1DWXCyamlaNt2WWRasXWlrU1WktSOJfCJQ1pcHOMY62VvtO9kr2JHGaNjOMVBJenR1s5K8Kcr2TW+17F8tGUHCMHTWrG6SvJZS9ZN3zT332gaUcXUVSNSrKahUgpUox1eDbVLWlTnldSybTvu9zy6HqVpLWqOcoSp05JzjFenK7ko2+zbVtc3Hg6bqKo43ktl5ScVla6jeydsr2GFwVOimqcWlxa0mkuJJvJcyA5/R2InSw9S9uFdGlKlNK9qb9CMbPfGTbfHrG3WxlbDxdSo6jpxnUUeEUNaUeC1ouWqvbjJLpJN6PouKi6acVB00s/Udrx/JdRhr6OvCFKKXB68ZzcpzlL0ZKSSve97WzeS4wNuhGUYRU3rSUUpPjds31mQCwAAqAAAAAAAAAAAAAAAAABQAVAKAVBQqAAAAAAAAAAAAAADQ05/JYn7qf6Wb5oac/ksT91P9LA6kAAUOU8JMNHxnWcYvWgs2luujqyJ8IsLr0lNbYbf7XtKXjcOjxbxXJG/rmaDUJxkkvRaeS4mdpicUoUJ1fsxg55Z5JXOKJGjpPWw1XCXXCzpzjRu7Xbi7Rb3cxnjtqdO3zMU2rFo+OG8H6GHrYj/GyapzulLNXqPZdrZ/yddonRtGnUg6Les5xclUd5OzWyW9c2006HgpGpohShTqPFxk5WknF610pQs91lt40SmHwNanOhKrTcLyhdXUtX0lk2jrx6eHm31/Td0V4PyUo1Kzs001Bca42dE3baCL0jjk1qQaftNbOgp7l0UpudQx14W1r7XN9XH+ZnwdHVq5NSSW1c+40o4vJKcVO2xttPrRK6Pu4X1VFN5JcXGaTPSk+PNJ3LaABkstlsfQQkSblsfQQkQLgUKgAW1PVfQ/gQsdZpNVatnmvTZAnAQNRSt9bV47qpJGpWr1o1FFVqlnG/rc9gOpBEWfKVO3IttLlKnbkEJkHM4mrUpwvGrV9aMVecntklv6TPQqT4Nyc6s2naym7g2nwRLi/bqduRgrayeVWqr/65W6mEp0HI4PG1pyjrVajTclbWe5tfsSLqvP8AiTyV36cslx7RsTpUgJTby4SeW21Sa67MZ+3U/EqfMCeKnP3fKVfxJ/MXlylX8Wp8xsT4IC8uUq/i1PmaOGxVXxiEeFqOOrO95yd3lbaxtDrQYMHJumm2289vSZwkKkfObu83te8prvjfWyNiRBFVakrbXtW98ZqeDtWUqctacpPnk3vfGTsT5U1rPjYz42BsghdPVJQwtVxlKLUXZptMrovWqUc5zunk9Z32c4EyDUhTcYpa0nbe3dvpILSNaaxkYqc1Hg5Oyk0r6yzsB1ANGeG1s3Kauo7JNbHf89/MVrXUZZvZxgboOX8HZzqU7TqTd1PNybe3nJ2MJJZylLndv2SA2wc9p6rOMsPqzkr1LO0mrrVltN/Cwc6cW5Sva21+0BIgAkVBQqANDTn8lifup/pZvmhpz+SxP3U/0sDqQUAFSkopqz2MADi/CDCvCekk3CT9F7k+Js1cDQ4GEsRWzqSXop7r7Pf+x3lWlGcXGaUovamro57TmgqtSzotSS+w8nfjT3mF6a7h6WHyYtquSdf5XeDuJrOhUqVJuUda0E/zz22v8CS8ffsrrMKw/A0KVJfZSu+N731mI8zyfJyY78az6Y2it7TbTY8ck2r21d65iAxdHxKvq/5FTOD9nmJmNOT2Js2aujo1qKp11dJpqzzXv/I08LLltM73/wC/8TW9cc9+p9tHA4R1Xf7C2vj5kTqVlZFlKlGEVGCSilZJbEi89eZ25b35SqCgIUUlsfQQkSblsfQQkQKlQALZ+q+h/AhqdPVioptpbL7lxEzUdoy6H8CDeJhx/kyEK1NjNGv9cv7I/nM2pVoyVk8zTqy/j9EYfql8iBMcZazFHEwz25h4iP8A1EjS0i/4a+9p/wDyI3ME/wCG3Zv0t3uNDSMrU4/e0/1o28FXUYWlfa2Bus1sRtiXvFQ431GCtVUmmtwEVo3bT/un+qRJrCU1KU9Ra0ra3Pb/APSJwNSzi9ylO/akSfj0P9XUvmQMk8PGTbaze3qsU8WjxfmW+O0+N9Q8dp8b6idjMDB47T431FHjafG+ogZyMoy/xUFzT+CNl4+H+rqXzNKlL/G0nxxqfBAdfgfq17/ibBr4H6te/wCJsEpRlWVm2+P9yJn4UYWM5QkppxbT6U7Elint6f3I/QVSWrXUN1SbWXHUlfi3FYSxVPCfCNZOfV/wQMdJKKi41HFpfZbT/I7im6jbU4pLPPJ3zy/IhPCehGc8NdK+vZ5LNWvZ9ROkIeOnZcvV65Fy09L+oqdbOtp0pakdWEGnGO1Jcd/2MlCk7+nTgtlsk+snQ46tpbXp1FKs560WrSbfUFplwfoVnFZZReR3KhFbIxXuRzWm8NB4+lLVj6jbVlZtNWuveNDRj4QSe3Ey/wC+4sqaThKpGTqJtU2r350dVqU1UUFShs26kdu22ziVzPUpR1GnCLVtmqhocjDwjqRy8YdlzRf7GeGn3L1sQmuJ2V/yL/BfDQiptwjLOeckvsvLN7DocOk7+hGLTs0oriT4ucjQ4/D6UVOnDUq6rV9m3NmxHwkqb8Q+zH5Ep4U0YzpUk0vrYK9uN2NmlCEKcbUaWUZt3jFZRtls5wOexWllUdJyqqdql75ZejJfuXUNOSjOMYV3ZyWSs1mzr4U4rZGPZRzuFw0PpDES1I5TgknFNK8U3bi2gdcVKFSwAFQBoac/ksT91P8ASzfNDTn8lifup/pYHmn0hX5er+JL5j6Qr8vV/El8zWAG0tIV+Xq/iS+ZesfX5ar+JP5mpEvRI2ljq3LVfxJ/Myxxlblqv4k/maaM0SYS21jK3LVfxJfM29GYio68L1JvPfKXEyNRvaK+vh7/AIM0rETMK3/WXTqvP25dpmljq9fXWpKVre0+N85soxV9vuOq1Y1p51LTEtNVMT7cl/7P5lYvE76su0zOipl/HDbnLElX31p9uQUK2+tPtSMxUn+OEc7MOpUzfDVO1L5nUx2HNs6SOwxzViNNsUzO9rgC2cmlkrmLdSqvQl/a/gcpXnOnFScYtXSyk9rdluOsqerLofwOV0j9VH7yl+tEIZsPRlOKl6K63vKywUuEc9aOcVFqz3Nu/wCZlw0HKlGzcc3mtu1mywNGVKa2KL97RbhJOq9iSs3e7e+xty9ZdBp6G2R/s/cDNidHyqRUdZK0oy2X2NO35F0sO0t35m1Thqq3O/zZbW9VgRdas4vVaje19r2dRmjSllnHNbMzUxj/AIz+7XxN6NBOdOpneMGtr323dZA046MlG6U42u2rxd83e23nH0dU9uHZfzN7VqcaMkb2z2jQjfo+p7UOqQ+j6nHD/cSYAi/o+p7UP9xR6Oqe1DqZKACJjo6rvnD3Jsw0INYujd3sprZbd0smyJh/NUumf6WB1mB+rXv+JsGvgfq17/ibBKUNin63T+5GaCx1Kk6iqTUXKUmr3t67+ZJYvbLp/c5bD4DE1J1HSpRmlOau5xX2nubIgdh9J4flodZEabqxqTw8oNOOvt/9WaEdGY1bcNF9FWn8zJPR2LepbDP0Xf6ylxNe1zgTuE0vh1TipVNVpWaae73Gf6Uw7aUaik3sST+RzEtHYzdhV+LT7xb9GY5//wCVfi0+8Ox1zqLjRA6Ukni6bTutSfxiQ+EhXnUnTjh7zg7SWtFWfS3mSEsDi3KL8WdlFq3CUt9v9XMB0dPG0Gk+Ep8ecltFTFUmmo1INvYlJNnH46hiaUHUnhnGC2vWi7dTNjR+GxMoqccM2nmmp00+pyQ7G34P1oU1eclFOVRXez1mT0cVh0rRqUkuJSijmI6Oxijbxa7vJ/WUt7v7RoTjXjXVGWHaqNXUdaOzjvexI6DwgqwnClqSUrVYXs77zdweJoaijOcFKLeUmsui5BS0fi3GKWGatKL+spbv/Yx4nBYuzfirss36cG+pMgdVLG0OVp9pEHQkljK8r5a9N/7YkTo6FatHXp4dyjx60V8Wjfp4LFKo28NK0pR+3TySSWfpAdgVKFSQKlASKmhpz+SxP3U/0s3zQ05/JYn7qf6WB5WAALol6LIl6JF8TNExRM0S0JZEb2ifr4e/4M0kb2ifr4+/4M0p7hTJ+suiRgr+t7jOi2VJN3OuXnR01ypnVCPOXcDErxlblDXBsqlHiK8HHiJ0cmqzpI7CFcFbYiajsObyI9OjBO9rgAc7pW1PVl0P4HKaQf8ADj95T/Wjqq3qS/tfwOUxlOc6cVGErqcG8tykrkShIYGV6UbNc/WZ2RcaT9iXZZdwb9mfZZGxtzfpe41NDSVo3aXob+kvpqSy1J9TNTDLg4KM4yTWT9FgTkFZJXvxsx4iS1XmR0akXsUn0RZVSt9mfZkTsamMf8V/2L4slqUlqrNbEReKpSdVSUJOLjuWzPeY9SXsT7EiBNlbEIk/Zl2WW62drTvxasvkNicsLEI9b2anZl8ijk96mumMvkNibsLEJKT/ANXVIas/ZqdmXyAmSHjL/FUV/ql+iRa4S9ifYl8itLOtQdn67WeWfBzvkQOvwX1a9/xNg18D9Wvf8TYLJQ2LfrdP7ml4PQUlXT2OUvznI2cW85dP7nNUtN1MLK1NRldy1lK/tO2xlYHZQwMU1Zyy2elz3NrVOL8tK/I0v93zKrw0r8jS65fMtsdnqFGjkoeGNZ/5NPrkbNPwlqyaTpQSe1ptsbSv0Vnjq/3n/wDNE7RhnL09bmWajzX2395y8Ma6VarUgk3wiaT2NaqNyPhLJNtYeN3ttNr9iNobvhLlg639rL8BS1qDikn6S27lbaucisfpZ4jD1Yzgoejkk27ln09PDtxhCMlle91n7iR1Madkla2Sy2nP6RivH1x8E/1GHysqvZQh2pGOvjuEqxqvbwTulx62wjY6aVGTmpfZtmr7Xbb+3/4VrL0ZdBzsPCqqkk6MHbfrNfsZKfhJKpJQlSjCLyb1m/2J2LvBqN6EVHJull0k5SptX9HVV8le5ymC0m8NTpuEVJ2s08sjch4VTcorxeObS9d730EbHUAAsKgoVAGhpz+SxP3U/wBLN80NOfyWJ+6n+lgeVgAC6JkRjiZESL4maJhiZ4loSyIkNEfXx6JfBkeiQ0P9euiXwNae4UyfpLoEXItRcjreaqVKFSQAAB7CYjsId7CYjsOXyfjq8f6uAByupbP1ZdD+BEWJep6r6GRdisilitiqRUhC2xFaWnalX/tfwJhIhNMO0K9+KQSyaNeU7K9rZLo2G1hazqQUnBwb3PaauiNk/d8CRJj0hayhcygEbpCN41Vdp6krNbcoFNHX9PVWeqrF+M21V/on/wDGzFox5zv7KIEpG9lfJ2zXOYMV6j9xico2/wAy3ErLdxIvxH1W/dt2lpgRLqa1So+KUUvdCJPROcjdTrX5S/XGLJmtFOWetmt3ERA2ZEU/5ml94/0TN6OJUrJRl0tLI0JQ/wAVSlxVGuuE/kB1mC+rXv8AibBr4L6te/4mwSlz2Ov6dld3dlx5nIY3ATbbjSrXbbziv2O2q+tLpfxLCo4DxKtyVTsSHiVbkqnYkd+HInY5HRWipTr04VJSipRUvR25tqzvs2M6haIw8L+nWVm1tT2e408H/Px/th+qZ0zwzztOS27Ocn2IV6Nwzu+Eqq+3PiX9pdT0DQkrxqVmr29Zbv8A1JjxaXKS/Lm+RkpUmlZycnxuw0Ij6Bpari5VWn/qj8jndPYGVLEUoQqNqpf1krq1r7Nu07twOW8I1/jMN0VPhEDNS8G42T8Yq7OKFvgZvJ+F78NU2W2Q+RJUpejHoXwLtYgQ78GocvV6ofIvp+D0Y/59R9Kh8iV1xrgRL8HIaqSrVUlzQ+RSn4NwUk+HquzT2Q+RLa5VSA3LCwKlhQqAANDTn8lifup/pZvmhpz+SxP3U/0sDysAAXxL0WRL0SMkTNEwxN7CYGrWf8KnKfQsuvYWhMLESOhvrl/bI06+GnSlq1IuMuJo3dDfXf8Aqzan7Qpl/SU6i5FqLkdbzVSpQqAAAB7CYjsId7CYjsOXyfjq8f6uAByupZWdoTfFF/A5dY+ftLqR1FaN4TT2OLT6jlfojD+x/ul8ysjIsfP2l1IqsfP2l1Ixy0RQe2HVKS+DL1oujn6O3nZXQr4/P2l1I0tK1tajUd82nc3noui/s/mUWiqKutV584Glg6sqaye3bkbSx0+bqMkdGUkrar6zJ9HUvZZKGDx6fEuoo8ZPm6jZWjqXF8A9G0nuf5fIkaU56yk3t1J37DMGBm4LWW9LqJaGj6cc0mt27f7hHRtJK2rl0hLV8enxL8zHUxEpKztZm/8ARtL2fzH0bS9n8whz8PSq1dyUo390Ir9iSWNmuJ+4kJYCnJ3azyV7RWS2bEUejqXs/ACPljZcxhi26tJ8dVdepUJb6Npez8AtHwTi0rastZWss7NZ5Z5NgS+D+rXv+JnMGEX8Ne/4mclKEq+tLpfxLSlapHXlnvfxLeEjxlRc2WTZR1Y8ZiqVY8YHO6bxDjUkouzcY5rmlIj/AKUxP9RW/En8yQ0tQlNt60Gt3o+kua5CNWyZaBt/SuJ/qK34k/mPpbE/1Fb8SfzNQJEjb+lsV/U1vxJ/M3oaT1+Ac23Knwl29rulb4EZSw0pcxI0cFKMba1P304yfWyNiUpeF8VFKVKV0rO0lYv8sYcjPrRy2IouErbegwgdf5Yw5GfWh5Yw5GfWjkATodf5Y0+Sn1ovo+FsJTjHgpZtLat7ONM+C+upf3x+KA9dKlABW4AAGjpz+SxP3U/0s3zQ05/JYn7qf6WB5YAALomRGOJkRIyQWZ3TxUKcI06drJKyRwsTpMNiU6SkoRjZJKVtlla1+Ms1xyppSprxu9zyMOhl/Ff9r/YyYhOpTbjnbPpLNDfWv+1/FGuOdyy8j9ZTaL0WouOt5YVAJAAqEqPYTEdhDvYTEdhy+T8dPj/VwAOV1Lanqy6H8CAOgZZ4tT9iHZREwINMuTJrxen7Eeyh4vD2I9lEaEMityZ4CHsR6kOAh7EepDQh0VJfgIexHqQ4CHsR6kNCKKkpwMPZj1IcDD2Y9SJ0IwqiT4KPsx6kOCj7K6kBGpF1iQ4OPsrqQ4OPsrqQEfYEhwceJdQ4OPsrqQEfYpckeDj7K6kOCj7MepDQx4X1F7/iZikYpZJW6CoHl+ktIVY4mvFSyVWaXaZq/SVX2z1Cei8NJtyw9Ftu7bpQbb43kU+icL/TUPwofIDy/wCkavtFHj6vtHqP0Thf6ah+FD5D6Jwv9NQ/Ch8gPK5Yqb2sxuV9p6x9E4X+mofhQ+Q+icL/AE1D8KHyA8muVU2esfROF/pqH4UPkPonC/01D8KHyJHlSryWxlyxlT2j1P6Jwv8ATUPwofIfROF/pqH4UPkQPLHipveupFvjEubqR6r9E4X+mofhQ+Q+icL/AE1D8KHyA8q4eXN1Ipwz5upHq30Thf6ah+FD5D6Jwv8ATUPwofIDyjhXzdSM2DqN1qez147udHqP0Thf6ah+FD5BaKwyd1hqCa2PgofIDaBUEgAAKmhpz+SxP3U/0s3zQ05/JYn7qf6WB5YAALoG7hZqk9eUIzvlqyV7c/SacDPGo87/AGtpaBWKuzbp1bZNNx4r295rQViVwWjHOHCVLxi8ovo2t8xpWvLpasTM9NSrjajjqR9GO+zzfvMmj8bwM7vNPJ9BjxlPUm48XyNRO7H6yi3fUu4hnmXmjoerrYeHGvRfu/4sb6OyJ3DzbRqdKAuKFlQAqBRkvHYRD2EvHYcvk/HV4/1cChU5XSAAAAVAAAAAAAAAAAAAAAAAoVAAAAAAAAAAoAVAAAAChUAAAAAAoVKFQKFQAAAAGhpz+SxP3U/0s3zQ05/JYn7qf6WB5YVJl+CmP/pp9qHzOn0LoeMV/Gwqc1Ba0XCM2nlnfZ++ZetdjgYIyo9DxOiYS1dTB07K7k3RSfMrHLeE2j3QrQfBxpxnFejFWSab/MtOPUb2NehoutUjCUIr0s0m0rq9rk5Ro8DRi66qrUyUY2eWed1zkRhtKNUVTd04v0Xu6GXz07n6rTW7ImtuLWvHSP0jiVOpKSyTztxFmAwlSrK0I3593WblPTjU5SavdrbbLKxt0dNTrTjTha8tm4pM7lH4+5lIaHoOlGUZO+d+gkotM1cHRmk+EtfdZ3NuMbHZj3Fe3n5uM3nitU+PnKqX/ffYrqIpqdO/8y7LoTuXFFHN+4qWFJbH0EtHYRT2MlY7Dl8n46MH1UqAcrpALACoKACoAAAoVAAAAAAAAAAAAAAAAAAAAAAKFQAAAAAFAKgAChUAAAAAAAAAAaGnP5LE/dT/AEs3zQ05/JYn7qf6WBOVptRy2vJdL/7ctpQUcl/y+d8+ZbN3qRXspy97yX5axebLMpC6UipTlGSUk0rpq6fuJe5EY9/xX7i2OOxyml9D04pTpRaetZxWaatfJbeLYQrwjbjfO/rJZP8APedHpyqlKknez13lm91rLrIfEYtycU5KST3q0tmy+0mYrtWWnHR0ntdjYw+HVKalFu/Gbzd4f8GovWS5y844qrtIRrz9uXWy5Ymp7cusxRLrGfKVtQyrFVPbY8cqe1+SMdhYtFpRxr/TMsbV9r8kV8fqca6kYLFbFotJwr/TP4/Utu6jqY7Dj2jrnNRjd7MjHNMzoisR6ZAYo14tXztZPZxuy+BdwsePfbY9ufyfUYpX3BZwytnlm1707FZVFquW1K4F4MXDx3vO17bfht2oqq0bX2dPRf4AZAWxmnexUCoLKtWMI60nZK1377FlHEwnDXi/RzzeWzaBmBideNr3y95XhVt3Zbnv2AZAWRmnez2FwFQUKgAAAAAAFCoAAoBUAAAAAAAAAAUKgAACgFQAABQqABRlkKqeziT9zAyGhpz+SxP3U/0s23Vtuey+41NNv/BYn7mf6QJWk7ym+ey6Fl8bmVfMxUE1FJ7dr6Xm/wA2XpnQsvTIjSMrTk+JfsSqZDaV9afuLVHOacjrV6avH0YJ+le2bfF0EDiqWpUa1ovenFu2ee8lPCGV8RJblGK+LIRrNIraVUhGq3bPK1y+hG8s9xrJWlY28Is2W3uENyKL0ikUXpGayliti5IqkWgW6pXVL7FbF4hLG4nTKvTazkt3zOeSPQ47EZZviJc6qlLbrLdve53+Ic6WfpLN3eb/AO7zogYoc66lG99ZXvfa9t7/ABRWNaklZSSXM2dCAOc1qPGut/8Ab85XXo+0tz2vdsOiAHPQrU1e01m230su8Zp+2ifAHPVK1KScZSTTyauI1KSjqpxUbWtusdCAOccqL2tdbKqpSX2lu3vdsOiAHPxrUle0lnzsr4zT9tE+AIDxmn7aHjNP20T4AgPGaftoeM0/bRPgCA8Zp+2h4zT9tE+AIDxmn7aHjNP20T4AgPGaftoeM0/bRPgCA8Zp+2h4zD20T4AgPGaftoeM0/bRPgCA8Zp+2h4zT9tE+AIDxmn7aHjNP20T4AgPGaftoeM0/bRPgCA8Zp+2h4zT9tE+AIDxmn7aHjNP20T4AgPGaftoeM0/bRPgCA8Zp+2ixVKSyUlsttezi/JHRADnnVpNt6yzVtr+Bq6YrRlhMRGL1pSpTSSzbbWxHVgCNuEyxPYVT2nVpK+5EaU9d9H7EoRmkV6T/tJgcbpmovGa64tX9KIulbXV9htaVbeMxKXGvySRoTXovo/dGdpQ35JN3TRs4Pa+IiMHPar233M+Lf8ADTu3Z7SInpCeijIkcjGvJbJSXRJmWGNqLZUn2myvJZ1iRVI5aOk6q/zZfkzJHS1Zf5r98V8i0XgdOkVsc3HTVZfbT6YoyQ07V/0P/wBX8y8ZIHQpHoEdiPJo6cmra0Ybd1z1iOxdBnktFtaJVlJJXewjKulLVoQtanO8dfepbvcW6TxFTW1YRk4R9bJ5v/gwUcOsTFrZHe96e63OcN8luXGrWtI1uzY0lKXAzzd8t/OjVhiJx2SfXcmuAi42ktbLO+/pMNXR9OSyWq+NfIrfDee4lhrvazBY1zdpLPjWw3TSjT4Naq272Z8PO6s9x0Y62iv5G+9M4BirYiFPOc4w/uaXxLpZSypVjG2s0ruyvxlVJNXTuiO03nTSWb1r232s87FMluNZlaleVohJFSG0HWk3KLk3FJWT3ZkyRjvzryTkpwtoAKGiioKFQAAAAAAYcTiadKOvVnGEdmtJpK/SzMQnhXhalbD040o3lw9J+rrJJSzk1vSAlqGIhVip05xnF7JRaa60WYrG0qKUq1SFNN2TnJRTfvOU0no+po2isVSqOpVVZyqRS1IT4RKGqoLLJqLW3eHouphquGqVqFTGwjh3CSSVWUa0pa0pWk9jzVwOxhNSScWmnmms00VOJxeCrxhQXiuIp0OCqatDD1W3TrSleLk7rK3uVytXRuMnDEOq6/CwwtF09Sc0nWUXe2q7SezrA6yekqEanBSrU1U2ajnFSv0CWkqCqqk61NVW0lBzWtd7FbacfjMHVnLHwngqlapiIUlSqakNWMlRScnJtWtL4Gzo3BV6OkajqKs03QWuqUJwm40lFyc3nHPiA6qvjKVOUI1KkISm7QUpJOT5k9pbisfRo24arCnfZryUb9FzldNUMRLSDqPCyr04KMYxUIOE6NtaT1m7qamlZc3Ob2ncBVxOLwUqS1Y6lbWnOnrqOtFWUou2b5wOjhNSScWmnmms0zXekqCqcE61PhL21NeOtfisclpLR1bByw2FwtWTjiaaoNuTvBxlrSqpbvRctnMUx2j5uWPoRwVSrKrOPAVXGOrG0IrW127qzQHblkK0ZOSjJNxdpJNOz4nxHEaTweM1661MXOrr0uCqU5vguCWrrJpPbdPdffxmTFaMr05Y9UqWIvOrTnrQm/To3i5xg7+tt92XMB2spJJtuyWbb3I18LpGhWbVGtTqNZtRkpNdRAaGw9VLSCdPERpyjHgI1pOUrcG1ZXb3/sQ+jtHYxR/hUqqqxwcoKU4Qpak8vRg162x5y6wPQDTr6VoU5SjKotaMoRlFJycXP1bpbLnJU8Fi5UpRgsVTg6uHsm5KSzfCTjeUnbj3GxV0bXp4mvwUazhw2C1Z3bcoRXpty323gdia9TH0YVI0pVYRqS9WDklJ9COX0fo/EweEqvxjhHiaiqqU5uKpNyteLdktnWZdL0HUx8EsJVVNVKdSrWjBSdSUfUSd/Rir5vmA6H6Sw+vOHDU9emm5x143iltbW4sqaYwsIwlLEUlGd9RucbSs7Oz6Tl9G6Jq8Ph6dTCtSo1a8q1eShq1YT1rJPbK91k+I04aIrwwdD+DWVR0cRSlGNOErKVWUoxal6t7r0uID0FMqa2jqMqeHown60KcIy6VFJmyBD3CeZj1hrZnbpLLcjtI7X0G65ZX4iC0rpzDRlbhVJ7Hq3l8COoHJYt/47EdMvijVrr1uh/sbEsRGWIr1FZqcnq3yybNXEX9JvbZ9BlMxxVUwUb6/R+5mxP1PvRTA+jCTa9a1ispfZ3MpyiK6X4TPbQsDcrUL7Npqzg1tM0TEwtAAQqX03bPeWBsJhm17tXe9fE9yj6q6DweDzXSvie8R9VdAJnbg6mkNerGpLWU4ucnLWbvvhFLclkjrMDi6cadNTajOUIzllZXks3fZtOSrYOEJyi4Rum1s5zrNFxp1sOm4pvUVOfRHcc+Pe3peZEcKzEdNuONpv7ateybyTfEm9vuEsZTUdZS1ldL0byzavu5iksDTbbcdrb2u13m8ufeUWApJWUbK98m1nZK+3mNfyeb0wYvEqdNujJNxaTbTtnxcZp4HX4aMpTb2q27NG9Xo04rUj6Lm07XeduJGLCUbzi7NWbbuslxG9J/Dtz5K/nEpQ4P/AMnpqOEmm01KaX+13v7jvDhf/KGJSo4ejlrObnz2St/9vyM27JoDwhoeLQo4eM4SSbm7K0XfO1277Tbkm7z101vm5Wt03zT5jk9C6JVTCqUMc6cpNxq0lG+rts3nldb7M6Olo+caHBwjKXpU1Fr0rpKedyl8Fckbn4Uz2x21H1LaIxkZSq6vpSjC+u1a/u/dknDhnBSU4NtJ2cWt2y9yO0VoudCFSdRq8oNaqzt7zfoYdypwvUnZxWXordsulcvWsVjUK2tNp3LPQxEZqNsm4qVuZlMRX1YztbWjHWt12+DLMRBQUZxVuD2r/RvXu2+4xSV6FWb2zTfQrWS6viLeplNfcQzvhVn6Euazj+d2XRxEdRTbsufLPi6SjxVNL14vmTu30JGBSlCMbtR15ybb+ze7t07jPevUtNbbMK8ZJtSTtt5i3D4mM1tV88veYqUlw3r6/oO+S41xCjWtSds5R13q78m9wixNWaGJhJ2Uk29nP0cZlI+tUTjH+KpNyjZJL2ls3okCa22iY0qUbKmjpenrUJJNRas027ZrPaaRG50padRtdjqdCbpqu4XjJTgpSt6S2O18zbucfh4VMVWipNyta7e6Ke8u09TqVcTKlZOTtwEJamq1wd7pT9F+kmpWvJJK1rl8lOHTPHk57n464HLYTRGLdWm+EnQpRzUdZycVwl9RWlq7ONSSTtuLaXjmIoxqVHOrTVScXCjNUpTjBaiqKWW2acrX2W2782rqwc1XhpNa6pt5U7Qu6Uo31FbNpNz19a7a1bbizFYPHSqPOrJrXjSqKpGCT104TqRi0pK18rAdQWTrQjHXcko+1fLrIvQNCvCWI4dSu6k3Fyba1deTjb03ua3R4syFxmtGpOjGetByuoxd087rJbGaY6c5ZZMnCHTwpUKtSNeOrOcIuMZp3sntXEbRy2lKFShgqcckm5upd2jrajcI1HujrWvu2J5GrR0Ri9WSoxdOEnHPWhHXi6kJRk1SaV1HhE2rXTSz2lJ1E9NKzMxuXZg5avSxsZTwlOu6mrh3PXyU9ZxlCNO7zScvSTbb9F5lKlDSMKc+AVWMXrOnBzp1KkZcGra0ptrVctZ2Tb2dBCXVA5jE0dJQVVUpVXedWUXei3dwjwaWssoJ611tvYxRwmkY1K0oOau5W9KLydVP0VKTWtqa1sopc+4OsBzcsLj5pcLJu3BeguDSfo+m3bnyte2bM+iKONhKlwrfBpasoWp6sUqULNWV762stoE6CoAoCoAoVAA5rD4unVV4TjLL7MkzK3sPJYVJRlrRbjJb07PrR0Wi/CypBamIvUjun9pdPGdVcsT7Sp4T6bnVqypQbVKLs7fae+/Mc/KQqTbbfG2yw57W3OxW5tYKavnuRqGSjfPoIidEe25Kd5N7jFr3ZgdR2sWq5XTSbM9Sdlky+i4ztGZrOWVn7i1MmFJlKPRkdzl+Ra9F8UvyJLAKUqUG+I2OD6Dp4RMKoN6Kl7S6mYMRhXBWe3bdbLHScGWVMNGStJJkTi/pMf5crD1l0r4nvMNi6EeJ6Rwqp1lqq0XZrmzPbIbF0GExqdIc94RYHVlw0fVfr8z4yK0TpKdOc6sfqIq0k/tvclxHbTgpJxkk01Zpq6a4mQGkfB1yUI0HGFNPODvs40+swvWd7q78Oes1/jyekrhdIQqU4VGnHXV0ntt7i+WLjsjm3s4r7rmrXotNKMXqxSUbLciyOHm/sv35HBfy88Xmta/6lhwp7RaxcqspcJlUi7TjxNcXMTmjMPKMded9Z7L7kUWi6brRryX8RRs7P0Xztb2bx6lbTNe0ZclZ6qqcX4baLliMXgtaMvF7uM5wi5NOTWTS2XslfnO0KEsHDz8Gp4bHz8WpTlRqwTvdNQld3i23/wBuTng7BxnWjJNNat08uMnBbfvLcvx0znHHLkqUKgq0Ya1DX2ylq74q1n05XMtioAtUUtxVq5UAYq0tSnKUY31YtpJbcthpU8VVTd6Ws3sl6t8sls4/yaJEAaUK09Z/wlZvN7PtJcWe25ugAVMOKqxhBymrrite99iS6TMaukKMp0mo5yTTSUnG9t2stgEdh9NRUrOkoU90ou/va1Vuz2vJMlauJpwajOcYuXqpySb6OM5uhRqznwap1VKOqnKUq8Y5RcbpvJ7b5X2W3m9j9BcPXpNzcaUKLpytbWl6cGlmnZWjtVmBKSxtFK7q00rXu5K1r2v15FZYykm06kE4pOScldJ7G+Ihqfg1FutKUnCcq8p03HVlqR9JWtJNZ8JUdrZOXMZJeDFFzctednmo2hZO8L/Zu78GsnktwErPGUo01UdSGo9ktZWfQ95TDY2lVUdScW5RU0r+lqyV02tq2kbU8HYSWqqtSKU5zilGm1Fzc9eycXtU2s9llzl+jvB+nh6qqRnOTUdVKWra7jGLle181BcwEsUUFe9lfjLgBQFQBQqAAAAAAAAAAAAAAAeCxV2ZHFPjXuMCqvmK8M+YLRpdUVmy0tc2ymsFVxt4OipRqNu2rFWz5zS1i5VXzBMMqhvLWzHwjGsDa42tHYThqijuWcug0tY2cHj50W3BRu1bNXJrrfaHYRikrLYXWRzK8Ia3s0+p/MeUNb2afVL5nV/NVLpbFlarCCvOSj0nNVNPV5LLVj0L5tmjUxEpO8nd8bK2zR8EpjcbwtX0dXVVkr7XntPZYbF0HgnCM6xf+RsalbgsP2anfOeZ3OyZeog8v84+N5LD9mp3x5x8byWH7NTvkIeng8w84+N5LD9mp3x5x8byWH7NTvgenlTy/wA4+N5LD9mp3x5x8byWH7NTvgeoA8v84+N5LD9mp3x5x8byWH7NTvgeoA8v84+N5LD9mp3x5x8byWH7NTvgeoA8v84+N5LD9mp3x5x8byWH7NTvgeoA8v8AOPjeSw/Zqd8ecfG8lh+zU74HqAPL/OPjeSw/Zqd8ecfG8lh+zU74HqAPL/OPjeSw/Zqd8ecfG8lh+zU74HqAPL/OPjeSw/Zqd8ecfG8lh+zU74HqBQ8w84+N5LD9mp3x5x8byWH7NTvgeng8w84+N5LD9mp3x5x8byWH7NTvgeoA8v8AOPjeSw/Zqd8ecfG8lh+zU74HqAPL/OPjeSw/Zqd8ecfG8lh+zU74HqAPL/OPjeSw/Zqd8ecfG8lh+zU74HqAPL/OPjeSw/Zqd8ecfG8lh+zU74HqAPL/ADj43ksP2anfHnHxvJYfs1O+B6gDy/zj43ksP2anfHnHxvJYfs1O+B6gDy/zj43ksP2anfHnHxvJYfs1O+B6gDy/zj43ksP2anfHnHxvJYfs1O+B6gDy/wA4+N5LD9mp3x5x8byWH7NTvgeoA8v84+N5LD9mp3x5x8byWH7NTvgceAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//Z\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"-4QjII981sM\", width=\"60%\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index bace325..82348a5 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,8 @@ Alternatively, the content can be viewed in a web browser Character Encodings) - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/03_summary.ipynb) - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/04_review.ipynb) + - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/05_resources.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/05_resources.ipynb) #### Videos From 84e05ab2988ddf8b861617faceadabfacefb5acf Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 12:00:32 +0200 Subject: [PATCH 8/8] Move detailed ToC in a file on its own --- CONTENTS.md | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 150 +---------------------------------------------- 2 files changed, 166 insertions(+), 147 deletions(-) create mode 100644 CONTENTS.md diff --git a/CONTENTS.md b/CONTENTS.md new file mode 100644 index 0000000..50bf5f4 --- /dev/null +++ b/CONTENTS.md @@ -0,0 +1,163 @@ +# Table of Contents + +The materials are designed to resemble an *interactive* book. + +It is recommended + to follow the [installation instructions](https://github.com/webartifex/intro-to-python#installation) + in the [README.md](README.md) file + and work through the content on one's own computer. + +If this is not possible, + the files can be viewed in a web browser + either statically (i.e., read-only) on [nbviewer ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/tree/develop/) + or interactively (i.e., code can be executed) on [Binder ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab). + +- *Chapter 0*: Introduction + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/00_intro/00_content.ipynb) + (Python's History & Background; + Open-source & Communities; + JupyterLab; + Programming vs. Computer Science; + Learning Tips) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/00_intro/01_exercises.ipynb) + (Mastering Markdown) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/02_review.ipynb) +- **Part A: Expressing Logic** + - *Chapter 1*: Elements of a Program + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/00_content.ipynb) + (A first Example: Averaging Even Numbers; + Operators; + Objects & Data Types; + Errors) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/01_exercises.ipynb) + (Printing Output) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/02_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/02_exercises.ipynb) + (Simple `for`-loops) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/03_content.ipynb) + (Memory in Detail; + Variables & References; + Mutability; + Expressions & Statements) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/04_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/04_exercises.ipynb) + (Python as a Calculator) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/05_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/06_review.ipynb) + - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/07_resources.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/07_resources.ipynb) + - *Chapter 2*: Functions & Modularization + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/00_content.ipynb) + (Built-in Functions & Constructors; + Function Definitions; + Function Calls & Scoping Rules; + Positional vs. Keyword Arguments; + Modularization) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/01_exercises.ipynb) + (Volume of a Sphere) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/02_content.ipynb) + (Standard Library: `math` & `random` Modules; + Third-party Packages: `numpy` Library; + Writing one's own Modules) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/03_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/04_review.ipynb) + - *Chapter 3*: Conditionals & Exceptions + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/00_content.ipynb) + (Boolean Expressions; + Relational Operators; + Logical Operators; + `if` statement; + Exception Handling) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/01_exercises.ipynb) + (Discounting Customer Orders) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/02_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/02_exercises.ipynb) + (Fizz Buzz) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/03_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/04_review.ipynb) + - *Chapter 4*: Recursion & Looping + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/00_content.ipynb) + (Recursion; + Examples: Factorial, Euclid's Algorithm, & Fibonacci; + Duck Typing; + Type Casting & Checking; + Input Validation) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/01_exercises.ipynb) + (Towers of Hanoi) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/02_content.ipynb) + (Looping with `while` & `for`; + Examples: Collatz Conjecture, Factorial, Euclid's Algorithm, & Fibonacci; + Containers vs. Iterables) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/03_content.ipynb) + (Customizing Loops with `break` and `continue`; + Indefinite Loops) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/04_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/04_exercises.ipynb) + (Throwing Dice) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/05_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/06_review.ipynb) +- **Part B: Managing Data and Memory** + - *Chapter 5*: Numbers & Bits + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/00_content.ipynb) + (`int` Type; + Binary & Hexadecimal Representations; + Bit Arithmetic; + Bitwise Operators) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/01_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/01_content.ipynb) + (`float` Type; + Floating-point Standard; + Special Values; + Imprecision; + Binary & Hexadecimal Representations) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/02_content.ipynb) + (`complex` Type; + Numerical Tower; + Duck vs. Goose Typing) + - [appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/03_appendix.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/03_appendix.ipynb) + (`Decimal` Type; + `Fraction` Type) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/04_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/05_review.ipynb) + - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/06_resources.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/06_resources.ipynb) + - *Chapter 6*: Text & Bytes + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_numbers/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/00_content.ipynb) + (`str` Type; + Reading Files; + Sequences; + Indexing & Slicing; + String Methods & Operations; + String Interpolation) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/01_exercises.ipynb) + (Detecting Palindromes) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/02_content.ipynb) + (Special Characters; + ASCII & Unicode; + Multi-line Strings; + `bytes` Type; + Character Encodings) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/03_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/04_review.ipynb) + - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/05_resources.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/05_resources.ipynb) \ No newline at end of file diff --git a/README.md b/README.md index 82348a5..db07011 100644 --- a/README.md +++ b/README.md @@ -6,163 +6,19 @@ This project is a *thorough* introductory course ### Table of Contents -The materials are designed to resemble an *interactive* book. -It is recommended - to follow the [installation instructions](https://github.com/webartifex/intro-to-python#installation) below - and work through the content on one's own computer. -Alternatively, the content can be viewed in a web browser - either statically (i.e., read-only) on [nbviewer ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/tree/develop/) - or interactively (i.e., code can be executed) on [Binder ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab). +The following is a high-level overview of the contents. +For a more *detailed version* with **clickable links** + see the [CONTENTS.md](CONTENTS.md) file. - *Chapter 0*: Introduction - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/00_intro/00_content.ipynb) - (Python's History & Background; - Open-source & Communities; - JupyterLab; - Programming vs. Computer Science; - Learning Tips) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/00_intro/01_exercises.ipynb) - (Mastering Markdown) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/02_review.ipynb) - **Part A: Expressing Logic** - *Chapter 1*: Elements of a Program - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/00_content.ipynb) - (A first Example: Averaging Even Numbers; - Operators; - Objects & Data Types; - Errors) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/01_exercises.ipynb) - (Printing Output) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/02_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/02_exercises.ipynb) - (Simple `for`-loops) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/03_content.ipynb) - (Memory in Detail; - Variables & References; - Mutability; - Expressions & Statements) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/04_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/04_exercises.ipynb) - (Python as a Calculator) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/05_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/06_review.ipynb) - - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/07_resources.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/07_resources.ipynb) - *Chapter 2*: Functions & Modularization - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/00_content.ipynb) - (Built-in Functions & Constructors; - Function Definitions; - Function Calls & Scoping Rules; - Positional vs. Keyword Arguments; - Modularization) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/01_exercises.ipynb) - (Volume of a Sphere) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/02_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/02_content.ipynb) - (Standard Library: `math` & `random` Modules; - Third-party Packages: `numpy` Library; - Writing one's own Modules) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/03_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/04_review.ipynb) - *Chapter 3*: Conditionals & Exceptions - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/00_content.ipynb) - (Boolean Expressions; - Relational Operators; - Logical Operators; - `if` statement; - Exception Handling) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/01_exercises.ipynb) - (Discounting Customer Orders) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/02_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/02_exercises.ipynb) - (Fizz Buzz) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/03_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/04_review.ipynb) - *Chapter 4*: Recursion & Looping - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/00_content.ipynb) - (Recursion; - Examples: Factorial, Euclid's Algorithm, & Fibonacci; - Duck Typing; - Type Casting & Checking; - Input Validation) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/01_exercises.ipynb) - (Towers of Hanoi) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/02_content.ipynb) - (Looping with `while` & `for`; - Examples: Collatz Conjecture, Factorial, Euclid's Algorithm, & Fibonacci; - Containers vs. Iterables) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/03_content.ipynb) - (Customizing Loops with `break` and `continue`; - Indefinite Loops) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/04_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/04_exercises.ipynb) - (Throwing Dice) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/05_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/06_review.ipynb) - **Part B: Managing Data and Memory** - *Chapter 5*: Numbers & Bits - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/00_content.ipynb) - (`int` Type; - Binary & Hexadecimal Representations; - Bit Arithmetic; - Bitwise Operators) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/01_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/01_content.ipynb) - (`float` Type; - Floating-point Standard; - Special Values; - Imprecision; - Binary & Hexadecimal Representations) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/02_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/02_content.ipynb) - (`complex` Type; - Numerical Tower; - Duck vs. Goose Typing) - - [appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/03_appendix.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/03_appendix.ipynb) - (`Decimal` Type; - `Fraction` Type) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/04_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/05_review.ipynb) - - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/06_resources.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/06_resources.ipynb) - *Chapter 6*: Text & Bytes - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_numbers/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/00_content.ipynb) - (`str` Type; - Reading Files; - Sequences; - Indexing & Slicing; - String Methods & Operations; - String Interpolation) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/01_exercises.ipynb) - (Detecting Palindromes) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/02_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/02_content.ipynb) - (Special Characters; - ASCII & Unicode; - Multi-line Strings; - `bytes` Type; - Character Encodings) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/03_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/04_review.ipynb) - - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/05_resources.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/05_resources.ipynb) #### Videos