Merge branch 'release-0.1.0'

This commit is contained in:
Alexander Hess 2024-04-08 22:15:26 +02:00
commit ed90f3df04
Signed by: alexander
GPG key ID: 344EA5AB10D868E0
118 changed files with 78982 additions and 14 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
**/.ipynb_checkpoints/
.python-version
.venv/

28
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,28 @@
default_stages: [commit]
fail_fast: true
repos:
- repo: local
hooks:
- id: doctests
name: Run the xdoctests in the source files
entry: poetry run nox -s doctests --
language: system
stages: [commit, merge-commit]
types: [python]
- id: fix-branch-references
name: Check for wrong branch references
entry: poetry run nox -s fix-branch-references --
language: system
stages: [commit, merge-commit]
types: [text]
# Enable hooks provided by the pre-commit project to
# enforce rules that local tools could not that easily.
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
hooks:
- id: check-added-large-files
args: [--maxkb=250]
- id: check-merge-conflict
- id: no-commit-to-branch
args: [--branch, main]
- id: trailing-whitespace

873
00_intro/00_content.ipynb Normal file
View file

@ -0,0 +1,873 @@
{
"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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/00_intro/00_content.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# An Introduction to Python and Programming"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"This book is a *thorough* introduction to programming in [Python <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://www.python.org/).\n",
"\n",
"It teaches the concepts behind and the syntax of the core Python language as defined by the [Python Software Foundation <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://www.python.org/psf/) in the official [language reference <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/reference/index.html). Furthermore, it introduces commonly used functionalities from the [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html) and popular third-party libraries like [numpy <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://www.numpy.org/), [pandas <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://pandas.pydata.org/), [matplotlib <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://matplotlib.org/), and others."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"source": [
"<img src=\"static/logo.png\" width=\"15%\" align=\"left\">"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"## Prerequisites"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"There are *no* prerequisites for reading this book."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Objective"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"The **main goal** of this introduction is to **prepare** the student **for further studies** in the \"field\" of **data science**."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"### Why data science?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"The term **[data science <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Data_science)** is rather vague and does *not* refer to an academic discipline. Instead, the term was popularized by the tech industry, who also coined non-meaningful job titles such as \"[rockstar](https://www.quora.com/Why-are-engineers-called-rockstars-and-ninjas)\" or \"[ninja developers](https://www.quora.com/Why-are-engineers-called-rockstars-and-ninjas).\" Most *serious* definitions describe the field as being **multi-disciplinary** *integrating* scientific methods, algorithms, and systems thinking to extract knowledge from structured and unstructured data, *and* also emphasize the importance of **[domain knowledge <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Domain_knowledge)**.\n",
"\n",
"Recently, this integration aspect feeds back into the academic world. The [MIT](https://www.mit.edu/), for example, created the new [Stephen A. Schwarzman College of Computing](http://computing.mit.edu) for [artificial intelligence <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Artificial_intelligence) with a 1 billion dollar initial investment where students undergo a \"bilingual\" curriculum with half the classes in quantitative and method-centric fields and the other half in domains such as biology, business, chemistry, politics, (art) history, or linguistics (cf., the [official Q&As](http://computing.mit.edu/faq/) or this [NYT article](https://www.nytimes.com/2018/10/15/technology/mit-college-artificial-intelligence.html)). Their strategists see a future where programming skills are just as naturally embedded into students' curricula as are nowadays subjects like calculus, statistics, or academic writing. Then, programming literacy is not just another \"nice to have\" skill but a prerequisite, or an enabler, to understanding more advanced topics in the actual domains studied."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"## Installation"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"To \"read\" this book in the most meaningful way, a working installation of **Python 3.8** with [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) is needed.\n",
"\n",
"For a tutorial on how to install Python on your computer, follow the instructions in the [README.md](https://github.com/webartifex/intro-to-python/blob/main/README.md#installation) file in the project's [GitHub repository <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_gh.png\">](https://github.com/webartifex/intro-to-python). If you cannot install Python on your own machine, you may open the book interactively in the cloud with [Binder <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab)."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Jupyter Notebooks"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"The document you are viewing is a so-called [Jupyter notebook](https://jupyter-notebook.readthedocs.io/en/stable/notebook.html), a file format introduced by the [Jupyter Project](https://jupyter.org/).\n",
"\n",
"\"Jupyter\" is an [acronym <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Acronym) derived from the names of the three major programming languages **[Julia](https://julialang.org/)**, **[Python <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://www.python.org)**, and **[R](https://www.r-project.org/)**, all of which play significant roles in the world of data science. The Jupyter Project's idea is to serve as an integrating platform such that different programming languages and software packages can be used together within the same project.\n",
"\n",
"Jupyter notebooks have become a de-facto standard for communicating and exchanging results in the data science community - both in academia and business - and provide an alternative to command-line interface (CLI or \"terminal\") based ways of running Python code. As an example for the latter case, we could start the default [Python interpreter <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/tutorial/interpreter.html) that comes with every installation by typing the `python` command into a CLI (or `poetry run python` if the project is managed with the [poetry](https://python-poetry.org/docs/) CLI tool as explained in the [README.md](https://github.com/webartifex/intro-to-python/blob/main/README.md#alternative-installation-for-instructors) file). Then, as the screenshot below shows, we could execute Python code like `1 + 2` or `print(\"Hello World\")` line by line simply by typing it following the `>>>` **prompt** and pressing the **Enter** key. For an introductory course, however, this would be rather tedious and probably scare off many beginners."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"<img src=\"static/cli_example.png\" width=\"50%\">"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"One reason for the popularity of Jupyter notebooks is that they allow mixing text with code in the same document. Text may be formatted with the [Markdown <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_gh.png\">](https://guides.github.com/features/mastering-markdown/) language and mathematical formulas typeset with [LaTeX](https://www.overleaf.com/learn/latex/Free_online_introduction_to_LaTeX_%28part_1%29). Moreover, we may include pictures, plots, and even videos. Because of these features, the notebooks developed for this book come in a self-contained \"tutorial\" style enabling students to simply read them from top to bottom while executing the code snippets.\n",
"\n",
"Other ways of running Python code are to use the [IPython <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://ipython.org/) CLI tool instead of the default interpreter or a full-fledged [Integrated Development Environment <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Integrated_development_environment) (e.g., the commercial [PyCharm](https://www.jetbrains.com/pycharm/) or the free [Spyder <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_gh.png\">](https://github.com/spyder-ide/spyder) that comes with the Anaconda Distribution)."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"### Markdown Cells vs. Code Cells"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"A Jupyter notebook consists of cells that have a type associated with them. So far, only cells of type \"Markdown\" have been used, which is the default way to present formatted text.\n",
"\n",
"The cells below are examples of \"Code\" cells containing actual Python code: They calculate the sum of `1` and `2` and print out `\"Hello World\"` when executed, respectively. To edit an existing code cell, enter into it with a mouse click. You are \"in\" a code cell if its frame is highlighted in blue. We call that the **edit mode**.\n",
"\n",
"There is also a **command mode** that you reach by hitting the **Escape** key. That un-highlights the frame. You are now \"out\" of but still \"on\" the cell. If you were already in command mode, hitting the Escape key does *nothing*.\n",
"\n",
"Using the **Enter** and **Escape** keys, you can now switch between the two modes.\n",
"\n",
"To **execute**, or \"run,\" a code cell, hold down the **Control** key and press **Enter**. Note how you do *not* go to the subsequent cell if you keep re-executing the cell you are on. Alternatively, you can hold the **Shift** key and press **Enter**, which executes a cell *and* places your focus on the subsequent cell or creates a new one if there is none."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"3"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"1 + 2"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Hello World\n"
]
}
],
"source": [
"print(\"Hello World\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Similarly, a Markdown cell is also in either edit or command mode. For example, double-click on the text you are reading: This puts you into edit mode. Now, you could change the formatting (e.g., print a word in *italics* or **bold**) and \"execute\" the cell to render the text as specified.\n",
"\n",
"To change a cell's type, choose either \"Code\" or \"Markdown\" in the navigation bar at the top. Alternatively, you can press either the **Y** or **M** key in command mode.\n",
"\n",
"Sometimes, a code cell starts with an exclamation mark `!`. Then, the Jupyter notebook behaves as if the following command were typed directly into a terminal. The cell below asks the `python` CLI to show its version number and is *not* Python code but a command in the [Shell <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Shell_%28computing%29) language. The `!` is useful to execute short CLI commands without leaving a Jupyter notebook."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Python 3.12.2\n"
]
}
],
"source": [
"!python --version"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"## Programming vs. Computer Science vs. IT"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"In this book, **programming** is defined as\n",
"- a *structured* way of *problem-solving*\n",
"- by *expressing* the steps of a *computation* or *process*\n",
"- and thereby *documenting* the process in a formal way.\n",
"\n",
"Programming is always *concrete* and based on a *particular case*. It exhibits elements of an *art* or a *craft* as we hear programmers call code \"beautiful\" or \"ugly\" or talk about the \"expressive\" power of an application."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"That is different from **computer science**, which is\n",
"- a field of study comparable to applied *mathematics* that\n",
"- asks *abstract* questions (e.g., \"Is something computable at all?\"),\n",
"- develops and analyses *algorithms* and *data structures*,\n",
"- and *proves* the *correctness* of a program.\n",
"\n",
"In a sense, a computer scientist does not need to know a programming language to work, and many computer scientists only know how to produce \"ugly\" looking code in the eyes of professional programmers."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"**IT** or **information technology** is a term that has many meanings to different people. Often, it has something to do with hardware or physical devices, both of which are out of scope for programmers and computer scientists. Sometimes, it refers to a [support function](https://en.wikipedia.org/wiki/Value_chain#Support_activities) within a company. Many computer scientists and programmers are more than happy if their printer and internet connection work as they often do not know a lot more about that than \"non-technical\" people."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"## Why Python?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"### What is Python?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Here is a brief history of and some background on Python (cf., also this [TechRepublic article](https://www.techrepublic.com/article/python-is-eating-the-world-how-one-developers-side-project-became-the-hottest-programming-language-on-the-planet/) for a more elaborate story):\n",
"\n",
"- [Guido van Rossum <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Guido_van_Rossum) (Pythons **[Benevolent Dictator for Life <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Benevolent_dictator_for_life)**) was bored during a week around Christmas 1989 and started Python as a hobby project \"that would keep \\[him\\] occupied\" for some days\n",
"- the idea was to create a **general-purpose** scripting **language** that would allow fast *prototyping* and would *run on every operating system*\n",
"- Python grew through the 90s as van Rossum promoted it via his \"Computer Programming for Everybody\" initiative that had the *goal to encourage a basic level of coding literacy* as an equal knowledge alongside English literacy and math skills\n",
"- to become more independent from its creator, the next major version **Python 2** - released in 2000 and still in heavy use as of today - was **open-source** from the get-go which attracted a *large and global community of programmers* that *contributed* their expertise and best practices in their free time to make Python even better\n",
"- **Python 3** resulted from a significant overhaul of the language in 2008 taking into account the *learnings from almost two decades*, streamlining the language, and getting ready for the age of **big data**\n",
"- the language is named after the sketch comedy group [Monty Python <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Monty_Python)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"#### Summary"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Python is a **general-purpose** programming **language** that allows for *fast development*, is *easy to read*, **open-source**, long-established, unifies the knowledge of *hundreds of thousands of experts* around the world, runs on basically every machine, and can handle the complexities of applications involving **big data**."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"### Why open-source?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Couldn't a company like Google, Facebook, or Microsoft come up with a better programming language? The following is an argument on why this can likely not be the case.\n",
"\n",
"Wouldn't it be weird if professors and scholars of English literature and language studies dictated how we'd have to speak in day-to-day casual conversations or how authors of poesy and novels should use language constructs to achieve a particular type of mood? If you agree with that premise, it makes sense to assume that even programming languages should evolve in a \"natural\" way as users *use* the language over time and in *new* and *unpredictable* contexts creating new conventions."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Loose *communities* are the primary building block around which open-source software projects are built. Someone - like Guido - starts a project and makes it free to use for anybody (e.g., on a code-sharing platform like [GitHub <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_gh.png\">](https://github.com/)). People find it useful enough to solve one of their daily problems and start using it. They see how a project could be improved and provide new use cases (e.g., via the popularized concept of a [pull request <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_gh.png\">](https://help.github.com/articles/about-pull-requests/)). The project grows both in lines of code and people using it. After a while, people start local user groups to share their same interests and meet regularly (e.g., this is a big market for companies like [Meetup](https://www.meetup.com/) or non-profits like [PyData <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://pydata.org/)). Out of these local and usually monthly meetups grow yearly conferences on the country or even continental level (e.g., the original [PyCon <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://us.pycon.org/) in the US, [EuroPython <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://europython.eu/), or [PyCon.DE <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://de.pycon.org/)). The content presented at these conferences is made publicly available via GitHub and YouTube (e.g., [PyCon 2019 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://www.youtube.com/channel/UCxs2IIVXaEHHA4BtTiWZ2mQ) or [EuroPython <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](http://europython.tv/)) and serves as references on what people are working on and introductions to the endless number of specialized fields."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"While these communities are somewhat loose and continuously changing, smaller in-groups, often democratically organized and elected (e.g., the [Python Software Foundation <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://www.python.org/psf/)), take care of, for example, the development of the \"core\" Python language itself.\n",
"\n",
"Python itself is just a specification (i.e., a set of rules) as to what is allowed and what not: It must first be implemented (c.f., next section below). The current version of Python can always be looked up in the [Python Language Reference <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/reference/index.html). To make changes to that, anyone can make a so-called **[Python Enhancement Proposal <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://www.python.org/dev/peps/)**, or **PEP** for short, where it needs to be specified what exact changes are to be made and argued why that is a good thing to do. These PEPs are reviewed by the [core developers <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://devguide.python.org/coredev/) and interested people and are then either accepted, modified, or rejected if, for example, the change introduces internal inconsistencies. This process is similar to the **double-blind peer review** established in academia, just a lot more transparent. Many of the contributors even held or hold positions in academia, one more indicator of the high quality standards in the Python community. To learn more about PEPs, check out [PEP 1 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://www.python.org/dev/peps/pep-0001/) that describes the entire process.\n",
"\n",
"In total, no one single entity can control how the language evolves, and the users' needs and ideas always feed back to the language specification via a quality controlled and \"democratic\" process."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Besides being **free** as in \"free beer,\" a major benefit of open-source is that one can always *look up how something works in detail*: That is the literal meaning of *open* source and a difference to commercial languages (e.g., [MATLAB](https://www.mathworks.com/products/matlab.html)) as a programmer can always continue to *study best practices* or find out how things are implemented. Along this way, many *errors are uncovered*, as well. Furthermore, if one runs an open-source application, one can be reasonably sure that no bad people built in a \"backdoor.\" [Free software <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Free_software) is consequently free of charge but brings *many other freedoms* with it, most notably the freedom to change the code."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"### Isn't C a lot faster?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"The default Python implementation is written in the C language and called CPython. This is also what the Anaconda Distribution uses.\n",
"\n",
"[C <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/C_%28programming_language%29) and [C++ <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/C%2B%2B) (cf., this [introduction](https://www.learncpp.com/)) are wide-spread and long-established (i.e., since the 1970s) programming languages employed in many mission-critical software systems (e.g., operating systems themselves, low latency databases and web servers, nuclear reactor control systems, airplanes, ...). They are fast, mainly because the programmer not only needs to come up with the **business logic** but also manage the computer's memory.\n",
"\n",
"In contrast, Python automatically manages the memory for the programmer. So, speed here is a trade-off between application run time and engineering/development time. Often, the program's run time is not that important: For example, what if C needs 0.001 seconds in a case where Python needs 0.1 seconds to do the same thing? When the requirements change and computing speed becomes an issue, the Python community offers many third-party libraries - usually also written in C - where specific problems can be solved in near-C time."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"#### Summary"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"While it is true that a language like C is a lot faster than Python when it comes to *pure* **computation time**, this does not matter in many cases as the *significantly shorter* **development cycles** are the more significant cost factor in a rapidly changing world."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Who uses it?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "-"
}
},
"source": [
"<img src=\"static/example_python_users.png\" width=\"70%\">"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"While ad-hominem arguments are usually not the best kind of reasoning, we briefly look at some examples of who uses Python and leave it up to the reader to decide if this is convincing or not:\n",
"\n",
"- **[Massachusetts Institute of Technology](https://www.mit.edu/)**\n",
" - teaches Python in its [introductory course](https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-0001-introduction-to-computer-science-and-programming-in-python-fall-2016/) to computer science independent of the student's major\n",
" - replaced the infamous course on the [Scheme](https://groups.csail.mit.edu/mac/projects/scheme/) language (cf., [source <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_hn.png\">](https://news.ycombinator.com/item?id=602307))\n",
"- **[Google](https://www.google.com/)**\n",
" - used the strategy \"Python where we can, C++ where we must\" from its early days on to stay flexible in a rapidly changing environment (cf., [source <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_so.png\">](https://stackoverflow.com/questions/2560310/heavy-usage-of-python-at-google))\n",
" - the very first web-crawler was written in Java and so difficult to maintain that it was rewritten in Python right away (cf., [source](https://www.amazon.com/Plex-Google-Thinks-Works-Shapes/dp/1416596585/ref=sr_1_1?ie=UTF8&qid=1539101827&sr=8-1&keywords=in+the+plex))\n",
" - Guido van Rossom was hired by Google from 2005 to 2012 to advance the language there\n",
"- **[NASA](https://www.nasa.gov/)** open-sources many of its projects, often written in Python and regarding analyses with big data (cf., [source](https://code.nasa.gov/language/python/))\n",
"- **[Facebook](https://facebook.com/)** uses Python besides C++ and its legacy PHP (a language for building websites; the \"cool kid\" from the early 2000s)\n",
"- **[Instagram](https://instagram.com/)** operates the largest installation of the popular **web framework [Django](https://www.djangoproject.com/)** (cf., [source](https://instagram-engineering.com/web-service-efficiency-at-instagram-with-python-4976d078e366))\n",
"- **[Spotify](https://spotify.com/)** bases its data science on Python (cf., [source](https://labs.spotify.com/2013/03/20/how-we-use-python-at-spotify/))\n",
"- **[Netflix](https://netflix.com/)** also runs its predictive models on Python (cf., [source](https://medium.com/netflix-techblog/python-at-netflix-86b6028b3b3e))\n",
"- **[Dropbox](https://dropbox.com/)** \"stole\" Guido van Rossom from Google to help scale the platform (cf., [source](https://medium.com/dropbox-makers/guido-van-rossum-on-finding-his-way-e018e8b5f6b1))\n",
"- **[JPMorgan Chase](https://www.jpmorganchase.com/)** requires new employees to learn Python as part of the onboarding process starting with the 2018 intake (cf., [source](https://www.ft.com/content/4c17d6ce-c8b2-11e8-ba8f-ee390057b8c9?segmentId=a7371401-027d-d8bf-8a7f-2a746e767d56))"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"As images tell more than words, here are two plots of popular languages' \"market shares\" based on the number of questions asked on [Stack Overflow <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_so.png\">](https://stackoverflow.blog/2017/09/06/incredible-growth-python/), the most relevant platform for answering programming-related questions: As of late 2017, Python surpassed [Java](https://www.java.com/en/), heavily used in big corporates, and [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript), the \"language of the internet\" that does everything in web browsers, in popularity. Two blog posts from \"technical\" people explain this in more depth to the layman: [Stack Overflow <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_so.png\">](https://stackoverflow.blog/2017/09/14/python-growing-quickly/) and [DataCamp](https://www.datacamp.com/community/blog/python-scientific-computing-case)."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"<img src=\"static/growth_of_major_programming_languages.png\" width=\"50%\">"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"As the graph below shows, neither Google's very own language **[Go](https://golang.org/)** nor **[R](https://www.r-project.org/)**, a domain-specific language in the niche of statistics, can compete with Python's year-to-year growth."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"<img src=\"static/growth_of_smaller_programming_languages.png\" width=\"50%\">"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"[IEEE Sprectrum](https://spectrum.ieee.org/computing/software/the-top-programming-languages-2019) provides a more recent comparison of programming language's popularity. Even news and media outlets notice the recent popularity of Python: [Economist](https://www.economist.com/graphic-detail/2018/07/26/python-is-becoming-the-worlds-most-popular-coding-language), [Huffington Post](https://www.huffingtonpost.com/entry/why-python-is-the-best-programming-language-with-which_us_59ef8f62e4b04809c05011b9), [TechRepublic](https://www.techrepublic.com/article/why-python-is-so-popular-with-developers-3-reasons-the-language-has-exploded/), and [QZ](https://qz.com/1408660/the-rise-of-python-as-seen-through-a-decade-of-stack-overflow/)."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## How to learn Programming"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"### ABC Rule"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"**A**lways **b**e **c**oding.\n",
"\n",
"Programming is more than just writing code into a text file. It means reading through parts of the [documentation <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/), blogs with best practices, and tutorials, or researching problems on [Stack Overflow <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_so.png\">](https://stackoverflow.com/) while trying to implement features in the application at hand. Also, it means using command-line tools to automate some part of the work or manage different versions of a program, for example, with **[git](https://git-scm.com/)**. In short, programming involves a lot of \"muscle memory,\" which can only be built and kept up through near-daily usage.\n",
"\n",
"Further, many aspects of software architecture and best practices can only be understood after having implemented some requirements for the very first time. Coding also means \"breaking\" things to find out what makes them work in the first place.\n",
"\n",
"Therefore, coding is learned best by just doing it for some time on a daily or at least a regular basis and not right before some task is due, just like learning a \"real\" language."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"### The Maker's Schedule"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"[Y Combinator <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_hn.png\">](https://www.ycombinator.com/) co-founder [Paul Graham <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Paul_Graham_%28programmer%29) wrote a very popular and often cited [article](http://www.paulgraham.com/makersschedule.html) where he divides every person into belonging to one of two groups:\n",
"\n",
"- **Managers**: People that need to organize things and command others (e.g., a \"boss\" or manager). Their schedule is usually organized by the hour or even 30-minute intervals.\n",
"- **Makers**: People that create things (e.g., programmers, artists, or writers). Such people think in half days or full days.\n",
"\n",
"Have you ever wondered why so many tech people work during nights and sleep at \"weird\" times? The reason is that many programming-related tasks require a \"flow\" state in one's mind that is hard to achieve when one can get interrupted, even if it is only for one short question. Graham describes that only knowing that one has an appointment in three hours can cause a programmer to not get into a flow state.\n",
"\n",
"As a result, do not set aside a certain amount of time for learning something but rather plan in an *entire evening* or a *rainy Sunday* where you can work on a problem in an *open end* setting. And do not be surprised anymore to hear \"I looked at it over the weekend\" from a programmer."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"### Phase Iteration"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"When being asked the above question, most programmers answer something that can be classified into one of two broader groups.\n",
"\n",
"**1) Toy Problem, Case Study, or Prototype**: Pick some problem, break it down into smaller sub-problems, and solve them with an end in mind.\n",
"\n",
"**2) Books, Video Tutorials, and Courses**: Research the best book, blog, video, or tutorial for something and work it through from start to end.\n",
"\n",
"The truth is that you need to iterate between these two phases.\n",
"\n",
"Building a prototype always reveals issues no book or tutorial can think of before. Data is never as clean as it should be. An algorithm from a textbook must be adapted to a peculiar aspect of a case study. It is essential to learn to \"ship a product\" because only then will one have looked at all the aspects.\n",
"\n",
"The major downside of this approach is that one likely learns bad \"patterns\" overfitted to the case at hand, and one does not get the big picture or mental concepts behind a solution. This gap can be filled in by well-written books: For example, check the Python/programming books offered by [Packt](https://www.packtpub.com/packt/offers/free-learning/) or [OReilly](https://www.oreilly.com/)."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"## Contents"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"**Part A: Expressing Logic**\n",
"\n",
"- What is a programming language? What kind of words exist?\n",
" - *Chapter 1*: [Elements of a Program <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb)\n",
" - *Chapter 2*: [Functions & Modularization <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/00_content.ipynb)\n",
"- What is the flow of execution? How can we form sentences from words?\n",
" - *Chapter 3*: [Conditionals & Exceptions <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/00_content.ipynb)\n",
" - *Chapter 4*: [Recursion & Looping <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/00_content.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"**Part B: Managing Data and Memory**\n",
"\n",
"- How is data stored in memory?\n",
" - *Chapter 5*: [Numbers & Bits <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/00_content.ipynb)\n",
" - *Chapter 6*: [Text & Bytes <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/00_content.ipynb)\n",
" - *Chapter 7*: [Sequential Data <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/00_content.ipynb)\n",
" - *Chapter 8*: [Map, Filter, & Reduce <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb)\n",
" - *Chapter 9*: [Mappings & Sets <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/00_content.ipynb)\n",
" - *Chapter 10*: Arrays & Dataframes\n",
"- How can we create custom data types?\n",
" - *Chapter 11*: [Classes & Instances <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"## xkcd Comic"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"As with every good book, there has to be a [xkcd](https://xkcd.com/353/) comic somewhere."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"import antigravity"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"<img src=\"static/xkcd.png\" width=\"30%\">"
]
}
],
"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.12.2"
},
"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
}

View file

@ -0,0 +1,115 @@
{
"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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/00_intro/02_exercises.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 0: Introduction (Coding Exercises)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The exercises below assume that you have read [Chapter 0 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/00_intro/00_content.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Mastering Markdown"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Briefly review GitHub's guide on [Mastering Markdown](https://guides.github.com/features/mastering-markdown/) and create nicely formatted \"text\" cells below!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q1**: Check the latest [Bundesliga standings](https://www.bundesliga.com/en/bundesliga/table) and provide a table of the top three teams with the following four columns: rank, team name, games played, and points scored. Render the rank in **bold**, make the team name a clickable link (to the team's website), and put both the games played and points scored in *italics*. The header row should be visually different from the three rows with the teams' information."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: The quote \"Education is what remains after one has forgotten what one has learned in school\" is attributed to Albert Einstein. Display the author and his quote appropriately!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3**: Integrate the image of the delicious dessert milk rice at this [URL](https://i.ytimg.com/vi/-BoSRlzy9c4/maxresdefault.jpg) into this notebook."
]
},
{
"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.12.2"
},
"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
}

215
00_intro/02_review.ipynb Normal file
View file

@ -0,0 +1,215 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 0: Introduction (Review Questions)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The questions below assume that you have read [Chapter 0 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/00_intro/00_content.ipynb).\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": [
"**Q1**: Describe the difference between the terms **programming** and **computer science**!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: Explain what is a **pull request** and elaborate on how this concept fits a *distributed* organization of work!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3**: In what sense are **open-source** communities democracies? How are they near-perfect [meritocracies <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Meritocracy)? How is open-source software development similar to academia?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q4**: What is a *significant* advantage of a \"slow\" programming language like Python over a faster one like C?"
]
},
{
"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**: Python has been the fastest-growing *major* programming language in recent years."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: Python is named after a snake to emphasize its agility and fast development speed right in its name."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q7**: Python was initially designed for highly intensive numerical computing, in particular for use cases from physics and astronomy."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q8**: JavaScript is a subset of the Java language."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q9**: Python is **free software**. That means it does not cost anything."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q10**: The primary purpose of PEPs is to regulate how code should be documented and styled."
]
},
{
"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.12.2"
},
"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
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

BIN
00_intro/static/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

BIN
00_intro/static/xkcd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

2188
01_elements/00_content.ipynb Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,172 @@
{
"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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/01_exercises.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 1: Elements of a Program (Coding Exercises)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The exercises below assume that you have read the [first part <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb) of Chapter 1.\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": [
"## Printing Output"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q1**: *Concatenate* `greeting` and `audience` below with the `+` operator and print out the resulting message `\"Hello World\"` with only *one* call of the built-in [print() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#print) function!\n",
"\n",
"Hint: You may have to \"add\" a space character in between `greeting` and `audience`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"greeting = \"Hello\"\n",
"audience = \"World\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(...)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: How is your answer to **Q1** an example of the concept of **operator overloading**?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3**: Read the documentation on the built-in [print() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#print) function! How can you print the above message *without* concatenating `greeting` and `audience` first in *one* call of [print() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#print)?\n",
"\n",
"Hint: The `*objects` in the documentation implies that we can put several *expressions* (i.e., variables) separated by commas within the same call of the [print() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#print) function."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(...)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q4**: What does the `sep=\" \"` mean in the documentation on the built-in [print() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#print) function? Adjust and use it to print out the three names referenced by `first`, `second`, and `third` on *one* line separated by *commas* with only *one* call of the [print() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#print) function!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"first = \"Anthony\"\n",
"second = \"Berta\"\n",
"third = \"Christian\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(...)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5**: Lastly, what does the `end=\"\\n\"` mean in the documentation? Adjust and use it within the `for`-loop to print the numbers `1` through `10` on *one* line with only *one* call of the [print() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#print) function!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for number in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:\n",
" print(...)"
]
}
],
"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.12.2"
},
"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
}

View file

@ -0,0 +1,218 @@
{
"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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/02_exercises.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 1: Elements of a Program (Coding Exercises)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The exercises below assume that you have read the [first part <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb) of Chapter 1.\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": [
"## Simple `for`-loops"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`for`-loops are extremely versatile in Python. That is different from many other programming languages.\n",
"\n",
"As shown in the first example in [Chapter 1 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List), we can create a `list` like `numbers` and loop over the numbers in it on a one-by-one basis."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q1**: Fill in the *condition* of the `if` statement such that only numbers divisible by `3` are printed! Adjust the call of the [print() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#print) function such that the `for`-loop prints out all the numbers on *one* line of output!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for number in numbers:\n",
" if ...:\n",
" print(...)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Instead of looping over an *existing* object referenced by a variable like `numbers`, we may also create a *new* object within the `for` statement and loop over it directly. For example, below we write out the `list` object as a *literal*.\n",
"\n",
"Generically, the objects contained in a `list` objects are referred to as its **elements**. We reflect that in the name of the *target* variable `element` that is assigned a different number in every iteration of the `for`-loop. While we could use *any* syntactically valid name, it is best to choose one that makes sense in the context (e.g., `number` in `numbers`).\n",
"\n",
"**Q2**: Fill in the condition of the `if` statement such that only numbers consisting of *one* digit are printed out! As before, print out all the numbers on *one* line of output!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for element in [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]:\n",
" if ...:\n",
" print(...)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"An easy way to loop over a `list` object in a sorted manner, is to wrap it with the built-in [sorted() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#sorted) function.\n",
"\n",
"**Q3**: Fill in the condition of the `if` statement such that only odd numbers are printed out! Put all the numbers on *one* line of output!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for number in sorted(numbers):\n",
" if ...:\n",
" print(...)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Whenever we want to loop over numbers representing a [series <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Series_%28mathematics%29) in the mathematical sense (i.e., a rule to calculate the next number from its predecessor), we may be able to use the [range() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#func-range) built-in.\n",
"\n",
"For example, to loop over the whole numbers from `0` to `9` (both including) in order, we could write them out in a `list` like below.\n",
"\n",
"**Q4**: Fill in the call to the [print() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#print) function such that all the numbers are printed on *one* line ouf output!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for number in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]:\n",
" print(...)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5**: Read the documentation on the [range() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#func-range) built-in! It may be used with either one, two, or three expressions \"passed\" in. What do `start`, `stop`, and `step` mean? Fill in the calls to [range() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#func-range) and [print() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#print) to mimic the output of **Q4**!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for number in range(...):\n",
" print(...)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: Fill in the calls to [range() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#func-range) and [print() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#print) to print out *all* numbers from `1` to `10` (both including)!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for number in range(...):\n",
" print(...)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q7**: Fill in the calls to [range() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#func-range) and [print() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#print) to print out the *even* numbers from `1` to `10` (both including)! Do *not* use an `if` statement to accomplish this!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for number in range(...):\n",
" print(...)"
]
}
],
"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.12.2"
},
"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
}

1725
01_elements/03_content.ipynb Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,212 @@
{
"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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/04_exercises.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 1: Elements of a Program (Coding Exercises)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The exercises below assume that you have read the [second part <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/03_content.ipynb) Chapter 1.\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": [
"## Python as a Calculator"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The [volume of a sphere <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Sphere) is defined as $\\frac{4}{3} * \\pi * r^3$.\n",
"\n",
"**Q1**: Calculate it for `r = 2.88` and approximate $\\pi$ with `pi = 3.14`!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pi = 3.14\n",
"r = 2.88"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"While Python may be used as a calculator, it behaves a bit differently compared to calculator apps that phones or computers come with and that we are accustomed to.\n",
"\n",
"A major difference is that Python \"forgets\" intermediate results that are not assigned to variables. On the contrary, the calculators we work with outside of programming always keep the last result and allow us to use it as the first input for the next calculation.\n",
"\n",
"One way to keep on working with intermediate results in Python is to write the entire calculation as just *one* big expression that is composed of many sub-expressions representing the individual steps in our overall calculation.\n",
"\n",
"**Q2.1**: Given `a` and `b` like below, subtract the smaller `a` from the larger `b`, divide the difference by `9`, and raise the result to the power of `2`! Use operators that preserve the `int` type of the final result! The entire calculations *must* be placed within *one* code cell.\n",
"\n",
"Hint: You may need to group sub-expressions with parentheses `(` and `)`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"a = 42\n",
"b = 87"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The code cell below contains nothing but a single underscore `_`. In both, a Python command-line prompt and Jupyter notebooks, the variable `_` is automatically updated and always references the object to which the *last* expression executed evaluated to.\n",
"\n",
"**Q2.2**: Execute the code cell below! It should evaluate to the *same* result as the previous code cell (i.e., your answer to **Q2.1** assuming you go through this notebook in order)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"_"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2.3**: Implement the same overall calculation as in your answer to **Q2.1** in several independent steps (i.e., code cells)! Use only *one* operator per code cell!\n",
"\n",
"Hint: You should need *two* more code cells after the `b - a` one immediately below. If you *need* to use parentheses, you must be doing something wrong."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"b - a"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"_ ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"_ ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3.1**: When answering the questions above, you should have used only **expressions** in the code cells. What are expressions syntactically?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3.2**: The code cells that provide the numbers to work with contain **statements** that are *not* expressions. What are statements? How are they different from expressions?"
]
},
{
"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.12.2"
},
"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
}

View file

@ -0,0 +1,142 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Chapter 1: Elements of a Program (TL;DR)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"We end each chapter with a summary of the main points (i.e., **TL;DR** = \"too long; didn't read\"). The essence in this first chapter is that just as a sentence in a real language like English may be decomposed into its parts (e.g., subject, predicate, and objects), the same may be done with programming languages.\n",
"\n",
"- program\n",
" - **sequence** of **instructions** that specify how to perform a computation (= a \"recipe\")\n",
" - a \"black box\" that processes **inputs** and transforms them into meaningful **outputs** in a *deterministic* way\n",
" - conceptually similar to a mathematical function $f$ that maps some input $x$ to an output $y = f(x)$\n",
"\n",
"\n",
"- input (examples)\n",
" - data from a CSV file\n",
" - text entered on a command line\n",
" - data obtained from a database\n",
" - etc.\n",
"\n",
"\n",
"- output (examples)\n",
" - result of a computation (e.g., statistical summary of a sample dataset)\n",
" - a \"side effect\" (e.g., a transformation of raw input data into cleaned data)\n",
" - a physical \"behavior\" (e.g., a robot moving or a document printed)\n",
" - etc.\n",
"\n",
"\n",
"- objects\n",
" - distinct and well-contained areas/parts of the memory that hold the actual data\n",
" - the concept by which Python manages the memory for us\n",
" - can be classified into objects of the same **type** (i.e., same abstract \"structure\" but different concrete data)\n",
" - built-in objects (incl. **literals**) vs. user-defined objects (cf., [Chapter 11 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb))\n",
" - e.g., `1`, `1.0`, and `\"one\"` are three different objects of distinct types that are also literals (i.e., by the way we type them into the command line Python knows what the value and type are)\n",
"\n",
"\n",
"- variables\n",
" - storage of intermediate **state**\n",
" - are **names** referencing **objects** in **memory**\n",
" - e.g., `x = 1` creates the variable `x` that references the object `1`\n",
"\n",
"\n",
"- operators\n",
" - special built-in symbols that perform operations with objects in memory\n",
" - usually, operate with one or two objects\n",
" - e.g., addition `+`, subtraction `-`, multiplication `*`, and division `/` all take two objects, whereas the negation `-` only takes one\n",
"\n",
"\n",
"- expressions\n",
" - **combinations** of **variables** (incl. **literals**) and **operators**\n",
" - do *not* change the involved objects/state of the program\n",
" - evaluate to a **value** (i.e., the \"result\" of the expression, usually a new object)\n",
" - e.g., `x + 2` evaluates to the (new) object `3` and `1 - 1.0` to `0.0`\n",
"\n",
"\n",
"- statements\n",
" - instructions that **\"do\" something** and **have side effects** in memory\n",
" - (re-)assign names to (different) objects, *change* an existing object *in place*, or, more conceptually, *change* the state of the program\n",
" - usually, work with expressions\n",
" - e.g., the assignment statement `=` makes a name reference an object\n",
"\n",
"\n",
"- comments\n",
" - **prose** supporting a **human's understanding** of the program\n",
" - ignored by Python\n",
"\n",
"\n",
"- functions (cf., [Chapter 2 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/00_content.ipynb))\n",
" - named sequences of instructions\n",
" - the smaller parts in a larger program\n",
" - make a program more modular and thus easier to understand\n",
" - include [built-in functions <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html) like [print() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#print), [sum() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#sum), or [len() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#len)\n",
"\n",
"\n",
"- flow control (cf., [Chapter 3 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/00_content.ipynb) and [Chapter 4 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/00_content.ipynb))\n",
" - expression of **business logic** or an **algorithm**\n",
" - conditional execution of parts of a program (e.g., `if` statements)\n",
" - repetitive execution of parts of a program (e.g., `for`-loops)"
]
}
],
"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.12.2"
},
"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": "303.333px"
},
"toc_section_display": false,
"toc_window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 4
}

215
01_elements/06_review.ipynb Normal file
View file

@ -0,0 +1,215 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 1: Elements of a Program (Review Questions)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The questions below assume that you have read the [first <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb) and the [second <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/03_content.ipynb) part of Chapter 1.\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": [
"**Q1**: Elaborate on how **modulo division** might be a handy operation to know!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: What is a **dynamically typed** language? How does it differ from a **statically typed** language? What does that mean for Python?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3**: Why is it useful to start counting at $0$?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q4**: What is **operator overloading**?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5**: What are the basic **naming conventions** for variables? What happens if a name collides with one of Python's [keywords <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/reference/lexical_analysis.html#keywords)?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: Advocates of the [functional programming <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Functional_programming) paradigm suggest not to use **mutable** data types in a program. What are the advantages of that approach? What might be a downside?"
]
},
{
"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": [
"**Q7**: \"**dunder**\" refers to a group of Australian (i.e., \"down under\") geeks that work on core Python."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q8**: The **Zen of Python** talks about Indian genius programmers."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q9**: When NASA famously converted some measurements to the wrong unit and lost a Mars satellite in 1999 (cf., [source](https://www.wired.com/2010/11/1110mars-climate-observer-report/)), this is an example of a so-called **runtime error**."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q10**: [PEP 8 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://www.python.org/dev/peps/pep-0008/) suggests that developers use **8 spaces** per level of indentation."
]
},
{
"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.12.2"
},
"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
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,241 @@
{
"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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/02_functions/01_exercises.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 2: Functions & Modularization (Coding Exercises)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The exercises below assume that you have read the [first part <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/00_content.ipynb) of Chapter 2.\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": [
"## Volume of a Sphere"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q1**: The [volume of a sphere <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Sphere) is defined as $\\frac{4}{3} * \\pi * r^3$. Calculate this value for $r=10.0$ and round it to 10 digits after the comma.\n",
"\n",
"Hints:\n",
"- use an appropriate approximation for $\\pi$\n",
"- you may use the [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html) to do so if you have already looked at the [second part <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/02_content.ipynb) of Chapter 2."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import ... # you may drop this cell and use your own approximation for Pi"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: Encapsulate the logic into a function `sphere_volume()` that takes one *positional* argument `radius` and one *keyword-only* argument `digits` defaulting to `5`. The volume should be returned as a `float` object under *all* circumstances."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def sphere_volume(...):\n",
" \"\"\"Calculate the volume of a sphere.\n",
"\n",
" Args:\n",
" radius (float): radius of the sphere\n",
" digits (optional, int): number of digits\n",
" for rounding the resulting volume\n",
"\n",
" Returns:\n",
" volume (float)\n",
" \"\"\"\n",
" return ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3**: Evaluate the function with `radius = 100.0` and 1, 5, 10, 15, and 20 digits respectively."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"radius = ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sphere_volume(...)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sphere_volume(...)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sphere_volume(...)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sphere_volume(...)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sphere_volume(...)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q4**: What observation do you make?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5**: Using the [range() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#func-range) built-in, write a `for`-loop and calculate the volume of a sphere with `radius = 42.0` for all `digits` from `1` through `20`. Print out each volume on a separate line.\n",
"\n",
"Note: This is the first task where you need to use the built-in [print() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#print) function."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"radius = ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for ... in ...:\n",
" ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: What lesson do you learn about the `float` type?"
]
},
{
"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.12.2"
},
"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
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,90 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Chapter 2: Functions & Modularization (TL;DR)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"A user-defined **function** is a **named sequence** of statements that perform a computation.\n",
"\n",
"Functions provide benefits as they\n",
"\n",
"- make programs easier to comprehend and debug for humans as they give names to the smaller parts of a larger program (i.e., they **modularize** a code base), and\n",
"- eliminate redundancies by allowing **reuse of code**.\n",
"\n",
"Functions are **defined** once with the `def` statement. Then, they may be **called** many times with the call operator `()`.\n",
"\n",
"They may process **parameterized** inputs, **passed** in as **arguments**, and output a **return value**.\n",
"\n",
"Arguments may be passed in by **position** or **keyword**. Some functions may even require **keyword-only** arguments.\n",
"\n",
"**Lambda expressions** create anonymous functions.\n",
"\n",
"Functions are a special kind of **callables**. Any object that may be **called** with the call operator `()` is a callable. Built-in functions and **constructors** are other kinds of callables.\n",
"\n",
"Core Python can be extended with code from either the **standard library** or **third-party** libraries.\n",
"\n",
"Outside Jupyter notebooks, Python code is put into **modules** that are grouped in **packages**."
]
}
],
"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.12.2"
},
"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
}

View file

@ -0,0 +1,229 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 2: Functions & Modularization (Review Questions)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The questions below assume that you have read the [first <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/00_content.ipynb) and the [second <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/02_content.ipynb) part of Chapter 2.\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": [
"**Q1**: What behavior of the `def` statement makes it a **statement**? Is there a way to use an **expression** to create a function?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: One of the first confusions of experienced programmers coming from other languages to Python regards the observation that **\"everything in Python is an object\"** (cf., this [discussion](https://www.reddit.com/r/learnpython/comments/8rypx9/everything_in_python_is_an_object/)). How does this relate to **functions**?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3**: What does it mean for a variable to go out of **scope**?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q4**: How can a **global** variable be **shadowed**? Is this good or bad?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5**: Explain the concept of **forwarding** a **function call**."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: What are **keyword-only arguments** and when is it appropriate to use them?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q7**: What are **callables**? How do they relate to `function` objects?"
]
},
{
"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": [
"**Q8**: A mere function **call** is just an **expression**."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q9**: When using the `import` statement, we need to ensure that the imported attributes do *not* overwrite any already defined variables and functions."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q10:** Functions always have a name by which we can call them."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q11**: The [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html) is a collection of numerical tools often used in scientific computing, for example, advanced mathematical functions or utilities for simulation."
]
},
{
"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.12.2"
},
"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
}

View file

@ -0,0 +1,76 @@
"""This is a sample module.
It defines three functions average(), average_evens(), and average_odds().
The point is to show how we can put Python code in a .py file to be re-used
in some other place.
We should never forget to document the code as well, both on the module
level (i.e., this docstring) but also in every function it defines.
When imported, Python modules are executed top to bottom before the flow of
execution returns to wherever they were imported into.
An important convention is to prefix variables and functions that are not to
be used outside the module with a single underscore "_". This way, we can
design the code within a module in a modular fashion and only "export" what we
want.
Here, all three functions internally forward parts of their computations
to the utility functions _round_all() and _scaled_average() that contain all
the logic common to the three functions.
While this example is stylized, it shows how Python modules are often
designed.
"""
def _round_all(numbers):
"""Internal utility function to round all numbers in a list."""
return [round(n) for n in numbers]
def _scaled_average(numbers, scalar):
"""Internal utility function to calculate scaled averages."""
average = sum(numbers) / len(numbers)
return scalar * average
def average(numbers, *, scalar=1):
"""Calculate the average of all numbers in a list.
Args:
numbers (list of int's/float's): numbers to be averaged;
if non-whole numbers are provided, they are rounded
scalar (float, optional): multiplies the average; defaults to 1
Returns:
scaled_average (float)
"""
return _scaled_average(_round_all(numbers), scalar)
def average_evens(numbers, *, scalar=1):
"""Calculate the average of all even numbers in a list.
Args:
numbers (list of int's/float's): numbers to be averaged;
if non-whole numbers are provided, they are rounded
scalar (float, optional): multiplies the average; defaults to 1
Returns:
scaled_average (float)
"""
return _scaled_average([n for n in _round_all(numbers) if n % 2 == 0], scalar)
def average_odds(numbers, *, scalar=1):
"""Calculate the average of all odd numbers in a list.
Args:
numbers (list of int's/float's): numbers to be averaged;
if non-whole numbers are provided, they are rounded
scalar (float, optional): multiplies the average; defaults to 1
Returns:
scaled_average (float)
"""
return _scaled_average([n for n in _round_all(numbers) if n % 2 != 0], scalar)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,205 @@
{
"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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/03_conditionals/01_exercises.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 3: Conditionals & Exceptions (Coding Exercises)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The exercises below assume that you have read [Chapter 3 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/00_content.ipynb).\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": [
"## Discounting Customer Orders"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q1**: Write a function `discounted_price()` that takes the positional arguments `unit_price` (of type `float`) and `quantity` (of type `int`) and implements a discount scheme for a line item in a customer order as follows:\n",
"\n",
"- if the unit price is over 100 dollars, grant 10% relative discount\n",
"- if a customer orders more than 10 items, one in every five items is for free\n",
"\n",
"Only one of the two discounts is granted, whichever is better for the customer.\n",
"\n",
"The function should then return the overall price for the line item. Do not forget to round appropriately."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def discounted_price(unit_price, quantity):\n",
" \"\"\"Calculate the price of a line item in an order.\n",
"\n",
" Args:\n",
" unit_price (float): price of one ordered item\n",
" quantity (int): number of items ordered\n",
"\n",
" Returns:\n",
" line_item_price (float)\n",
" \"\"\"\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
"\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
"\n",
" return ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: Calculate the final price for the following line items of an order:\n",
"- $7$ smartphones @ $99.00$ USD\n",
"- $3$ workstations @ $999.00$ USD\n",
"- $19$ GPUs @ $879.95$ USD\n",
"- $14$ Raspberry Pis @ $35.00$ USD"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"discounted_price(...)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"discounted_price(...)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"discounted_price(...)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"discounted_price(...)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3**: Calculate the last two line items with order quantities of $20$ and $15$. What do you observe?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"discounted_price(...)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"discounted_price(...)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q4**: Looking at the `if`-`else`-logic in the function, why do you think the four example line items in **Q2** were chosen as they were?"
]
},
{
"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.12.2"
},
"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
}

View file

@ -0,0 +1,140 @@
{
"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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/03_conditionals/02_exercises.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 3: Conditionals & Exceptions (Coding Exercises)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The exercises below assume that you have read [Chapter 3 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/00_content.ipynb).\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": [
"## Fizz Buzz"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The kids game [Fizz Buzz <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Fizz_buzz) is said to be often used in job interviews for entry-level positions. However, opinions vary as to how good of a test it is (cf., [source <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_hn.png\">](https://news.ycombinator.com/item?id=16446774)).\n",
"\n",
"In its simplest form, a group of people starts counting upwards in an alternating fashion. Whenever a number is divisible by $3$, the person must say \"Fizz\" instead of the number. The same holds for numbers divisible by $5$ when the person must say \"Buzz.\" If a number is divisible by both numbers, one must say \"FizzBuzz.\" Probably, this game would also make a good drinking game with the \"right\" beverages."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q1**: First, create a list `numbers` with the numbers from 1 through 100. You could type all numbers manually, but there is, of course, a smarter way. The built-in [range() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#func-range) may be useful here. Read how it works in the documentation. To make the output of [range() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#func-range) a `list` object, you have to wrap it with the [list() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#func-list) built-in (i.e., `list(range(...))`)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"numbers = ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: Loop over the `numbers` list and *replace* numbers for which one of the two (or both) conditions apply with text strings `\"Fizz\"`, `\"Buzz\"`, or `\"FizzBuzz\"` using the indexing operator `[]` and the assignment statement `=`.\n",
"\n",
"In [Chapter 1 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/03_content.ipynb#Who-am-I?-And-how-many?), we saw that Python starts indexing with `0` as the first element. Keep that in mind.\n",
"\n",
"So in each iteration of the `for`-loop, you have to determine an `index` variable as well as check the actual `number` for its divisors.\n",
"\n",
"Hint: the order of the conditions is important!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for number in numbers:\n",
" ...\n",
"\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3**: Create a loop that prints out either the number or any of the Fizz Buzz substitutes in `numbers`! Do it in such a way that the output is concise!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for number in numbers:\n",
" print(...)"
]
}
],
"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.12.2"
},
"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
}

View file

@ -0,0 +1,76 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Chapter 3: Conditionals & Exceptions (TL;DR)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"- **boolean expressions** evaluate to either `True` or `False`\n",
"- **relational operators** (e.g., `==` or `!=`) compare operands according to \"human\" interpretations\n",
"- **logical operators** (e.g., `and` )combine boolean sub-expressions to more \"complex\" expressions\n",
"- the **conditional statement** (i.e., the `if` statement) allows **controlling** the **flow of execution** depending on some **conditions**\n",
"- a **conditional expression** is a short form of a conditional statement\n",
"- **exception handling** is also a common way of **controlling** the **flow of execution**, in particular, if we have to be prepared for bad input data"
]
}
],
"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.12.2"
},
"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
}

View file

@ -0,0 +1,201 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 3: Conditionals & Exceptions (Review Questions)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The questions below assume that you have read [Chapter 3 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/00_content.ipynb).\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": [
"**Q1**: What is the **singleton** design pattern? How many objects does the expression `[True, False, True, False]` generate in memory?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: What do we mean when we talk about **truthy** and **falsy** expressions?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3**: Describe in your own words the concept of **short-circuiting**! What does it imply for an individual sub-expression in a boolean expression?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q4**: Explain how the conceptual difference between a **statement** and an **expression** relates to the difference between a **conditional statement** and a **conditional expression**."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5**: Why is the use of **temporary variables** encouraged in Python?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: What does the `finally`-clause enforce in this code snippet? How can a `try` statement be useful *without* an `except`-clause?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```python\n",
"try:\n",
" print(\"Make a request to a service on the internet\")\n",
"finally:\n",
" print(\"This could be clean-up code\")\n",
"```"
]
},
{
"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": [
"**Q7**: The objects `True`, `False`, and `None` represent the idea of *yes*, *no*, and *maybe* answers in a natural language.\n",
"\n",
"Hint: you also respond with a code cell."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q8**: The `try` statement is useful for handling **syntax** errors."
]
},
{
"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.12.2"
},
"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
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,610 @@
{
"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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/04_iteration/01_exercises.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 4: Recursion & Looping (Coding Exercises)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The exercises below assume that you have read the [first part <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/00_content.ipynb) of Chapter 4.\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": [
"## Towers of Hanoi"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A popular example of a problem that is solved by recursion art the **[Towers of Hanoi <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Tower_of_Hanoi)**.\n",
"\n",
"In its basic version, a tower consisting of, for example, four disks with increasing radii, is placed on the left-most of **three** adjacent spots. In the following, we refer to the number of disks as $n$, so here $n = 4$.\n",
"\n",
"The task is to move the entire tower to the right-most spot whereby **two rules** must be obeyed:\n",
"\n",
"1. Disks can only be moved individually, and\n",
"2. a disk with a larger radius must *never* be placed on a disk with a smaller one.\n",
"\n",
"Although the **[Towers of Hanoi <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Tower_of_Hanoi)** are a **classic** example, introduced by the mathematician [Édouard Lucas <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/%C3%89douard_Lucas) already in 1883, it is still **actively** researched as this scholarly [article](https://www.worldscientific.com/doi/abs/10.1142/S1793830919300017?journalCode=dmaa&) published in January 2019 shows.\n",
"\n",
"Despite being so easy to formulate, the game is quite hard to solve.\n",
"\n",
"Below is an interactive illustration of the solution with the minimal number of moves for $n = 4$."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<img src=\"static/towers_of_hanoi.gif\" width=\"60%\" align=\"left\">"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Watch the following video by [MIT](https://www.mit.edu/)'s professor [Richard Larson](https://idss.mit.edu/staff/richard-larson/) for a comprehensive introduction.\n",
"\n",
"The [MIT Blossoms Initiative](https://blossoms.mit.edu/) is primarily aimed at high school students and does not have any prerequisites.\n",
"\n",
"The video consists of three segments, the last of which is *not* necessary to have watched to solve the tasks below. So, watch the video until 37:55."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from IPython.display import YouTubeVideo\n",
"YouTubeVideo(\"UuIneNBbscc\", width=\"60%\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Video Review Questions"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q1**: Explain for the $n = 3$ case why it can be solved as a **recursion**!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: How does the number of minimal moves needed to solve a problem with three spots and $n$ disks grow as a function of $n$? How does this relate to the answer to **Q1**?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3**: The **[Towers of Hanoi <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Tower_of_Hanoi)** problem is of **exponential growth**. What does that mean? What does that imply for large $n$?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q4**: The video introduces the recursive relationship $Sol(4, 1, 3) = Sol(3, 1, 2) ~ \\bigoplus ~ Sol(1, 1, 3) ~ \\bigoplus ~ Sol(3, 2, 3)$. The $\\bigoplus$ is to be interpreted as some sort of \"plus\" operation. How does this \"plus\" operation work? How does this way of expressing the problem relate to the answer to **Q1**?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Naive Translation to Python"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As most likely the first couple of tries will result in *semantic* errors, it is advisable to have some sort of **visualization tool** for the program's output: For example, an online version of the game can be found [here](https://www.mathsisfun.com/games/towerofhanoi.html)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's first **generalize** the mathematical relationship from above and then introduce the variable names used in our `sol()` implementation below.\n",
"\n",
"Unsurprisingly, the recursive relationship in the video may be generalized into:\n",
"\n",
"$Sol(n, o, d) = Sol(n-1, o, i) ~ \\bigoplus ~ Sol(1, o, d) ~ \\bigoplus ~ Sol(n-1, i, d)$\n",
"\n",
"$Sol(\\cdot)$ takes three \"arguments\" $n$, $o$, and $d$ and is defined with *three* references to itself that take modified versions of $n$, $o$, and $d$ in different orders. The middle reference, Sol(1, o, d), constitutes the \"end\" of the recursive definition: It is the problem of solving Towers of Hanoi for a \"tower\" of only one disk.\n",
"\n",
"While the first \"argument\" of $Sol(\\cdot)$ is a number that we refer to as `n_disks` below, the second and third \"arguments\" are merely **labels** for the spots, and we refer to the **roles** they take in a given problem as `origin` and `destination` below. Instead of labeling individual spots with the numbers `1`, `2`, and `3` as in the video, we may also call them `\"left\"`, `\"center\"`, and `\"right\"`. Both ways are equally correct! So, only the first \"argument\" of $Sol(\\cdot)$ is really a number!\n",
"\n",
"As an example, the notation $Sol(4, 1, 3)$ from above can then be \"translated\" into Python as either the function call `sol(4, 1, 3)` or `sol(4, \"left\", \"right\")`. This describes the problem of moving a tower consisting of `n_disks=4` disks from either the `origin=1` spot to the `destination=3` spot or from the `origin=\"left\"` spot to the `destination=\"right\"` spot.\n",
"\n",
"To adhere to the rules, an `intermediate` spot $i$ is needed. In `sol()` below, this is a temporary variable within a function call and *not* a parameter of the function itself."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In summary, to move a tower consisting of `n_disks` (= $n$) disks from an `origin` (= $o$) to a `destination` (= $d$), three steps must be executed:\n",
"\n",
"1. Move the tower's topmost `n_disks - 1` (= $n - 1$) disks from the `origin` (= $o$) to an `intermediate` (= $i$) spot (= **Sub-Problem 1**),\n",
"2. move the remaining and largest disk from the `origin` (= $o$) to the `destination` (= $d$), and\n",
"3. move the `n_disks - 1` (= $n - 1$) disks from the `intermediate` (= $i$) spot to the `destination` (= $d$) spot (= **Sub-Problem 2**).\n",
"\n",
"The two sub-problems themselves are solved via the same *recursive* logic."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Write your answers to **Q5** to **Q7** into the skeleton of `sol()` below.\n",
"\n",
"`sol()` takes three arguments `n_disks`, `origin`, and `destination` that mirror $n$, $o$, and $d$ above.\n",
"\n",
"For now, assume that all arguments to `sol()` are `int` objects! We generalize this into real labels further below in the `hanoi()` function.\n",
"\n",
"Once completed, `sol()` should *print* out all the moves in the correct order. For example, *print* `\"1 -> 3\"` to mean \"Move the top-most `n_disks - 1` disks from spot `1` to spot `3`.\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def sol(n_disks, origin, destination):\n",
" \"\"\"A naive implementation of Towers of Hanoi.\n",
"\n",
" This function prints out the moves to solve a Towers of Hanoi problem.\n",
"\n",
" Args:\n",
" n_disks (int): number of disks in the tower\n",
" origin (int): spot of the tower at the start; 1, 2, or 3\n",
" destination (int): spot of the tower at the end; 1, 2, or 3\n",
" \"\"\"\n",
" # answer to Q5\n",
" ...\n",
" ...\n",
"\n",
" # answer to Q6\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
"\n",
" # answer to Q7\n",
" ...\n",
" ...\n",
" ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5**: What is the `n_disks` argument when the function reaches its **base case**? Check for the base case with a simple `if` statement and return from the function using the **early exit** pattern!\n",
"\n",
"Hint: The base case in the Python implementation may be slightly different than the one shown in the generalized mathematical relationship above!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: If not in the base case, `sol()` determines the `intermediate` spot given concrete `origin` and `destination` arguments. For example, if called with `origin=1` and `destination=2`, `intermediate` must be `3`.\n",
"\n",
"Add *one* compound `if` statement to `sol()` that has a branch for *every* possible `origin`-`destination`-pair that assigns the correct temporary spot to a variable `intermediate`.\n",
"\n",
"Hint: How many 2-tuples of 3 elements can there be if the order matters?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q7**: `sol()` calls itself *two* more times with the correct 2-tuples chosen from the three available spots `origin`, `intermediate`, and `destination`.\n",
"\n",
"*In between* the two recursive function calls, use [print() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#print) to print out from where to where the \"remaining and largest\" disk has to be moved!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q8**: Execute the code cells below and confirm that the moves are correct!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sol(1, 1, 3)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sol(2, 1, 3)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sol(3, 1, 3)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sol(4, 1, 3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Pythonic Refactoring"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The previous `sol()` implementation does the job, but the conditional statement is unnecessarily tedious. \n",
"\n",
"Let's create a concise `hanoi()` function that, in addition to a positional `n_disks` argument, takes three keyword-only arguments `origin`, `intermediate`, and `destination` with default values `\"left\"`, `\"center\"`, and `\"right\"`.\n",
"\n",
"Write your answers to **Q9** and **Q10** into the subsequent code cell and finalize `hanoi()`!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def hanoi(n_disks, *, origin=\"left\", intermediate=\"center\", destination=\"right\"):\n",
" \"\"\"A Pythonic implementation of Towers of Hanoi.\n",
"\n",
" This function prints out the moves to solve a Towers of Hanoi problem.\n",
"\n",
" Args:\n",
" n_disks (int): number of disks in the tower\n",
" origin (str, optional): label for the spot of the tower at the start\n",
" intermediate (str, optional): label for the intermediate spot\n",
" destination (str, optional): label for the spot of the tower at the end\n",
" \"\"\"\n",
" # answer to Q9\n",
" ...\n",
" ...\n",
"\n",
" # answer to Q10\n",
" ...\n",
" ...\n",
" ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q9**: Copy the base case from `sol()`!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q10**: Instead of conditional logic, `hanoi()` calls itself *two* times with the *three* arguments `origin`, `intermediate`, and `destination` passed on in a *different* order.\n",
"\n",
"Figure out how the arguments are passed on in the two recursive `hanoi()` calls and finish `hanoi()`.\n",
"\n",
"Hint: Do not forget to use [print() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#print) to print out the moves!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q11**: Execute the code cells below and confirm that the moves are correct!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"hanoi(1)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"hanoi(2)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"hanoi(3)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"hanoi(4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We could, of course, also use *numeric* labels for the three steps like so."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"hanoi(3, origin=1, intermediate=2, destination=3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Passing a Value \"down\" the Recursion Tree"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The above `hanoi()` prints the optimal solution's moves in the correct order but fails to label each move with an order number. This is built in the `hanoi_ordered()` function below by passing on a \"private\" `_offset` argument \"down\" the recursion tree. The leading underscore `_` in the parameter name indicates that it is *not* to be used by the caller of the function. That is also why the parameter is *not* mentioned in the docstring.\n",
"\n",
"Write your answers to **Q12** and **Q13** into the subsequent code cell and finalize `hanoi_ordered()`! As the logic gets a bit \"involved,\" `hanoi_ordered()` below is almost finished."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def hanoi_ordered(n_disks, *, origin=\"left\", intermediate=\"center\", destination=\"right\", _offset=None):\n",
" \"\"\"A Pythonic implementation of Towers of Hanoi.\n",
"\n",
" This function prints out the moves to solve a Towers of Hanoi problem.\n",
" Each move is labeled with an order number.\n",
"\n",
" Args:\n",
" n_disks (int): number of disks in the tower\n",
" origin (str, optional): label for the spot of the tower at the start\n",
" intermediate (str, optional): label for the intermediate spot\n",
" destination (str, optional): label for the spot of the tower at the end\n",
" \"\"\"\n",
" # answer to Q12\n",
" ...\n",
" ...\n",
"\n",
" total = (2 ** n_disks - 1)\n",
" half = (2 ** (n_disks - 1) - 1)\n",
" count = total - half\n",
"\n",
" if _offset is not None:\n",
" count += _offset\n",
"\n",
" # answer to Q18\n",
" hanoi_ordered(..., _offset=_offset)\n",
" ...\n",
" hanoi_ordered(..., _offset=count)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q12**: Copy the base case from the original `hanoi()`!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q13**: Complete the two recursive function calls with the same arguments as in `hanoi()`! Do *not* change the already filled in `offset` arguments!\n",
"\n",
"Then, adjust the use of [print() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#print) from above to print out the moves with their order number!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q14**: Execute the code cells below and confirm that the order numbers are correct!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"hanoi_ordered(1)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"hanoi_ordered(2)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"hanoi_ordered(3)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"hanoi_ordered(4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Lastly, it is to be mentioned that for problem instances with a small `n_disks` argument, it is easier to collect all the moves first in a `list` object and then add the order number with the [enumerate() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#enumerate) built-in."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Open Question"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q15**: Conducting your own research on the internet, what can you say about generalizing the **[Towers of Hanoi <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Tower_of_Hanoi)** problem to a setting with *more than three* landing spots?"
]
},
{
"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.12.2"
},
"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
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,991 @@
{
"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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/04_iteration/03_content.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Chapter 4: Recursion & Looping (continued)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"While what we learned about the `for` and `while` statements in the [second part <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/02_content.ipynb) of this chapter suffices to translate any iterative algorithm into code, both come with some syntactic sugar to make life easier for the developer. This last part of the chapter shows how we can further customize the looping logic and introduces as \"trick\" for situations where we cannot come up with a stopping criterion in a `while`-loop."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Stopping Loops prematurely"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"This section introduces additional syntax to customize `for` and `while` statements in our code even further. They are mostly syntactic sugar in that they do not change how a program runs but make its code more readable. We illustrate them for the `for` statement only. However, everything presented in this section also works for the `while` statement."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Example: Is the square of a number in `[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]` greater than `100`?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Let's say we have a list of `numbers` and want to check if the square of at least one of its elements is greater than `100`. So, conceptually, we are asking the question if a list of numbers as a whole satisfies a certain condition."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"A first naive implementation could look like this: We loop over *every* element in `numbers` and set an **indicator variable** `is_above`, initialized as `False`, to `True` once we encounter an element satisfying the condition.\n",
"\n",
"This implementation is *inefficient* as even if the *first* element in `numbers` has a square greater than `100`, we loop until the last element: This could take a long time for a big list.\n",
"\n",
"Moreover, we must initialize `is_above` *before* the `for`-loop and write an `if`-`else`-logic *after* it to check for the result. The actual business logic is *not* conveyed in a clear way."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"7 11 8 5 3 12 2 6 9 10 1 4 => at least one number satisfies the condition\n"
]
}
],
"source": [
"is_above = False\n",
"\n",
"for number in numbers:\n",
" print(number, end=\" \") # added for didactical purposes\n",
" if number ** 2 > 100:\n",
" is_above = True\n",
"\n",
"if is_above:\n",
" print(\"=> at least one number satisfies the condition\")\n",
"else:\n",
" print(\"=> no number satisfies the condition\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### The `break` Statement"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Python provides the `break` statement (cf., [reference <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/reference/simple_stmts.html#the-break-statement)) that lets us stop a loop prematurely at any iteration. It is yet another means of controlling the flow of execution, and we say that we \"break out of a loop.\""
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"7 11 => at least one number satisfies the condition\n"
]
}
],
"source": [
"is_above = False\n",
"\n",
"for number in numbers:\n",
" print(number, end=\" \") # added for didactical purposes\n",
" if number ** 2 > 100:\n",
" is_above = True\n",
" break\n",
"\n",
"if is_above:\n",
" print(\"=> at least one number satisfies the condition\")\n",
"else:\n",
" print(\"=> no number satisfies the condition\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"This is a computational improvement. However, the code still consists of *three* sections: Some initialization *before* the `for`-loop, the loop itself, and some finalizing logic. We prefer to convey the program's idea in *one* compound statement instead."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### The `else`-clause"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"To express the logic in a prettier way, we add an `else`-clause at the end of the `for`-loop (cf., [reference <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/reference/compound_stmts.html#the-for-statement)). The `else`-clause is executed *only if* the `for`-loop is *not* stopped with a `break` statement *prematurely* (i.e., *before* reaching the *last* iteration in the loop). The word \"else\" implies a somewhat unintuitive meaning and may have better been named a `then`-clause. In most use cases, however, the `else`-clause logically goes together with some `if` statement in the loop's body.\n",
"\n",
"Overall, the code's expressive power increases. Not many programming languages support an optional `else`-branching for the `for` and `while` statements, which turns out to be very useful in practice."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"7 11 => at least one number satisfies the condition\n"
]
}
],
"source": [
"for number in numbers:\n",
" print(number, end=\" \") # added for didactical purposes\n",
" if number ** 2 > 100:\n",
" is_above = True\n",
" break\n",
"else:\n",
" is_above = False\n",
"\n",
"if is_above:\n",
" print(\"=> at least one number satisfies the condition\")\n",
"else:\n",
" print(\"=> no number satisfies the condition\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Lastly, we incorporate the finalizing `if`-`else` logic into the `for`-loop, avoiding the `is_above` variable altogether."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"7 11 => at least one number satisfies the condition\n"
]
}
],
"source": [
"for number in numbers:\n",
" print(number, end=\" \") # added for didactical purposes\n",
" if number ** 2 > 100:\n",
" print(\"=> at least one number satisfies the condition\")\n",
" break\n",
"else:\n",
" print(\"=> no number satisfies the condition\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Of course, if we choose the number an element's square has to pass to be larger, for example, to `200`, we have to loop over all `numbers`. There is *no way* to optimize this **[linear search <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Linear_search)** further."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"7 11 8 5 3 12 2 6 9 10 1 4 => no number satisfies the condition\n"
]
}
],
"source": [
"for number in numbers:\n",
" print(number, end=\" \") # added for didactical purposes\n",
" if number ** 2 > 200:\n",
" print(\"=> at least one number satisfies the condition\")\n",
" break\n",
"else:\n",
" print(\"=> no number satisfies the condition\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## A first Glance at the **Map-Filter-Reduce** Paradigm"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Often, we process some iterable with numeric data, for example, a list of `numbers` as in this book's introductory example in [Chapter 1 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List) or, more realistically, data from a CSV file with many rows and columns.\n",
"\n",
"Processing numeric data usually comes down to operations that may be grouped into one of the following three categories:\n",
"\n",
"- **mapping**: transform a number according to some functional relationship $y = f(x)$\n",
"- **filtering**: throw away individual numbers (e.g., statistical outliers in a sample)\n",
"- **reducing**: collect individual numbers into summary statistics\n",
"\n",
"We study this **map-filter-reduce** paradigm extensively in [Chapter 8 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb) after introducing more advanced data types that are needed to work with \"big\" data.\n",
"\n",
"Here, we focus on *filtering out* some numbers in a `for`-loop."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Example: A simple Filter"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Calculate the sum of all even numbers in `[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]` after squaring them and adding `1` to the squares:\n",
"\n",
"- \"*all*\" => **loop** over an iterable\n",
"- \"*even*\" => **filter** out the odd numbers\n",
"- \"*square and add $1$*\" => apply the **map** $y = f(x) = x^2 + 1$\n",
"- \"*sum*\" => **reduce** the remaining and mapped numbers to their sum"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"8 -> 65 12 -> 145 2 -> 5 6 -> 37 10 -> 101 4 -> 17 "
]
},
{
"data": {
"text/plain": [
"370"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"total = 0\n",
"\n",
"for number in numbers:\n",
" if number % 2 == 0: # only keep even numbers\n",
" square = (number ** 2) + 1\n",
" print(number, \"->\", square, end=\" \") # added for didactical purposes\n",
" total += square\n",
"\n",
"total"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"The above code is easy to read as it involves only two levels of indentation.\n",
"\n",
"In general, code gets harder to comprehend the more **horizontal space** it occupies. It is commonly considered good practice to grow a program **vertically** rather than horizontally. Code compliant with [PEP 8 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://www.python.org/dev/peps/pep-0008/#maximum-line-length) requires us to use *at most* 79 characters in a line!\n",
"\n",
"Consider the next example, whose implementation in code already starts to look unbalanced."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Example: Several Filters"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"Calculate the sum of every third and even number in `[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]` after squaring them and adding `1` to the squares:\n",
"\n",
"- \"*every*\" => **loop** over an iterable\n",
"- \"*third*\" => **filter** out all numbers except every third\n",
"- \"*even*\" => **filter** out the odd numbers\n",
"- \"*square and add $1$*\" => apply the **map** $y = f(x) = x^2 + 1$\n",
"- \"*sum*\" => **reduce** the remaining and mapped numbers to their sum"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"8 -> 65 12 -> 145 4 -> 17 "
]
},
{
"data": {
"text/plain": [
"227"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"total = 0\n",
"\n",
"for i, number in enumerate(numbers, start=1):\n",
" if i % 3 == 0: # only keep every third number\n",
" if number % 2 == 0: # only keep even numbers\n",
" square = (number ** 2) + 1\n",
" print(number, \"->\", square, end=\" \") # added for didactical purposes \n",
" total += square\n",
"\n",
"total"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"With already three levels of indentation, less horizontal space is available for the actual code block. Of course, one could flatten the two `if` statements with the logical `and` operator, as shown in [Chapter 3 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/00_content.ipynb#The-if-Statement). Then, however, we trade off horizontal space against a more \"complex\" `if` logic, and this is *not* a real improvement."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"### The `continue` Statement"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"A Pythonista would instead make use of the `continue` statement (cf., [reference <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/reference/simple_stmts.html#the-continue-statement)) that causes a loop to jump into the next iteration skipping the rest of the code block.\n",
"\n",
"The revised code fragment below occupies more vertical space and less horizontal space: A *good* trade-off.\n",
"\n",
"One caveat is that we need to negate the conditions in the `if` statements. Conceptually, we are now filtering \"out\" and not \"in.\""
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"8 -> 65 12 -> 145 4 -> 17 "
]
},
{
"data": {
"text/plain": [
"227"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"total = 0\n",
"\n",
"for i, number in enumerate(numbers, start=1):\n",
" if i % 3 != 0: # only keep every third number\n",
" continue\n",
" elif number % 2 != 0: # only keep even numbers\n",
" continue\n",
"\n",
" square = (number ** 2) + 1\n",
" print(number, \"->\", square, end=\" \") # added for didactical purposes \n",
" total += square\n",
"\n",
"total"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"This is yet another illustration of why programming is an art. The two preceding code cells do the *same* with *identical* time complexity. However, the latter is arguably easier to read for a human, even more so when the business logic grows beyond two filters."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Indefinite Loops"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Sometimes we find ourselves in situations where we *cannot* know ahead of time how often or until which point in time a code block is to be executed."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Example: Guessing a Coin Toss"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Let's consider a game where we randomly choose a variable to be either \"Heads\" or \"Tails\" and the user of our program has to guess it.\n",
"\n",
"Python provides the built-in [input() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#input) function that prints a message to the user, called the **prompt**, and reads in what was typed in response as a `str` object. We use it to process a user's \"unreliable\" input to our program (i.e., a user might type in some invalid response). Further, we use the [random() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/random.html#random.random) function in the [random <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/random.html) module to model the coin toss.\n",
"\n",
"A popular pattern to approach such **indefinite loops** is to go with a `while True` statement, which on its own would cause Python to enter into an infinite loop. Then, once a particular event occurs, we `break` out of the loop.\n",
"\n",
"Let's look at a first and naive implementation."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"import random"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"random.seed(42)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"code_folding": [],
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"Guess if the coin comes up as heads or tails: heads\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Ooops, it was tails\n"
]
},
{
"name": "stdin",
"output_type": "stream",
"text": [
"Guess if the coin comes up as heads or tails: heads\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Yes, it was heads\n"
]
}
],
"source": [
"while True:\n",
" guess = input(\"Guess if the coin comes up as heads or tails: \")\n",
"\n",
" if random.random() < 0.5:\n",
" if guess == \"heads\":\n",
" print(\"Yes, it was heads\")\n",
" break\n",
" else:\n",
" print(\"Ooops, it was heads\")\n",
" else:\n",
" if guess == \"tails\":\n",
" print(\"Yes, it was tails\")\n",
" break\n",
" else:\n",
" print(\"Ooops, it was tails\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"This version exhibits two *severe* issues where we should improve on:\n",
"\n",
"1. If a user enters *anything* other than `\"heads\"` or `\"tails\"`, for example, `\"Heads\"` or `\"Tails\"`, the program keeps running *without* the user knowing about the mistake!\n",
"2. The code *intermingles* the coin tossing with the processing of the user's input: Mixing *unrelated* business logic in the *same* code block makes a program harder to read and, more importantly, maintain in the long run."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### Example: Guessing a Coin Toss (revisited)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Let's refactor the code and make it *modular*.\n",
"\n",
"First, we divide the business logic into two functions `get_guess()` and `toss_coin()` that are controlled from within a `while`-loop.\n",
"\n",
"`get_guess()` not only reads in the user's input but also implements a simple input validation pattern in that the [.strip() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/stdtypes.html?highlight=__contains__#str.strip) and [.lower() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/stdtypes.html?highlight=__contains__#str.lower) methods remove preceding and trailing whitespace and lower case the input ensuring that the user may spell the input in any possible way (e.g., all upper or lower case). Also, `get_guess()` checks if the user entered one of the two valid options. If so, it returns either `\"heads\"` or `\"tails\"`; if not, it returns `None`."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"def get_guess():\n",
" \"\"\"Process the user's input.\n",
" \n",
" Returns:\n",
" guess (str / NoneType): either \"heads\" or \"tails\"\n",
" if the input can be parsed and None otherwise\n",
" \"\"\"\n",
" guess = input(\"Guess if the coin comes up as heads or tails: \")\n",
" # handle frequent cases of \"misspelled\" user input\n",
" guess = guess.strip().lower()\n",
"\n",
" if guess in [\"heads\", \"tails\"]:\n",
" return guess\n",
" return None"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"`toss_coin()` models a fair coin toss when called with default arguments."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"def toss_coin(p_heads=0.5):\n",
" \"\"\"Simulate the tossing of a coin.\n",
"\n",
" Args:\n",
" p_heads (optional, float): probability that the coin comes up \"heads\";\n",
" defaults to 0.5 resembling a fair coin\n",
"\n",
" Returns:\n",
" side_on_top (str): \"heads\" or \"tails\"\n",
" \"\"\"\n",
" if random.random() < p_heads:\n",
" return \"heads\"\n",
" return \"tails\""
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Second, we rewrite the `if`-`else`-logic to handle the case where `get_guess()` returns `None` explicitly: Whenever the user enters something invalid, a warning is shown, and another try is granted. We use the `is` operator and not the `==` operator as `None` is a singleton object.\n",
"\n",
"The `while`-loop takes on the role of **glue code** that manages how other parts of the program interact with each other."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"random.seed(42)"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdin",
"output_type": "stream",
"text": [
"Guess if the coin comes up as heads or tails: invalid\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Make sure to enter your guess correctly!\n"
]
},
{
"name": "stdin",
"output_type": "stream",
"text": [
"Guess if the coin comes up as heads or tails: Heads\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Yes, it was heads\n"
]
}
],
"source": [
"while True:\n",
" guess = get_guess()\n",
" result = toss_coin()\n",
"\n",
" if guess is None:\n",
" print(\"Make sure to enter your guess correctly!\")\n",
" elif guess == result:\n",
" print(\"Yes, it was\", result)\n",
" break\n",
" else:\n",
" print(\"Ooops, it was\", result)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Now, the program's business logic is expressed in a clearer way. More importantly, we can now change it more easily. For example, we could make the `toss_coin()` function base the tossing on a probability distribution other than the uniform (i.e., replace the [random.random() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/random.html#random.random) function with another one). In general, modular architecture leads to improved software maintenance."
]
}
],
"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.12.2"
},
"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
}

View file

@ -0,0 +1,403 @@
{
"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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/04_iteration/04_exercises.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 4: Recursion & Looping (Coding Exercises)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The exercises below assume that you have read the [third part <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/03_content.ipynb) of Chapter 4.\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": [
"## Throwing Dice"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this exercise, you will model the throwing of dice within the context of a guessing game similar to the one shown in the \"*Example: Guessing a Coin Toss*\" section in [Chapter 4 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/03_content.ipynb#Example:-Guessing-a-Coin-Toss).\n",
"\n",
"As the game involves randomness, we import the [random <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/random.html) module from the [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html). To follow best practices, we set the random seed as well."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import random"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"random.seed(42)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A die has six sides that we labeled with integers `1` to `6` in this exercise. For a fair die, the probability for each side is the same.\n",
"\n",
"**Q1**: Model a `fair_die` as a `list` object!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fair_die = ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: What function from the [random <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/random.html) module that we have seen already is useful for modeling a single throw of the `fair_die`? Write a simple expression (i.e., one function call) that draws one of the equally likely sides! Execute the cell a couple of times to \"see\" the probability distribution!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's check if the `fair_die` is indeed fair. To do so, we create a little numerical experiment and throw the `fair_die` `100000` times. We track the six different outcomes in a `list` object called `throws` that we initialize with all `0`s for each outcome.\n",
"\n",
"**Q3**: Complete the `for`-loop below such that it runs `100000` times! In the body, use your answer to **Q2** to simulate a single throw of the `fair_die` and update the corresponding count in `throws`!\n",
"\n",
"Hints: You need to use the indexing operator `[]` and calculate an `index` in each iteration of the loop. Do do not actually need the target variable provided by the `for`-loop and may want to indicate that with an underscore `_`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"throws = [0, 0, 0, 0, 0, 0]\n",
"\n",
"for ... in ...:\n",
" ...\n",
" ...\n",
" ...\n",
"\n",
"throws"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`throws` contains the simulation results as absolute counts.\n",
"\n",
"**Q4**: Complete the `for`-loop below to convert the counts in `throws` to relative frequencies stored in a `list` called `frequencies`! Round the frequencies to three decimals with the built-in [round() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#round) function!\n",
"\n",
"Hints: Initialize `frequencies` just as `throws` above. How many iterations does the `for`-loop have? `6` or `100000`? You may want to obtain an `index` variable with the [enumerate() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#enumerate) built-in."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"frequencies = [0, 0, 0, 0, 0, 0]\n",
"\n",
"for ... in ...:\n",
" ...\n",
"\n",
"frequencies"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5**: How could we adapt the `list` object used above to model an `unfair_die` where `1` is as likely as `2`, `2` is twice as likely as `3`, and `3` is twice as likely as `4`, `5`, or `6`, who are all equally likely?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"unfair_die = ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: Copy your solution to **Q2** for the `unfair_die`! Execute the cell a couple of times to \"see\" the probability distribution!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q7**: Copy and adapt your solutions to **Q3** and **Q4** to calculate the `frequencies` for the `unfair_die`!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"throws = [0, 0, 0, 0, 0, 0]\n",
"frequencies = [0, 0, 0, 0, 0, 0]\n",
"\n",
"for ... in ...:\n",
" ...\n",
" ...\n",
" ...\n",
"\n",
"for ... in ...:\n",
" ...\n",
"\n",
"frequencies"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q8**: The built-in [input() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#input) allows us to ask the user to enter a `guess`. What is the data type of the object returned by [input() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#input)? Assume the user enters the `guess` as a number (i.e., \"1\", \"2\", ...) and not as a text (e.g., \"one\")."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"guess = input(\"Guess the side of the die: \")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"guess"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q9**: Use a built-in constructor to cast `guess` as an `int` object!\n",
"\n",
"Hint: Simply wrap `guess` or `input(\"Guess the side of the die: \")` with the constructor you choose."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q10**: What type of error is raised if `guess` cannot be cast as an `int` object?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q11**: Write a `try` statement that catches the type of error (i.e., your answer to **Q10**) raised if the user's input cannot be cast as an `int` object! Print out some nice error message notifying the user of the bad input!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"try:\n",
" ...\n",
"except ...:\n",
" ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q12**: Write a function `get_guess()` that takes a user's input and checks if it is a valid side of the die! The function should *return* either an `int` object between `1` and `6` or `None` if the user enters something invalid.\n",
"\n",
"Hints: You may want to re-use the `try` statement from **Q11**. Instead of printing out an error message, you can also `return` directly from the `except`-clause (i.e., early exit) with `None`. So, the user can make *two* kinds of input errors and maybe you want to model that with two *distinct* `return None` statements. Also, you may want to allow the user to enter leading and trailing whitespace that gets removed without an error message."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def get_guess():\n",
" \"\"\"Process the user's input.\n",
" \n",
" Returns:\n",
" guess (int / NoneType): either 1, 2, 3, 4, 5 or 6\n",
" if the input can be parsed and None otherwise\n",
" \"\"\"\n",
" ...\n",
"\n",
" # Check if the user entered an integer.\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
"\n",
" # Check if the user entered a valid side.\n",
" ...\n",
" ...\n",
" ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q13** Test your function for all *three* cases!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"get_guess()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q14**: Write an *indefinite* loop where in each iteration a `fair_die` is thrown and the user makes a guess! Print out an error message if the user does not enter something that can be understood as a number between `1` and `6`! The game should continue until the user makes a correct guess."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"...\n",
"...\n",
"...\n",
"\n",
"...\n",
"...\n",
"...\n",
"...\n",
"...\n",
"...\n",
"..."
]
}
],
"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.12.2"
},
"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
}

View file

@ -0,0 +1,79 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Chapter 4: Recursion & Looping (TL;DR)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"**Iteration** is about **running blocks of code repeatedly**.\n",
"\n",
"There are two redundant approaches to achieving that.\n",
"\n",
"First, we combine functions that call themselves with conditional statements. This concept is known as **recursion** and suffices to control the flow of execution in *every* way we desire. For a beginner, this approach of **backward** reasoning might not be intuitive, but it turns out to be a handy tool to have in one's toolbox.\n",
"\n",
"Second, the `while` and `for` statements are alternative and potentially more intuitive ways to express iteration as they correspond to a **forward** reasoning. The `for` statement is **syntactic sugar** that allows rewriting common occurrences of the `while` statement concisely. Python provides the `break` and `continue` statements as well as an optional `else`-clause that make working with the `for` and `while` statements even more convenient.\n",
"\n",
"**Iterables** are any **concrete data types** that support being looped over, for example, with the `for` statement. The idea behind iterables is an **abstract concept** that may or may not be implemented by any given concrete data type. For example, both `list` and `range` objects can be looped over. The `list` type is also a **container** as any given `list` objects \"contains\" references to other objects in memory. On the contrary, the `range` type does not reference any other object but instead creates *new* `int` objects \"on the fly\" (i.e., when being looped over)."
]
}
],
"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.12.2"
},
"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
}

View file

@ -0,0 +1,268 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 4: Recursion & Looping (Review Questions)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The questions below assume that you have read the [first <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/00_content.ipynb), [second <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/02_content.ipynb), and the [third <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/03_content.ipynb) part of Chapter 4.\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": [
"**Q1**: Solving a problem by **recursion** is not only popular in computer science and math. Name some examples from the fields of business or economics where problems are also solved in a **backward** fashion!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: Explain what **duck typing** means! Why can it cause problems? Why is it [not a bug but a feature](https://www.urbandictionary.com/define.php?term=It%27s%20not%20a%20bug%2C%20it%27s%20a%20feature)?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3**: What is **syntactic sugar**?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q4**: Describe in your own words why the **recursive** version of `fibonacci()`, the \"Easy at first Glance\" example in the chapter, is computationally **inefficient**! Why does the **iterative** version of `fibonacci()`, the \"Hard at first Glance\" example, run so much faster?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5**: What is the conceptual difference between a **container** and a **list**?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: What is a good use case for the `for`-loop's optional `else`-clause?"
]
},
{
"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": [
"**Q7**: When a **recursion** does **not** reach the base case, this is an example of the **early exit** strategy."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q8**: Any programming language **without** looping constructs like the `for` or `while` statements is **not** Turing complete."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q9**: A **recursive** formulation is the same as a **circular** one: The terms are **synonyms**."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q10**: Formulating a computational problem as a **recursion** results in an **efficient** implementation."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q11**: Whereas a **recursion** may accidentally result in a **never-ending** program, `while`-loops and `for`-loops are guaranteed to **terminate**."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q12**: Before writing **any** kind of **loop**, we **always** need to think about a **stopping criterion** ahead of time."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q13**: **Container** types such as `list` objects are characterized by their **support** for **being looped over**, for example as in:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```python\n",
"for element in container:\n",
" # do something for every element\n",
" ...\n",
"```"
]
},
{
"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.12.2"
},
"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
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

2454
05_numbers/00_content.ipynb Normal file

File diff suppressed because it is too large Load diff

2180
05_numbers/01_content.ipynb Normal file

File diff suppressed because it is too large Load diff

1519
05_numbers/02_content.ipynb Normal file

File diff suppressed because it is too large Load diff

1508
05_numbers/03_appendix.ipynb Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,85 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Chapter 5: Numbers & Bits (TL;DR)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"There exist three numeric types in core Python:\n",
"- `int`: a near-perfect model for whole numbers (i.e., $\\mathbb{Z}$); inherently precise\n",
"- `float`: the \"gold\" standard to approximate real numbers (i.e., $\\mathbb{R}$); inherently imprecise\n",
"- `complex`: layer on top of the `float` type to approximate complex numbers (i.e., $\\mathbb{C}$); inherently imprecise\n",
"\n",
"Furthermore, the [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html) provides two more types that can be used as substitutes for the `float` type:\n",
"- `Decimal`: similar to `float` but allows customizing the precision; still inherently imprecise\n",
"- `Fraction`: a near-perfect model for rational numbers (i.e., $\\mathbb{Q}$); built on top of the `int` type and therefore inherently precise\n",
"\n",
"The *important* takeaways for the data science practitioner are:\n",
"\n",
"1. **Do not mix** precise and imprecise data types, and\n",
"2. actively expect `nan` results when working with `float` numbers as there are no **loud failures**.\n",
"\n",
"The **numerical tower** is Python's way of implementing various **abstract** ideas of what numbers are in mathematics."
]
}
],
"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.12.2"
},
"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
}

327
05_numbers/05_review.ipynb Normal file
View file

@ -0,0 +1,327 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 5: Numbers & Bits (Review Questions)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The questions below assume that you have read the [first <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/00_content.ipynb), [second <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/01_content.ipynb), and the [third <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/02_content.ipynb) part of Chapter 5. Some questions regard the [Appendix <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/03_appendix.ipynb); that is indicated with a **\\***.\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 with *at most* 300 characters per question!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q1**: In what way is the **binary representation** of `int` objects *similar* to the **decimal system** taught in elementary school?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: Why may objects of type `bool` be regarded a **numeric type** as well?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3**: Colors are commonly expressed in the **hexadecimal system** in websites (cf., the [HTML <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/HTML) and [CSS <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Cascading_Style_Sheets) formats).\n",
"\n",
"For example, $#000000$, $#ff9900$, and $#ffffff$ turn out to be black, orange, and white. The six digits are read in *pairs of two* from left to right, and the *three pairs* correspond to the proportions of red, green, and blue mixed together. They reach from $0_{16} = 0_{10}$ for $0$% to $\\text{ff}_{16} = 255_{10}$ for $100$% (cf., this [article <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/RGB_color_model) for an in-depth discussion).\n",
"\n",
"In percent, what are the proportions of red, green, and blue that make up orange? Calculate the three percentages separately! How many **bytes** are needed to encode a color? How many **bits** are that?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q4**: What does it mean for a code fragment to **fail silently**?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5**: Explain why the mathematical set of all real numbers $\\mathbb{R}$ can only be **approximated** by floating-point numbers on a computer!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: How do we deal with a `float` object's imprecision if we need to **check for equality**?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q7**: What data type, built-in or from the [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html), is best suited to represent the [transcendental numbers <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Transcendental_number) $\\pi$ and $\\text{e}$?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q8**: How can **abstract base classes**, for example, as defined in the **numerical tower**, be helpful in enabling **duck typing**?"
]
},
{
"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": [
"**Q9**: The precision of `int` objects depends on how we choose to represent them in memory. For example, using a **hexadecimal representation** gives us $16^8$ digits whereas with a **binary representation** an `int` object can have *at most* $2^8$ digits."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q10**: With the built-in [round() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#round) function, we obtain a *precise* representation for any `float` object if we can live with *less than* $15$ digits of precision."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q11**: As most currencies operate with $2$ or $3$ decimals (e.g., EUR $9.99$), the `float` type's limitation of *at most* $15$ digits is *not* a problem in practice."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q12**: The [IEEE 754 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/IEEE_754) standard's **special values** provide no benefit in practice as we could always use a **[sentinel <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Sentinel_value)** value (i.e., a \"dummy\"). For example, instead of `nan`, we can always use `0` to indicate a *missing* value."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q13**: The following code fragment raises an `InvalidOperation` exception. That is an example of code **failing loudly**.\n",
"```python\n",
"float(\"inf\") + float(\"-inf\")\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q14**: Python provides a `scientific` type (e.g., `1.23e4`) that is useful mainly to model problems in the domains of physics or astrophysics."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q15**: From a practitioner's point of view, the built-in [format() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#format) function does the *same* as the built-in [round() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#round) function."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q16\\***: The `Decimal` type from the [decimal <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/decimal.html) module in the [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html) allows us to model the set of the real numbers $\\mathbb{R}$ *precisely*."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q17\\***: The `Fraction` type from the [fractions <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/fractions.html) module in the [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html) allows us to model the set of the rational numbers $\\mathbb{Q}$ *precisely*."
]
},
{
"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.12.2"
},
"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
}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

3970
06_text/00_content.ipynb Normal file

File diff suppressed because it is too large Load diff

View file

@ -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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/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() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#reversed) and [zip() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](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() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](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() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](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.12.2"
},
"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
}

2110
06_text/02_content.ipynb Normal file

File diff suppressed because it is too large Load diff

75
06_text/03_summary.ipynb Normal file
View file

@ -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.12.2"
},
"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
}

199
06_text/04_review.ipynb Normal file
View file

@ -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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/00_content.ipynb) and [second <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/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.12.2"
},
"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
}

365
06_text/05_resources.ipynb Normal file

File diff suppressed because one or more lines are too long

1
06_text/full_house.bin Normal file
View file

@ -0,0 +1 @@
🂧🂷🃗🃎🃞

6
06_text/lorem_ipsum.txt Normal file
View file

@ -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.

12
06_text/umlauts.txt Normal file
View file

@ -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.

View file

@ -0,0 +1,958 @@
{
"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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/00_content.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Chapter 7: Sequential Data"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"We studied numbers (cf., [Chapter 5 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/00_content.ipynb)) and textual data (cf., [Chapter 6 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/00_content.ipynb)) first mainly because objects of the presented data types are \"simple.\" That is so for two reasons: First, they are *immutable*, and, as we saw in the \"*Who am I? And how many?*\" section in [Chapter 1 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/03_content.ipynb#Who-am-I?-And-how-many?), mutable objects can quickly become hard to reason about. Second, they are \"flat\" in the sense that they are *not* composed of other objects.\n",
"\n",
"The `str` type is a bit of a corner case in this regard. While one could argue that a longer `str` object, for example, `\"text\"`, is composed of individual characters, this is *not* the case in memory as the literal `\"text\"` only creates *one* object (i.e., one \"bag\" of $0$s and $1$s modeling all characters).\n",
"\n",
"This chapter, [Chapter 8 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb), [Chapter 9 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/00_content.ipynb), and [Chapter 10 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/10_arrays/00_content.ipynb) introduce various \"complex\" data types. While some are mutable and others are not, they all share that they are primarily used to \"manage,\" or structure, the memory in a program (i.e., they provide references to other objects). Unsurprisingly, computer scientists refer to the ideas behind these data types as **[data structures <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Data_structure)**.\n",
"\n",
"In this chapter, we focus on data types that model all kinds of sequential data. Examples of such data are [spreadsheets <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Spreadsheet) or [matrices <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Matrix_%28mathematics%29) and [vectors <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Vector_%28mathematics_and_physics%29). These formats share the property that they are composed of smaller units that come in a sequence of, for example, rows/columns/cells or elements/entries."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Collections vs. Sequences"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"[Chapter 6 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/00_content.ipynb#A-\"String\"-of-Characters) already describes the **sequence** properties of `str` objects. In this section, we take a step back and study these properties one by one.\n",
"\n",
"The [collections.abc <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.abc.html) module in the [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html) defines a variety of **abstract base classes** (ABCs). We saw ABCs already in [Chapter 5 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/02_content.ipynb#The-Numerical-Tower), where we use the ones from the [numbers <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/numbers.html) module in the [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html) to classify Python's numeric data types according to mathematical ideas. Now, we take the ABCs from the [collections.abc <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.abc.html) module to classify the data types in this chapter according to their behavior in various contexts.\n",
"\n",
"As an illustration, consider `numbers` and `text` below, two objects of *different* types."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]\n",
"text = \"Lorem ipsum dolor sit amet.\""
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Among others, one commonality between the two is that we may loop over them with the `for` statement. So, in the context of iteration, both exhibit the *same* behavior."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"7 11 8 5 3 12 2 6 9 10 1 4 "
]
}
],
"source": [
"for number in numbers:\n",
" print(number, end=\" \")"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"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": [
"In [Chapter 4 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/02_content.ipynb#Containers-vs.-Iterables), we referred to such types as *iterables*. That is *not* a proper [English](https://dictionary.cambridge.org/spellcheck/english-german/?q=iterable) word, even if it may sound like one at first sight. Yet, it is an official term in the Python world formalized with the `Iterable` ABC in the [collections.abc <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.abc.html) module.\n",
"\n",
"For the data science practitioner, it is worthwhile to know such terms as, for example, the documentation on the [built-ins <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html) uses them extensively: In simple words, any built-in that takes an argument called \"*iterable*\" may be called with *any* object that supports being looped over. Already familiar [built-ins <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html) include [enumerate() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#enumerate), [sum() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#sum), or [zip() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#zip). So, they do *not* require the argument to be of a certain data type (e.g., `list`); instead, any *iterable* type works."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"import collections.abc as abc"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"collections.abc.Iterable"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"abc.Iterable"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"As seen in [Chapter 5 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/02_content.ipynb#Goose-Typing), we can use ABCs with the built-in [isinstance() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#isinstance) function to check if an object supports a behavior.\n",
"\n",
"So, let's \"ask\" Python if it can loop over `numbers` or `text`."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(numbers, abc.Iterable)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(text, abc.Iterable)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Contrary to `list` or `str` objects, numeric objects are *not* iterable."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(999, abc.Iterable)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Instead of asking, we could try to loop over `999`, but this results in a `TypeError`."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"ename": "TypeError",
"evalue": "'int' object is not iterable",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[0;32mIn[9], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mdigit\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;241;43m999\u001b[39;49m\u001b[43m:\u001b[49m\n\u001b[1;32m 2\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mprint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mdigit\u001b[49m\u001b[43m)\u001b[49m\n",
"\u001b[0;31mTypeError\u001b[0m: 'int' object is not iterable"
]
}
],
"source": [
"for digit in 999:\n",
" print(digit)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Most of the data types in this chapter and [Chapter 9 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/00_content.ipynb) and [Chapter 10 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/10_arrays/00_content.ipynb) exhibit three [orthogonal <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Orthogonality) (i.e., \"independent\") behaviors, formalized by ABCs in the [collections.abc <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.abc.html) module as:\n",
"- `Iterable`: An object may be looped over.\n",
"- `Container`: An object \"contains\" references to other objects; a \"whole\" is composed of many \"parts.\"\n",
"- `Sized`: The number of references to other objects, the \"parts,\" is *finite*.\n",
"\n",
"The characteristical operation supported by `Container` types is the `in` operator for membership testing."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"0 in numbers"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"\"l\" in text"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Alternatively, we could also check if `numbers` and `text` are `Container` types with [isinstance() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#isinstance)."
]
},
{
"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": [
"isinstance(numbers, abc.Container)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(text, abc.Container)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Numeric objects do *not* \"contain\" references to other objects, and that is why they are considered \"flat\" data types. The `in` operator raises a `TypeError`. Conceptually speaking, Python views numeric types as \"wholes\" without any \"parts.\""
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(999, abc.Container)"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"ename": "TypeError",
"evalue": "argument of type 'int' is not iterable",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[0;32mIn[15], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;241;43m9\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;241;43m999\u001b[39;49m\n",
"\u001b[0;31mTypeError\u001b[0m: argument of type 'int' is not iterable"
]
}
],
"source": [
"9 in 999"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Analogously, being `Sized` types, we can pass `numbers` and `text` as the argument to the built-in [len() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#len) function and obtain \"meaningful\" results. The exact meaning depends on the data type: For `numbers`, [len() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#len) tells us how many elements are in the `list` object; for `text`, it tells us how many [Unicode characters <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Unicode) make up the `str` object. *Abstractly* speaking, both data types exhibit the *same* behavior of *finiteness*."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"12"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(numbers)"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"27"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(text)"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(numbers, abc.Sized)"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(text, abc.Sized)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"On the contrary, even though `999` consists of three digits for humans, numeric objects in Python have no concept of a \"size\" or \"length,\" and the [len() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#len) function raises a `TypeError`."
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(999, abc.Sized)"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"ename": "TypeError",
"evalue": "object of type 'int' has no len()",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[0;32mIn[21], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;43mlen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m999\u001b[39;49m\u001b[43m)\u001b[49m\n",
"\u001b[0;31mTypeError\u001b[0m: object of type 'int' has no len()"
]
}
],
"source": [
"len(999)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"These three behaviors are so essential that whenever they coincide for a data type, it is called a **collection**, formalized with the `Collection` ABC. That is where the [collections.abc <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.abc.html) module got its name from: It summarizes all ABCs related to collections; in particular, it defines a hierarchy of specialized kinds of collections.\n",
"\n",
"Without going into too much detail, one way to read the summary table at the beginning of the [collections.abc <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.abc.html) module's documention is as follows: The first column, titled \"ABC\", lists all collection-related ABCs in Python. The second column, titled \"Inherits from,\" indicates if the idea behind the ABC is *original* (e.g., the first row with the `Container` ABC has an empty \"Inherits from\" column) or a *combination* (e.g., the row with the `Collection` ABC has `Sized`, `Iterable`, and `Container` in the \"Inherits from\" column). The third and fourth columns list the methods that come with a data type following an ABC. We keep ignoring the methods named in the dunder style for now.\n",
"\n",
"So, let's confirm that both `numbers` and `text` are collections."
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(numbers, abc.Collection)"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(text, abc.Collection)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"They share one more common behavior: When looping over them, we can *predict* the *order* of the elements or characters. The ABC in the [collections.abc <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.abc.html) module corresponding to this behavior is `Reversible`. While sounding unintuitive at first, it is evident that if something is reversible, it must have a forward order, to begin with.\n",
"\n",
"The [reversed() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#reversed) built-in allows us to loop over the elements or characters in reverse order."
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"4 1 10 9 6 2 12 3 5 8 11 7 "
]
}
],
"source": [
"for number in reversed(numbers):\n",
" print(number, end=\" \")"
]
},
{
"cell_type": "code",
"execution_count": 25,
"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": "code",
"execution_count": 26,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(numbers, abc.Reversible)"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(text, abc.Reversible)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Collections that exhibit this fourth behavior are referred to as **sequences**, formalized with the `Sequence` ABC in the [collections.abc <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.abc.html) module."
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(numbers, abc.Sequence)"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isinstance(text, abc.Sequence)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"The data types introduced in this chapter are sequences. Nevertheless, we also look at some data types that are neither collections nor sequences but are still useful to model sequential data in practice in [Chapter 8 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb).\n",
"\n",
"In Python-related documentations, the terms collection and sequence are heavily used, and the data science practitioner should always think of them in terms of the three or four behaviors they exhibit.\n",
"\n",
"Data types that are collections but not sequences are covered in [Chapter 9 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/00_content.ipynb)."
]
}
],
"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.12.2"
},
"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
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,257 @@
{
"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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/02_exercises.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 7: Sequential Data (Coding Exercises)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The exercises below assume that you have read the [second part <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/01_content.ipynb) of Chapter 7.\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": [
"## Working with Lists"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q1**: Write a function `nested_sum()` that takes a `list` object as its argument, which contains other `list` objects with numbers, and adds up the numbers! Use `nested_numbers` below to test your function!\n",
"\n",
"Hint: You need at least one `for`-loop."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"nested_numbers = [[1, 2, 3], [4], [5], [6, 7], [8], [9]]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def nested_sum(list_of_lists):\n",
" \"\"\"Add up numbers in nested lists.\n",
" \n",
" Args:\n",
" list_of_lists (list): A list containing the lists with the numbers\n",
" \n",
" Returns:\n",
" sum (int or float)\n",
" \"\"\"\n",
" ...\n",
" ...\n",
" ...\n",
"\n",
" return ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"nested_sum(nested_numbers)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: Generalize `nested_sum()` into a function `mixed_sum()` that can process a \"mixed\" `list` object, which contains numbers and other `list` objects with numbers! Use `mixed_numbers` below for testing!\n",
"\n",
"Hints: Use the built-in [isinstance() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#isinstance) function to check how an element is to be processed."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"mixed_numbers = [[1, 2, 3], 4, 5, [6, 7], 8, [9]]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import collections.abc as abc"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def mixed_sum(list_of_lists_or_numbers):\n",
" \"\"\"Add up numbers in nested lists.\n",
" \n",
" Args:\n",
" list_of_lists_or_numbers (list): A list containing both numbers and\n",
" lists with numbers\n",
" \n",
" Returns:\n",
" sum (int or float)\n",
" \"\"\"\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
"\n",
" return ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"mixed_sum(mixed_numbers)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3.1**: Write a function `cum_sum()` that takes a `list` object with numbers as its argument and returns a *new* `list` object with the **cumulative sums** of these numbers! So, `sum_up` below, `[1, 2, 3, 4, 5]`, should return `[1, 3, 6, 10, 15]`.\n",
"\n",
"Hint: The idea behind is similar to the [cumulative distribution function <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Cumulative_distribution_function) from statistics."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sum_up = [1, 2, 3, 4, 5]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def cum_sum(numbers):\n",
" \"\"\"Create the cumulative sums for some numbers.\n",
"\n",
" Args:\n",
" numbers (list): A list with numbers for that the cumulative sums\n",
" are calculated\n",
" \n",
" Returns:\n",
" cum_sums (list): A list with all the cumulative sums\n",
" \"\"\"\n",
" ...\n",
" ...\n",
"\n",
" ...\n",
" ...\n",
" ...\n",
"\n",
" return ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cum_sum(sum_up)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3.2**: We should always make sure that our functions also work in corner cases. What happens if your implementation of `cum_sum()` is called with an empty list `[]`? Make sure it handles that case *without* crashing! What would be a good return value in this corner case?\n",
"\n",
"Hint: It is possible to write this without any extra input validation."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cum_sum([])"
]
},
{
"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.12.2"
},
"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
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,663 @@
{
"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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/04_exercises.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 7: Sequential Data (Coding Exercises)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The exercises below assume that you have read the [third part <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/03_content.ipynb) of Chapter 7.\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": [
"## Packing & Unpacking with Functions"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In the \"*Function Definitions & Calls*\" section in [Chapter 7 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/03_content.ipynb#Function-Definitions-&-Calls), we define the following function `product()`. In this exercise, you will improve it by making it more \"user-friendly.\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def product(*args):\n",
" \"\"\"Multiply all arguments.\"\"\"\n",
" result = args[0]\n",
"\n",
" for arg in args[1:]:\n",
" result *= arg\n",
"\n",
" return result"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `*` in the function's header line *packs* all *positional* arguments passed to `product()` into one *iterable* called `args`.\n",
"\n",
"**Q1**: What is the data type of `args` within the function's body?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Because of the packing, we may call `product()` with an abitrary number of *positional* arguments: The product of just `42` remains `42`, while `2`, `5`, and `10` multiplied together result in `100`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(42)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(2, 5, 10)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"However, \"abitrary\" does not mean that we can pass *no* argument. If we do so, we get an `IndexError`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: What line in the body of `product()` causes this exception? What is the exact problem?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In [Chapter 7 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/00_content.ipynb#Function-Definitions-&-Calls), we also pass a `list` object, like `one_hundred`, to `product()`, and *no* exception is raised."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"one_hundred = [2, 5, 10]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(one_hundred)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3**: What is wrong with that? What *kind* of error (cf., [Chapter 1 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb#Formal-vs.-Natural-Languages)) is that conceptually? Describe precisely what happens to the passed in `one_hundred` in every line within `product()`!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Of course, one solution is to *unpack* `one_hundred` with the `*` symbol. We look at another solution further below."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(*one_hundred)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's continue with the issue when calling `product()` *without* any argument.\n",
"\n",
"This revised version of `product()` avoids the `IndexError` from before."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def product(*args):\n",
" \"\"\"Multiply all arguments.\"\"\"\n",
" result = None\n",
"\n",
" for arg in args:\n",
" result *= arg\n",
"\n",
" return result"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q4**: Describe why no error occurs by going over every line in `product()`!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Unfortunately, the new version cannot process any arguments we pass in any more."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(42)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(2, 5, 10)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5**: What line causes troubles now? What is the exact problem?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: Replace the `None` in `product()` above with something reasonable that does *not* cause exceptions! Ensure that `product(42)` and `product(2, 5, 10)` return a correct result.\n",
"\n",
"Hints: It is ok if `product()` returns a result *different* from the `None` above. Look at the documentation of the built-in [sum() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#sum) function for some inspiration."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def product(*args):\n",
" \"\"\"Multiply all arguments.\"\"\"\n",
" result = ...\n",
"\n",
" for arg in args:\n",
" result *= arg\n",
"\n",
" return result"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(42)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(2, 5, 10)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, calling `product()` without any arguments returns what we would best describe as a *default* or *start* value. To be \"philosophical,\" what is the product of *no* numbers? We know that the product of *one* number is just the number itself, but what could be a reasonable result when multiplying *no* numbers? The answer is what you use as the initial value of `result` above, and there is only *one* way to make `product(42)` and `product(2, 5, 10)` work."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q7**: Rewrite `product()` so that it takes a *keyword-only* argument `start`, defaulting to the above *default* or *start* value, and use `start` internally instead of `result`!\n",
"\n",
"Hint: Remember that a *keyword-only* argument is any parameter specified in a function's header line after the first and only `*` (cf., [Chapter 2 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/00_content.ipynb#Keyword-only-Arguments))."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def product(*args, ...):\n",
" \"\"\"Multiply all arguments.\"\"\"\n",
" ...\n",
" ...\n",
"\n",
" return ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, we can call `product()` with a truly arbitrary number of *positional* arguments."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(42)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(2, 5, 10)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Without any *positional* arguments but only the *keyword* argument `start`, for example, `start=0`, we can adjust the answer to the \"philosophical\" problem of multiplying *no* numbers. Because of the *keyword-only* syntax, there is *no* way to pass in a `start` number *without* naming it."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(start=0)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We could use `start` to inject a multiplier, for example, to double the outcomes."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(42, start=2)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(2, 5, 10, start=2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There is still one issue left: Because of the function's name, a user of `product()` may assume that it is ok to pass a *collection* of numbers, like `one_hundred`, which are then multiplied."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(one_hundred)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q8**: What is a **collection**? How is that different from a **sequence**?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q9**: Rewrite the latest version of `product()` to check if the *only* positional argument is a *collection* type! If so, its elements are multiplied together. Otherwise, the logic remains the same.\n",
"\n",
"Hints: Use the built-in [len() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#len) and [isinstance() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#isinstance) functions to check if there is only *one* positional argument and if it is a *collection* type. Use the *abstract base class* `Collection` from the [collections.abc <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.abc.html) module in the [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html). You may want to *re-assign* `args` inside the body."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import collections.abc as abc"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def product(*args, ...):\n",
" \"\"\"Multiply all arguments.\"\"\"\n",
" ...\n",
" ...\n",
"\n",
" ...\n",
" ...\n",
"\n",
" return ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"All *five* code cells below now return correct results. We may unpack `one_hundred` or not."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(42)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(2, 5, 10)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(one_hundred)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(*one_hundred)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Side Note**: Above, we make `product()` work with a single *collection* type argument instead of a *sequence* type to keep it more generic: For example, we can pass in a `set` object, like `{2, 5, 10}` below, and `product()` continues to work correctly. The `set` type is introducted in [Chapter 9 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/00_content.ipynb#The-set-Type), and one essential difference to the `list` type is that objects of type `set` have *no* order regarding their elements. So, even though `[2, 5, 10]` and `{2, 5, 10}` look almost the same, the order implied in the literal notation gets lost in memory!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product([2, 5, 10]) # the argument is a collection that is also a sequence"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product({2, 5, 10}) # the argument is a collection that is NOT a sequence"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"isinstance({2, 5, 10}, abc.Sequence) # sets are NO sequences"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's continue to improve `product()` and make it more Pythonic. It is always a good idea to mimic the behavior of built-ins when writing our own functions. And, [sum() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#sum), for example, raises a `TypeError` if called *without* any arguments. It does *not* return the \"philosophical\" answer to adding *no* numbers, which would be `0`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sum()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q10**: Adapt the latest version of `product()` to also raise a `TypeError` if called *without* any *positional* arguments!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def product(*args, ...):\n",
" \"\"\"Multiply all arguments.\"\"\"\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
"\n",
" ...\n",
" ...\n",
"\n",
" return ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product()"
]
}
],
"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.12.2"
},
"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
}

View file

@ -0,0 +1,609 @@
{
"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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/05_appendix.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Chapter 7: Sequential Data (Appendix)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"In the [third part <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/03_content.ipynb#Tuples-are-like-\"Immutable-Lists\") of the chapter, we proposed the idea that `tuple` objects are like \"immutable lists.\" Often, however, we use `tuple` objects to represent a **record** of related **fields**. Then, each element has a *semantic* meaning (i.e., a descriptive name).\n",
"\n",
"As an example, think of a spreadsheet with information on students in a course. Each row represents a record and holds all the data associated with an individual student. The columns (e.g., matriculation number, first name, last name) are the fields that may come as *different* data types (e.g., `int` for the matriculation number, `str` for the names).\n",
"\n",
"A simple way of modeling a single student is as a `tuple` object, for example, `(123456, \"John\", \"Doe\")`. A disadvantage of this approach is that we must remember the order and meaning of the elements/fields in the `tuple` object.\n",
"\n",
"An example from a different domain is the representation of $(x, y)$-points in the $x$-$y$-plane. Again, we could use a `tuple` object like `current_position` below to model the point $(4, 2)$."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"current_position = (4, 2)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"We implicitly assume that the first element represents the $x$ and the second the $y$ coordinate. While that follows intuitively from convention in math, we should at least add comments somewhere in the code to document this assumption."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## The `namedtuple` Type"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"A better way is to create a *custom* data type. While that is covered in depth in [Chapter 11 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb), the [collections <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.html) module in the [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html) provides a [namedtuple() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.html#collections.namedtuple) **factory function** that creates \"simple\" custom data types on top of the standard `tuple` type."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"from collections import namedtuple"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"[namedtuple() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.html#collections.namedtuple) takes two arguments. The first argument is the name of the data type. That could be different from the variable `Point` we use to refer to the new type, but in most cases it is best to keep them in sync. The second argument is a sequence with the field names as `str` objects. The names' order corresponds to the one assumed in `current_position`."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"Point = namedtuple(\"Point\", [\"x\", \"y\"])"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"The `Point` object is a so-called **class**. That is what it means if an object is of type `type`. It can be used as a **factory** to create *new* `tuple`-like objects of type `Point`. In a way, [namedtuple() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.html#collections.namedtuple) gives us a way to create our own custom **constructors**."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"94457911453856"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"id(Point)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"type"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(Point)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"The value of `Point` is just itself in a *literal notation*."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"__main__.Point"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Point"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"We write `Point(4, 2)` to create a *new* object of type `Point`."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [],
"source": [
"current_position = Point(4, 2)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Now, `current_position` has a somewhat nicer representation. In particular, the coordinates are named `x` and `y`."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"Point(x=4, y=2)"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"current_position"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"It is *not* a `tuple` any more but an object of type `Point`."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"140376178109184"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"id(current_position)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"__main__.Point"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(current_position)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"We use the dot operator `.` to access the defined attributes."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"4"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"current_position.x"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"2"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"current_position.y"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"As before, we get an `AttributeError` if we try to access an undefined attribute."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"ename": "AttributeError",
"evalue": "'Point' object has no attribute 'z'",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[0;32mIn[13], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mcurrent_position\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mz\u001b[49m\n",
"\u001b[0;31mAttributeError\u001b[0m: 'Point' object has no attribute 'z'"
]
}
],
"source": [
"current_position.z"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"`current_position` continues to work like a `tuple` object! That is why we can use `namedtuple` as a replacement for `tuple`. The underlying implementations exhibit the *same* computational efficiencies and memory usages.\n",
"\n",
"For example, we can index into or loop over `current_position` as it is still a sequence with the familiar four properties."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"4"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"current_position[0]"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"2"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"current_position[1]"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"4\n",
"2\n"
]
}
],
"source": [
"for number in current_position:\n",
" print(number)"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2\n",
"4\n"
]
}
],
"source": [
"for number in reversed(current_position):\n",
" print(number)"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [
{
"data": {
"text/plain": [
"2"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(current_position)"
]
}
],
"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.12.2"
},
"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
}

View file

@ -0,0 +1,85 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Chapter 7: Sequential Data (TL;DR)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"**Sequences** are an *abstract* concept that summarizes *four* behaviors an object may or may not exhibit. Sequences are\n",
"- **finite** and\n",
"- **ordered**\n",
"- **containers** that we may\n",
"- **loop over**.\n",
"\n",
"Examples are the `list`, `tuple`, but also the `str` types.\n",
"\n",
"Objects that exhibit all behaviors *except* being ordered are referred to as **collections**.\n",
"\n",
"The objects inside a sequence are called its **elements** and may be labeled with a unique **index**, an `int` object in the range $0 \\leq \\text{index} < \\lvert \\text{sequence} \\rvert$.\n",
"\n",
"`list` objects are **mutable**. That means we can change the references to the other objects it contains, and, in particular, re-assign them.\n",
"\n",
"On the contrary, `tuple` objects are like **immutable** lists: We can use them in place of any `list` object as long as we do *not* need to mutate it. Often, `tuple` objects are also used to model **records** of related **fields**."
]
}
],
"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.12.2"
},
"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
}

View file

@ -0,0 +1,252 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 7: Sequential Data (Review Questions)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The questions below assume that you have read the [first <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/00_content.ipynb), [second <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/01_content.ipynb), and the [third <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/03_content.ipynb) part of Chapter 7. Some questions regard the [Appendix <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/05_appendix.ipynb); that is indicated with a **\\***.\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**: We have seen **containers** and **iterables** before in [Chapter 4 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/02_content.ipynb#Containers-vs.-Iterables). How do they relate to **sequences**? "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: What are **abstract base classes**? How can we make use of the ones from the [collections.abc <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.abc.html) module in the [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html)?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3**: How are the *abstract behaviors* of **reversibility** and **finiteness** essential for *indexing* and *slicing* sequences?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q4**: Explain the difference between **mutable** and **immutable** objects in Python with the examples of the `list` and `tuple` types!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5**: What is the difference between a **shallow** and a **deep** copy of an object? How can one of them become a \"problem?\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: Many **list methods** change `list` objects \"**in place**.\" What do we mean by that?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q7.1**: `tuple` objects have *two* primary usages. First, they can be used in place of `list` objects where **mutability** is *not* required. Second, we use them to model data **records**.\n",
"\n",
"Describe why `tuple` objects are a suitable replacement for `list` objects in general!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q7.2\\***: What do we mean by a **record**? How are `tuple` objects suitable to model records? How can we integrate a **semantic meaning** when working with records into our code?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q8**: How is (iterable) **packing** and **unpacking** useful in the context of **function definitions** and **calls**?"
]
},
{
"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": [
"**Q9**: `sequence` objects are *not* part of core Python but may be imported from the [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q10**: The built-in [.sort() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/stdtypes.html#list.sort) function takes a *finite* **iterable** as its argument an returns a *new* `list` object. On the contrary, the [sorted() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#sorted) method on `list` objects *mutates* them *in place*."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q11**: Passing **mutable** objects as arguments to functions is not problematic because functions operate in a **local** scope without affecting the **global** scope."
]
},
{
"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.12.2"
},
"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
}

1381
08_mfr/00_content.ipynb Normal file

File diff suppressed because it is too large Load diff

2384
08_mfr/01_content.ipynb Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,760 @@
{
"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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/08_mfr/02_exercises.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 8: Map, Filter, & Reduce (Coding Exercises)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The exercises below assume that you have read the [first <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb) and [second <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/01_content.ipynb) part of Chapter 8.\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": [
"## Removing Outliers in Streaming Data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's say we are given a `list` object with random integers like `sample` below, and we want to calculate some basic statistics on them."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sample = [\n",
" 45, 46, 40, 49, 36, 53, 49, 42, 25, 40, 39, 36, 38, 40, 40, 52, 36, 52, 40, 41,\n",
" 35, 29, 48, 43, 42, 30, 29, 33, 55, 33, 38, 50, 39, 56, 52, 28, 37, 56, 45, 37,\n",
" 41, 41, 37, 30, 51, 32, 23, 40, 53, 40, 45, 39, 99, 42, 34, 42, 34, 39, 39, 53,\n",
" 43, 37, 46, 36, 45, 42, 32, 38, 57, 34, 36, 44, 47, 51, 46, 39, 28, 40, 35, 46,\n",
" 41, 51, 41, 23, 46, 40, 40, 51, 50, 32, 47, 36, 38, 29, 32, 53, 34, 43, 39, 41,\n",
" 40, 34, 44, 40, 41, 43, 47, 57, 50, 42, 38, 25, 45, 41, 58, 37, 45, 55, 44, 53,\n",
" 82, 31, 45, 33, 32, 39, 46, 48, 42, 47, 40, 45, 51, 35, 31, 46, 40, 44, 61, 57,\n",
" 40, 36, 35, 55, 40, 56, 36, 35, 86, 36, 51, 40, 54, 50, 49, 36, 41, 37, 48, 41,\n",
" 42, 44, 40, 43, 51, 47, 46, 50, 40, 23, 40, 39, 28, 38, 42, 46, 46, 42, 46, 31,\n",
" 32, 40, 48, 27, 40, 40, 30, 32, 25, 31, 30, 43, 44, 29, 45, 41, 63, 32, 33, 58,\n",
"]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"len(sample)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q1**: `list` objects are **sequences**. What *four* behaviors do they always come with?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: Write a function `mean()` that calculates the simple arithmetic mean of a given `sequence` with numbers!\n",
"\n",
"Hints: You can solve this task with [built-in functions <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html) only. A `for`-loop is *not* needed."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def mean(sequence):\n",
" ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sample_mean = mean(sample)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sample_mean"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3**: Write a function `std()` that calculates the [standard deviation <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Standard_deviation) of a `sequence` of numbers! Integrate your `mean()` version from before and the [sqrt() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/math.html#math.sqrt) function from the [math <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/math.html) module in the [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html) provided to you below. Make sure `std()` calls `mean()` only *once* internally! Repeated calls to `mean()` would be a waste of computational resources.\n",
"\n",
"Hints: Parts of the code are probably too long to fit within the suggested 79 characters per line. So, use *temporary variables* inside your function. Instead of a `for`-loop, you may want to use a `list` comprehension or, even better, a memoryless `generator` expression."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from math import sqrt"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def std(sequence):\n",
" ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sample_std = std(sample)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sample_std"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q4**: Complete `standardize()` below that takes a `sequence` of numbers and returns a `list` object with the **[z-scores <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Standard_score)** of these numbers! A z-score is calculated by subtracting the mean and dividing by the standard deviation. Re-use `mean()` and `std()` from before. Again, ensure that `standardize()` calls `mean()` and `std()` only *once*! Further, round all z-scores with the built-in [round() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#round) function and pass on the keyword-only argument `digits` to it.\n",
"\n",
"Hint: You may want to use a `list` comprehension instead of a `for`-loop."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def standardize(sequence, *, digits=3):\n",
" ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"z_scores = standardize(sample)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The [pprint() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/pprint.html#pprint.pprint) function from the [pprint <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/pprint.html) module in the [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html) allows us to \"pretty print\" long `list` objects compactly."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from pprint import pprint"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pprint(z_scores, compact=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We know that `standardize()` works correctly if the resulting z-scores' mean and standard deviation approach `0` and `1` for a long enough `sequence`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"mean(z_scores), std(z_scores)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Even though `standardize()` calls `mean()` and `std()` only once each, `mean()` is called *twice*! That is so because `std()` internally also re-uses `mean()`!\n",
"\n",
"**Q5.1**: Rewrite `std()` to take an optional keyword-only argument `seq_mean`, defaulting to `None`. If provided, `seq_mean` is used instead of the result of calling `mean()`. Otherwise, the latter is called.\n",
"\n",
"Hint: You must check if `seq_mean` is still the default value."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def std(sequence, *, seq_mean=None):\n",
" ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`std()` continues to work as before."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sample_std = std(sample)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sample_std"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5.2**: Now, rewrite `standardize()` to pass on the return value of `mean()` to `std()`! In summary, `standardize()` calculates the z-scores for the numbers in the `sequence` with as few computational steps as possible."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def standardize(sequence, *, digits=3):\n",
" ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"z_scores = standardize(sample)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"mean(z_scores), std(z_scores)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: With both `sample` and `z_scores` being materialized `list` objects, we can loop over pairs consisting of a number from `sample` and its corresponding z-score. Write a `for`-loop that prints out all the \"outliers,\" as which we define numbers with an absolute z-score above `1.96`. There are *four* of them in the `sample`.\n",
"\n",
"Hint: Use the [abs() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#abs) and [zip() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#zip) built-ins."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We provide a `stream` module with a `data` object that models an *infinite* **stream** of data (cf., the [*stream.py* <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_gh.png\">](https://github.com/webartifex/intro-to-python/blob/main/08_mfr/stream.py) file in the repository)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from stream import data"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`data` is of type `generator` and has *no* length."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"type(data)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"len(data)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So, the only thing we can do with it is to pass it to the built-in [next() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#next) function and go over the numbers it streams one by one."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"next(data)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q7**: What happens if you call `mean()` with `data` as the argument? What is the problem?\n",
"\n",
"Hints: If you try it out, you may have to press the \"Stop\" button in the toolbar at the top. Your computer should *not* crash, but you will *have to* restart this Jupyter notebook with \"Kernel\" > \"Restart\" and import `data` again."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"mean(data)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q8**: Write a function `take_sample()` that takes an `iterable` as its argument, like `data`, and creates a *materialized* `list` object out of its first `n` elements, defaulting to `1_000`!\n",
"\n",
"Hints: [next() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#next) and the [range() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#func-range) built-in may be helpful. You may want to use a `list` comprehension instead of a `for`-loop and write a one-liner. Audacious students may want to look at [isclice() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/itertools.html#itertools.islice) in the [itertools <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/itertools.html) module in the [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def take_sample(iterable, *, n=1_000):\n",
" ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We take a `new_sample` from the stream of `data`, and its statistics are similar to the initial `sample`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"new_sample = take_sample(data)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"len(new_sample)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"mean(new_sample)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"std(new_sample)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q9**: Convert `standardize()` into a *new* function `standardized()` that implements the *same* logic but works on a possibly *infinite* stream of data, provided as an `iterable`, instead of a *finite* `sequence`.\n",
"\n",
"To calculate a z-score, we need the stream's overall mean and standard deviation, and that is *impossible* to calculate if we do not know how long the stream is, and, in particular, if it is *infinite*. So, `standardized()` first takes a sample from the `iterable` internally, and uses the sample's mean and standard deviation to calculate the z-scores.\n",
"\n",
"Hint: `standardized()` *must* return a `generator` object. So, use a `generator` expression as the return value; unless you know about the `yield` statement already (cf., [reference <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/reference/simple_stmts.html#the-yield-statement))."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def standardized(iterable, *, digits=3):\n",
" ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`standardized()` works almost like `standardize()` except that we use it with [next() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#next) to obtain the z-scores one by one."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"z_scores = standardized(data)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"z_scores"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"type(z_scores)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"next(z_scores)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q10.1**: `standardized()` allows us to go over an *infinite* stream of z-scores. What we want to do instead is to loop over the stream's raw numbers and skip the outliers. In the remainder of this exercise, you look at the parts that make up the `skip_outliers()` function below to achieve precisely that.\n",
"\n",
"The first steps in `skip_outliers()` are the same as in `standardized()`: We take a `sample` from the stream of `data` and calculate its statistics."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sample = ...\n",
"seq_mean = ...\n",
"seq_std = ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q10.2**: Just as in `standardized()`, write a `generator` expression that produces z-scores one by one! However, instead of just generating a z-score, the resulting `generator` object should produce `tuple` objects consisting of a \"raw\" number from `data` and its z-score.\n",
"\n",
"Hint: Look at the revisited \"*Averaging Even Numbers*\" example in [Chapter 7 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/01_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List-%28revisited%29) for some inspiration, which also contains a `generator` expression producing `tuple` objects."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"standardizer = (... for ... in data)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`standardizer` should produce `tuple` objects."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"next(standardizer)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q10.3**: Write another `generator` expression that loops over `standardizer`. It contains an `if`-clause that keeps only numbers with an absolute z-score below the `threshold_z`. If you fancy, use `tuple` unpacking."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"threshold_z = 1.96"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"no_outliers = (... for ... in standardizer if ...)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`no_outliers` should produce `int` objects."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"next(no_outliers)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q10.4**: Lastly, put everything together in the `skip_outliers()` function! Make sure you refer to `iterable` inside the function and not the global `data`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def skip_outliers(iterable, *, threshold_z=1.96):\n",
" sample = ...\n",
" seq_mean = ...\n",
" seq_std = ...\n",
" standardizer = ...\n",
" no_outliers = ...\n",
" return no_outliers"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, we can create a `generator` object and loop over the `data` in the stream with outliers skipped. Instead of the default `1.96`, we use a `threshold_z` of only `0.05`: That filters out all numbers except `42`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"skipper = skip_outliers(data, threshold_z=0.05)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"skipper"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"type(skipper)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"next(skipper)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q11**: You implemented the functions `mean()`, `std()`, `standardize()`, `standardized()`, and `skip_outliers()`. Which of them are **eager**, and which are **lazy**? How do these two concepts relate to **finite** and **infinite** data?"
]
},
{
"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.12.2"
},
"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
}

View file

@ -0,0 +1,455 @@
{
"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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/08_mfr/03_exercises.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 8: Map, Filter, & Reduce (Coding Exercises)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The exercises below assume that you have read the [first <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb) and [second <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/01_content.ipynb) part of Chapter 8.\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": [
"## Packing & Unpacking with Functions (continued)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q1**: Copy your solution to **Q10** from the \"*Packing & Unpacking with Functions*\" exercise in [Chapter 7 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/04_exercises.ipynb) into the code cell below!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import collections.abc as abc"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def product(*args, ...):\n",
" \"\"\"Multiply all arguments.\"\"\"\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
"\n",
" ...\n",
" ...\n",
"\n",
" return ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: Verify that all test cases below work (i.e., the `assert` statements must *not* raise an `AssertionError`)!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"assert product(42) == 42"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"assert product(2, 5, 10) == 100"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"assert product(2, 5, 10, start=2) == 200"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"one_hundred = [2, 5, 10]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"assert product(one_hundred) == 100"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"assert product(*one_hundred) == 100"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3**: Verify that `product()` raises a `TypeError` when called without any arguments!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This implementation of `product()` is convenient to use, in particular, because we can pass it any *collection* object with or without *unpacking* it.\n",
"\n",
"However, `product()` suffers from one last flaw: We cannot pass it a **stream** of data, as modeled, for example, with a `generator` object that produces elements on a one-by-one basis.\n",
"\n",
"**Q4**: Click through the following code cells and observe what they do!\n",
"\n",
"The [*stream.py* <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_gh.png\">](https://github.com/webartifex/intro-to-python/blob/main/08_mfr/stream.py) module in the book's repository provides a `make_finite_stream()` function. It is a *factory* function creating objects of type `generator` that we use to model *streaming* data."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from stream import make_finite_stream"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"data = make_finite_stream()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"data"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"type(data)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`generator` objects are good for only *one* thing: Giving us the \"next\" element in a series of possibly *infinitely* many objects. While the `data` object is finite (i.e., execute the next code cell until you see a `StopIteration` exception), ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"next(data)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"... it has *no* concept of a \"length:\" The built-in [len() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#len) function raises a `TypeError`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"len(data)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can use the built-in [list() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#func-list) constructor to *materialize* all elements. However, in a real-world scenario, these may *not* fit into our machine's memory! If you get an empty `list` object below, you have to create a *new* `data` object above again."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"list(data)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To be more realistic, `make_finite_stream()` creates `generator` objects producing a varying number of elements."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"list(make_finite_stream())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"list(make_finite_stream())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"list(make_finite_stream())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's see what happens if we pass a `generator` object, as created by `make_finite_stream()`, instead of a materialized *collection*, like `one_hundred`, to `product()`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(make_finite_stream())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5**: What line causes the `TypeError`? What line is really the problem in `product()`? Hint: These may be different lines. Describe what happens on each line in the function's body until the exception is raised!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: Adapt `product()` one last time to make it work with `generator` objects, or more generallz *iterators*, as well!\n",
"\n",
"Hints: This task is as easy as replacing `Collection` with something else. Which of the three behaviors of *collections* do `generator` objects also exhibit? You may want to look at the documentations on the built-in [max() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#max), [min() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#min), and [sum() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#sum) functions: What kind of argument do they take?"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def product(*args, ...):\n",
" \"\"\"Multiply all arguments.\"\"\"\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
"\n",
" ...\n",
" ...\n",
"\n",
" return ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The final version of `product()` behaves like built-ins in edge cases (i.e., `sum()` also raises a `TypeError` when called without arguments), ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"... works with the arguments passed either separately as *positional* arguments, *packed* together into a single *collection* argument, or *unpacked*, ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(42)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(2, 5, 10)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product([2, 5, 10])"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(*[2, 5, 10])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"... and can handle *streaming* data with *indefinite* \"length.\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"product(make_finite_stream())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In real-world projects, the data science practitioner must decide if it is worthwhile to make a function usable in various different forms as we do in this exercise. This may be over-engineered.\n",
"\n",
"Yet, two lessons are important to take away:\n",
"- It is a good idea to *mimic* the behavior of *built-ins* when accepting arguments, and\n",
"- make functions capable of working with *streaming* data."
]
}
],
"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.12.2"
},
"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
}

1672
08_mfr/04_content.ipynb Normal file

File diff suppressed because it is too large Load diff

75
08_mfr/05_summary.ipynb Normal file
View file

@ -0,0 +1,75 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Chapter 8: Map, Filter, & Reduce (TL;DR)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"The operations we do with sequential data commonly follow the **map-filter-reduce paradigm**: We apply the same transformation to all elements, filter some of them out, and calculate summary statistics from the remaining ones.\n",
"\n",
"An essential idea in this chapter is that, in many situations, we need *not* have all the data **materialized** in memory. Instead, **iterators** allow us to process sequential data on a one-by-one basis.\n",
"\n",
"Examples for iterators are the `map`, `filter`, and `generator` types."
]
}
],
"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.12.2"
},
"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
}

214
08_mfr/06_review.ipynb Normal file
View file

@ -0,0 +1,214 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 8: Map, Filter, & Reduce (Review Questions)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The questions below assume that you have read the [first <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb), [second <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/01_content.ipynb), and [third <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/04_content.ipynb) part in Chapter 8.\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": [
"**Q1**: With the [map() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#map) and [filter() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#filter) built-ins and the [reduce() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functools.html#functools.reduce) function from the [functools <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functools.html) module in the [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html), we can replace many tedious `for`-loops and `if` statements. What are some advantages of doing so?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: Looking at the `lambda` expression inside [reduce() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functools.html#functools.reduce) below, what [built-in function <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html) is mimicked here?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```python\n",
"from functools import reduce\n",
"\n",
"numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]\n",
"\n",
"reduce(lambda x, y: x if x > y else y, numbers)\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3**: What is the primary use case of **`list` comprehensions**? Why do we describe them as **eager**?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q4**: **`generator` expressions** may replace `list` objects and list comprehensions in many scenarios. When evaluated, they create a **lazy** `generator` object that does *not* **materialize** its elements right away. What do we mean by that? What does it mean for a `generator` object to be **exhausted**?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5**: What does it mean for the **boolean reducers**, the built-in [all() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#all) and [any() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#any) functions, to follow the **short-circuiting** strategy?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: What is an **iterator**? How does it relate to an **iterable**?"
]
},
{
"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": [
"**Q7**: `lambda` expressions are useful in the context of the **map-filter-reduce** paradigm, where we often do *not* re-use a `function` object more than once."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q8**: Using **`generator` expressions** in place of **`list` comprehensions** wherever possible is a good practice as it makes our programs use memory more efficiently."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q9**: Just as **`list` comprehensions** create `list` objects, **`tuple` comprehensions** create `tuple` objects."
]
},
{
"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.12.2"
},
"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
}

51
08_mfr/stream.py Normal file
View file

@ -0,0 +1,51 @@
"""Simulation of random streams of data.
This module defines:
- a generator object `data` modeling an infinite stream of integers
- a function `make_finite_stream()` that creates finite streams of data
The probability distribution underlying the integers is Gaussian-like with a
mean of 42 and a standard deviation of 8. The left tail of the distribution is
cut off meaning that the streams only produce non-negative numbers. Further,
one in a hundred random numbers has an increased chance to be an outlier.
"""
import itertools as _itertools
import random as _random
_random.seed(87)
def _infinite_stream():
"""Internal generator function to simulate an infinite stream of data."""
while True:
number = max(0, int(_random.gauss(42, 8)))
if _random.randint(1, 100) == 1:
number *= 2
yield number
def make_finite_stream(min_=5, max_=15):
"""Simulate a finite stream of data.
The returned stream is finite, but the number of elements to be produced
by it is still random. This default behavior may be turned off by passing
in `min_` and `max_` arguments with `min_ == max_`.
Args:
min_ (optional, int): minimum numbers in the stream; defaults to 5
max_ (optional, int): maximum numbers in the stream; defaults to 15
Returns:
finite_stream (generator)
Raises:
ValueError: if max_ < min_
"""
stream = _infinite_stream()
n = _random.randint(min_, max_)
yield from _itertools.islice(stream, n)
data = _infinite_stream()

3773
09_mappings/00_content.ipynb Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,428 @@
{
"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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/01_exercises.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 9: Mappings & Sets (Coding Exercises)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The exercises below assume that you have read the [first part <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/00_content.ipynb) of Chapter 9.\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": [
"## Working with Nested Data"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's write some code to analyze the historic soccer game [Brazil vs. Germany <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Brazil_v_Germany_%282014_FIFA_World_Cup%29) during the 2014 World Cup.\n",
"\n",
"Below, `players` consists of two nested `dict` objects, one for each team, that hold `tuple` objects (i.e., records) with information on the players. Besides the jersey number, name, and position, each `tuple` objects contains a `list` object with the times when the player scored."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"players = {\n",
" \"Brazil\": [\n",
" (12, \"Júlio César\", \"Goalkeeper\", []),\n",
" (4, \"David Luiz\", \"Defender\", []),\n",
" (6, \"Marcelo\", \"Defender\", []),\n",
" (13, \"Dante\", \"Defender\", []),\n",
" (23, \"Maicon\", \"Defender\", []),\n",
" (5, \"Fernandinho\", \"Midfielder\", []),\n",
" (7, \"Hulk\", \"Midfielder\", []),\n",
" (8, \"Paulinho\", \"Midfielder\", []),\n",
" (11, \"Oscar\", \"Midfielder\", [90]),\n",
" (16, \"Ramires\", \"Midfielder\", []),\n",
" (17, \"Luiz Gustavo\", \"Midfielder\", []),\n",
" (19, \"Willian\", \"Midfielder\", []),\n",
" (9, \"Fred\", \"Striker\", []),\n",
" ],\n",
" \"Germany\": [\n",
" (1, \"Manuel Neuer\", \"Goalkeeper\", []),\n",
" (4, \"Benedikt Höwedes\", \"Defender\", []),\n",
" (5, \"Mats Hummels\", \"Defender\", []),\n",
" (16, \"Philipp Lahm\", \"Defender\", []),\n",
" (17, \"Per Mertesacker\", \"Defender\", []),\n",
" (20, \"Jérôme Boateng\", \"Defender\", []),\n",
" (6, \"Sami Khedira\", \"Midfielder\", [29]),\n",
" (7, \"Bastian Schweinsteiger\", \"Midfielder\", []),\n",
" (8, \"Mesut Özil\", \"Midfielder\", []),\n",
" (13, \"Thomas Müller\", \"Midfielder\", [11]),\n",
" (14, \"Julian Draxler\", \"Midfielder\", []),\n",
" (18, \"Toni Kroos\", \"Midfielder\", [24, 26]),\n",
" (9, \"André Schürrle\", \"Striker\", [69, 79]),\n",
" (11, \"Miroslav Klose\", \"Striker\", [23]),\n",
" ],\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q1**: Write a dictionary comprehension to derive a new `dict` object, called `brazilian_players`, that maps a Brazilian player's name to his position!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"brazilian_players = {...: ...}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"brazilian_players"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: Generalize the code fragment into a `get_players()` function: Passed a `team` name, it returns a `dict` object like `brazilian_players`. Verify that the function works for the German team as well!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def get_players(team):\n",
" \"\"\"Creates a dictionary mapping the players' names to their position.\"\"\"\n",
" return {...: ...}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"get_players(\"Germany\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Often, we are given a `dict` object like the one returned from `get_players()`: Its main characteristic is that it maps a large set of unique keys (i.e., the players' names) onto a smaller set of non-unique values (i.e., the positions).\n",
"\n",
"**Q3**: Create a generic `invert()` function that swaps the keys and values of a `mapping` argument passed to it and returns them in a *new* `dict` object! Ensure that *no* key gets lost! Verify your implementation with the `brazilian_players` dictionary!\n",
"\n",
"Hints: Think of this as a grouping operation. The *new* values are `list` or `tuple` objects that hold the original keys. You may want to use either the [defaultdict <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.html#collections.defaultdict) type from the [collections <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.html) module in the [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html) or the [.setdefault() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/stdtypes.html#dict.setdefault) method on the ordinary `dict` type."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def invert(mapping):\n",
" \"\"\"Invert the keys and values of a mapping argument.\"\"\"\n",
" ...\n",
" ...\n",
" ...\n",
" return ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"invert(brazilian_players)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q4**: Write a `score_at_minute()` function: It takes two arguments, `team` and `minute`, and returns the number of goals the `team` has scored up until this time in the game.\n",
"\n",
"Hints: The function may reference the global `players` for simplicity. Earn bonus points if you can write this in a one-line expression using some *reduction* function and a `generator` expression."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def score_at_minute(team, minute):\n",
" \"\"\"Determine the number of goals scored by a team until a given minute.\"\"\"\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" return ..."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The score at half time was:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"score_at_minute(\"Brazil\", 45)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"score_at_minute(\"Germany\", 45)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The final score was:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"score_at_minute(\"Brazil\", 90)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"score_at_minute(\"Germany\", 90)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5**: Write a `goals_by_player()` function that takes an argument like the global `players`, and returns a `dict` object mapping the players to the number of goals they scored!\n",
"\n",
"Hints: Do *not* \"hard code\" the names of the teams! Earn bonus points if you can solve it in a one-line `dict` comprehension."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def goals_by_player(players):\n",
" \"\"\"Create a dictionary mapping the players' names to the number of goals.\"\"\"\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" return ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"goals_by_player(players)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: Write a `dict` comprehension to filter out the players who did *not* score from the preceding result.\n",
"\n",
"Hints: Reference the `goals_by_player()` function from before."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"{...: ...}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q7**: Write a `all_goals()` function that takes one argument like the global `players` and returns a `list` object containing $2$-element `tuple` objects where the first element is the minute a player scored and the second his name! The list should be sorted by the time.\n",
"\n",
"Hints: You may want to use either the built-in [sorted() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#sorted) function or the `list` type's [.sort() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/stdtypes.html#list.sort) method. Earn bonus points if you can write a one-line expression with a `generator` expression."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def all_goals(players):\n",
" \"\"\"Create a time table of the individual goals.\"\"\"\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" return ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"all_goals(players)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q8**: Lastly, write a `summary()` function that takes one argument like the global `players` and prints out a concise report of the goals, the score at the half, and the final result.\n",
"\n",
"Hints: Use the `all_goals()` and `score_at_minute()` functions from before.\n",
"\n",
"The output should look similar to this:\n",
"```\n",
"12' Gerd Müller scores\n",
"...\n",
"HALFTIME: TeamA 1 TeamB 2\n",
"77' Ronaldo scores\n",
"...\n",
"FINAL: TeamA 1 TeamB 3\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def summary(players):\n",
" \"\"\"Create a written summary of the game.\"\"\"\n",
" # Create two lists with the goals of either half.\n",
" ...\n",
" ...\n",
" ...\n",
"\n",
" # Print the goals of the first half.\n",
" ...\n",
" ...\n",
"\n",
" # Print the half time score.\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
"\n",
" # Print the goals of the second half.\n",
" ...\n",
" ...\n",
"\n",
" # Print the final score.\n",
" ...\n",
" ...\n",
" ...\n",
" ...\n",
" ..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"summary(players)"
]
}
],
"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.12.2"
},
"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
}

1236
09_mappings/02_content.ipynb Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,248 @@
{
"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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/03_exercises.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 9: Mappings & Sets (Coding Exercises)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The exercises below assume that you have read the [second part <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/02_content.ipynb) of Chapter 9.\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": [
"## Memoization without Side Effects"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"It is considered *bad practice* to make a function and thereby its correctness dependent on a program's *global state*: For example, in the \"*Easy at second Glance: Fibonacci Numbers*\" section in [Chapter 9 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/02_content.ipynb#\"Easy-at-second-Glance\"-Example:-Fibonacci-Numbers--%28revisited%29), we use a global `memo` to store the Fibonacci numbers that have already been calculated.\n",
"\n",
"That `memo` dictionary could be \"manipulated.\" More often than not, such things happen by accident: Imagine we wrote two independent recursive functions that both rely on memoization to solve different problems, and, unintentionally, we made both work with the *same* global `memo`. As a result, we would observe \"random\" bugs depending on the order in which we executed these functions. Such bugs are hard to track down in practice.\n",
"\n",
"A common remedy is to avoid global state and pass intermediate results \"down\" the recursion tree in a \"hidden\" argument. By convention, we prefix parameter names with a single leading underscore `_`, such as with `_memo` below, to indicate that the caller of our `fibonacci()` function *must not* use it. Also, we make `_memo` a *keyword-only* argument to force ourselves to always explicitly name it in a function call. Because it is an **implementation detail**, the `_memo` parameter is *not* mentioned in the docstring.\n",
"\n",
"Your task is to complete this version of `fibonacci()` so that the function works *without* any **side effects** in the global scope."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"### \"Easy at third Glance\" Example: [Fibonacci Numbers <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Fibonacci_number)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"code_folding": [],
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"def fibonacci(i, *, debug=False, _memo=None):\n",
" \"\"\"Calculate the ith Fibonacci number.\n",
"\n",
" Args:\n",
" i (int): index of the Fibonacci number to calculate\n",
" debug (bool): show non-cached calls; defaults to False\n",
"\n",
" Returns:\n",
" ith_fibonacci (int)\n",
" \"\"\"\n",
" # answer to Q1\n",
" if ...:\n",
" ... = {\n",
" 0: 0,\n",
" 1: 1,\n",
" }\n",
"\n",
" # answer to Q2\n",
" if ...:\n",
" return ...\n",
"\n",
" if debug: # added for didactical purposes\n",
" print(f\"fibonacci({i}) is calculated\")\n",
"\n",
" # answer to Q3\n",
" recurse = (\n",
" fibonacci(...)\n",
" + fibonacci(...)\n",
" )\n",
" # answer to Q4\n",
" ... = ...\n",
" return ..."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"**Q1**: When `fibonacci()` is initially called, `_memo` is set to `None`. So, there is *no* `dict` object yet. Implement the *two* base cases in the first `if` statement!\n",
"\n",
"Hints: All you need to do is create a *new* `dict` object with the results for `i=0` and `i=1`. This object is then passed on in the recursive function calls. Use the `is` operator in the `if` statement."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: When `fibonacci()` is called for non-base cases (i.e., `i > 1`), it first checks if the result is already in the `_memo`. Implement that step in the second `if` statement!\n",
"\n",
"Hint: Use the early exit pattern."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3**: If `fibonacci()` is called for an `i` argument whose result is not yet in the `_memo`, it must calculate it with the usual recursive function calls. Fill in the arguments to the two recursive `fibonacci()` calls!\n",
"\n",
"Hint: You must pass on the hidden `_memo`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q4**: Lastly, after the two recursive calls have returned, `fibonacci()` must store the `recurse` result for the given `i` in the `_memo` *before* returning it. Implement that logic!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5**: What happens to the hidden `_memo` after the initial call to `fibonacci()` returned? How many hidden `_memo` objects exist in memory during the entire computation?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Because `fibonacci()` is now independent of the *global state*, the same eleven recursive function calls are made each time! So, this `fibonacci()` is a **pure** function, meaning it has *no* side effects.\n",
"\n",
"**Q6**: Execute the following code cell a couple of times to observe that!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"fibonacci(12, debug=True) # = 13th Fibonacci number -> 11 recursive calls necessary"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The runtime of `fibonacci()` is now stable: There is no message that \"an intermediate result is being cached\" as in [Chapter 9 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/02_content.ipynb#\"Easy-at-second-Glance\"-Example:-Fibonacci-Numbers--%28revisited%29).\n",
"\n",
"**Q7**: Execute the following code cells a couple of times to observe that!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%%timeit -n 1\n",
"fibonacci(99)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%%timeit -n 1\n",
"fibonacci(999)"
]
}
],
"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.12.2"
},
"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
}

1605
09_mappings/04_content.ipynb Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,970 @@
{
"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 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_mb.png\">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/05_appendix.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Chapter 9: Mappings & Sets (Appendix)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"The [collections <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.html) module in the [standard library <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/index.html) provides specialized mapping types for common use cases."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"## The `defaultdict` Type"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"The [defaultdict <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.html#collections.defaultdict) type allows us to define a factory function that creates default values whenever we look up a key that does not yet exist. Ordinary `dict` objects would throw a `KeyError` exception in such situations.\n",
"\n",
"Let's say we have a `list` with *records* of goals scored during a soccer game. The records consist of the fields \"Country,\" \"Player,\" and the \"Time\" when a goal was scored. Our task is to group the goals by player and/or country."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"goals = [\n",
" (\"Germany\", \"Müller\", 11), (\"Germany\", \"Klose\", 23),\n",
" (\"Germany\", \"Kroos\", 24), (\"Germany\", \"Kroos\", 26),\n",
" (\"Germany\", \"Khedira\", 29), (\"Germany\", \"Schürrle\", 69),\n",
" (\"Germany\", \"Schürrle\", 79), (\"Brazil\", \"Oscar\", 90),\n",
"]"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Using a normal `dict` object, we have to tediously check if a player has already scored a goal before. If not, we must create a *new* `list` object with the first time the player scored. Otherwise, we append the goal to an already existing `list` object."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"{'Müller': [11],\n",
" 'Klose': [23],\n",
" 'Kroos': [24, 26],\n",
" 'Khedira': [29],\n",
" 'Schürrle': [69, 79],\n",
" 'Oscar': [90]}"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"goals_by_player = {}\n",
"\n",
"for _, player, minute in goals:\n",
" if player not in goals_by_player:\n",
" goals_by_player[player] = [minute]\n",
" else:\n",
" goals_by_player[player].append(minute)\n",
"\n",
"goals_by_player"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Instead, with a `defaultdict` object, we can portray the code fragment's intent in a concise form. We pass a reference to the [list() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#func-list) built-in to `defaultdict`."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"from collections import defaultdict"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"defaultdict(list,\n",
" {'Müller': [11],\n",
" 'Klose': [23],\n",
" 'Kroos': [24, 26],\n",
" 'Khedira': [29],\n",
" 'Schürrle': [69, 79],\n",
" 'Oscar': [90]})"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"goals_by_player = defaultdict(list)\n",
"\n",
"for _, player, minute in goals:\n",
" goals_by_player[player].append(minute)\n",
"\n",
"goals_by_player"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"collections.defaultdict"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(goals_by_player)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"A reference to the factory function is stored in the `default_factory` attribute."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"list"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"goals_by_player.default_factory"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"If we want this code to produce a normal `dict` object, we pass `goals_by_player` to the [dict() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#func-dict) constructor."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"{'Müller': [11],\n",
" 'Klose': [23],\n",
" 'Kroos': [24, 26],\n",
" 'Khedira': [29],\n",
" 'Schürrle': [69, 79],\n",
" 'Oscar': [90]}"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dict(goals_by_player)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Being creative, we use a factory function, created with a `lambda` expression, that returns another [defaultdict <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.html#collections.defaultdict) with [list() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#func-list) as its factory to group on the country and the player level simultaneously."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"defaultdict(<function __main__.<lambda>()>,\n",
" {'Germany': defaultdict(list,\n",
" {'Müller': [11],\n",
" 'Klose': [23],\n",
" 'Kroos': [24, 26],\n",
" 'Khedira': [29],\n",
" 'Schürrle': [69, 79]}),\n",
" 'Brazil': defaultdict(list, {'Oscar': [90]})})"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"goals_by_country_and_player = defaultdict(lambda: defaultdict(list))\n",
"\n",
"for country, player, minute in goals:\n",
" goals_by_country_and_player[country][player].append(minute)\n",
"\n",
"goals_by_country_and_player"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Conversion into a normal and nested `dict` object is now a bit tricky but can be achieved in one line with a comprehension."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"{'Germany': {'Müller': [11],\n",
" 'Klose': [23],\n",
" 'Kroos': [24, 26],\n",
" 'Khedira': [29],\n",
" 'Schürrle': [69, 79]},\n",
" 'Brazil': {'Oscar': [90]}}"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"{country: dict(by_player) for country, by_player in goals_by_country_and_player.items()}"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"## The `Counter` Type"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"A common task is to count the number of occurrences of elements in an iterable.\n",
"\n",
"The [Counter <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.html#collections.Counter) type provides an easy-to-use interface that can be called with any iterable and returns a `dict`-like object of type `Counter` that maps each unique elements to the number of times it occurs.\n",
"\n",
"To continue the previous example, let's create an overview that shows how many goals a player scorred. We use a generator expression as the argument to `Counter`."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[('Germany', 'Müller', 11),\n",
" ('Germany', 'Klose', 23),\n",
" ('Germany', 'Kroos', 24),\n",
" ('Germany', 'Kroos', 26),\n",
" ('Germany', 'Khedira', 29),\n",
" ('Germany', 'Schürrle', 69),\n",
" ('Germany', 'Schürrle', 79),\n",
" ('Brazil', 'Oscar', 90)]"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"goals"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"from collections import Counter"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"scorers = Counter(x[1] for x in goals)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"Counter({'Kroos': 2,\n",
" 'Schürrle': 2,\n",
" 'Müller': 1,\n",
" 'Klose': 1,\n",
" 'Khedira': 1,\n",
" 'Oscar': 1})"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"scorers"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"collections.Counter"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"type(scorers)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Now we can look up individual players. `scores` behaves like a normal dictionary with regard to key look-ups."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"1"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"scorers[\"Müller\"]"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"By default, it returns `0` if a key is not found. So, we do not have to handle a `KeyError`."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"scorers[\"Lahm\"]"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"`Counter` objects have a [.most_common() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.html#collections.Counter.most_common) method that returns a `list` object containing $2$-element `tuple` objects, where the first element is the element from the original iterable and the second the number of occurrences. The `list` object is sorted in descending order of occurrences."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[('Kroos', 2), ('Schürrle', 2)]"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"scorers.most_common(2)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"We can increase the count of individual entries with the [.update() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.html#collections.Counter.update) method: That takes an *iterable* of the elements we want to count.\n",
"\n",
"Imagine if [Philipp Lahm <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Philipp_Lahm) had also scored against Brazil."
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"scorers.update([\"Lahm\"])"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"Counter({'Kroos': 2,\n",
" 'Schürrle': 2,\n",
" 'Müller': 1,\n",
" 'Klose': 1,\n",
" 'Khedira': 1,\n",
" 'Oscar': 1,\n",
" 'Lahm': 1})"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"scorers"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"If we use a `str` object as the argument instead, each individual character is treated as an element to be updated. That is most likely not what we want."
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"scorers.update(\"Lahm\")"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"data": {
"text/plain": [
"Counter({'Kroos': 2,\n",
" 'Schürrle': 2,\n",
" 'Müller': 1,\n",
" 'Klose': 1,\n",
" 'Khedira': 1,\n",
" 'Oscar': 1,\n",
" 'Lahm': 1,\n",
" 'L': 1,\n",
" 'a': 1,\n",
" 'h': 1,\n",
" 'm': 1})"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"scorers"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"## The `ChainMap` Type"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Consider `to_words`, `more_words`, and `even_more_words` below. Instead of merging the items of the three `dict` objects together into a *new* one, we want to create an object that behaves as if it contained all the unified items in it without materializing them in memory a second time."
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"to_words = {\n",
" 0: \"zero\",\n",
" 1: \"one\",\n",
" 2: \"two\",\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"more_words = {\n",
" 2: \"TWO\", # to illustrate a point\n",
" 3: \"three\",\n",
" 4: \"four\",\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"even_more_words = {\n",
" 4: \"FOUR\", # to illustrate a point\n",
" 5: \"five\",\n",
" 6: \"six\",\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"The [ChainMap <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/collections.html#collections.ChainMap) type allows us to do precisely that."
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"from collections import ChainMap"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"We simply pass all mappings as positional arguments to `ChainMap` and obtain a **proxy** object that occupies almost no memory but gives us access to the union of all the items."
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [],
"source": [
"chain = ChainMap(to_words, more_words, even_more_words)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"Let's loop over the items in `chain` and see what is \"in\" it. The order is obviously *unpredictable* but all seven items we expected are there. Keys of later mappings do *not* overwrite earlier keys."
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"4 four\n",
"5 five\n",
"6 six\n",
"2 two\n",
"3 three\n",
"0 zero\n",
"1 one\n"
]
}
],
"source": [
"for number, word in chain.items():\n",
" print(number, word)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"When looking up a non-existent key, `ChainMap` objects raise a `KeyError` just like normal `dict` objects would."
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"outputs": [
{
"ename": "KeyError",
"evalue": "10",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[0;32mIn[28], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mchain\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m10\u001b[39;49m\u001b[43m]\u001b[49m\n",
"File \u001b[0;32m/usr/lib64/python3.12/collections/__init__.py:1014\u001b[0m, in \u001b[0;36mChainMap.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 1012\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m:\n\u001b[1;32m 1013\u001b[0m \u001b[38;5;28;01mpass\u001b[39;00m\n\u001b[0;32m-> 1014\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__missing__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mkey\u001b[49m\u001b[43m)\u001b[49m\n",
"File \u001b[0;32m/usr/lib64/python3.12/collections/__init__.py:1006\u001b[0m, in \u001b[0;36mChainMap.__missing__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 1005\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__missing__\u001b[39m(\u001b[38;5;28mself\u001b[39m, key):\n\u001b[0;32m-> 1006\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m(key)\n",
"\u001b[0;31mKeyError\u001b[0m: 10"
]
}
],
"source": [
"chain[10]"
]
}
],
"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.12.2"
},
"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
}

View file

@ -0,0 +1,77 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Chapter 9: Mappings & Sets (TL;DR)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"`dict` objects are **mutable** one-to-one **mappings** from a set of **key** objects to a set of **value** objects. The association between a key-value pair is also called **item**.\n",
"\n",
"The items contained in a `dict` object have **no order** that is *predictable*.\n",
"\n",
"The underlying **data structure** of the `dict` type are **hash tables**. They make key look-ups *extremely* fast by converting the items' keys into *deterministic* hash values specifiying *precisely* one of a fixed number of equally \"wide\" buckets in which an item's references are stored. A limitation is that objects used as keys must be *immutable* (for technical reasons) and *unique* (for practical reasons).\n",
"\n",
"A `set` object is a **mutable** and **unordered collection** of **immutable** objects. The `set` type mimics sets we know from math."
]
}
],
"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.12.2"
},
"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
}

247
09_mappings/07_review.ipynb Normal file
View file

@ -0,0 +1,247 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 9: Mappings & Sets (Review Questions)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The questions below assume that you have read the [first <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/00_content.ipynb), [second <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/02_content.ipynb), and [third <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/04_content.ipynb) part of Chapter 9.\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": [
"**Q1**: `dict` objects are well-suited **to model** discrete mathematical **functions** and to approximate continuous ones. What property of dictionaries is the basis for that claim, and how does it relate to functions in the mathematical sense?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: Explain why **hash tables** are a **trade-off** between **computational speed** and **memory** usage!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3:** The `dict` type is an **iterable** that **contains** a **finite** number of key-value pairs. Despite that, why is it *not* considered a **sequence**?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q4**: Whereas *key* **look-ups** in a `dict` object run in so-called **[constant time <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_wiki.png\">](https://en.wikipedia.org/wiki/Time_complexity#Constant_time)** (i.e., *extremely* fast), that does not hold for *reverse* look-ups. Why is that?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5**: Why is it conceptually correct that the Python core developers do not implement **slicing** with the `[]` operator for `dict` objects?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: **Memoization** is an essential concept to know to solve problems in the real world. Together with the idea of **recursion**, it enables us to solve problems in a \"backwards\" fashion *effectively*.\n",
"\n",
"Compare the **recursive** formulation of `fibonacci()` in [Chapter 9 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/02_content.ipynb#\"Easy-at-second-Glance\"-Example:-Fibonacci-Numbers--(revisited)), the \"*Easy at second Glance*\" example, with the **iterative** version in [Chapter 4 <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/02_content.ipynb#\"Hard-at-first-Glance\"-Example:-Fibonacci-Numbers-%28revisited%29), the \"*Hard at first Glance*\" example!\n",
"\n",
"How are they similar and how do they differ? Also consider how the flow of execution behaves when the functions are being executed."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q7**: How are the `set` and the `dict` types related? How could we use the latter to mimic the former?"
]
},
{
"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": [
"**Q8**: We may *not* put `dict` objects inside other `dict` objects because they are **mutable**."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q9**: **Mutable** objects (e.g., `list`) may generally *not* be used as keys in a `dict` object. However, if we collect, for example, `list` objects in a `tuple` object, the composite object becomes **hashable**."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q10**: **Mutability** of a `dict` object works until the underlying hash table becomes too crowded. Then, we cannot insert any items any more making the `dict` object effectively **immutable**. Luckily, that almost never happens in practice."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q11**: A `dict` object's [.update() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/stdtypes.html#dict.update) method only inserts key-value pairs whose key is *not* yet in the `dict` object. So, it does *not* overwrite anything."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q12**: The `set` type is both a mapping and a sequence."
]
},
{
"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.12.2"
},
"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
}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

1
10_arrays/TODO.md Normal file
View file

@ -0,0 +1 @@
Here, a new section on `numpy` Arrays and, maybe, `pandas` DataFrames will be added.

2147
11_classes/00_content.ipynb Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1983
11_classes/02_content.ipynb Normal file

File diff suppressed because it is too large Load diff

2413
11_classes/03_content.ipynb Normal file

File diff suppressed because one or more lines are too long

2991
11_classes/04_content.ipynb Normal file

File diff suppressed because one or more lines are too long

100
11_classes/05_summary.ipynb Normal file
View file

@ -0,0 +1,100 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Chapter 11: Classes & Instances (TL;DR)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "skip"
}
},
"source": [
"With the `class` statement, we can create a *user-defined* data type that we also call a **class**.\n",
"\n",
"Then, to create new **instances** of the data type, we simply call the class, just as we do with the built-in constructors.\n",
"\n",
"Conceptually, a class is the **blueprint** defining the **behavior** each instance exhibits.\n",
"\n",
"In the example used throughout the chapter, the `Vector` and `Matrix` classes implement the linear algebra rules all `Vector` and `Matrix` instances follow *in general*. On the contrary, the instances **encapsulate** the **state** of *concrete* vectors and matrices.\n",
"\n",
"The `class` statement acts as a *namespace* that consists of simple *variable assignments* and *function definitions*:\n",
"1. Variables become the **class attributes** that are shared among all instances.\n",
"2. Functions may take a different role:\n",
" - By default, they become **instance methods** by going through a **binding process** where a reference to the instance on which the method is *invoked* is passed in as the first argument. By convention, the corresponding parameter is called `self`; it embodies an instance's state: That means that instance methods set and get **instance attributes** on and from `self`.\n",
" - They may be declared as **class methods**. Then, the binding process is adjusted such that the first argument passed in is a reference to the class itself and, by convention, named `cls`. A common use case is to design **alternative constructors**.\n",
" - They may also be declared as **properties**. A use case for that are *derived* attributes that follow semantically from an instance's state.\n",
" \n",
"The **Python Data Model** concerns what special methods (i.e., the ones with the dunder names) exists and how they work together.\n",
"\n",
"The instantiation process is controlled by the `.__init__()` method.\n",
"\n",
"The `__repr__()` and `__str__()` methods implement the **text representation** of an instance, which can be regarded as a Unicode encoded representation of all the state encapsulated in an instance.\n",
"\n",
"**Sequence emulation** means that a user-defined data type exhibits the same four properties as the built-in sequences, which are regarded as finite and iterable containers with a predictable order. The `.__len__()`, `.__iter__()`, `__reversed__()`, `__getitem__()`, and some others are used to implement the corresponding behaviors.\n",
"\n",
"Similarly, **number emulation** means that an instance of a user-defined data type behaves like a built-in number. For example, by implementing the `.__abs__()` method, an instance may be passed to the built-in [abs() <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_py.png\">](https://docs.python.org/3/library/functions.html#abs) function.\n",
"\n",
"If different data types, built-in or user-defined, share a well-defined set of behaviors, a single function may be written to work with objects of all the data types. We describe such functions as **polymorphic**.\n",
"\n",
"Classes may specify how *operators* are **overloaded**. Examples for that are the `.__add__()`, `.__sub__()`, or `.__eq__()` methods.\n",
"\n",
"**Packages** are folders containing **modules** (i.e., \\*.py files) and a \"*\\_\\_init\\_\\_.py*\" file. We use them to design coherent libraries with reusable code."
]
}
],
"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.12.2"
},
"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
}

271
11_classes/06_review.ipynb Normal file
View file

@ -0,0 +1,271 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Chapter 11: Classes & Instances (Review Questions)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The questions below assume that you have read the [first <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb), [second <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/02_content.ipynb), [third <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/03_content.ipynb), and [fourth <img height=\"12\" style=\"display: inline-block\" src=\"../static/link/to_nb.png\">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/04_content.ipynb) part of Chapter 11.\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": [
"**Q1**: How are **classes** a way to manage the **state** in a big program? How should we think of classes conceptually?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q2**: What do we mean with **instantiation**? How do **instances** relate to **classes**?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q3:** What is an **implementation detail**? Name two different examples of implementation details regarding the `Vector` and `Matrix` classes!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q4**: How are **instance methods** different from **class methods**? How do **special methods** fit into the picture?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q5**: How do **mutability** and **immutability** come into play when designing a user-defined data type?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q6**: Explain the concept of **method chaining**!"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q7**: How can we implement **operator overloading** for a user-defined data type? When do we need to user *reverse* special methods?"
]
},
{
"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": [
"**Q8**: An instance's **text representation** is a `bytes` object with a special encoding."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q9**: Computed **properties** are special kinds of **instance methods**."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q10**: **Sequence emulation** means designing a user-defined data type around the built-in `list` or `tuple` types."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q11**: The **Python Data Model** can be regarded as the \"Theory\" or \"Mental Model\" behind the Python language."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q12**: **Polymorphism** means that two instances have the same data type, be it a built-in or user-defined one."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q13**: **Number emulation** means that two instances of the same user-defined data type can be added together."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" < your answer >"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Q14**: **Packages** are a good place to collect all the code to be reused in a data science project, for example, across different Jupyter notebooks."
]
},
{
"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.12.2"
},
"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
}

View file

@ -0,0 +1,34 @@
"""This package provides linear algebra functionalities.
The package is split into three modules:
- matrix: defines the Matrix class
- vector: defines the Vector class
- utils: defines the norm() function that is shared by Matrix and Vector
and package-wide constants
The classes implement arithmetic operations involving vectors and matrices.
See the docstrings in the modules and classes for further info.
"""
# Import the classes here so that they are available
# from the package's top level. That means that a user
# who imports this package with `import sample_package`
# may then refer to, for example, the Matrix class with
# simply `sample_package.Matrix` instead of the longer
# `sample_package.matrix.Matrix`.
from sample_package.matrix import Matrix
from sample_package.vector import Vector
# Define meta information for the package.
# There are other (and more modern) ways of
# doing this, but specifying the following
# dunder variables here is the traditional way.
__name__ = "linear_algebra_tools"
__version__ = "0.1.0" # see https://semver.org/ for how the format works
__author__ = "Alexander Hess"
# Define what is imported with the "star import"
# (i.e., with `from sample_package import *`).
__all__ = ["Matrix", "Vector"]

View file

@ -0,0 +1,443 @@
"""This module defines a Matrix class."""
import numbers
# Note the import at the bottom of this file, and
# see the comments about imports in the matrix module.
from sample_package import utils
class Matrix:
"""An m-by-n-dimensional matrix from linear algebra.
All entries are converted to floats, or whatever is set in the typing attribute.
Attributes:
storage (callable): data type used to store the entries internally;
defaults to tuple
typing (callable): type casting applied to all entries upon creation;
defaults to float
vector_cls (vector.Vector): a reference to the Vector class to work with
zero_threshold (float): max. tolerance when comparing an entry to zero;
defaults to 1e-12
"""
storage = utils.DEFAULT_ENTRIES_STORAGE
typing = utils.DEFAULT_ENTRY_TYPE
# the `vector_cls` attribute is set at the bottom of this file
zero_threshold = utils.ZERO_THRESHOLD
def __init__(self, data):
"""Create a new matrix.
Args:
data (sequence of sequences): the matrix's entries;
viewed as a sequence of the matrix's rows (i.e., row-major order);
use the .from_columns() class method if the data come as a sequence
of the matrix's columns (i.e., column-major order)
Raises:
ValueError:
- if no entries are provided
- if the number of columns is inconsistent across the rows
Example Usage:
>>> Matrix([(1, 2), (3, 4)])
Matrix(((1.0, 2.0,), (3.0, 4.0,)))
"""
self._entries = self.storage(
self.storage(self.typing(x) for x in r) for r in data
)
for row in self._entries[1:]:
if len(row) != self.n_cols:
raise ValueError("rows must have the same number of entries")
if len(self) == 0:
raise ValueError("a matrix must have at least one entry")
@classmethod
def from_columns(cls, data):
"""Create a new matrix.
This is an alternative constructor for data provided in column-major order.
Args:
data (sequence of sequences): the matrix's entries;
viewed as a sequence of the matrix's columns (i.e., column-major order);
use the normal constructor method if the data come as a sequence
of the matrix's rows (i.e., row-major order)
Raises:
ValueError:
- if no entries are provided
- if the number of rows is inconsistent across the columns
Example Usage:
>>> Matrix.from_columns([(1, 2), (3, 4)])
Matrix(((1.0, 3.0,), (2.0, 4.0,)))
"""
return cls(data).transpose()
@classmethod
def from_rows(cls, data):
"""See docstring for .__init__()."""
# Some users may want to use this .from_rows() constructor
# to explicitly communicate that the data are in row-major order.
# Otherwise, this method is redundant.
return cls(data)
def __repr__(self):
"""Text representation of a Matrix."""
name = self.__class__.__name__
args = ", ".join(
"(" + ", ".join(repr(c) for c in r) + ",)" for r in self._entries
)
return f"{name}(({args}))"
def __str__(self):
"""Human-readable text representation of a Matrix."""
name = self.__class__.__name__
first, last, m, n = self[0], self[-1], self.n_rows, self.n_cols
return f"{name}(({first!r}, ...), ..., (..., {last!r}))[{m:d}x{n:d}]"
@property
def n_rows(self):
"""Number of rows in a Matrix."""
return len(self._entries)
@property
def n_cols(self):
"""Number of columns in a Matrix."""
return len(self._entries[0])
def __len__(self):
"""Number of entries in a Matrix."""
return self.n_rows * self.n_cols
def __getitem__(self, index):
"""Obtain an individual entry of a Matrix.
Args:
index (int / tuple of int's): if index is an integer,
the Matrix is viewed as a sequence in row-major order;
if index is a tuple of integers, the first one refers to
the row and the second one to the column of the entry
Returns:
entry (Matrix.typing)
Example Usage:
>>> m = Matrix([(1, 2), (3, 4)])
>>> m[0]
1.0
>>> m[-1]
4.0
>>> m[0, 1]
2.0
"""
# Sequence-like indexing (one-dimensional)
if isinstance(index, int):
if index < 0:
index += len(self)
if not (0 <= index < len(self)):
raise IndexError("integer index out of range")
row, col = divmod(index, self.n_cols)
return self._entries[row][col]
# Mathematical-like indexing (two-dimensional)
elif (
isinstance(index, tuple)
and len(index) == 2
and isinstance(index[0], int)
and isinstance(index[1], int)
):
return self._entries[index[0]][index[1]]
raise TypeError("index must be either an int or a tuple of two int's")
def rows(self):
"""Loop over a Matrix's rows.
Returns:
rows (generator): produces a Matrix's rows as Vectors
"""
return (self.vector_cls(r) for r in self._entries)
def cols(self):
"""Loop over a Matrix's columns.
Returns:
columns (generator): produces a Matrix's columns as Vectors
"""
return (
self.vector_cls(self._entries[r][c] for r in range(self.n_rows))
for c in range(self.n_cols)
)
def entries(self, *, reverse=False, row_major=True):
"""Loop over a Matrix's entries.
Args:
reverse (bool): flag to loop backwards; defaults to False
row_major (bool): flag to loop in row-major order; defaults to True
Returns:
entries (generator): produces a Matrix's entries
"""
if reverse:
rows = range(self.n_rows - 1, -1, -1)
cols = range(self.n_cols - 1, -1, -1)
else:
rows, cols = range(self.n_rows), range(self.n_cols)
if row_major:
return (self._entries[r][c] for r in rows for c in cols)
return (self._entries[r][c] for c in cols for r in rows)
def __iter__(self):
"""Loop over a Matrix's entries.
See .entries() for more customization options.
"""
return self.entries()
def __reversed__(self):
"""Loop over a Matrix's entries in reverse order.
See .entries() for more customization options.
"""
return self.entries(reverse=True)
def __add__(self, other):
"""Handle `self + other` and `other + self`.
This may be either matrix addition or broadcasting addition.
Example Usage:
>>> Matrix([(1, 2), (3, 4)]) + Matrix([(2, 3), (4, 5)])
Matrix(((3.0, 5.0,), (7.0, 9.0,)))
>>> Matrix([(1, 2), (3, 4)]) + 5
Matrix(((6.0, 7.0,), (8.0, 9.0,)))
>>> 10 + Matrix([(1, 2), (3, 4)])
Matrix(((11.0, 12.0,), (13.0, 14.0,)))
"""
# Matrix addition
if isinstance(other, self.__class__):
if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):
raise ValueError("matrices must have the same dimensions")
return self.__class__(
(s_col + o_col for (s_col, o_col) in zip(s_row, o_row))
for (s_row, o_row) in zip(self._entries, other._entries)
)
# Broadcasting addition
elif isinstance(other, numbers.Number):
return self.__class__((c + other for c in r) for r in self._entries)
return NotImplemented
def __radd__(self, other):
"""See docstring for .__add__()."""
if isinstance(other, self.vector_cls):
raise TypeError("vectors and matrices cannot be added")
# As both matrix and broadcasting addition are commutative,
# we dispatch to .__add__().
return self + other
def __sub__(self, other):
"""Handle `self - other` and `other - self`.
This may be either matrix subtraction or broadcasting subtraction.
Example Usage:
>>> Matrix([(2, 3), (4, 5)]) - Matrix([(1, 2), (3, 4)])
Matrix(((1.0, 1.0,), (1.0, 1.0,)))
>>> Matrix([(1, 2), (3, 4)]) - 1
Matrix(((0.0, 1.0,), (2.0, 3.0,)))
>>> 10 - Matrix([(1, 2), (3, 4)])
Matrix(((9.0, 8.0,), (7.0, 6.0,)))
"""
# As subtraction is the inverse of addition,
# we first dispatch to .__neg__() to invert the signs of
# all entries in other and then dispatch to .__add__().
return self + (-other)
def __rsub__(self, other):
"""See docstring for .__sub__()."""
if isinstance(other, self.vector_cls):
raise TypeError("vectors and matrices cannot be subtracted")
# Same comments as in .__sub__() apply
# with the roles of self and other swapped.
return (-self) + other
def _matrix_multiply(self, other):
"""Internal utility method to multiply to Matrix instances."""
if self.n_cols != other.n_rows:
raise ValueError("matrices must have compatible dimensions")
# Matrix-matrix multiplication means that each entry of the resulting
# Matrix is the dot product of the respective row of the "left" Matrix
# and column of the "right" Matrix. So, the rows/columns are represented
# by the Vector instances provided by the .cols() and .rows() methods.
return self.__class__((rv * cv for cv in other.cols()) for rv in self.rows())
def __mul__(self, other):
"""Handle `self * other` and `other * self`.
This may be either scalar multiplication, matrix-vector multiplication,
vector-matrix multiplication, or matrix-matrix multiplication.
Example Usage:
>>> Matrix([(1, 2), (3, 4)]) * Matrix([(1, 2), (3, 4)])
Matrix(((7.0, 10.0,), (15.0, 22.0,)))
>>> 2 * Matrix([(1, 2), (3, 4)])
Matrix(((2.0, 4.0,), (6.0, 8.0,)))
>>> Matrix([(1, 2), (3, 4)]) * 3
Matrix(((3.0, 6.0,), (9.0, 12.0,)))
Matrix-vector and vector-matrix multiplication are not commutative.
>>> from sample_package import Vector
>>> Matrix([(1, 2), (3, 4)]) * Vector([5, 6])
Vector((17.0, 39.0))
>>> Vector([5, 6]) * Matrix([(1, 2), (3, 4)])
Vector((23.0, 34.0))
"""
# Scalar multiplication
if isinstance(other, numbers.Number):
return self.__class__((x * other for x in r) for r in self._entries)
# Matrix-vector multiplication: Vector is a column Vector
elif isinstance(other, self.vector_cls):
# First, cast the other Vector as a Matrix, then do matrix-matrix
# multiplication, and lastly return the result as a Vector again.
return self._matrix_multiply(other.as_matrix()).as_vector()
# Matrix-matrix multiplication
elif isinstance(other, self.__class__):
return self._matrix_multiply(other)
return NotImplemented
def __rmul__(self, other):
"""See docstring for .__mul__()."""
# As scalar multiplication is commutative, we dispatch to .__mul__().
if isinstance(other, numbers.Number):
return self * other
# Vector-matrix multiplication: Vector is a row Vector
elif isinstance(other, self.vector_cls):
return other.as_matrix(column=False)._matrix_multiply(self).as_vector()
return NotImplemented
def __truediv__(self, other):
"""Handle `self / other`.
Divide a Matrix by a scalar.
Example Usage:
>>> Matrix([(1, 2), (3, 4)]) / 4
Matrix(((0.25, 0.5,), (0.75, 1.0,)))
"""
# As scalar division division is the same as multiplication
# with the inverse, we dispatch to .__mul__().
if isinstance(other, numbers.Number):
return self * (1 / other)
return NotImplemented
def __eq__(self, other):
"""Handle `self == other`.
Compare two Matrix instances for equality.
Example Usage:
>>> Matrix([(1, 2), (3, 4)]) == Matrix([(1, 2), (3, 4)])
True
>>> Matrix([(1, 2), (3, 4)]) == Matrix([(5, 6), (7, 8)])
False
"""
if isinstance(other, self.__class__):
if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):
raise ValueError("matrices must have the same dimensions")
for x, y in zip(self, other):
if abs(x - y) > self.zero_threshold:
return False # exit early if two corresponding entries differ
return True
return NotImplemented
def __pos__(self):
"""Handle `+self`.
This is simply an identity operator returning the Matrix itself.
"""
return self
def __neg__(self):
"""Handle `-self`.
Negate all entries of a Matrix.
"""
return self.__class__((-x for x in r) for r in self._entries)
def __abs__(self):
"""The Frobenius norm of a Matrix."""
return utils.norm(self) # uses the norm() function shared vector.Vector
def __bool__(self):
"""A Matrix is truthy if its Frobenius norm is strictly positive."""
return bool(abs(self))
def __float__(self):
"""Cast a Matrix as a scalar.
Returns:
scalar (float)
Raises:
RuntimeError: if the Matrix has more than one entry
"""
if not (self.n_rows == 1 and self.n_cols == 1):
raise RuntimeError("matrix must have exactly one entry to become a scalar")
return self[0]
def as_vector(self):
"""Get a Vector representation of a Matrix.
Returns:
vector (vector.Vector)
Raises:
RuntimeError: if one of the two dimensions, .n_rows or .n_cols, is not 1
Example Usage:
>>> Matrix([(1, 2, 3)]).as_vector()
Vector((1.0, 2.0, 3.0))
"""
if not (self.n_rows == 1 or self.n_cols == 1):
raise RuntimeError("one dimension (m or n) must be 1")
return self.vector_cls(x for x in self)
def transpose(self):
"""Switch the rows and columns of a Matrix.
Returns:
matrix (Matrix)
Example Usage:
>>> m = Matrix([(1, 2), (3, 4)])
>>> m
Matrix(((1.0, 2.0,), (3.0, 4.0,)))
>>> m.transpose()
Matrix(((1.0, 3.0,), (2.0, 4.0,)))
"""
return self.__class__(zip(*self._entries))
# This import needs to be made here as otherwise an ImportError is raised.
# That is so as both the matrix and vector modules import a class from each other.
# We call that a circular import. Whereas Python handles "circular" references
# (e.g., both the Matrix and Vector classes have methods that reference the
# respective other class), that is forbidden for imports.
from sample_package import vector
# This attribute cannot be set in the class definition
# as the vector module is only imported down here.
Matrix.vector_cls = vector.Vector

View file

@ -0,0 +1,35 @@
"""This module provides utilities for the whole package.
The defined constants are used as defaults in the Vector and Matrix classes.
The norm() function is shared by Vector.__abs__() and Matrix.__abs__().
"""
import math
# Define constants (i.e., normal variables that are, by convention, named in UPPERCASE)
# that are used as the defaults for class attributes within Vector and Matrix.
DEFAULT_ENTRIES_STORAGE = tuple
DEFAULT_ENTRY_TYPE = float
ZERO_THRESHOLD = 1e-12
def norm(vec_or_mat):
"""Calculate the Frobenius or Euclidean norm of a matrix or vector.
Find more infos here: https://en.wikipedia.org/wiki/Matrix_norm#Frobenius_norm
Args:
vec_or_mat (Vector / Matrix): object whose entries are squared and summed up
Returns:
norm (float)
Example Usage:
As Vector and Matrix objects are by design non-empty sequences,
norm() may be called, for example, with `[3, 4]` as the argument:
>>> norm([3, 4])
5.0
"""
return math.sqrt(sum(x ** 2 for x in vec_or_mat))

View file

@ -0,0 +1,262 @@
"""This module defines a Vector class."""
# Imports from the standard library go first ...
import numbers
# ... and are followed by project-internal ones.
# If third-party libraries are needed, they are
# put into a group on their own in between.
# Within a group, imports are sorted lexicographically.
from sample_package import matrix
from sample_package import utils
class Vector:
"""A one-dimensional vector from linear algebra.
All entries are converted to floats, or whatever is set in the typing attribute.
Attributes:
matrix_cls (matrix.Matrix): a reference to the Matrix class to work with
storage (callable): data type used to store the entries internally;
defaults to tuple
typing (callable): type casting applied to all entries upon creation;
defaults to float
zero_threshold (float): max. tolerance when comparing an entry to zero;
defaults to 1e-12
"""
matrix_cls = matrix.Matrix
storage = utils.DEFAULT_ENTRIES_STORAGE
typing = utils.DEFAULT_ENTRY_TYPE
zero_threshold = utils.ZERO_THRESHOLD
def __init__(self, data):
"""Create a new vector.
Args:
data (sequence): the vector's entries
Raises:
ValueError: if no entries are provided
Example Usage:
>>> Vector([1, 2, 3])
Vector((1.0, 2.0, 3.0))
>>> Vector(range(3))
Vector((0.0, 1.0, 2.0))
"""
self._entries = self.storage(self.typing(x) for x in data)
if len(self) == 0:
raise ValueError("a vector must have at least one entry")
def __repr__(self):
"""Text representation of a Vector."""
name = self.__class__.__name__
args = ", ".join(repr(x) for x in self)
return f"{name}(({args}))"
def __str__(self):
"""Human-readable text representation of a Vector."""
name = self.__class__.__name__
first, last, n_entries = self[0], self[-1], len(self)
return f"{name}({first!r}, ..., {last!r})[{n_entries:d}]"
def __len__(self):
"""Number of entries in a Vector."""
return len(self._entries)
def __getitem__(self, index):
"""Obtain an individual entry of a Vector."""
if not isinstance(index, int):
raise TypeError("index must be an integer")
return self._entries[index]
def __iter__(self):
"""Loop over a Vector's entries."""
return iter(self._entries)
def __reversed__(self):
"""Loop over a Vector's entries in reverse order."""
return reversed(self._entries)
def __add__(self, other):
"""Handle `self + other` and `other + self`.
This may be either vector addition or broadcasting addition.
Example Usage:
>>> Vector([1, 2, 3]) + Vector([2, 3, 4])
Vector((3.0, 5.0, 7.0))
>>> Vector([1, 2, 3]) + 4
Vector((5.0, 6.0, 7.0))
>>> 10 + Vector([1, 2, 3])
Vector((11.0, 12.0, 13.0))
"""
# Vector addition
if isinstance(other, self.__class__):
if len(self) != len(other):
raise ValueError("vectors must be of the same length")
return self.__class__(x + y for (x, y) in zip(self, other))
# Broadcasting addition
elif isinstance(other, numbers.Number):
return self.__class__(x + other for x in self)
return NotImplemented
def __radd__(self, other):
"""See docstring for .__add__()."""
# As both vector and broadcasting addition are commutative,
# we dispatch to .__add__().
return self + other
def __sub__(self, other):
"""Handle `self - other` and `other - self`.
This may be either vector subtraction or broadcasting subtraction.
Example Usage:
>>> Vector([7, 8, 9]) - Vector([1, 2, 3])
Vector((6.0, 6.0, 6.0))
>>> Vector([1, 2, 3]) - 1
Vector((0.0, 1.0, 2.0))
>>> 10 - Vector([1, 2, 3])
Vector((9.0, 8.0, 7.0))
"""
# As subtraction is the inverse of addition,
# we first dispatch to .__neg__() to invert the signs of
# all entries in other and then dispatch to .__add__().
return self + (-other)
def __rsub__(self, other):
"""See docstring for .__sub__()."""
# Same comments as in .__sub__() apply
# with the roles of self and other swapped.
return (-self) + other
def __mul__(self, other):
"""Handle `self * other` and `other * self`.
This may be either the dot product of two vectors or scalar multiplication.
Example Usage:
>>> Vector([1, 2, 3]) * Vector([2, 3, 4])
20.0
>>> 2 * Vector([1, 2, 3])
Vector((2.0, 4.0, 6.0))
>>> Vector([1, 2, 3]) * 3
Vector((3.0, 6.0, 9.0))
"""
# Dot product
if isinstance(other, self.__class__):
if len(self) != len(other):
raise ValueError("vectors must be of the same length")
return sum(x * y for (x, y) in zip(self, other))
# Scalar multiplication
elif isinstance(other, numbers.Number):
return self.__class__(x * other for x in self)
return NotImplemented
def __rmul__(self, other):
"""See docstring for .__mul__()."""
# As both dot product and scalar multiplication are commutative,
# we dispatch to .__mul__().
return self * other
def __truediv__(self, other):
"""Handle `self / other`.
Divide a Vector by a scalar.
Example Usage:
>>> Vector([9, 6, 12]) / 3
Vector((3.0, 2.0, 4.0))
"""
# As scalar division division is the same as multiplication
# with the inverse, we dispatch to .__mul__().
if isinstance(other, numbers.Number):
return self * (1 / other)
return NotImplemented
def __eq__(self, other):
"""Handle `self == other`.
Compare two Vectors for equality.
Example Usage:
>>> Vector([1, 2, 3]) == Vector([1, 2, 3])
True
>>> Vector([1, 2, 3]) == Vector([4, 5, 6])
False
"""
if isinstance(other, self.__class__):
if len(self) != len(other):
raise ValueError("vectors must be of the same length")
for x, y in zip(self, other):
if abs(x - y) > self.zero_threshold:
return False # exit early if two corresponding entries differ
return True
return NotImplemented
def __pos__(self):
"""Handle `+self`.
This is simply an identity operator returning the Vector itself.
"""
return self
def __neg__(self):
"""Handle `-self`.
Negate all entries of a Vector.
"""
return self.__class__(-x for x in self)
def __abs__(self):
"""The Euclidean norm of a vector."""
return utils.norm(self) # uses the norm() function shared matrix.Matrix
def __bool__(self):
"""A Vector is truthy if its Euclidean norm is strictly positive."""
return bool(abs(self))
def __float__(self):
"""Cast a Vector as a scalar.
Returns:
scalar (float)
Raises:
RuntimeError: if the Vector has more than one entry
"""
if len(self) != 1:
raise RuntimeError("vector must have exactly one entry to become a scalar")
return self[0]
def as_matrix(self, *, column=True):
"""Get a Matrix representation of a Vector.
Args:
column (bool): if the vector is interpreted as a
column vector or a row vector; defaults to True
Returns:
matrix (matrix.Matrix)
Example Usage:
>>> v = Vector([1, 2, 3])
>>> v.as_matrix()
Matrix(((1.0,), (2.0,), (3.0,)))
>>> v.as_matrix(column=False)
Matrix(((1.0, 2.0, 3.0,)))
"""
if column:
return self.matrix_cls([x] for x in self)
return self.matrix_cls([(x for x in self)])

294
CONTENTS.md Normal file
View file

@ -0,0 +1,294 @@
# Table of Contents
The materials are designed to resemble an *interactive* book.
The files come
primarily in the [Jupyter Notebook](https://jupyter-notebook.readthedocs.io/en/stable/)
format (i.e., \*.ipynb)
but also as [modules and packages <img height="12" style="display: inline-block" src="static/link/to_py.png">](https://docs.python.org/3/tutorial/modules.html)
(i.e., \*.py).
Together with some other static files (e.g., images),
they are stored in one folder per chapter in this repository.
They are to be opened
from within the [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) application,
even though other ways are certainly possible as well.
Both the files and the folders
are appropriately named with prefixes
indicating the order in which they should be read
and starting with "00_".
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 <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/tree/main/)
or interactively (i.e., code can be executed) on [Binder <img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab).
- *Chapter 0*: Introduction
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/00_intro/00_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/00_intro/00_content.ipynb)
(Python's History & Background;
Open-source & Communities;
JupyterLab;
Programming vs. Computer Science;
Learning Tips)
- [exercises <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/00_intro/01_exercises_markdown.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/00_intro/01_exercises_markdown.ipynb)
(Mastering Markdown)
- [review questions <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/00_intro/02_review.ipynb)
- **Part A: Expressing Logic**
- *Chapter 1*: Elements of a Program
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/00_content.ipynb)
(A first Example: Averaging Even Numbers;
Operators;
Objects & Data Types;
Errors)
- [exercises <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/01_exercises_print.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/01_exercises_print.ipynb)
(Printing Output)
- [exercises <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/02_exercises_for-loops.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/02_exercises_for-loops.ipynb)
(Simple `for`-loops)
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/03_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/03_content.ipynb)
(Memory in Detail;
Variables & References;
Mutability;
Expressions & Statements)
- [exercises <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/04_exercises_calculator.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/04_exercises_calculator.ipynb)
(Python as a Calculator)
- [summary <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/05_summary.ipynb)
- [review questions <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/06_review.ipynb)
- [further resources <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/07_resources.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/07_resources.ipynb)
- *Chapter 2*: Functions & Modularization
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/00_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/02_functions/00_content.ipynb)
(Built-in Functions & Constructors;
Function Definitions;
Function Calls & Scoping Rules;
Positional vs. Keyword Arguments;
Modularization)
- [exercises <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/01_exercises_sphere-volume.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/02_functions/01_exercises_sphere-volume.ipynb)
(Volume of a Sphere)
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/02_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/02_functions/02_content.ipynb)
(Standard Library: `math` & `random` Modules;
Third-party Packages: `numpy` Library;
Writing one's own Modules)
- [summary <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/03_summary.ipynb)
- [review questions <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/04_review.ipynb)
- *Chapter 3*: Conditionals & Exceptions
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/00_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/03_conditionals/00_content.ipynb)
(Boolean Expressions;
Relational Operators;
Logical Operators;
`if` statement;
Exception Handling)
- [exercises <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/01_exercises_discounts.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/03_conditionals/01_exercises_discounts.ipynb)
(Discounting Customer Orders)
- [exercises <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/02_exercises_fizz-buzz.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/03_conditionals/02_exercises_fizz-buzz.ipynb)
(Fizz Buzz)
- [summary <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/03_summary.ipynb)
- [review questions <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/04_review.ipynb)
- *Chapter 4*: Recursion & Looping
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/00_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/04_iteration/00_content.ipynb)
(Recursion;
Examples: Factorial, Euclid's Algorithm, & Fibonacci;
Duck Typing;
Type Casting & Checking;
Input Validation)
- [exercises <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/01_exercises_hanoi-towers.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/04_iteration/01_exercises_hanoi-towers.ipynb)
(Towers of Hanoi)
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/02_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/04_iteration/02_content.ipynb)
(Looping with `while` & `for`;
Examples: Collatz Conjecture, Factorial, Euclid's Algorithm, & Fibonacci;
Containers vs. Iterables)
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/03_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/04_iteration/03_content.ipynb)
(Customizing Loops with `break` and `continue`;
Indefinite Loops)
- [exercises <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/04_exercises_dice.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/04_iteration/04_exercises_dice.ipynb)
(Throwing Dice)
- [summary <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/05_summary.ipynb)
- [review questions <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/06_review.ipynb)
- **Part B: Managing Data and Memory**
- *Chapter 5*: Numbers & Bits
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/00_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/05_numbers/00_content.ipynb)
(`int` Type;
Binary & Hexadecimal Representations;
Bit Arithmetic;
Bitwise Operators)
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/01_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/05_numbers/01_content.ipynb)
(`float` Type;
Floating-point Standard;
Special Values;
Imprecision;
Binary & Hexadecimal Representations)
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/02_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/05_numbers/02_content.ipynb)
(`complex` Type;
Numerical Tower;
Duck vs. Goose Typing)
- [appendix <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/03_appendix.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/05_numbers/03_appendix.ipynb)
(`Decimal` Type;
`Fraction` Type)
- [summary <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/04_summary.ipynb)
- [review questions <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/05_review.ipynb)
- [further resources <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/06_resources.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/05_numbers/06_resources.ipynb)
- *Chapter 6*: Text & Bytes
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/00_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/06_text/00_content.ipynb)
(`str` Type;
Reading Files;
Sequences;
Indexing & Slicing;
String Methods & Operations;
String Interpolation)
- [exercises <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/01_exercises_palindromes.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/06_text/01_exercises_palindromes.ipynb)
(Detecting Palindromes)
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/02_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/06_text/02_content.ipynb)
(Special Characters;
ASCII & Unicode;
Multi-line Strings;
`bytes` Type;
Character Encodings)
- [summary <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/03_summary.ipynb)
- [review questions <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/04_review.ipynb)
- [further resources <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/05_resources.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/06_text/05_resources.ipynb)
- *Chapter 7*: Sequential Data
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/00_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/00_content.ipynb)
(Collections vs. Sequences;
ABCs: `Container`, `Iterable`, `Sized`, & `Reversible`)
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/01_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/01_content.ipynb)
(`list` Type;
Indexing & Slicing;
Shallow vs. Deep Copies;
List Methods & Operations)
- [exercises <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/02_exercises_lists.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/02_exercises_lists.ipynb)
(Working with Lists)
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/03_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/03_content.ipynb)
(Modifiers vs. Pure Functions;
`tuple` Type;
Packing & Unpacking;
`*args` in Function Definitions)
- [exercises <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/04_exercises_un-packing.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/04_exercises_un-packing.ipynb)
(Packing & Unpacking with Functions)
- [appendix <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/05_appendix.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/05_appendix.ipynb)
(`namedtuple` Type)
- [summary <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/06_summary.ipynb)
- [review questions <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/07_review.ipynb)
- *Chapter 8*: Map, Filter, & Reduce
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/08_mfr/00_content.ipynb)
(Mapping;
Filtering;
Reducing;
`lambda` Expression)
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/01_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/08_mfr/01_content.ipynb)
(`list` Comprehension;
`generator` Expression;
Streams of Data;
Boolean Reducers)
- [exercises <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/02_exercises_outliers.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/08_mfr/02_exercises_outliers.ipynb)
(Removing Outliers in Streaming Data)
- [exercises <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/03_exercises_un-packing.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/08_mfr/03_exercises_un-packing.ipynb)
(Packing & Unpacking with Functions, continued)
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/04_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/08_mfr/04_content.ipynb)
(Iterators vs. Iterables;
Example: `sorted()` vs. `reversed()`)
- [summary <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/05_summary.ipynb)
- [review questions <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/06_review.ipynb)
- *Chapter 9*: Mappings & Sets
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/00_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/00_content.ipynb)
(`dict` Type;
Nested Data;
Hash Tables;
`dict` Methods & Behavior;
`dict` Comprehension)
- [exercises <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/01_exercises_nested-data.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/01_exercises_nested-data.ipynb)
(Working with Nested Data)
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/02_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/02_content.ipynb)
(`**kwargs` in Function Definitions;
Memoization)
- [exercises <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/03_exercises_fibonacci.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/03_exercises_fibonacci.ipynb)
(Memoization without Side Effects)
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/04_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/04_content.ipynb)
(`set` Type;
`set` Methods & Operations;
`set` Comprehension;
`frozenset` Type)
- [appendix <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/05_appendix.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/05_appendix.ipynb)
(`defaultdict` Type;
`Counter` Type;
`ChainMap` Type)
- [summary <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/06_summary.ipynb)
- [review questions <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/07_review.ipynb)
- [further resources <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/08_resources.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/08_resources.ipynb)
- *Chapter 10*: Arrays & Dataframes
- *Chapter 11*: Classes & Instances
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/11_classes/00_content.ipynb)
(`class` Statement;
Instantiation;
Text Representations;
Instance Methods vs. Class Methods;
Computed Properties)
- [exercises <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/01_exercises_tsp.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/11_classes/01_exercises_tsp.ipynb)
(A Traveling Salesman Problem)
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/02_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/11_classes/02_content.ipynb)
(Sequence Emulation & Iteration;
Python's Data Model;
(Im)mutable Data Types;
Method Chaining;
Polymorphism)
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/03_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/11_classes/03_content.ipynb)
(Operator Overloading: Arithmetic & Relational Operators;
Number Emulation)
- [content <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/04_content.ipynb)
[<img height="12" style="display: inline-block" src="static/link/to_mb.png">](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/11_classes/04_content.ipynb)
(Writing one's own Packages;
The final `Vector` & `Matrix` Classes;
Comparison with `numpy`)
- [summary <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/05_summary.ipynb)
- [review questions <img height="12" style="display: inline-block" src="static/link/to_nb.png">](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/06_review.ipynb)

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2018-2020 Alexander Hess [alexander@webartifex.biz]
Copyright (c) 2018-2024 Alexander Hess [alexander@webartifex.biz]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

256
README.md
View file

@ -1,7 +1,257 @@
# An Introduction to Python and Programming
This project is a thorough introductory course
in programming with **[Python <img height="12" style="display: inline-block" src="static/link/to_py.png">](https://www.python.org/)**.
This project is a *thorough* introductory course
in programming with **[Python <img height="12" style="display: inline-block" src="static/link/to_py.png">](https://www.python.org/)**.
### Table of Contents
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
- **Part A: Expressing Logic**
- *Chapter 1*: Elements of a Program
- *Chapter 2*: Functions & Modularization
- *Chapter 3*: Conditionals & Exceptions
- *Chapter 4*: Recursion & Looping
- **Part B: Managing Data and Memory**
- *Chapter 5*: Numbers & Bits
- *Chapter 6*: Text & Bytes
- *Chapter 7*: Sequential Data
- *Chapter 8*: Map, Filter, & Reduce
- *Chapter 9*: Mappings & Sets
- *Chapter 10*: Arrays & Dataframes
- *Chapter 11*: Classes & Instances
#### Videos
Presentations of the chapters are available on this [YouTube playlist <img height="12" style="display: inline-block" src="static/link/to_yt.png">](https://www.youtube.com/playlist?list=PL-2JV1G3J10kRUPgP7EwLhyeN5lOZW2kH).
The recordings are about 25 hours long in total
and were made in spring 2020
after a corresponding in-class Bachelor course was cancelled due to Corona.
### Objective
The **main goal** is to **prepare** students
for **further studies** in the "field" of **data science**.
for **further studies** in the "field" of **data science**,
including but not limited to topics such as:
- algorithms & data structures
- data cleaning & wrangling
- data visualization
- data engineering (incl. SQL databases)
- data mining (incl. web scraping)
- linear algebra
- machine learning (incl. feature generation & deep learning)
- optimization & (meta-)heuristics (incl. management science & operations research)
- statistics & econometrics
- quantitative finance (e.g., option valuation)
- quantitative marketing (e.g., customer segmentation)
- quantitative supply chain management (e.g., forecasting)
- web development (incl. APIs)
### Prerequisites
To be suitable for *beginners*, there are *no* formal prerequisites.
It is only expected that the student has:
- a *solid* understanding of the **English** language,
- knowledge of **basic mathematics** from high school,
- the ability to **think conceptually** and **reason logically**, and
- the willingness to **invest** around **90-120 hours** on this course.
## Getting started
If you are a total beginner,
follow the instructions in the "Installation" section next.
If you are familiar with
the [git](https://git-scm.com/)
and [poetry](https://python-poetry.org/docs/) command-line tools,
you may want to look at the "Alternative Installation" section further below.
### Installation
To follow this course, an installation of **Python 3.11** or higher is expected.
A popular and beginner friendly way is
to install the [Anaconda Distribution](https://www.anaconda.com/download)
that not only ships Python itself
but also comes pre-packaged with a lot of third-party libraries.
<img src="static/anaconda_download.png" width="50%">
Scroll down to the "Anaconda Installers" section
and install the latest version for your operating system
(i.e., *2024-02* with Python 3.11 at the time of this writing).
After installation,
you find an entry "[Anaconda Navigator](https://docs.anaconda.com/free/navigator/)"
in your start menu.
Click on it.
<img src="static/anaconda_start_menu.png" width="50%">
A window opens giving you several options to start various applications.
In the beginning, we will work mostly with [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/).
Click on "Launch".
<img src="static/anaconda_navigator.png" width="50%">
A new tab in your web browser opens:
The website is "localhost" and some number (e.g., 8888).
This is the [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) application
that is used to display the course materials.
On the left, you see the files and folders on your computer.
This file browser works like any other.
In the center, you see several options to launch (i.e., "create") new files.
<img src="static/jupyter_lab.png" width="50%">
To check if your Python installation works,
double-click on the "Python 3" tile under the "Notebook" section.
That opens a new [Jupyter notebook](https://jupyter-notebook.readthedocs.io/en/stable/)
named "Untitled.ipynb".
<img src="static/jupyter_notebook_blank.png" width="50%">
Enter some basic Python in the **code cell**, for example, `1 + 2`.
Then, press the **Enter** key *while* holding down the **Control** key
(if that does not work, try with the **Shift** key)
to **execute** the snippet.
The result of the calculation, `3` in the example, shows up below the cell.
<img src="static/jupyter_notebook_example.png" width="50%">
After setting up Python,
click on the green "Code" button on the top right on this website
to download the course materials.
As a beginner, choosing "Download ZIP" is likely the easiest option.
Then, unpack the ZIP file into a folder of your choice,
ideally somewhere within your personal user folder
so that the files show up right away in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/).
<img src="static/repo_download.png" width="50%">
### Alternative Installation (for Instructors using Linux)
Python can also be installed in a "pure" way
obtained directly from its core development team [here](https://www.python.org/downloads/).
Then, it comes *without* any third-party packages,
which is *not* a problem at all.
Managing third-party packages can be automated to a large degree,
for example, with tools such as [poetry](https://python-poetry.org/docs/).
However, this may be too "advanced" for a beginner
as it involves working with a [command-line interface <img height="12" style="display: inline-block" src="static/link/to_wiki.png">](https://en.wikipedia.org/wiki/Command-line_interface) (CLI),
also called a **terminal**,
which looks like the one below.
It is used *without* a mouse by typing commands into it.
The following instructions assume that
[git](https://git-scm.com/), [poetry](https://python-poetry.org/docs/),
and [pyenv](https://github.com/pyenv/pyenv) are installed.
<img src="static/cli_install.png" width="50%" align="center">
The screenshot above shows how this project can be set up in an alternative way
with the [zsh](https://en.wikipedia.org/wiki/Z_shell) CLI.
First, [git](https://git-scm.com/) is used
to **clone** the course materials as a **repository**
into a new folder called "*intro-to-python*"
that lives under a "*repos*" folder.
- `git clone https://github.com/webartifex/intro-to-python.git`
The `cd` command is used to "change directories".
In the screenshot, [pyenv](https://github.com/pyenv/pyenv) is used
to set the project's Python version.
[pyenv](https://github.com/pyenv/pyenv)'s purpose is
to manage *many* parallel Python installations on the same computer.
It is highly recommended for professional users;
however, any other way of installing Python works as well.
- `pyenv local ...`
On the contrary, [poetry](https://python-poetry.org/docs/)'s purpose is
to manage third-party packages within the *same* Python installation
and, more importantly, on a per-project basis.
So, for example,
whereas "Project A" may depend on [numpy](https://numpy.org/) *v1.19*
from June 2020 be installed,
"Project B" may use *v1.14* from January 2018 instead
(cf., numpy's [release history](https://pypi.org/project/numpy/#history)).
To achieve this per-project **isolation**,
[poetry](https://python-poetry.org/docs/) uses so-called **virtual environments**
behind the scenes.
While one could do that manually,
for example, by using Python's built-in
[venv <img height="12" style="display: inline-block" src="static/link/to_py.png">](https://docs.python.org/3/library/venv.html) module,
it is more convenient and reliable to have [poetry](https://python-poetry.org/docs/)
automate this.
The following *one* command not only
creates a new virtual environment (manually: `python -m venv venv`)
and *activates* it (manually: `source venv/bin/activate`),
it also installs the versions of the project's third-party dependencies
as specified in the [poetry.lock](poetry.lock) file
(manually: `python -m pip install -r requirements.txt`
if a [requirements.txt](https://docs.python.org/3/tutorial/venv.html#managing-packages-with-pip)
file is used;
the `python -m` part is often left out [but should not be](https://snarky.ca/why-you-should-use-python-m-pip/)):
- `poetry install`
[poetry](https://python-poetry.org/docs/) is also used
to execute commands in the project's (virtual) environment.
To do that, the command is prefixed with `poetry run ...`.
The project uses [nox](https://nox.thea.codes/en/stable/)
to manage various maintenance tasks.
After cloning the repository and setting up the virual environment,
it is recommended to run the initialization task.
That needs to be done only once.
- `poetry run nox -s init-project`
To do the equivalent of clicking "Launch" in the Anaconda Navigator:
- `poetry run jupyter lab`
This opens a new tab in your web browser just as above.
The command-line interface stays open in the background,
like in the screenshot below,
and prints log messages as we work in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/).
<img src="static/cli_jupyter_lab.png" width="50%" align="center">
## Contributing
Feedback **is highly encouraged** and will be incorporated.
Open an issue in the [issues tracker <img height="12" style="display: inline-block" src="static/link/to_gh.png">](https://github.com/webartifex/intro-to-python/issues)
or initiate a [pull request <img height="12" style="display: inline-block" src="static/link/to_gh.png">](https://help.github.com/en/articles/about-pull-requests)
if you are familiar with the concept.
Simple issues that *anyone* can **help fix** are, for example,
**spelling mistakes** or **broken links**.
If you feel that some topic is missing entirely, you may also mention that.
The materials here are considered a **permanent work-in-progress**.
A "Show HN" post about this course was made on [Hacker News <img height="12" style="display: inline-block" src="static/link/to_hn.png">](https://news.ycombinator.com/item?id=22669084)
and some ideas for improvement were discussed there.
## About the Author
Alexander Hess is a PhD student
at the Chair of Logistics Management at [WHU - Otto Beisheim School of Management](https://www.whu.edu)
where he conducts research on urban delivery platforms
and teaches coding courses based on Python in the BSc and MBA programs.
Connect him on [LinkedIn](https://www.linkedin.com/in/webartifex).

154
noxfile.py Normal file
View file

@ -0,0 +1,154 @@
"""Configure nox as the task runner.
Nox provides the following tasks:
- "init-project": install the pre-commit hooks
- "doctests": run the xdoctests in the source files
- "fix-branch-references": adjusts links with git branch references in
various files (e.g., Mardown or notebooks)
"""
import contextlib
import glob
import os
import re
import shutil
import subprocess
import tempfile
import nox
REPOSITORY = "webartifex/intro-to-python"
SRC_LOCATIONS = (
"02_functions/sample_module.py",
"11_classes/sample_package",
)
# Use a unified .cache/ folder for all develop tools.
nox.options.envdir = ".cache/nox"
# All tools except git and poetry are project dependencies.
# Avoid accidental successes if the environment is not set up properly.
nox.options.error_on_external_run = True
@nox.session(name="init-project", venv_backend="none")
def init_project(session):
"""Install the pre-commit hooks."""
for type_ in (
"pre-commit",
"pre-merge-commit",
):
session.run("poetry", "run", "pre-commit", "install", f"--hook-type={type_}")
@nox.session(venv_backend="none")
def doctests(session):
"""Run the xdoctests in the source files."""
for location in SRC_LOCATIONS:
session.run("poetry", "run", "xdoctest", "--silent", location)
@nox.session(name="fix-branch-references", venv_backend="none")
def fix_branch_references(_session):
"""Change git branch references.
Intended to be run as a pre-commit hook.
Many files in the project (e.g., README.md) contain links to resources on
github.com, nbviewer.jupyter.org, or mybinder.org that contain git branch
labels.
This task rewrites branch labels into either "main" or "develop".
"""
# Glob patterns that expand into the files whose links are re-written.
paths = ["*.md", "**/*.ipynb"]
branch = (
subprocess.check_output(
("git", "rev-parse", "--abbrev-ref", "HEAD"),
)
.decode()
.strip()
)
# If the current branch is only temporary and will be merged into "main", ...
if branch.startswith("release-") or branch.startswith("hotfix-"):
branch = "main"
# If the branch is not "main", we assume it is a feature branch.
elif branch != "main":
branch = "develop"
rewrites = [
{
"name": "github",
"pattern": re.compile(
rf"((((http)|(https))://github\.com/{REPOSITORY}/((blob)|(tree))/)([\w-]+)/)"
),
"replacement": rf"\2{branch}/",
},
{
"name": "nbviewer",
"pattern": re.compile(
rf"((((http)|(https))://nbviewer\.jupyter\.org/github/{REPOSITORY}/((blob)|(tree))/)([\w-]+)/)",
),
"replacement": rf"\2{branch}/",
},
{
"name": "mybinder",
"pattern": re.compile(
rf"((((http)|(https))://mybinder\.org/v2/gh/{REPOSITORY}/)([\w-]+)\?)",
),
"replacement": rf"\2{branch}?",
},
]
for expanded in _expand(*paths):
with _line_by_line_replace(expanded) as (old_file, new_file):
for line in old_file:
for rewrite in rewrites:
line = re.sub(rewrite["pattern"], rewrite["replacement"], line)
new_file.write(line)
def _expand(*patterns):
"""Expand glob patterns into paths.
Args:
*patterns: the patterns to be expanded
Yields:
path: a single expanded path
"""
for pattern in patterns:
yield from glob.glob(pattern.strip())
@contextlib.contextmanager
def _line_by_line_replace(path):
"""Replace/change the lines in a file one by one.
This generator function yields two file handles, one to the current file
(i.e., `old_file`) and one to its replacement (i.e., `new_file`).
Usage: loop over the lines in `old_file` and write the files to be kept
to `new_file`. Files not written to `new_file` are removed!
Args:
path: the file whose lines are to be replaced
Yields:
old_file, new_file: handles to a file and its replacement
"""
file_handle, new_file_path = tempfile.mkstemp()
with os.fdopen(file_handle, "w") as new_file:
with open(path) as old_file:
yield old_file, new_file
shutil.copymode(path, new_file_path)
os.remove(path)
shutil.move(new_file_path, path)

2238
poetry.lock generated

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more