From f0152355a30ca5c63b9d46b37c8bc5522ef91a35 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Sun, 15 Sep 2019 12:20:18 +0200 Subject: [PATCH 001/242] Initial commit Add the following files: - LICENSE (MIT) - README.md (empty) - pyproject.toml (after poetry init) --- LICENSE | 21 +++++++++++++++++++++ README.md | 1 + pyproject.toml | 15 +++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 pyproject.toml diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5d77230 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 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 +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6eece6b --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# An Introduction to Python and Programming diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..6303a1d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,15 @@ +[tool.poetry] +name = "intro-to-python" +version = "0.1.0" +description = "An introduction to Python and programming for wanna-be data scientists" +authors = ["Alexander Hess "] +license = "MIT" + +[tool.poetry.dependencies] +python = "^3.6" + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry>=0.12"] +build-backend = "poetry.masonry.api" From d2fdda7c09c7533d7f43dee95078226a12f2f9ff Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 16 Sep 2019 00:09:00 +0200 Subject: [PATCH 002/242] Add "develop" dependencies for the presenter - the Jupyter notebooks are assumed to be run within an installation of the Anaconda Distribution (version 2019-07 with Python 3.7.3) by the students (assumed to be total beginners) - for the presenter some special third-party libraries are added to allow for executable code cells in presentation mode (via rise) - black / blackcellmagic provide a convenient way to style the code while working on the notebooks --- poetry.lock | 739 +++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 5 + 2 files changed, 744 insertions(+) create mode 100644 poetry.lock diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..75af38f --- /dev/null +++ b/poetry.lock @@ -0,0 +1,739 @@ +[[package]] +category = "dev" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +name = "appdirs" +optional = false +python-versions = "*" +version = "1.4.3" + +[[package]] +category = "main" +description = "Disable App Nap on OS X 10.9" +marker = "python_version >= \"3.3\" and sys_platform == \"darwin\" or sys_platform == \"darwin\"" +name = "appnope" +optional = false +python-versions = "*" +version = "0.1.0" + +[[package]] +category = "main" +description = "Classes Without Boilerplate" +name = "attrs" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "19.1.0" + +[[package]] +category = "main" +description = "Specifications for callback functions passed in to an API" +name = "backcall" +optional = false +python-versions = "*" +version = "0.1.0" + +[[package]] +category = "dev" +description = "The uncompromising code formatter." +name = "black" +optional = false +python-versions = ">=3.6" +version = "18.9b0" + +[package.dependencies] +appdirs = "*" +attrs = ">=17.4.0" +click = ">=6.5" +toml = ">=0.9.4" + +[[package]] +category = "dev" +description = "IPython wrapper to format cell using black." +name = "blackcellmagic" +optional = false +python-versions = "*" +version = "0.0.2" + +[package.dependencies] +black = "*" +ipython = "*" + +[[package]] +category = "main" +description = "An easy safelist-based HTML-sanitizing tool." +name = "bleach" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "3.1.0" + +[package.dependencies] +six = ">=1.9.0" +webencodings = "*" + +[[package]] +category = "dev" +description = "Composable command line interface toolkit" +name = "click" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "7.0" + +[[package]] +category = "main" +description = "Cross-platform colored terminal text." +marker = "python_version >= \"3.3\" and sys_platform == \"win32\" or sys_platform == \"win32\"" +name = "colorama" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.4.1" + +[[package]] +category = "main" +description = "Better living through Python with decorators" +name = "decorator" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*" +version = "4.4.0" + +[[package]] +category = "main" +description = "XML bomb protection for Python stdlib modules" +name = "defusedxml" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "0.6.0" + +[[package]] +category = "main" +description = "Discover and load entry points from installed packages." +name = "entrypoints" +optional = false +python-versions = ">=2.7" +version = "0.3" + +[[package]] +category = "main" +description = "IPython Kernel for Jupyter" +name = "ipykernel" +optional = false +python-versions = ">=3.4" +version = "5.1.2" + +[package.dependencies] +ipython = ">=5.0.0" +jupyter-client = "*" +tornado = ">=4.2" +traitlets = ">=4.1.0" + +[[package]] +category = "main" +description = "IPython: Productive Interactive Computing" +name = "ipython" +optional = false +python-versions = ">=3.5" +version = "7.8.0" + +[package.dependencies] +appnope = "*" +backcall = "*" +colorama = "*" +decorator = "*" +jedi = ">=0.10" +pexpect = "*" +pickleshare = "*" +prompt-toolkit = ">=2.0.0,<2.1.0" +pygments = "*" +setuptools = ">=18.5" +traitlets = ">=4.2" + +[[package]] +category = "main" +description = "Vestigial utilities from IPython" +name = "ipython-genutils" +optional = false +python-versions = "*" +version = "0.2.0" + +[[package]] +category = "main" +description = "IPython HTML widgets for Jupyter" +name = "ipywidgets" +optional = false +python-versions = "*" +version = "7.5.1" + +[package.dependencies] +ipykernel = ">=4.5.1" +nbformat = ">=4.2.0" +traitlets = ">=4.3.1" +widgetsnbextension = ">=3.5.0,<3.6.0" + +[package.dependencies.ipython] +python = ">=3.3" +version = ">=4.0.0" + +[[package]] +category = "main" +description = "An autocompletion tool for Python that can be used for text editors." +name = "jedi" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.15.1" + +[package.dependencies] +parso = ">=0.5.0" + +[[package]] +category = "main" +description = "A small but fast and easy to use stand-alone template engine written in pure python." +name = "jinja2" +optional = false +python-versions = "*" +version = "2.10.1" + +[package.dependencies] +MarkupSafe = ">=0.23" + +[[package]] +category = "main" +description = "An implementation of JSON Schema validation for Python" +name = "jsonschema" +optional = false +python-versions = "*" +version = "3.0.2" + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0" +setuptools = "*" +six = ">=1.11.0" + +[[package]] +category = "main" +description = "Jupyter metapackage. Install all the Jupyter components in one go." +name = "jupyter" +optional = false +python-versions = "*" +version = "1.0.0" + +[package.dependencies] +ipykernel = "*" +ipywidgets = "*" +jupyter-console = "*" +nbconvert = "*" +notebook = "*" +qtconsole = "*" + +[[package]] +category = "main" +description = "Jupyter protocol implementation and client libraries" +name = "jupyter-client" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "5.3.2" + +[package.dependencies] +jupyter-core = "*" +python-dateutil = ">=2.1" +pywin32 = ">=1.0" +pyzmq = ">=13" +tornado = ">=4.1" +traitlets = "*" + +[[package]] +category = "main" +description = "Jupyter terminal console" +name = "jupyter-console" +optional = false +python-versions = ">=3.5" +version = "6.0.0" + +[package.dependencies] +ipykernel = "*" +ipython = "*" +jupyter-client = "*" +prompt-toolkit = ">=2.0.0,<2.1.0" +pygments = "*" + +[[package]] +category = "dev" +description = "Common utilities for jupyter-contrib projects." +name = "jupyter-contrib-core" +optional = false +python-versions = "*" +version = "0.3.3" + +[package.dependencies] +jupyter-core = "*" +notebook = ">=4.0" +setuptools = "*" +tornado = "*" +traitlets = "*" + +[[package]] +category = "dev" +description = "A collection of Jupyter nbextensions." +name = "jupyter-contrib-nbextensions" +optional = false +python-versions = "*" +version = "0.5.1" + +[package.dependencies] +ipython-genutils = "*" +jupyter-contrib-core = ">=0.3.3" +jupyter-core = "*" +jupyter-highlight-selected-word = ">=0.1.1" +jupyter-latex-envs = ">=1.3.8" +jupyter-nbextensions-configurator = ">=0.4.0" +lxml = "*" +nbconvert = ">=4.2" +notebook = ">=4.0" +pyyaml = "*" +tornado = "*" +traitlets = ">=4.1" + +[[package]] +category = "main" +description = "Jupyter core package. A base package on which Jupyter projects rely." +name = "jupyter-core" +optional = false +python-versions = ">=2.7, !=3.0, !=3.1, !=3.2" +version = "4.5.0" + +[package.dependencies] +traitlets = "*" + +[[package]] +category = "dev" +description = "Jupyter notebook extension that enables highlighting every instance of the current word in the notebook." +name = "jupyter-highlight-selected-word" +optional = false +python-versions = "*" +version = "0.2.0" + +[[package]] +category = "dev" +description = "Jupyter notebook extension which supports (some) LaTeX environments within markdown cells. Also provides support for labels and crossreferences, document wide numbering, bibliography, and more..." +name = "jupyter-latex-envs" +optional = false +python-versions = "*" +version = "1.4.6" + +[package.dependencies] +ipython = "*" +jupyter_core = "*" +nbconvert = "*" +notebook = ">=4.0" +traitlets = ">=4.1" + +[[package]] +category = "dev" +description = "jupyter serverextension providing configuration interfaces for nbextensions." +name = "jupyter-nbextensions-configurator" +optional = false +python-versions = "*" +version = "0.4.1" + +[package.dependencies] +jupyter_contrib_core = ">=0.3.3" +jupyter_core = "*" +notebook = ">=4.0" +pyyaml = "*" +tornado = "*" +traitlets = "*" + +[[package]] +category = "dev" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +name = "lxml" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" +version = "4.4.1" + +[[package]] +category = "main" +description = "Safely add untrusted strings to HTML/XML markup." +name = "markupsafe" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +version = "1.1.1" + +[[package]] +category = "main" +description = "The fastest markdown parser in pure Python" +name = "mistune" +optional = false +python-versions = "*" +version = "0.8.4" + +[[package]] +category = "main" +description = "Converting Jupyter Notebooks" +name = "nbconvert" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "5.6.0" + +[package.dependencies] +bleach = "*" +defusedxml = "*" +entrypoints = ">=0.2.2" +jinja2 = ">=2.4" +jupyter-core = "*" +mistune = ">=0.8.1,<2" +nbformat = ">=4.4" +pandocfilters = ">=1.4.1" +pygments = "*" +testpath = "*" +traitlets = ">=4.2" + +[[package]] +category = "main" +description = "The Jupyter Notebook format" +name = "nbformat" +optional = false +python-versions = "*" +version = "4.4.0" + +[package.dependencies] +ipython-genutils = "*" +jsonschema = ">=2.4,<2.5.0 || >2.5.0" +jupyter-core = "*" +traitlets = ">=4.1" + +[[package]] +category = "main" +description = "A web-based notebook environment for interactive computing" +name = "notebook" +optional = false +python-versions = ">=3.5" +version = "6.0.1" + +[package.dependencies] +Send2Trash = "*" +ipykernel = "*" +ipython-genutils = "*" +jinja2 = "*" +jupyter-client = ">=5.3.1" +jupyter-core = ">=4.4.0" +nbconvert = "*" +nbformat = "*" +prometheus-client = "*" +pyzmq = ">=17" +terminado = ">=0.8.1" +tornado = ">=5.0" +traitlets = ">=4.2.1" + +[[package]] +category = "main" +description = "Utilities for writing pandoc filters in python" +name = "pandocfilters" +optional = false +python-versions = "*" +version = "1.4.2" + +[[package]] +category = "main" +description = "A Python Parser" +name = "parso" +optional = false +python-versions = "*" +version = "0.5.1" + +[[package]] +category = "main" +description = "Pexpect allows easy control of interactive console applications." +marker = "python_version >= \"3.3\" and sys_platform != \"win32\" or sys_platform != \"win32\"" +name = "pexpect" +optional = false +python-versions = "*" +version = "4.7.0" + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +category = "main" +description = "Tiny 'shelve'-like database with concurrency support" +name = "pickleshare" +optional = false +python-versions = "*" +version = "0.7.5" + +[[package]] +category = "main" +description = "Python client for the Prometheus monitoring system." +name = "prometheus-client" +optional = false +python-versions = "*" +version = "0.7.1" + +[[package]] +category = "main" +description = "Library for building powerful interactive command lines in Python" +name = "prompt-toolkit" +optional = false +python-versions = "*" +version = "2.0.9" + +[package.dependencies] +six = ">=1.9.0" +wcwidth = "*" + +[[package]] +category = "main" +description = "Run a subprocess in a pseudo terminal" +marker = "sys_platform != \"win32\" or os_name != \"nt\" or python_version >= \"3.3\" and sys_platform != \"win32\"" +name = "ptyprocess" +optional = false +python-versions = "*" +version = "0.6.0" + +[[package]] +category = "main" +description = "Pygments is a syntax highlighting package written in Python." +name = "pygments" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "2.4.2" + +[[package]] +category = "main" +description = "Persistent/Functional/Immutable data structures" +name = "pyrsistent" +optional = false +python-versions = "*" +version = "0.15.4" + +[package.dependencies] +six = "*" + +[[package]] +category = "main" +description = "Extensions to the standard Python datetime module" +name = "python-dateutil" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +version = "2.8.0" + +[package.dependencies] +six = ">=1.5" + +[[package]] +category = "main" +description = "Python for Window Extensions" +marker = "sys_platform == \"win32\"" +name = "pywin32" +optional = false +python-versions = "*" +version = "225" + +[[package]] +category = "main" +description = "Python bindings for the winpty library" +marker = "os_name == \"nt\"" +name = "pywinpty" +optional = false +python-versions = "*" +version = "0.5.5" + +[[package]] +category = "dev" +description = "YAML parser and emitter for Python" +name = "pyyaml" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "5.1.2" + +[[package]] +category = "main" +description = "Python bindings for 0MQ" +name = "pyzmq" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*" +version = "18.1.0" + +[[package]] +category = "main" +description = "Jupyter Qt console" +name = "qtconsole" +optional = false +python-versions = "*" +version = "4.5.5" + +[package.dependencies] +ipykernel = ">=4.1" +ipython-genutils = "*" +jupyter-client = ">=4.1" +jupyter-core = "*" +pygments = "*" +traitlets = "*" + +[[package]] +category = "dev" +description = "Reveal.js - Jupyter/IPython Slideshow Extension" +name = "rise" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4" +version = "5.5.1" + +[package.dependencies] +notebook = ">=5.5.0" + +[[package]] +category = "main" +description = "Send file to trash natively under Mac OS X, Windows and Linux." +name = "send2trash" +optional = false +python-versions = "*" +version = "1.5.0" + +[[package]] +category = "main" +description = "Python 2 and 3 compatibility utilities" +name = "six" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*" +version = "1.12.0" + +[[package]] +category = "main" +description = "Terminals served to xterm.js using Tornado websockets" +name = "terminado" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.8.2" + +[package.dependencies] +ptyprocess = "*" +pywinpty = ">=0.5" +tornado = ">=4" + +[[package]] +category = "main" +description = "Test utilities for code working with files and commands" +name = "testpath" +optional = false +python-versions = "*" +version = "0.4.2" + +[[package]] +category = "dev" +description = "Python Library for Tom's Obvious, Minimal Language" +name = "toml" +optional = false +python-versions = "*" +version = "0.10.0" + +[[package]] +category = "main" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +name = "tornado" +optional = false +python-versions = ">= 3.5" +version = "6.0.3" + +[[package]] +category = "main" +description = "Traitlets Python config system" +name = "traitlets" +optional = false +python-versions = "*" +version = "4.3.2" + +[package.dependencies] +decorator = "*" +ipython-genutils = "*" +six = "*" + +[[package]] +category = "main" +description = "Measures number of Terminal column cells of wide-character codes" +name = "wcwidth" +optional = false +python-versions = "*" +version = "0.1.7" + +[[package]] +category = "main" +description = "Character encoding aliases for legacy web content" +name = "webencodings" +optional = false +python-versions = "*" +version = "0.5.1" + +[[package]] +category = "main" +description = "IPython HTML widgets for Jupyter" +name = "widgetsnbextension" +optional = false +python-versions = "*" +version = "3.5.1" + +[package.dependencies] +notebook = ">=4.4.1" + +[metadata] +content-hash = "431dace6d8d5c3e390b59e964116bb6219fb45c7a8c09cbfe99d70c704a54320" +python-versions = "^3.6" + +[metadata.hashes] +appdirs = ["9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", "d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"] +appnope = ["5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0", "8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"] +attrs = ["69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", "f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"] +backcall = ["38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4", "bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"] +black = ["817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739", "e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5"] +blackcellmagic = ["089b98d28aa647f2d86c3161b60cbf09793384ea94ceb1227f4f1d425db4002c"] +bleach = ["213336e49e102af26d9cde77dd2d0397afabc5a6bf2fed985dc35b5d1e285a16", "3fdf7f77adcf649c9911387df51254b813185e32b2c6619f690b593a617e19fa"] +click = ["2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", "5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"] +colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"] +decorator = ["86156361c50488b84a3f148056ea716ca587df2f0de1d34750d35c21312725de", "f069f3a01830ca754ba5258fde2278454a0b5b79e0d7f5c13b3b97e57d4acff6"] +defusedxml = ["6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93", "f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"] +entrypoints = ["589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19", "c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"] +ipykernel = ["167c3ef08450f5e060b76c749905acb0e0fbef9365899377a4a1eae728864383", "b503913e0b4cce7ed2de965457dfb2edd633e8234161a60e23f2fe2161345d12"] +ipython = ["c4ab005921641e40a68e405e286e7a1fcc464497e14d81b6914b4fd95e5dee9b", "dd76831f065f17bddd7eaa5c781f5ea32de5ef217592cf019e34043b56895aa1"] +ipython-genutils = ["72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", "eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"] +ipywidgets = ["13ffeca438e0c0f91ae583dc22f50379b9d6b28390ac7be8b757140e9a771516", "e945f6e02854a74994c596d9db83444a1850c01648f1574adf144fbbabe05c97"] +jedi = ["786b6c3d80e2f06fd77162a07fed81b8baa22dde5d62896a790a331d6ac21a27", "ba859c74fa3c966a22f2aeebe1b74ee27e2a462f56d3f5f7ca4a59af61bfe42e"] +jinja2 = ["065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013", "14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b"] +jsonschema = ["5f9c0a719ca2ce14c5de2fd350a64fd2d13e8539db29836a86adc990bb1a068f", "8d4a2b7b6c2237e0199c8ea1a6d3e05bf118e289ae2b9d7ba444182a2959560d"] +jupyter = ["3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7", "5b290f93b98ffbc21c0c7e749f054b3267782166d72fa5e3ed1ed4eaf34a2b78", "d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f"] +jupyter-client = ["4274f2eef4265bfd00216f44993aa50d890a1abe55e414c817128cf0338ae66a", "4c274aaa3d87b83fa6c6853463f54b8bbff137aeb71e65896ab111c17a62ae63"] +jupyter-console = ["308ce876354924fb6c540b41d5d6d08acfc946984bf0c97777c1ddcb42e0b2f5", "cc80a97a5c389cbd30252ffb5ce7cefd4b66bde98219edd16bf5cb6f84bb3568"] +jupyter-contrib-core = ["1ec81e275a8f5858d56b0c4c6cd85335aa8e915001b8657fe51c620c3cdde50f", "e65bc0e932ff31801003cef160a4665f2812efe26a53801925a634735e9a5794"] +jupyter-contrib-nbextensions = ["2c071f0aa208c569666f656bdc0f66906ca493cf9f06f46db6350db11030ff40", "eecd28ecc2fc410226c0a3d4932ed2fac4860ccf8d9e9b1b29548835a35b22ab"] +jupyter-core = ["2c6e7c1e9f2ac45b5c2ceea5730bc9008d92fe59d0725eac57b04c0edfba24f7", "f4fa22d6cf25f34807c995f22d2923693575c70f02557bcbfbe59bd5ec8d8b84"] +jupyter-highlight-selected-word = ["9545dfa9cb057eebe3a5795604dcd3a5294ea18637e553f61a0b67c1b5903c58", "9fa740424859a807950ca08d2bfd28a35154cd32dd6d50ac4e0950022adc0e7b"] +jupyter-latex-envs = ["070a31eb2dc488bba983915879a7c2939247bf5c3b669b398bdb36a9b5343872"] +jupyter-nbextensions-configurator = ["e5e86b5d9d898e1ffb30ebb08e4ad8696999f798fef3ff3262d7b999076e4e83"] +lxml = ["02ca7bf899da57084041bb0f6095333e4d239948ad3169443f454add9f4e9cb4", "096b82c5e0ea27ce9138bcbb205313343ee66a6e132f25c5ed67e2c8d960a1bc", "0a920ff98cf1aac310470c644bc23b326402d3ef667ddafecb024e1713d485f1", "17cae1730a782858a6e2758fd20dd0ef7567916c47757b694a06ffafdec20046", "17e3950add54c882e032527795c625929613adbd2ce5162b94667334458b5a36", "1f4f214337f6ee5825bf90a65d04d70aab05526c08191ab888cb5149501923c5", "2e8f77db25b0a96af679e64ff9bf9dddb27d379c9900c3272f3041c4d1327c9d", "4dffd405390a45ecb95ab5ab1c1b847553c18b0ef8ed01e10c1c8b1a76452916", "6b899931a5648862c7b88c795eddff7588fb585e81cecce20f8d9da16eff96e0", "726c17f3e0d7a7200718c9a890ccfeab391c9133e363a577a44717c85c71db27", "760c12276fee05c36f95f8040180abc7fbebb9e5011447a97cdc289b5d6ab6fc", "796685d3969815a633827c818863ee199440696b0961e200b011d79b9394bbe7", "891fe897b49abb7db470c55664b198b1095e4943b9f82b7dcab317a19116cd38", "a471628e20f03dcdfde00770eeaf9c77811f0c331c8805219ca7b87ac17576c5", "a63b4fd3e2cabdcc9d918ed280bdde3e8e9641e04f3c59a2a3109644a07b9832", "b0b84408d4eabc6de9dd1e1e0bc63e7731e890c0b378a62443e5741cfd0ae90a", "be78485e5d5f3684e875dab60f40cddace2f5b2a8f7fede412358ab3214c3a6f", "c27eaed872185f047bb7f7da2d21a7d8913457678c9a100a50db6da890bc28b9", "c81cb40bff373ab7a7446d6bbca0190bccc5be3448b47b51d729e37799bb5692", "d11874b3c33ee441059464711cd365b89fa1a9cf19ae75b0c189b01fbf735b84", "e9c028b5897901361d81a4718d1db217b716424a0283afe9d6735fe0caf70f79", "fe489d486cd00b739be826e8c1be188ddb74c7a1ca784d93d06fda882a6a1681"] +markupsafe = ["00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", "09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", "43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", "62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", "717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", "88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", "98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", "ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", "b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", "ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", "c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", "cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", "e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"] +mistune = ["59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e", "88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"] +nbconvert = ["427a468ec26e7d68a529b95f578d5cbf018cb4c1f889e897681c2b6d11897695", "48d3c342057a2cf21e8df820d49ff27ab9f25fc72b8f15606bd47967333b2709"] +nbformat = ["b9a0dbdbd45bb034f4f8893cafd6f652ea08c8c1674ba83f2dc55d3955743b0b", "f7494ef0df60766b7cabe0a3651556345a963b74dbc16bc7c18479041170d402"] +notebook = ["660976fe4fe45c7aa55e04bf4bccb9f9566749ff637e9020af3422f9921f9a5d", "b0a290f5cc7792d50a21bec62b3c221dd820bf00efa916ce9aeec4b5354bde20"] +pandocfilters = ["b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"] +parso = ["63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc", "666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c"] +pexpect = ["2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1", "9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"] +pickleshare = ["87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", "9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"] +prometheus-client = ["71cd24a2b3eb335cb800c7159f423df1bd4dcd5171b234be15e3f31ec9f622da"] +prompt-toolkit = ["11adf3389a996a6d45cc277580d0d53e8a5afd281d0c9ec71b28e6f121463780", "2519ad1d8038fd5fc8e770362237ad0364d16a7650fb5724af6997ed5515e3c1", "977c6583ae813a37dc1c2e1b715892461fcbdaa57f6fc62f33a528c4886c8f55"] +ptyprocess = ["923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0", "d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"] +pygments = ["71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", "881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297"] +pyrsistent = ["34b47fa169d6006b32e99d4b3c4031f155e6e68ebcc107d6454852e8e0ee6533"] +python-dateutil = ["7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", "c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"] +pywin32 = ["0443e9bb196e72480f50cbddc2cf98fbb858a77d02e281ba79489ea3287b36e9", "09bbe7cdb29eb40ab2e83f7a232eeeedde864be7a0622b70a90f456aad07a234", "0d8e0f47808798d320c983574c36c49db642678902933a210edd40157d206fd0", "0db7c9f4b93528afd080d35912a60be2f86a1d6c49c0a9cf9cedd106eed81ea3", "749e590875051661ecefbd9dfa957a485016de0f25e43f5e70f888ef1e29587b", "779d3e9d4b934f2445d2920c3941416d99af72eb7f7fd57a63576cc8aa540ad6", "7c89d2c11a31c7aaa16dc4d25054d7e0e99d6f6b24193cf62c83850484658c87", "81f7732b662c46274d7d8c411c905d53e71999cba95457a0686467c3ebc745ca", "9db1fb8830bfa99c5bfd335d4482c14db5c6f5028db3b006787ef4200206242b", "bd8d04835db28646d9e07fd0ab7c7b18bd90e89dfdc559e60389179495ef30da", "fc6822a68afd79e97b015985dd455767c72009b81bcd18957068626c43f11e75", "fe6cfc2045931866417740b575231c7e12d69d481643be1493487ad53b089959"] +pywinpty = ["0e01321e53a230233358a6d608a1a8bc86c3882cf82769ba3c62ca387dc9cc51", "333e0bc5fca8ad9e9a1516ebedb2a65da38dc1f399f8b2ea57d6cccec1ff2cc8", "3ca3123aa6340ab31bbf9bd012b92e72f9ec905e4c9ee152cc997403e1778cd3", "44a6dddcf2abf402e22f87e2c9a341f7d0b296afbec3d28184c8de4d7f514ee4", "53d94d574c3d4da2df5b1c3ae728b8d90e4d33502b0388576bbd4ddeb4de0f77", "c3955f162c53dde968f3fc11361658f1d83b683bfe601d4b6f94bb01ea4300bc", "cec9894ecb34de3d7b1ca121dd98433035b9f8949b5095e84b103b349231509c", "dcd45912e2fe2e6f72cee997a4da6ed1ad2056165a277ce5ec7f7ac98dcdf667", "f2bcdd9a2ffd8b223752a971b3d377fb7bfed85f140ec9710f1218d760f2ccb7"] +pyyaml = ["0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9", "01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4", "5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8", "5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696", "7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34", "7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9", "87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73", "9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299", "a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b", "b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae", "b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681", "bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41", "f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8"] +pyzmq = ["01636e95a88d60118479041c6aaaaf5419c6485b7b1d37c9c4dd424b7b9f1121", "021dba0d1436516092c624359e5da51472b11ba8edffa334218912f7e8b65467", "0463bd941b6aead494d4035f7eebd70035293dd6caf8425993e85ad41de13fa3", "05fd51edd81eed798fccafdd49c936b6c166ffae7b32482e4d6d6a2e196af4e6", "1fadc8fbdf3d22753c36d4172169d184ee6654f8d6539e7af25029643363c490", "22efa0596cf245a78a99060fe5682c4cd00c58bb7614271129215c889062db80", "260c70b7c018905ec3659d0f04db735ac830fe27236e43b9dc0532cf7c9873ef", "2762c45e289732d4450406cedca35a9d4d71e449131ba2f491e0bf473e3d2ff2", "2fc6cada8dc53521c1189596f1898d45c5f68603194d3a6453d6db4b27f4e12e", "343b9710a61f2b167673bea1974e70b5dccfe64b5ed10626798f08c1f7227e72", "41bf96d5f554598a0632c3ec28e3026f1d6591a50f580df38eff0b8067efb9e7", "856b2cdf7a1e2cbb84928e1e8db0ea4018709b39804103d3a409e5584f553f57", "85b869abc894672de9aecdf032158ea8ad01e2f0c3b09ef60e3687fb79418096", "93f44739db69234c013a16990e43db1aa0af3cf5a4b8b377d028ff24515fbeb3", "98fa3e75ccb22c0dc99654e3dd9ff693b956861459e8c8e8734dd6247b89eb29", "9a22c94d2e93af8bebd4fcf5fa38830f5e3b1ff0d4424e2912b07651eb1bafb4", "a7d3f4b4bbb5d7866ae727763268b5c15797cbd7b63ea17f3b0ec1067da8994b", "b645a49376547b3816433a7e2d2a99135c8e651e50497e7ecac3bd126e4bea16", "cf0765822e78cf9e45451647a346d443f66792aba906bc340f4e0ac7870c169c", "dc398e1e047efb18bfab7a8989346c6921a847feae2cad69fedf6ca12fb99e2c", "dd5995ae2e80044e33b5077fb4bc2b0c1788ac6feaf15a6b87a00c14b4bdd682", "e03fe5e07e70f245dc9013a9d48ae8cc4b10c33a1968039c5a3b64b5d01d083d", "ea09a306144dff2795e48439883349819bef2c53c0ee62a3c2fae429451843bb", "f4e37f33da282c3c319849877e34f97f0a3acec09622ec61b7333205bdd13b52", "fa4bad0d1d173dee3e8ef3c3eb6b2bb6c723fc7a661eeecc1ecb2fa99860dd45"] +qtconsole = ["40d5d8e00d070ea266dbf6f0da74c4b9597b8b8d67cd8233c3ffd8debf923703", "b91e7412587e6cfe1644696538f73baf5611e837be5406633218443b2827c6d9"] +rise = ["5a3d529170d707ff0af42bf12f9584b5089baf401643d136d052a66cb4210052", "ba94321aee993cb2f0647c0db8688064afcff6000576f0fcb1e98652abc629e3"] +send2trash = ["60001cc07d707fe247c94f74ca6ac0d3255aabcb930529690897ca2a39db28b2", "f1691922577b6fa12821234aeb57599d887c4900b9ca537948d2dac34aea888b"] +six = ["3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"] +terminado = ["d9d012de63acb8223ac969c17c3043337c2fcfd28f3aea1ee429b345d01ef460", "de08e141f83c3a0798b050ecb097ab6259c3f0331b2f7b7750c9075ced2c20c2"] +testpath = ["46c89ebb683f473ffe2aab0ed9f12581d4d078308a3cb3765d79c6b2317b0109", "b694b3d9288dbd81685c5d2e7140b81365d46c29f5db4bc659de5aa6b98780f8"] +toml = ["229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", "235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e", "f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"] +tornado = ["349884248c36801afa19e342a77cc4458caca694b0eda633f5878e458a44cb2c", "398e0d35e086ba38a0427c3b37f4337327231942e731edaa6e9fd1865bbd6f60", "4e73ef678b1a859f0cb29e1d895526a20ea64b5ffd510a2307b5998c7df24281", "559bce3d31484b665259f50cd94c5c28b961b09315ccd838f284687245f416e5", "abbe53a39734ef4aba061fca54e30c6b4639d3e1f59653f0da37a0003de148c7", "c845db36ba616912074c5b1ee897f8e0124df269468f25e4fe21fe72f6edd7a9", "c9399267c926a4e7c418baa5cbe91c7d1cf362d505a1ef898fde44a07c9dd8a5"] +traitlets = ["9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835", "c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9"] +wcwidth = ["3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", "f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"] +webencodings = ["a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", "b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"] +widgetsnbextension = ["079f87d87270bce047512400efd70238820751a11d2d8cb137a5a5bdbaf255c7", "bd314f8ceb488571a5ffea6cc5b9fc6cba0adaf88a9d2386b93a489751938bcd"] diff --git a/pyproject.toml b/pyproject.toml index 6303a1d..1cd387e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,8 +7,13 @@ license = "MIT" [tool.poetry.dependencies] python = "^3.6" +jupyter = "^1.0" [tool.poetry.dev-dependencies] +black = {version = "^18.3-alpha.0", allows-prereleases = true} +blackcellmagic = "^0.0.2" +jupyter-contrib-nbextensions = "^0.5" +rise = "^5.5" [build-system] requires = ["poetry>=0.12"] From f33af9684775f387743c2f425b4bc83f6fa2d969 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 16 Sep 2019 00:14:56 +0200 Subject: [PATCH 003/242] Add a simple .gitignore file --- .gitignore | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c4b1ff0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# Environments +.env/ +env/ +.venv/ +venv/ + +# Jupyter +.ipynb_checkpoints/ + +# pyenv +.python-version From bb2cfce3c79f1ad5890de2d5413632c1226d6f03 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 16 Sep 2019 00:18:38 +0200 Subject: [PATCH 004/242] Add initial version of notebook 00 --- 00_start_up.ipynb | 897 ++++++++++++++++++++++++++++ static/anaconda.png | Bin 0 -> 142513 bytes static/growth_major_languages.png | Bin 0 -> 210392 bytes static/growth_smaller_languages.png | Bin 0 -> 175532 bytes static/logo.png | Bin 0 -> 7821 bytes static/logos.png | Bin 0 -> 364253 bytes static/terminal.png | Bin 0 -> 29931 bytes static/xkcd.png | Bin 0 -> 90835 bytes 8 files changed, 897 insertions(+) create mode 100644 00_start_up.ipynb create mode 100644 static/anaconda.png create mode 100644 static/growth_major_languages.png create mode 100644 static/growth_smaller_languages.png create mode 100644 static/logo.png create mode 100644 static/logos.png create mode 100644 static/terminal.png create mode 100644 static/xkcd.png diff --git a/00_start_up.ipynb b/00_start_up.ipynb new file mode 100644 index 0000000..588d7ab --- /dev/null +++ b/00_start_up.ipynb @@ -0,0 +1,897 @@ +{ + "cells": [ + { + "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 course is set up to be a *thorough* introduction to programming in [Python](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](https://www.python.org/psf/) in the official [language reference](https://docs.python.org/3/reference/index.html) and introduces additions to the language as distributed with the [standard library](https://docs.python.org/3/library/index.html) that come with every installation.\n", + "\n", + "Furthermore, some very popular third-party libraries like [numpy](https://www.numpy.org/), [pandas](https://pandas.pydata.org/), [matplotlib](https://matplotlib.org/), and others are portrayed." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## Prerequisites" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To be suitable for *total beginners*, there are *no* formal prerequisites. It is only expected that the student has:\n", + "\n", + "- a *solid* understanding of the **English language** (i.e., usage of *technical* terms with *narrow* and *distinct* meanings),\n", + "- knowledge of **basic mathematics** from high school (i.e., addition, subtraction, multiplication, division, and a little bit of calculus and statistics),\n", + "- the ability to **think conceptually** and **reason logically** (i.e., *not* just memorizing), and\n", + "- the willingness to **invest 2-4 hours a day for a month** (cf., \"ABC\"-rule at the end)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Objective" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "The course's **main goal** is to **prepare** the student **for further studies** in the \"field\" of **data science**." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This includes but is not limited to more advanced courses on topics such as:\n", + "- linear algebra\n", + "- statistics & econometrics\n", + "- data cleaning & wrangling\n", + "- data visualization\n", + "- data engineering (incl. SQL databases)\n", + "- data mining (incl. web scraping)\n", + "- feature generation, machine learning, & deep learning\n", + "- optimization & (meta-)heuristics\n", + "- algorithms & data structures\n", + "- quantitative finance (e.g., option valuation)\n", + "- quantitative marketing (e.g., customer segmentation)\n", + "- quantitative supply chain management (e.g., forecasting)\n", + "- management science & decision models\n", + "- backend / API / web development (to serve data products to clients)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### Why data science?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The term **[data science](https://en.wikipedia.org/wiki/Data_science)** is rather vague and does actually *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](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 [artifical intelligence](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 (like the ones mentioned above) 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 every students' studies 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. This could make teaching easier for top-notch researchers who use a lot of programming in their day-to-day lifes answering big questions: The student and the teacher are then more likely to \"speak\" the same language." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## Installation" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To follow this course, a working installation of **Python 3.6** or higher is needed.\n", + "\n", + "A popular and beginner friendly way is to install the [Anaconda Distribution](https://www.anaconda.com/distribution/) that not only ships Python and the standard library but comes pre-packaged with a lot of third-party libraries from the so-called \"scientific stack\". Just go to the [download](https://www.anaconda.com/download/) page and install the latest version (i.e., *2019-07* with Python 3.7 at the time of this writing) for your operating system.\n", + "\n", + "Then, among others, you will find an entry \"Jupyter Notebook\" in your start menu like below. Click on it and a new tab in your web browser will open where you can switch between folders as you could in your computer's default file browser." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To download the course's materials as a ZIP file, open the accompanying [GitHub repository](https://github.com/webartifex/intro-to-python) in a web browser and click on the green \"Clone or download\" button on the right. Then, unpack the ZIP file into a folder of your choosing (ideally somewhere within your personal user folder so that the files show up right away)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### Alternative Installation" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Python can also be installed in a \"pure\" way as obtained from its core development team. However, this is somewhat too \"advanced\" for a beginner who would then also be responsible for setting up all the third-party libraries needed to actually view this document. Plus, many of the involveld steps must be done in a [terminal](https://en.wikipedia.org/wiki/Terminal_emulator) window, which tends to be a bit intimidating for most beginners." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For more \"couragous\" beginners wanting to learn how to accomplish this, here is a rough sketch of the aspects to know. First, all Python realeases are available for free on the official [download](https://www.python.org/downloads/) page for any supported operating system. Choose the one you want, then download and install it (cf., the [instruction notes](https://wiki.python.org/moin/BeginnersGuide/Download)). As this only includes core Python and the standard library, the beginner then needs to learn about the [pip](https://pip.pypa.io/en/stable/) module, which is to be used inside a terminal. With the command `python -m pip install jupyter`, all necessary third-party libraries can be installed (cf., more background [here](https://jupyter.readthedocs.io/en/latest/install.html)). However, this would be done in a *system-wide* fashion and is not recommended. Instead, the best practice is to create a so-called **virtual environment** with the [venv](https://docs.python.org/3/library/venv.html) module with which the installed third-party packages can be *isolated* on a per-project basis (the command `python -m venv env-name` creates a virtual enviroment called \"env-name\"). This tactic is employed to avoid a situation known as **[dependency hell](https://en.wikipedia.org/wiki/Dependency_hell)** Once created, the virtual environment must then be activated each time before resuming work in each terminal (with the command `source env-name/bin/activate`). While there exist convenience tools that automate parts of this (e.g., [poetry](https://poetry.eustace.io/docs/) or [virtualenvwrapper](https://virtualenvwrapper.readthedocs.io/en/latest/)), it only distracts a beginner from actually studying the Python language. Yet, it is still worthwhile to have heard about these terms and concepts as many online resources often implicitly assume the user to know about them." + ] + }, + { + "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](https://en.wikipedia.org/wiki/Acronym) derived from the names of the three major programming languages **[Julia](https://julialang.org/)**, **[Python](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 easily.\n", + "\n", + "Furthermore, Jupyter notebooks have become a de-facto standard for communicating and exchanging results in the data science community (both in academia and business) and often provide a more intuitive alternative to terminal based ways of running Python (e.g., the default [Python interpreter](https://docs.python.org/3/tutorial/interpreter.html) as shown above or a more advanced interactive version like [IPython](https://ipython.org/)) or even a full-fledged [Integrated Development Environment](https://en.wikipedia.org/wiki/Integrated_development_environment) (e.g., the commercial [PyCharm](https://www.jetbrains.com/pycharm/) or the free [Spyder](https://github.com/spyder-ide/spyder)).\n", + "\n", + "In particular, they allow to mix plain English text with Python code cells. The plain text can be formatted using the [Markdown](https://guides.github.com/features/mastering-markdown/) language and mathematical expressions can be typeset with [LaTeX](https://www.overleaf.com/learn/latex/Free_online_introduction_to_LaTeX_%28part_1%29). Lastly, we can include pictures, plots, and even videos. Because of these features, the notebooks developed for this course come in a self-contained \"tutorial\" style that enables students to learn and review the material on their own." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### Markdown 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 next cell below is an example of a \"Code\" cell containing a line of actual Python code: it simply outputs the text \"Hello world\" when executed. To edit an existing code cell, just enter into it with a mouse click. You know that you are \"in\" a code cell when you see the frame of the code cell turn green.\n", + "\n", + "Besides this **edit mode** there is also a so-called **command mode** that you can reach by hitting the \"Escape\" key after entering a code cell, which turns the frame's color into blue. Using the Enter\" and \"Escape\" keys you can now switch between the two modes.\n", + "\n", + "To *execute* (or *run*) a code cell, hold the \"Control\" key and press \"Enter\". Note how you do not go to the subsequent cell. Alternatively, you can hold the \"Shift\" key and press \"Enter\", which executes the cell and places your focus on the next cell right after.\n", + "\n", + "Similarly, a Markdown cell is also in either the edit or command mode. For example, simply double-click on the text you are just reading, which takes you into edit mode. Now you could change the formatting (e.g., make a word printed in *italics* or **bold** with single or double asterisks) and then \"execute\" the cell to actually 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." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello world\n" + ] + } + ], + "source": [ + "print(\"Hello world\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Sometimes a code cell starts with an exclamation mark `!`. Then, the Jupyter notebook behaves as if you just typed the following commands right into a terminal. The cell below asks `python` to show its version number. This is actually *not* Python code but a command in the [Shell](https://en.wikipedia.org/wiki/Shell_script) language. The `!` is useful to execute short commands without leaving a Jupyter notebook." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.7.3\r\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": [ + "For this course *programming* is \"defined\" as\n", + "- a **structured** way of **problem solving**\n", + "- by **expressing** the steps of a **computation / process**\n", + "- and thereby **documenting** the process in a formal way\n", + "\n", + "Programming is always **concrete** and based on a **particular case**.\n", + "\n", + "Programming definitely exhibits elements of an **art** or a **craft** as we will 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 (\"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. In fact, many computer scientists only know how to produce \"ugly\" 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 many people. Often, it has something to do with hardware and devices both of which are out of scope for programmers and computer scientists. In fact, many people from the two aforementioned fields are more than happy if their printer and internet just work as they do not know a lot more about that than non-technical people." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Why Python?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### What is Python?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "- [Guido van Rossum](https://en.wikipedia.org/wiki/Guido_van_Rossum) (Python’s **[Benevolent Dictator for Life](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; still in heavy use as of today) was **open-sourced** from the get-go which attracted a **large and global community of programmers** that **contributed** their individual knowledge and best practices in their free time to make Python even better\n", + "- **Python 3** resulted from a major 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](https://en.wikipedia.org/wiki/Monty_Python)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### Summary" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Python is a **general-purpose** programming language that allows for **fast development**, is **easy to comprehend**, **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 argument provides hints why this cannot really 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 certain 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 just *use* the language over time and in new and unpredictable contexts making up their own new conventions." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Loose *communities* are the main building block around which open-source software is built. Someone starts a project (like Guido) and makes it free to use for anybody (e.g., on a code-sharing platform like [GitHub](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 (via the popularized concept of a \"[pull request](https://help.github.com/articles/about-pull-requests/)\"). The project grows both in lines of code and number of people using it. After a while, people start local user groups to share their same interests and meet on a regular basis (e.g., this is a big market for companies like [Meetup](https://www.meetup.com/) or non-profits like [PyData](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](https://us.pycon.org/) in the US, [EuroPython](https://europython.eu/), or [PyCon.DE](https://de.pycon.org/)). The content presented at these conferences is made publicly available via GitHub and YouTube (e.g., [PyCon 2019](https://www.youtube.com/channel/UCxs2IIVXaEHHA4BtTiWZ2mQ) or [EuroPython](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 rather loose and constantly changing, smaller in-groups, often democratically organized and elected (e.g., the Python Software Foundation), take care of, for example, the development of the \"core\" Python language itself.\n", + "\n", + "Interestingly, Python is just a specification (i.e., a set of rules) as to what is allowed and what not. The current version of Python can always be lookep up in the [Python Language Reference](https://docs.python.org/3/reference/index.html). In order to make changes to that, anyone can make a so-called **[Python Enhancement Proposal](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](https://devguide.python.org/coredev/) and anyone interested and are then either accepted, modified, or rejected if, for example, the change introduces internal inconsistencies. This is very similar to the **double blind peer review** established in academia. In fact, many of the contributors 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](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). This is a huge benefit compared to commercial languages (e.g., MATLAB) since a programmer can always continue to **study best practices** or how things are done. 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](https://en.wikipedia.org/wiki/Free_software) is consequently free of charge but brings many other freedoms with it, most notable 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 \"weird\" thing is that the default Python implementation is actually written in the C language.\n", + "\n", + "[C](https://en.wikipedia.org/wiki/C_%28programming_language%29) and [C++](https://en.wikipedia.org/wiki/C%2B%2B) (check this [introduction](https://www.learncpp.com/)) are wide-spread and long established (i.e., since the 1970s) programming languages that are employed in many mission critical softwares (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 \"manually\" (and the knowledge necessary to do that is not easy to learn).\n", + "\n", + "In contrast, Python automatically manages the memory for the programmer. This speeds up the development process a lot. So speed here is really a trade-off of application run time vs. engineering / development time and in many applications the program's run time is not that important (e.g., what if C needs 0.001 seconds in a case where Python needs 0.1 seconds to train a linear regression model?). 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.\n", + "\n", + "**In a nutshell**: While it is of course true that C is a lot faster than Python when it comes to **pure computation time**, in many cases this does not really matter as the **significantly shorter development cycles** are the more important cost factor in a rapidly changing business world." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Who uses it?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While it is usually not the best argument to quote authorative figures like the pope, we will do this in this section anyways:\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 ([source](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 ([source](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 **re-written in Python** right away ([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", + " - Python and C++, Java, and Go are the only four server-side languages to be deployed to production\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 a lot of its projects, many of which are written in Python and regard working with really big data ([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/)** ([source](https://instagram-engineering.com/web-service-efficiency-at-instagram-with-python-4976d078e366))\n", + "- **[Spotify](https://spotify.com/)** bases its data science on Python ([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 ([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 ([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 ([source](https://www.ft.com/content/4c17d6ce-c8b2-11e8-ba8f-ee390057b8c9?segmentId=a7371401-027d-d8bf-8a7f-2a746e767d56))\n", + "- ...\n", + "- ... this list is intentionally shortened as Python is simply very popular ..." + ] + }, + { + "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](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](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": "slide" + } + }, + "source": [ + "" + ] + }, + { + "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 very popular language in the niche of statistics and data science, can compete with Python's year-to-year growth." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Even popular 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": "slide" + } + }, + "source": [ + "### The ABC Rule" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Simply put, **always be coding**.\n", + "\n", + "Programming is more than just writing code into a text file. It means reading through parts of documentation, blogs with best practices, and tutorials, or researching some problem on Stack Overflow while trying to implement some feature 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 software simultaneously using software like **[git](https://git-scm.com/)**. In short, programming involves a lot of \"muscle memory\". This 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 requirement for the very first time. Coding also means \"breaking\" things to find out what makes them actually work to begin with.\n", + "\n", + "Coding is learned best by just doing it for some time on a daily or at least regular basis and not right before some task is due, just like learning a \"real\" language." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### The Maker's Schedule" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[Y Combinator](https://www.ycombinator.com/)'s co-founder [Paul Graham](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 (like a \"boss\"). Their schedule is usually organized by the hour or even by 30 minute intervals.\n", + "- **Makers**: People that create things (like programmers, artists, or writers). Such people think in half days or full days.\n", + "\n", + "Have you ever wondered why so many tech nerds work all the nights and sleep during \"weird\" hours? This is mainly because many programming related tasks require a certain \"flow\" state of one's mind. This is hard to achieve when one can get interupted, 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 any more to hear a \"I looked at it over the weekend\" from a programmer." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Phase Iteration - The $\\pi$ Rule" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When being asked the above question, most programmers will answer something that can be classified into one of two broader groups.\n", + "\n", + "**1) Toy Problem / Case Study / Prototype**: Pick some problem that you want to write a programatic solution for, break it down into smaller packages, and solve them \"backwards\".\n", + "\n", + "**2) Books / Video Tutorials / Courses**: Research the best book / blog post / video 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 will always reveal issues no book or tutorial can think of before. Data is never clean as it comes. Some algorithm from a text book must be adapted to a very specific but important aspect of a case study. It is important to learn to \"ship a product\", i.e., to finish some project to the very end because only then you will have looked at all the aspects.\n", + "\n", + "The major downside of this approach is that you likely learn bad \"patterns\" as whatever you learn is overfitted to the case and you do not get the big picture or mental concepts behind a solution. This is a gap well written books can fill in (e.g., check the Python / Programming books by [Packt](https://www.packtpub.com/packt/offers/free-learning/) or [O’Reilly](https://www.oreilly.com/))." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Contents" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "**Part 1: Expressing Logic**\n", + "\n", + "- What is a programming language?\n", + " 1. Structure of a Program\n", + "- What kind of words exist?\n", + " 2. Variables, Expressions, Statements, & Comments\n", + " 3. Functions\n", + "- How can we form sentences from words?\n", + " 4. Conditionals\n", + " 5. Iteration" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "**Part 2: Managing Data**\n", + "\n", + "- How is data stored in memory?\n", + " 6. Numbers\n", + " 7. Text\n", + " 8. Sequences\n", + " 9. Mappings & Sets\n", + "- How can we create our own data types?\n", + " 10. Classes & Instances" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## xkcd Comic" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As with every good lecture, there always has to be a [xkcd](https://xkcd.com/353/) comic somewhere." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import antigravity" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "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": 2 +} diff --git a/static/anaconda.png b/static/anaconda.png new file mode 100644 index 0000000000000000000000000000000000000000..f82cbfa6c4db758358f17de439b31d3f45e49711 GIT binary patch literal 142513 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 literal 0 HcmV?d00001 diff --git a/static/growth_major_languages.png b/static/growth_major_languages.png new file mode 100644 index 0000000000000000000000000000000000000000..acfdfbbf30734aedd1c5f7440025cd24b2ed9998 GIT binary patch literal 210392 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 ^4j`D+{B_bB)<`Rp9j=_hf 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%0Ai4;=nC%k6N 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 literal 0 HcmV?d00001 diff --git a/static/growth_smaller_languages.png b/static/growth_smaller_languages.png new file mode 100644 index 0000000000000000000000000000000000000000..51123f2757b6353f410636398c0c93bea7b7b982 GIT binary patch literal 175532 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


8nV@+eg16#<_Mr;vE$dLL5ww8485;<-}6DudM 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 literal 0 HcmV?d00001 diff --git a/static/xkcd.png b/static/xkcd.png new file mode 100644 index 0000000000000000000000000000000000000000..23a4c6e3a5b50e9114d5b8f01a2b8bc9cca8e451 GIT binary patch literal 90835 zcmV)oK%BpcP)004R=004l4008;_004mL004C`008P>0026e000+nl3&F}00009 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 z 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 Date: Mon, 16 Sep 2019 10:56:41 +0200 Subject: [PATCH 005/242] Add a requirements.txt file This file makes it easier to install all dependencies for users without poetry installed --- requirements.txt | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..41753e1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,56 @@ +appdirs==1.4.3 +attrs==19.1.0 +backcall==0.1.0 +black==18.9b0 +blackcellmagic==0.0.2 +bleach==3.1.0 +Click==7.0 +decorator==4.4.0 +defusedxml==0.6.0 +entrypoints==0.3 +ipykernel==5.1.2 +ipython==7.8.0 +ipython-genutils==0.2.0 +ipywidgets==7.5.1 +jedi==0.15.1 +Jinja2==2.10.1 +jsonschema==3.0.2 +jupyter==1.0.0 +jupyter-client==5.3.2 +jupyter-console==6.0.0 +jupyter-contrib-core==0.3.3 +jupyter-contrib-nbextensions==0.5.1 +jupyter-core==4.5.0 +jupyter-highlight-selected-word==0.2.0 +jupyter-latex-envs==1.4.6 +jupyter-nbextensions-configurator==0.4.1 +lxml==4.4.1 +MarkupSafe==1.1.1 +mistune==0.8.4 +nbconvert==5.6.0 +nbformat==4.4.0 +notebook==6.0.1 +pandocfilters==1.4.2 +parso==0.5.1 +pexpect==4.7.0 +pickleshare==0.7.5 +prometheus-client==0.7.1 +prompt-toolkit==2.0.9 +ptyprocess==0.6.0 +Pygments==2.4.2 +pyrsistent==0.15.4 +python-dateutil==2.8.0 +PyYAML==5.1.2 +pyzmq==18.1.0 +qtconsole==4.5.5 +rise==5.5.1 +Send2Trash==1.5.0 +six==1.12.0 +terminado==0.8.2 +testpath==0.4.2 +toml==0.10.0 +tornado==6.0.3 +traitlets==4.3.2 +wcwidth==0.1.7 +webencodings==0.5.1 +widgetsnbextension==3.5.1 From 33d6d91c9acffcea45dc4a1f2caca8036adb9e67 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 16 Sep 2019 10:58:33 +0200 Subject: [PATCH 006/242] Add course info to README.md - course description (objective & prerequisites) - installation notes for Python & third-party packages --- README.md | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/README.md b/README.md index 6eece6b..4fd2b52 100644 --- a/README.md +++ b/README.md @@ -1 +1,141 @@ # An Introduction to Python and Programming + +The purpose of this repository is to serve as an interactive "book" for a +thorough introductory course on programming in the +**[Python](https://www.python.org/)** language. + +The course's **main goal** is to **prepare** the student for **further +studies** in the "field" of **data science**. + +The "chapters" are written in [Jupyter notebooks](https://jupyter-notebook.readthedocs.io/en/stable/) +which are a de-facto standard for exchanging code and results among data +science professionals and researchers. +As such they can be viewed in a plain web browser: + +- [00 - Start up](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb) + +However, it is recommended that students **install Python and Jupyter +locally** and run the code in the notebooks on their own. +This way, the student can play with the code and learn more efficiently. +Precise **installation instructions** are either in the [00th notebook]( +https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb) +or further below. + +Feedback is encouraged and will be incorporated. +Open an issue in the [issues tracker](https://github.com/webartifex/intro-to-python/issues) +or initiate a [pull request](https://help.github.com/en/articles/about-pull-requests) +if you are familiar with the concept. + + +## Prerequisites + +To be suitable for *total 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 2-4 hours a day for a month**. + + +## Installation + +To follow this course, a working installation of **Python 3.6** or higher is +expected. + +A popular and beginner friendly way is to install the [Anaconda Distribution]( +https://www.anaconda.com/distribution/) that not only ships Python but comes +pre-packaged with a lot of third-party libraries from the so-called +"scientific stack". +Just go to the [download](https://www.anaconda.com/distribution/#download-section) +section and install the latest version (i.e., *2019-07* with Python 3.7 at the +time of this writing) for your operating system. + +Then, among others, you will find an entry "Jupyter Notebook" in your start +menu like below. +Click on it and a new tab in your web browser will open where you can switch +between folders as you could in your computer's default file browser. + + + +To download the course's materials as a ZIP file, click on the green "Clone or +download" button on the top right on this website. +Then, unpack the ZIP file into a folder of your choosing (ideally somewhere +within your personal user folder so that the files show up right away). + + +### Alternative Installation + +Python can also be installed in a "pure" way as obtained from its core +development team (i.e., without any third-party packages installed). +However, this is somewhat too "advanced" for a beginner as it involves working +with a terminal emulator, which looks similar to the picture below and is +used without a mouse by typing commands into it. + + + +Assuming that you already have a working version of Python 3.6 or higher +installed (cf., the official [download page](https://www.python.org/downloads/)), +the following summarizes the commands to be typed into a terminal emulator to +get the course materials up and running on a local machine without Anaconda. +You are then responsible for understanding the concepts behind them. + +First, the [git](https://git-scm.com/) command line tool is a more professional +way of "cloning" the course materials as compared to downloading them in a ZIP +file. + +- `git clone https://github.com/webartifex/intro-to-python.git` + +This creates a new folder *intro-to-python* with all the materials of this +repository in it. + +Inside this folder, it is recommended to create a so-called **virtual +environment** with Python's [venv](https://docs.python.org/3/library/venv.html) +module. +This must only be done the first time. +A virtual environment is a way of *isolating* the third-party packages +installed by different projects, which is considered a best practice. + +- `python -m venv venv` + +The second *venv* is the environment's name and by convention often chosen to +be *venv*. +However, it could be another name as well. + +From then on, each time you want to resume work, go back into the +*intro-to-python* folder inside your terminal and "activate" the virtual +environment (*venv* is the name chosen before). + +- `source venv/bin/activate` + +This may change how the terminal's [command prompt](https://en.wikipedia.org/wiki/Command-line_interface#Command_prompt) +looks. + +[poetry](https://poetry.eustace.io/docs/) and [virtualenvwrapper](https://virtualenvwrapper.readthedocs.io/en/latest/) +are popular tools to automate the described management of virtual environments. + +After activation for the first time, you must install the project's +**dependencies** (= the third-party packages needed to run the code), most +notably [Jupyter](https://pypi.org/project/jupyter/) in this project (the +"python -m" is often left out; if you have poetry installed, you may just +type `poetry install` instead). + +- `python -m pip install -r requirements.txt` + + +With everything installed, you can now do the equivalent of clicking the +"Jupyter Notebook" entry in your start menu. + +- `jupyter notebook` + +This opens a new tab in your web browser just as above. + + +## About the Author + +Alexander Hess is a PhD student at the Chair of Logistics Management at the +[WHU - Otto Beisheim School of Management](https://www.whu.edu) where he +conducts research on urban delivery platforms and teaches an introductory +course on Python (cf., [course listing](https://vlv.whu.edu/campus/all/event.asp?objgguid=0xE57C2715B01B441AAFD3E79AA05CACCF&from=vvz&gguid=0x6A2B0ED5B2B949E69957A2099E7DE2F1&mode=own&tguid=0x3980A9BBC3BF4A638E977F2DC163F44B&lang=en)). + +Connect him on [LinkedIn](https://www.linkedin.com/in/webartifex). From b5382758eab7febe096c0c5bfb324490b9f612a8 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 19 Sep 2019 16:03:10 +0200 Subject: [PATCH 007/242] Add initial version of notebook 01 --- 01_elements_of_a_program.ipynb | 3708 ++++++++++++++++++++++++++++++++ 1 file changed, 3708 insertions(+) create mode 100644 01_elements_of_a_program.ipynb diff --git a/01_elements_of_a_program.ipynb b/01_elements_of_a_program.ipynb new file mode 100644 index 0000000..66c8e64 --- /dev/null +++ b/01_elements_of_a_program.ipynb @@ -0,0 +1,3708 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "\n", + "# Chapter 1: Elements of a Program" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Do you remember how you first learned to speak in your mother tounge? Probably not. No one's memory goes back that far. Your earliest memory as a child should probably be around the age of three or four years old when you could already say simple things and interact with your environment. Although you did not know any grammar rules yet, other people just understood what you said. Well, most of the time.\n", + "\n", + "It is intuitively best to take the very mindset of a small child when learing a foreign language and we do so for learning the Python language as well. This first chapter introduces simplistic examples and we better just accept them as they are without knowing any of the \"grammar\" rules yet. Then, we analyze them in parts and slowly build up our understanding.\n", + "\n", + "Consequently, if parts of this chapter do not make sense right away, let's not worry too much. Besides introducing some basics (that we need to understand), it also serves as an outlook for what is to come. So, many terms and concepts referenced here will be covered in great detail in following chapters." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Example: Average of a Subset of Numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As our introductory example, we want to calculate the average of all even numbers from one through ten.\n", + "\n", + "While we could come up with an [analytical solution](https://math.stackexchange.com/questions/935405/what-s-the-difference-between-analytical-and-numerical-approaches-to-problems/935446#935446) (i.e., derive some equation with \"pen and paper\" from, e.g., one of [Faulhaber's formulas](https://en.wikipedia.org/wiki/Faulhaber%27s_formula)), we instead solve the task programmatically.\n", + "\n", + "We start by creating a **list** called `numbers` that holds all the individual numbers." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To verify that something happened in our computer's memory, we simply **reference** `numbers` and observe that Python indeed knows about." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "So far, so good. Let's see how the desired result could be expressed as a **sequence of instructions** in Python.\n", + "\n", + "Intuitively, the line `for number in numbers` describes a \"loop\" over all the numbers in the `numbers` list, one at a time.\n", + "\n", + "The `if number % 2 == 0` may look disturbing at first sight. Both the `%` and `==` must have an unintuitive meaning here. Luckily, the **comment** in the same line after the `#` symbol has the answer: The program only does something if the current `number` is even.\n", + "\n", + "In particular, it increases `count` by $1$ and adds the current `number` onto the [running](https://en.wikipedia.org/wiki/Running_total) `total`. Both `count` and `number` were initially set to $0$ and the single `=` reads as \"... is *set* equal to ...\". It could not indicate a mathematical equation as, for example, `count` is generally not equal to `count + 1`.\n", + "\n", + "Lastly, the `average` is calculated as the ratio of the final **values** of `total` and `count`. Overall, we divide the sum of all even numbers by the count of all even numbers, which is exactly what we are looking for.\n", + "\n", + "We also observe how the lines of code \"within\" the `for` and `if` **statements** are *indented* and *aligned* with multiples of four spaces: This shows immediately how the lines relate to each other." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "count = 0 # initialize variables to keep track of the sum\n", + "total = 0 # so far and the count of the even numbers\n", + "\n", + "for number in numbers:\n", + " if number % 2 == 0: # only look at even numbers\n", + " count = count + 1\n", + " total = total + number\n", + "\n", + "average = total / count" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Yet, we do not see any **output** and obtain the value of `average` by simply referencing it again." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6.0" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Generating Cell Output in a Jupyter Notebook" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Note how only two of the previous four code cells generate an **output** while two remained \"silent\" (i.e., there is no \"**Out[...]**\" after running the cell).\n", + "\n", + "By default, Jupyter notebooks show the value of a cell's last so-called **expression**. This output can be suppressed by ending the last line with a semicolon.\n", + "\n", + "To visualize something before the end of the cell, we can use the [print()](https://docs.python.org/3/library/functions.html#print) built-in function." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'I am feeling great :-)'" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Hello, World!\"\n", + "\"I am feeling great :-)\"" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "\"I am invisible!\";" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello, World!\n", + "I am feeling great :-)\n" + ] + } + ], + "source": [ + "print(\"Hello, World!\")\n", + "print(\"I am feeling great :-)\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## (Arithmetic) Operators" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Python comes with basic mathematical operators built in. **Operators** are built-in **tokens** that have a special meaning to the Python interpreter. Most operators either \"operate\" with the object immediately following them (= **unary** operators; e.g., negation) or somehow \"process\" the two objects \"around\" them (= **binary** operators; e.g., addition). But we will see some exceptions from that as well.\n", + "\n", + "By definition, operators have **no** permanent **side effects** in the computer's memory. Although the code cells in this section do indeed lead to *new* objects being created in memory, they are immediately \"forgotten\" as they are not stored in a **variable** (like `numbers` above). We will revisit this idea further below when we compare **expressions** with **statements**.\n", + "\n", + "Let's see some examples of operators. We start with the binary `+` and the `-` operators for addition and subtraction. Binary operators resemble what mathematicians call [infix notation](https://en.wikipedia.org/wiki/Infix_notation) and have the expected meaning." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "90" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "77 + 13" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "8" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "101 - 93" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `-` operator can be used as a unary operator as well. Then it just flips the sign of a number." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "-1" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When we compare the output of the `*` and `/` operators for multiplication and division, we note the subtle difference between the $42$ and the $42.0$. This is a first illustration of the concept of a **data type**." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "2 * 21" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42.0" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "84 / 2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The so-called **floor division operator** `//` always rounds down to the next integer and is thus also called **integer division operator**. This is a first example of an operator we commonly do not know from high school mathematics." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "84 // 2" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "85 // 2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To obtain the remainder of a division, we can use the **modulo operator** `%`." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "85 % 2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Note that the remainder is $0$ if a number is divisable by another." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "49 % 7" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Modulo division can be useful if we, for example, need to get the last couple of digits of a large integer." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "123 % 10" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "23" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "123 % 100" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [divmod()](https://docs.python.org/3/library/functions.html#divmod) built-in **function** combines the integer and modulo divisions into one operation. However, this is not an operator any more (but a function). Also observe that [divmod()](https://docs.python.org/3/library/functions.html#divmod) returns a \"pair\" of integers." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(4, 2)" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "divmod(42, 10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Raising a number to a power is performed with the **exponentiation operator** `**`. Note that this is different from the `^` operator many other programming languages might use and that also exists in Python with a *different* meaning." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "8" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "2 ** 3" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The normal order of precedence from mathematics applies (i.e., \"PEMDAS\" rule) but parentheses help avoid confusion." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "18" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "3 ** 2 * 2 " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The parentheses here serve as a **delimiter**." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "18" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(3 ** 2) * 2 # same result as before but much clearer code" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "81" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "3 ** (2 * 2) # different result" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Some programmers also use \"style\" conventions. For example, we can play with the **whitespace**, which is an umbrella term that refers to any non-printable sign like spaces, tabs, or the like. However, parentheses convey a much clearer picture." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "18" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "3**2 * 2 # bad style; it is better to use parentheses here" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "There are plenty more mathematical and non-mathematical operators that are introduced throughout this book together with the concepts they implement or support. Some of these are already shown in the next section." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Objects vs. Values vs. Types" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Python is a so-called **object-oriented** language, which is a paradigm of organizing a program's memory.\n", + "\n", + "An **object** can be viewed as a \"bag\" of $0$s and $1$s in a distinct memory location that not only portrayes the idea of a certain **value** but also has some associated rules as to how this value is treated and may be worked with.\n", + "\n", + "An object *always* has **three** main characteristics. Let's look at the following examples and work them out." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "a = 789\n", + "b = 42.0\n", + "c = \"Python rocks\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Identity / \"Memory Location\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [id()](https://docs.python.org/3/library/functions.html#id) built-in function shows an object's \"address\" in memory." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140382247181072" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(a)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140382247352144" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(b)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140382247028656" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(c)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "These addresses are really not that meaningful for anything other than checking if two variables actually point at the same object. This may be helpful as different objects can of course have the same value." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "d = 789" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`a` and `d` indeed have the same value as can be checked with the **equality operator** `==`. The resulting `True` (and the `False` below) is yet another data type, a so-called **boolean**." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a == d" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "On the contrary, `a` and `d` are different objects as can be seen with the **identity operator** `is`: they are stored at seperate addresses in the memory." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a is d" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### (Data) Type / \"Behavior\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [type()](https://docs.python.org/3/library/functions.html#type) built-in function shows an object's type. For example, `a` is an integer (`int`) while `b` is a so-called [floating-point number](https://en.wikipedia.org/wiki/Floating-point_arithmetic) (`float`)." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "int" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(a)" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "float" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(b)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Different types imply different behaviors for the objects. The `b` object, for example, can be \"asked\" if it could also be interpreted as an `int` with the [is_integer()](https://docs.python.org/3/library/stdtypes.html#float.is_integer) \"functionality\" that comes with every `float` object.\n", + "\n", + "Formally, we call such type-specific functionalities **methods** (to differentiate them from functions) and we will eventually fully introduce them when we talk about object-orientation in Chapter 10. For now, it suffices to know that we can access them using the **dot operator** `.`. Of course `b` could be converted into an `int`, which the boolean value `True` tells us.\n", + "\n", + "Also note how the `.` operator is neiter a unary nor a binary operator as specified above." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b.is_integer()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For an `int` object this [is_integer()](https://docs.python.org/3/library/stdtypes.html#float.is_integer) check does not make sense as we know it is an `int` to begin with. This is why we see the `AttributeError` below as `a` does not even know what `is_integer()` means." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'int' object has no attribute 'is_integer'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0ma\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mis_integer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m: 'int' object has no attribute 'is_integer'" + ] + } + ], + "source": [ + "a.is_integer()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `c` object is a so-called **string** type `str`, which we can view as Python's way of representing \"text\". Strings also come with their own behaviors, for example, to convert a text to lower or upper case." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "str" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(c)" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'python rocks'" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c.lower()" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'PYTHON ROCKS'" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c.upper()" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Python Rocks'" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c.title()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Value" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Almost trivially, every object also has a value to which it \"evaluates\" when referenced. We think of the value as the **conceptual idea** of what the $0$s and $1$s in memory mean to us humans. A machine does not really see beyond the $0$s and $1$s. At least, not yet.\n", + "\n", + "For built-in data types, Python prints out the object's value in a so-called **[literal](https://docs.python.org/3/reference/lexical_analysis.html#literals)** notation. This basically means that we can just copy & paste the output back into a code cell to create a *new* object with the *same* value." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "789" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42.0" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In this book, we follow the convention of creating strings with **double quotes** `\"` instead of the **single quotes** `'` to which Python defaults in its literal output. Both types of quotes can be used interchangebly." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Python rocks'" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Formal vs. Natural Languages" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Just like the language of mathematics is good at expressing relationships among numbers and symbols, any programming language is just a formal language that is good at expressing computations.\n", + "\n", + "Formal languages come with their own \"grammatical rules\" called **syntax**." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Syntax Errors" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we do not follow the rules, the code cannot be **parsed** correctly, i.e., the program does not even start to run but raises a **syntax error** (indicated as `SyntaxError`). Computers are very dumb in the sense that the slightest syntax error leads to the machine not understanding our code.\n", + "\n", + "For example, if we wanted to write an accounting program that adds up currencies, we would have to model dollar prices as `float` objects as the dollar symbol cannot be read by Python." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 3.99 $ + 10.40 $\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + ] + } + ], + "source": [ + "3.99 $ + 10.40 $" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Python requires certain symbols at certain places (e.g., a `:` is missing here) ..." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m for number in numbers\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + ] + } + ], + "source": [ + "for number in numbers\n", + " print(number)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... and relies on whitespace / indentation unlike many other programming languages. A `IndentationError` is just a special type of a `SyntaxError`." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "IndentationError", + "evalue": "expected an indented block (, line 2)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m2\u001b[0m\n\u001b[0;31m print(number)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mIndentationError\u001b[0m\u001b[0;31m:\u001b[0m expected an indented block\n" + ] + } + ], + "source": [ + "for number in numbers:\n", + "print(number)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Runtime Errors" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Syntax errors as above are easy to find as the code will not even run to begin with.\n", + "\n", + "However, there are also so-called **runtime errors** (also called **exceptions**) that occur if the code would run given correct input.\n", + "\n", + "This example does not work because just like in the \"real\" world, Python does not know how to divide by $0$. The syntactically correct code leads to a `ZeroDivisionError`." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "ZeroDivisionError", + "evalue": "division by zero", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m1\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" + ] + } + ], + "source": [ + "1 / 0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Semantic Errors" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "So-called **semantic errors**, on the contrary, can be very hard to spot as they do *not* crash the program. The only way to find such errors is to run the program with test input for which we know the answer already and can then verify it. However, testing software is a whole discipline on its own and often very hard to do in practice.\n", + "\n", + "The cell below copies our introductory example from above with a \"tiny\" error." + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "count = 0\n", + "total = 0\n", + "\n", + "for number in numbers:\n", + " if number % 2 == 0:\n", + " count = count + 1\n", + " total = total + count # count is wrong here, it should be number\n", + "\n", + "average = total / count" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3.0" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Finding errors is is called **debugging**. For the history of the term, check this [link](https://en.wikipedia.org/wiki/Debugging)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Best Practices" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Adhering to just syntax rules is therefore *never* enough. Over time, **best practices** and common **style guides** were created to make it less likely for a developer to mess up a program and also to allow \"onboarding\" him as a contributor to an established code base (often called **legacy code**) faster. These rules are not enforced by Python itself: Badly styled and un-readable code will still run. At the very least, Python programs should be styled according to [PEP 8](https://www.python.org/dev/peps/pep-0008/) and documented \"inline\" (i.e., in the code itself) according to [PEP 257](https://www.python.org/dev/peps/pep-0257/).\n", + "\n", + "An easier to read version of PEP 8 can be found [here](https://pep8.org/). The video below features a well known \"[Pythonista](https://en.wiktionary.org/wiki/Pythonista)\" talking about the importance of code style." + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAUDBAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChANCAgOCggIDRUNDhERExMTCAsWGBYSGBASExIBBQUFCAcIDwkJDxUVEBUVFhUVFRUSFRUVFRISEhUVFRUVFRUVFRUVFRIVEhUVFRUVFRUVFRUVFRUVFRUVFRUVFf/AABEIAWgB4AMBIgACEQEDEQH/xAAcAAEAAgMBAQEAAAAAAAAAAAAABgcEBQgDAgH/xABfEAABAwICBQUHDgsGAggFBQABAAIDBBEFEgYHEyExQVFTYZIUFyJxgZHTCCMyNUJSYnJzdKGxs8EVJCUzNIKDorK0wkNjZJOjw9HwJ1RVlKS10uEWJmWVpTY3RFZ1/8QAHAEBAAIDAQEBAAAAAAAAAAAAAAUGAwQHAgEI/8QAQBEAAQIDAwgGBwcEAwEAAAAAAQACAwQRBSExBhITQVFxkcEWYWKBobEUIjRTcpLRIyQyMzWC4QdSsvBCovEV/9oADAMBAAIRAxEAPwDjJEREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREX6innexrOmpu3L6JO9jWdNTduX0SUKmuj0/7p3BQNFPO9jWdNTduX0Sd7Gs6am7cvokoU6PT/uncFA0U872NZ01N25fRJ3sazpqbty+iShTo9P8AuncFA0U872NZ01N25fRJ3sazpqbty+iShTo9P+6dwUDRTzvY1nTU3bl9EnexrOmpu3L6JKFOj0/7p3BQNFPO9jWdNTduX0Sd7Gs6am7cvokoU6PT/uncFA0U872NZ01N25fRJ3sazpqbty+iShTo9P8AuncFA0U872NZ01N25fRJ3sazpqbty+iShTo9P+6dwUDRTzvY1nTU3bl9EnexrOmpu3L6JKFOj0/7p3BQNFPO9jWdNTduX0Sd7Gs6am7cvokoU6PT/uncFA0U872NZ01N25fRJ3sazpqbty+iShTo9P8AuncFA0U872NZ01N25fRJ3sazpqbty+iShTo9P+6dwUDRTzvY1nTU3bl9EnexrOmpu3L6JKFOj0/7p3BQNFPO9jWdNTduX0Sd7Gs6am7cvokoU6PT/uncFA0U872NZ01N25fRJ3sazpqbty+iShTo9P8AuncFA0U872NZ01N25fRJ3sazpqbty+iShTo9P+6dwUDRTzvY1nTU3bl9EnexrOmpu3L6JKFOj0/7p3BQNFPO9jWdNTduX0Sd7Gs6am7cvokoU6PT/uncFA0U872NZ01N25fRJ3sazpqbty+iShTo9P8AuncFA0U872NZ01N25fRJ3sazpqbty+iShTo9P+6dwUDRTzvY1nTU3bl9EnexrOmpu3L6JKFOj0/7p3BQNFPO9jWdNTduX0Sd7Gs6am7cvokoU6PT/uncFA0U872NZ01N25fRJ3sazpqbty+iShTo9P8AuncFA0U872NZ01N25fRJ3sazpqbty+iShTo9P+6dwUDRTzvY1nTU3bl9EnexrOmpu3L6JKFOj0/7p3BQNFPO9jWdNTduX0Sd7Gs6am7cvokoU6PT/uncFA0U872NZ01N25fRJ3sazpqbty+iShTo9P8AuncFA0U872NZ01N25fRJ3sazpqbty+iShTo9P+6dwUDRTzvY1nTU3bl9EnexrOmpu3L6JKFOj0/7p3BQNFPO9jWdNTduX0Sd7Gs6am7cvokoU6PT/uncFbCIi2F3RERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERTTVfoizE5JZJ3HueAtaYmOLXyvcMwDnDeyMDlG8ncCLFa81NMl4RiPwCwTMwyBDMR+AULRdEt0MwoC34PpfGYWl3lefCJ67rT41qxw2YEwiSlfyOheXM8Topcwy/FynrUDDyplnGjgR10B5qHZlDBJo4OHXd9VRyKRaX6H1eG+FIBJASA2oiByXO4NlYd8Lid2+4NwASdyjqsMCPDjMDoZBB1hTUGOyM3OYahERFlWVEUz0Q1eVOIQCoMzKWJxOyzxOkfI0btoGh7bMJuASd9r8LEyin1Qwf2tbO75KKKP+POomYtuUguLXPvFxABN/BRsW15WG4tc68agCeVFUiK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U0gTprZ/a+VWyiqbvnVnQUvYl9KnfOrOgpexL6VNIE6a2f2vlUDREWNceREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREX//2Q==\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from IPython.display import YouTubeVideo\n", + "YouTubeVideo(\"Hwckt4J96dI\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For example, while the above code to calculate the average of the even numbers from 1 through 10 is correct, a Pythonista would re-write it in a more \"Pythonic\" way and use the [sum()](https://docs.python.org/3/library/functions.html#sum) and [len()](https://docs.python.org/3/library/functions.html#len) (= \"length\") built-in functions. Pythonic code runs faster in many cases and is less error prone." + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "evens = [n for n in numbers if n % 2 == 0] # example for a so-called list comprehension" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[2, 4, 6, 8, 10]" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "evens" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "average = sum(evens) / len(evens) # built-in functions are much faster than a for-loop" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6.0" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To get a rough overview of the mindsets of a typical Python programmer, check these rules by an early Python core developer that are deemed so important that they are actually included in every Python installation." + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The Zen of Python, by Tim Peters\n", + "\n", + "Beautiful is better than ugly.\n", + "Explicit is better than implicit.\n", + "Simple is better than complex.\n", + "Complex is better than complicated.\n", + "Flat is better than nested.\n", + "Sparse is better than dense.\n", + "Readability counts.\n", + "Special cases aren't special enough to break the rules.\n", + "Although practicality beats purity.\n", + "Errors should never pass silently.\n", + "Unless explicitly silenced.\n", + "In the face of ambiguity, refuse the temptation to guess.\n", + "There should be one-- and preferably only one --obvious way to do it.\n", + "Although that way may not be obvious at first unless you're Dutch.\n", + "Now is better than never.\n", + "Although never is often better than *right* now.\n", + "If the implementation is hard to explain, it's a bad idea.\n", + "If the implementation is easy to explain, it may be a good idea.\n", + "Namespaces are one honking great idea -- let's do more of those!\n" + ] + } + ], + "source": [ + "import this" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### Jupyter Notebook Aspects to keep in Mind" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### The Order of Code Cells is arbitrary" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Observe that you can run the code cells in a Jupyter notebook in any arbitrary order.\n", + "\n", + "That means, for example, that a variable defined towards the bottom could accidently be referenced at the top of the notebook. This happens easily if we iteratively built a program and go back and forth between cells.\n", + "\n", + "As a good practice, it is recommended to click on \"Kernel\" > \"Restart & Run All\" in the navigation bar once a notebook is finished. That restarts the Python process forgetting any **state** (i.e., all variables) and ensures that the notebook runs top to bottom without any errors the next time it is opened." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### Notebooks are linear" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While this book is built with Jupyter notebooks, it is important to understand that \"real\" programs are almost always just \"linear\" (= top to bottom) sequences of instructions but instead can take many different **flows of execution**.\n", + "\n", + "At the same time, for a beginner's course it is often easier to just code in a linear fashion.\n", + "\n", + "In real data science projects one would probably employ a mixed approach and put re-usable code into so-called Python modules (= \\*.py files) and then use Jupyter notebooks to built up a linear report or story line for a business argument to be made." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Variables / Names" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Variables** are created with the **assignment statement** `=`. As its name suggests, it is *not* an operator, mainly because of its side effect of making a **name** \"point\" to an object in memory." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "a = 20.0\n", + "b = 789" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When referenced, a variable just evaluates to the value of the object it points to. Colloquially, we could say that `a` evaluates to `20.0` here but this would not be an accurate description of what is really going on in memory.\n", + "\n", + "We will see some more colloquial jargons in this section but should always remind ourselves what we better said instead." + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "20.0" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A variable can be **re-assigned** as often as we wish. Thereby, we could also assign an object of a different type. Because this is allowed, Python is said to be a **dynamically typed** language. On the contrary, a **statically typed** language like C also allows re-assignment but only with objects of the same type. This subtle distinction is one reason why Python is slower at execution than C: As it runs a program, it needs to figure out an object's type each time it is referenced. But as mentioned before, this can be mitigated with third-party libraries." + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "a = 20" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "20" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we want to re-assign a variable while referencing its \"old\" (i.e., current) object, we can also **update** it using a so-called **augmented assignment statement** (*not* operator). This implicitly inserts the current \"value\" as the first token on the right-hand side." + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "a *= 4 # same as a = a * 4" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "a //= 2 # same as a = a // 2, \"//\" to retain the integer type" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "a += 2 # same as a = a + 2" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Variables can be **de-referenced** (i.e., \"deleted\") with the `del` statement. This does *not* \"delete\" the object to which a variable points to. It merely removes the variable from the \"list of all variables\"." + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "789" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "del b" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we refer to an unknown name, a runtime exception occurs, namely a `NameError`." + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'b' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mb\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mNameError\u001b[0m: name 'b' is not defined" + ] + } + ], + "source": [ + "b" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Some names magically exist when we start Python. In this introductory book, we can safely ignore such variables." + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'__main__'" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "__name__" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To see all defined names, the built-in function [dir()](https://docs.python.org/3/library/functions.html#dir) is helpful." + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['In',\n", + " 'Out',\n", + " 'YouTubeVideo',\n", + " '_',\n", + " '_10',\n", + " '_11',\n", + " '_12',\n", + " '_13',\n", + " '_14',\n", + " '_15',\n", + " '_16',\n", + " '_17',\n", + " '_18',\n", + " '_19',\n", + " '_2',\n", + " '_20',\n", + " '_21',\n", + " '_22',\n", + " '_23',\n", + " '_24',\n", + " '_26',\n", + " '_27',\n", + " '_28',\n", + " '_30',\n", + " '_31',\n", + " '_32',\n", + " '_33',\n", + " '_34',\n", + " '_36',\n", + " '_37',\n", + " '_38',\n", + " '_39',\n", + " '_4',\n", + " '_40',\n", + " '_41',\n", + " '_42',\n", + " '_48',\n", + " '_49',\n", + " '_5',\n", + " '_52',\n", + " '_54',\n", + " '_57',\n", + " '_59',\n", + " '_63',\n", + " '_64',\n", + " '_67',\n", + " '_8',\n", + " '_9',\n", + " '__',\n", + " '___',\n", + " '__builtin__',\n", + " '__builtins__',\n", + " '__doc__',\n", + " '__loader__',\n", + " '__name__',\n", + " '__package__',\n", + " '__spec__',\n", + " '_dh',\n", + " '_i',\n", + " '_i1',\n", + " '_i10',\n", + " '_i11',\n", + " '_i12',\n", + " '_i13',\n", + " '_i14',\n", + " '_i15',\n", + " '_i16',\n", + " '_i17',\n", + " '_i18',\n", + " '_i19',\n", + " '_i2',\n", + " '_i20',\n", + " '_i21',\n", + " '_i22',\n", + " '_i23',\n", + " '_i24',\n", + " '_i25',\n", + " '_i26',\n", + " '_i27',\n", + " '_i28',\n", + " '_i29',\n", + " '_i3',\n", + " '_i30',\n", + " '_i31',\n", + " '_i32',\n", + " '_i33',\n", + " '_i34',\n", + " '_i35',\n", + " '_i36',\n", + " '_i37',\n", + " '_i38',\n", + " '_i39',\n", + " '_i4',\n", + " '_i40',\n", + " '_i41',\n", + " '_i42',\n", + " '_i43',\n", + " '_i44',\n", + " '_i45',\n", + " '_i46',\n", + " '_i47',\n", + " '_i48',\n", + " '_i49',\n", + " '_i5',\n", + " '_i50',\n", + " '_i51',\n", + " '_i52',\n", + " '_i53',\n", + " '_i54',\n", + " '_i55',\n", + " '_i56',\n", + " '_i57',\n", + " '_i58',\n", + " '_i59',\n", + " '_i6',\n", + " '_i60',\n", + " '_i61',\n", + " '_i62',\n", + " '_i63',\n", + " '_i64',\n", + " '_i65',\n", + " '_i66',\n", + " '_i67',\n", + " '_i68',\n", + " '_i7',\n", + " '_i8',\n", + " '_i9',\n", + " '_ih',\n", + " '_ii',\n", + " '_iii',\n", + " '_oh',\n", + " 'a',\n", + " 'average',\n", + " 'c',\n", + " 'count',\n", + " 'd',\n", + " 'evens',\n", + " 'exit',\n", + " 'get_ipython',\n", + " 'number',\n", + " 'numbers',\n", + " 'quit',\n", + " 'this',\n", + " 'total']" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dir()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Who am I? And how many?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "It is important to understand that *several* variables can point to the *same* object in memory. This can be counter-intuitive in the beginning and lead to many hard to track down bugs.\n", + "\n", + "This makes `b` point to whatever object `a` points to." + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "b = a # this is different from b == a" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For \"simple\" types like `int` or `float` this will never cause confusion.\n", + "\n", + "Let's \"change the value\" of `a`. Really, let's create a *new* `123` object and make `a` point to it." + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "a = 123" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "123" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`b` \"is still the same\" as before. Really, `b` still points to the same object as before." + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, if a name points to an object of a more \"complex\" object, for example, of type `list`, \"weird\" things can happen." + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "x = [1, 2, 3]" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "list" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(x)" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "y = x" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's change the first element of `x`.\n", + "\n", + "Chapter 7 discusses lists in more depth. For now, let's just view a list as some sort of **container** object that holds an arbitrary number of pointers to other objects and treat the brackets `[...]` attached to `x` as just another operator, called the **indexing operator**. `x[0]` instructs Python to first \"follow\" the pointer from the \"global\" directory of all names to the list object. Then, it follows the first pointer it finds there to the `1` object. The indexing operator must be an operator as we merely read the first element an do not change anything in memory.\n", + "\n", + "Note how Python **begins counting at 0**. This is not the case for many other languages, for example, MATLAB, R, or Stata. To understand why this makes sense, see this short [note](https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html) by one of the all-time greats in computer science, the late [Edsger Dijkstra](https://en.wikipedia.org/wiki/Edsger_W._Dijkstra)." + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To change the first entry in the list, we use the assignment statement `=` again. Here, this does actually *not* create a *new* variable but only changes the object to which the first pointer in the `x` list points to. As we only change parts of the `x` list, we say that we change its **state**." + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "x[0] = 99" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[99, 2, 3]" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The changes made to `x` can also be seen through the `y` variable." + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[99, 2, 3]" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "y" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The illustrated difference in behavior has to do with the fact that integers and floats are **immutable** types while lists are **mutable**.\n", + "\n", + "In the first case, an object cannot be changed \"in place\" once it is created in memory. When we assigned `123` to the already existing `a`, we actually did not change the $0$s and $1$s in the object `a` pointed to before the assignment but created a new integer object and made `a` point to it while the `b` variable is *not* affected.\n", + "\n", + "In the second case, `x[0] = 99` creates a *new* integer object `99` and merely changes the first pointer in the `x` list.\n", + "\n", + "In general, the assignment statement (re-)creates a variable and makes it point to whatever object is on the right-hand side *if* the left-hand side is a *pure* variable name. Otherwise, it changes some object on the left-hand side (this is strictly not a must but we should expect it).\n", + "\n", + "In the beginning, visualizing the memory with a tool like [PythonTutor](http://pythontutor.com/visualize.html#code=x%20%3D%20%5B1,%202,%203%5D%0Ay%20%3D%20x%0Ax%5B0%5D%20%3D%2099%0Adel%20x,%20y%0Ax%20%3D%20%5B1,%202,%203%5D%0Ay%20%3D%20x.copy%28%29%0Ax%5B0%5D%20%3D%2099&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) will assist in understanding what is going on." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Naming Conventions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**[Phil Karlton](https://skeptics.stackexchange.com/questions/19836/has-phil-karlton-ever-said-there-are-only-two-hard-things-in-computer-science)** famously noted (during his time at [Netscape](https://en.wikipedia.org/wiki/Netscape)):\n", + "\n", + "> \"There are two hard problems in computer science: naming things and cache invalidation ... and off-by-one errors.\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Variable names may contain upper and lower case letters, numbers, and underscores (\"\\_\") and be as long as we want them to be. However, they must not begin with a number. Also, they must not be any of Python's **[keywords](https://docs.python.org/3/reference/lexical_analysis.html#keywords)**.\n", + "\n", + "Variable names are usually chosen such that they do not need any more documentation and are self-explanatory. A very common convention is to use so-called **[snake\\_case](https://en.wikipedia.org/wiki/Snake_case)**: Keep everything lowercase and use underscores to seperate words.\n", + "\n", + "See this [link](https://en.wikipedia.org/wiki/Naming_convention_%28programming%29#Python_and_Ruby) for a comparison of different naming conventions." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### Good examples" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "pi = 3.14" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "answer_to_everything = 42" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "my_name = \"Alexander\"" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "work_address = \"Burgplatz 2, Vallendar\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### Bad examples" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "PI = 3.14 # unless used as a \"global\" constant" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "answerToEverything = 42 # this is a style used in languages like Java" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "name = \"Alexander\" # name of what ?" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "can't assign to operator (, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m address@work = \"Burgplatz 2, Vallendar\"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m can't assign to operator\n" + ] + } + ], + "source": [ + "address@work = \"Burgplatz 2, Vallendar\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If a variable name collides with a built-in name, just add a trailing underscore." + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "type_ = \"student\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Variables with leading and trailing double underscores (referred to as **dunder** in Python \"slang\") are used for important built-in variables. Do *not* use this style for custom variables!" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'__main__'" + ] + }, + "execution_count": 91, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "__name__" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### The big Picture" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This PyCon talk by [Ned Batchelder](https://nedbatchelder.com/) (a software engineer at [edX](https://www.edx.org/) and the organizer of the [Python User Group](https://www.meetup.com/bostonpython/) in Boston) summarizes all situations where some sort of variable assignment is done in Python. The content is intermediate and therefore it is ok if you do not understand everything at this point. However, the contents should be known by everyone claiming to be a Pythonista." + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 92, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from IPython.display import YouTubeVideo\n", + "YouTubeVideo(\"_AEJHKGk9ns\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Expressions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "An **expression** is any syntactically correct **combination** of **variables** and **literals** with **operators**. See the [language reference](https://docs.python.org/3/reference/expressions.html) for a full list.\n", + "\n", + "In simple words, anything that can be used on the right-hand side of an assignment statement without creating a `SyntaxError` is an expression.\n", + "\n", + "What we said about individual operators above (namely that they have *no* side effects) should have been put here to begin with. The examples in the section on operators were actually all expressions.\n", + "\n", + "The simplest possible expression contains only one variable (or literal)." + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "123" + ] + }, + "execution_count": 93, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a" + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "165" + ] + }, + "execution_count": 94, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a + b" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The definition of an expression is **recursive**. So here the sub-expression `a + b` is combined with the literal `3` by the operator `**` to form another expression." + ] + }, + { + "cell_type": "code", + "execution_count": 95, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4492125" + ] + }, + "execution_count": 95, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(a + b) ** 3" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As before, the bracket operator `[...]` can be used for indexing." + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 96, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "y[2]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When not used as a **delimiter**, parentheses also constitute an operator, namely the **call operator** `(...)`. We have seen this syntax above when we \"called\" (i.e., executed) built-in functions and methods." + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "104" + ] + }, + "execution_count": 97, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(x)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Operator Overloading" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Python **overloads** certain operators. For example you can not only add numbers but also strings. This is called **string concatenation**." + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "greeting = \"Hi \"\n", + "audience = \"class\"" + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Hi class'" + ] + }, + "execution_count": 99, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "greeting + audience" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Duplicate strings using multiplication." + ] + }, + { + "cell_type": "code", + "execution_count": 100, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi '" + ] + }, + "execution_count": 100, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b * greeting" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Statements" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A **statement** is anything that changes the state of the program's memory or has some other side effect. Statements do not just evaluate to a value like expressions; instead, they create or change values. See the [language reference](https://docs.python.org/3/reference/simple_stmts.html) for a full list.\n", + "\n", + "Most notably of course are the `=` and `del` statements." + ] + }, + { + "cell_type": "code", + "execution_count": 101, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "a = 123" + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "del a" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [print()](https://docs.python.org/3/library/functions.html#print) function is regarded a \"statement\" as well. In fact, it used to be a real statement in Python 2 and has all the necessary properties. It is a bit of a corner case as expressions are also \"printed\" in a Jupyter notebook when evaluated last in a code cell." + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "I change the display of the computer\n" + ] + } + ], + "source": [ + "print(\"I change the display of the computer\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Comments" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We can use the `#` symbol to write comments in plain English right into the code.\n", + "\n", + "As a good practice, comments should not describe what happens (this should be evident by reading the code, otherwise it is most likely badly written code) but why something happens.\n", + "\n", + "Comments can be either added at the end of a line of code (by convention seperated with two spaces) or be on a line on their own." + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "distance = 891 # in meters\n", + "elapsed_time = 93 # in seconds\n", + "# Calculate the speed in km/h.\n", + "speed = 3.6 * distance / elapsed_time" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "But let's think wisely if we really need to use a comment.\n", + "The second cell is a lot more \"Pythonic\"." + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "seconds = 365 * 24 * 60 * 60 # = seconds in the year" + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "seconds_per_year = 365 * 24 * 60 * 60" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## TL;DR" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We end each chapter with a summary of the main points. The essence in this first chapter is that just like a sentence in a real language like English can be decomposed into its parts (subject, predicate, objects, ...) the same can be done with programming languages." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "- 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", + " - (numeric) data from a CSV file\n", + " - text entered on a command line\n", + " - (relational) data obtained from a database\n", + " - ...\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", + " - ...\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 8)\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", + " - **names** that point to **objects** in **memory**\n", + " - e.g., `x = 1` creates the variable `x` that points to 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-map names to different objects and *change* the state of the program\n", + " - usually work with expressions\n", + " - e.g., the assignment statement `=` makes a name point to 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)\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", + "\n", + "\n", + "- flow control (cf., Chapter 3)\n", + " - expression of **logic** or an **algorithm**\n", + " - conditional execution of a small **branch** within a program (i.e., `if`-statements)\n", + " - repetitive execution of parts of a program (i.e., `for`-loops and `while`-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.7.3" + }, + "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": 2 +} From 387708f253f7040cbade6fdabaa8e1f0b5e75b14 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 19 Sep 2019 16:04:19 +0200 Subject: [PATCH 008/242] Adjust content overview in notebook 00 - the old chapters 01 and 02 were merged -> reflect that in the contents overview in notebook 00 - streamline text --- 00_start_up.ipynb | 51 +++++++++++++++++++---------------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/00_start_up.ipynb b/00_start_up.ipynb index 588d7ab..7551dcd 100644 --- a/00_start_up.ipynb +++ b/00_start_up.ipynb @@ -295,15 +295,7 @@ "slide_type": "slide" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello world\n" - ] - } - ], + "outputs": [], "source": [ "print(\"Hello world\")" ] @@ -316,7 +308,7 @@ } }, "source": [ - "Sometimes a code cell starts with an exclamation mark `!`. Then, the Jupyter notebook behaves as if you just typed the following commands right into a terminal. The cell below asks `python` to show its version number. This is actually *not* Python code but a command in the [Shell](https://en.wikipedia.org/wiki/Shell_script) language. The `!` is useful to execute short commands without leaving a Jupyter notebook." + "Sometimes a code cell starts with an exclamation mark `!`. Then, the Jupyter notebook behaves as if you just typed the following command right into a terminal. The cell below asks `python` to show its version number. This is actually *not* Python code but a command in the [Shell](https://en.wikipedia.org/wiki/Shell_script) language. The `!` is useful to execute short commands without leaving a Jupyter notebook." ] }, { @@ -671,7 +663,7 @@ } }, "source": [ - "### The ABC Rule" + "### ABC Rule" ] }, { @@ -728,7 +720,7 @@ } }, "source": [ - "### Phase Iteration - The $\\pi$ Rule" + "### Phase Iteration" ] }, { @@ -756,7 +748,7 @@ "cell_type": "markdown", "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "skip" } }, "source": [ @@ -767,39 +759,38 @@ "cell_type": "markdown", "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "skip" } }, "source": [ "**Part 1: Expressing Logic**\n", "\n", - "- What is a programming language?\n", - " 1. Structure of a Program\n", - "- What kind of words exist?\n", - " 2. Variables, Expressions, Statements, & Comments\n", - " 3. Functions\n", - "- How can we form sentences from words?\n", - " 4. Conditionals\n", - " 5. Iteration" + "- What is a programming language? What kind of words exist?\n", + " 1. Elements of a Program\n", + " 2. Functions & Modularization\n", + "- What is the flow of execution? How can we form sentences from words?\n", + " 3. Boolean Logic & Conditionals\n", + " 4. Recursion & Looping" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "skip" } }, "source": [ - "**Part 2: Managing Data**\n", + "**Part 2: Managing Data and Memory**\n", "\n", "- How is data stored in memory?\n", - " 6. Numbers\n", - " 7. Text\n", - " 8. Sequences\n", - " 9. Mappings & Sets\n", + " 5. Numbers\n", + " 6. Text\n", + " 7. Sequences\n", + " 8. Mappings & Sets\n", + " 9. Arrays\n", "- How can we create our own data types?\n", - " 10. Classes & Instances" + " 10. Object-Orientation" ] }, { @@ -821,7 +812,7 @@ } }, "source": [ - "As with every good lecture, there always has to be a [xkcd](https://xkcd.com/353/) comic somewhere." + "As with every good lecture, there has to be a [xkcd](https://xkcd.com/353/) comic somewhere." ] }, { From 3ee72c08ae8da37af688311f9aa8a13c6631a9b8 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 19 Sep 2019 16:05:04 +0200 Subject: [PATCH 009/242] Add review and exercises for notebook 00 --- 00_start_up_review_and_exercises.ipynb | 290 +++++++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 00_start_up_review_and_exercises.ipynb diff --git a/00_start_up_review_and_exercises.ipynb b/00_start_up_review_and_exercises.ipynb new file mode 100644 index 0000000..26eb93c --- /dev/null +++ b/00_start_up_review_and_exercises.ipynb @@ -0,0 +1,290 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 0: Start up" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Content Review" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read chapter 0 of the book. Then work through the ten review questions." + ] + }, + { + "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**: Describe the difference between the terms *programming* and *computer science*!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: Explain what is a *pull request* and elaborate how this concept fits to a *distributed* organization of work!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: In what sense are open-source communities democracies? How are they near-\"perfect\" [meritocracies](https://en.wikipedia.org/wiki/Meritocracy)? How is open-source software development similar to academia?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: What is a major advantage of a \"slow\" programming language like Python over a faster one like C?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: Python was originally designed for highly intensive numerical computing, in particular for use cases from physics and astronomy." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: JavaScript is a special subset of the Java language." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: Python is *free software*. That means it will never cost anything." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: The main purpose of PEPs is to regulate how code should be documented and/or styled." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## \"Coding\" Exercises" + ] + }, + { + "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": [ + "**Q11**: 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q12**: The quote \"Education is what remains after one has forgotten what one has learned in school\" is attributed to Albert Einstein. Use a special Markdown syntax to display the author and his quote appropriately!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q13**: Integrate this image (https://i.ytimg.com/vi/-BoSRlzy9c4/maxresdefault.jpg) of the delicious dessert **milk rice** into this notebook." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "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": 2 +} From d90d90ecbb09e3cda9bb875e5227f6a4fe19d7ff Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 19 Sep 2019 16:05:16 +0200 Subject: [PATCH 010/242] Add review and exercises for notebook 01 --- ...ts_of_a_program_review_and_exercises.ipynb | 386 ++++++++++++++++++ 1 file changed, 386 insertions(+) create mode 100644 01_elements_of_a_program_review_and_exercises.ipynb diff --git a/01_elements_of_a_program_review_and_exercises.ipynb b/01_elements_of_a_program_review_and_exercises.ipynb new file mode 100644 index 0000000..088001b --- /dev/null +++ b/01_elements_of_a_program_review_and_exercises.ipynb @@ -0,0 +1,386 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "# Chapter 1: Elements of a Program" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Content Review" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read chapter 1 of the book. Then work through the ten review questions." + ] + }, + { + "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**: Elaborate on how **modulo division** might be a very useful operation to know!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: Why is it useful to start counting at $0$?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: What is **operator overloading**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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](https://docs.python.org/3/reference/lexical_analysis.html#keywords)?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: Advocates of the [functional programming](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": [ + " " + ] + }, + { + "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 (\"down under\") geeks that work on core Python." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: The **Zen of Python** has a high opinion on Indian genius programmers." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: When NASA famously converted some measurements to the wrong unit and lost a Mars satellite in 1999 ([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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: [PEP 8](https://www.python.org/dev/peps/pep-0008/) suggests that developers use **$8$ spaces** per level of indentation." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Coding Exercises" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Printing Output" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11.1**: Read about the [print()](https://docs.python.org/3/library/functions.html#print) built-in. How can you use it to print both `greeting` and `audience` *without* concatenating the two strings with the `+` operator?\n", + "\n", + "Hint: The `*objects` in the documentation implies that we can insert several comma-seperated variables." + ] + }, + { + "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": [ + "**Q11.2**: What does the `sep=\" \"` mean in the documentation? Use it to print out the three names in `first`, `second`, and `third` on one line seperated by commas with one [print()](https://docs.python.org/3/library/functions.html#print) statement." + ] + }, + { + "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": [ + "**Q11.3**: Lastly, what does the `end=\"\\n\"` mean in the documentation? Use it in the `for`-loop to print the numbers $1$ through $10$ in just one line." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:\n", + " print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fizz Buzz" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The kids game [Fizz Buzz](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 actually is ([source](https://news.ycombinator.com/item?id=16446774)).\n", + "\n", + "In its simplest form, a group of people start 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.\n", + "\n", + "With just Chapter 1, we actually do not yet know all of Python's language constructs we need to write an implementation of Fizz Buzz in a Pythonic way. Yet, we will tweak what we know a bit and make it work." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11.1**: 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()](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()](https://docs.python.org/3/library/functions.html#func-range) a `list` object, you have to \"wrap\" it with the [list()](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": [ + "**Q11.2**: 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 `=`. In the chapter we saw that Python starts indexing with `0` as the first element. So in each iteration of the `for`-loop you have to determine the *index* as well as checking the actual `number`.\n", + "\n", + "Also note that for numbers divisible by both $3$ and $5$ we need some sort of a \"third\" condition check: As we only know about the `if` statement so far (and have not heard about `elif` and `else` from Chapter 3), there will be three `if` statements in total within the loop. And the order of them matters!\n", + "\n", + "Hint: Is there a single condition that checks for both $3$ and $5$?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in numbers:\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11.3**: Create a loop that prints out either the number or any of the Fizz Buzz substitutes. Do it in such a way that we do not end up with $100$ lines of output here." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in numbers:\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.7.3" + }, + "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": 2 +} From b41c5b53026678b68d8cebb719f2946bfe519c84 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 19 Sep 2019 16:06:21 +0200 Subject: [PATCH 011/242] Adjust content overview in README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4fd2b52..78bdff9 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ science professionals and researchers. As such they can be viewed in a plain web browser: - [00 - Start up](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb) +- [01 - Elements of a Program](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_of_a_program.ipynb) However, it is recommended that students **install Python and Jupyter locally** and run the code in the notebooks on their own. From f6068383ba28bec764345046ae113e8a1e3f8f22 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Sun, 22 Sep 2019 20:19:28 +0200 Subject: [PATCH 012/242] Add numpy to the dependencies The numpy library is briefly introduced in notebook 02 --- poetry.lock | 11 ++++++++++- pyproject.toml | 1 + requirements.txt | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 75af38f..ad1610a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -423,6 +423,14 @@ terminado = ">=0.8.1" tornado = ">=5.0" traitlets = ">=4.2.1" +[[package]] +category = "main" +description = "NumPy is the fundamental package for array computing with Python." +name = "numpy" +optional = false +python-versions = ">=3.5" +version = "1.17.2" + [[package]] category = "main" description = "Utilities for writing pandoc filters in python" @@ -673,7 +681,7 @@ version = "3.5.1" notebook = ">=4.4.1" [metadata] -content-hash = "431dace6d8d5c3e390b59e964116bb6219fb45c7a8c09cbfe99d70c704a54320" +content-hash = "7c3d541c65a27324b49fc7ed2b84067e223eb4e20c20310ff9e485b016f65f91" python-versions = "^3.6" [metadata.hashes] @@ -711,6 +719,7 @@ mistune = ["59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e", " nbconvert = ["427a468ec26e7d68a529b95f578d5cbf018cb4c1f889e897681c2b6d11897695", "48d3c342057a2cf21e8df820d49ff27ab9f25fc72b8f15606bd47967333b2709"] nbformat = ["b9a0dbdbd45bb034f4f8893cafd6f652ea08c8c1674ba83f2dc55d3955743b0b", "f7494ef0df60766b7cabe0a3651556345a963b74dbc16bc7c18479041170d402"] notebook = ["660976fe4fe45c7aa55e04bf4bccb9f9566749ff637e9020af3422f9921f9a5d", "b0a290f5cc7792d50a21bec62b3c221dd820bf00efa916ce9aeec4b5354bde20"] +numpy = ["05dbfe72684cc14b92568de1bc1f41e5f62b00f714afc9adee42f6311738091f", "0d82cb7271a577529d07bbb05cb58675f2deb09772175fab96dc8de025d8ac05", "10132aa1fef99adc85a905d82e8497a580f83739837d7cbd234649f2e9b9dc58", "12322df2e21f033a60c80319c25011194cd2a21294cc66fee0908aeae2c27832", "16f19b3aa775dddc9814e02a46b8e6ae6a54ed8cf143962b4e53f0471dbd7b16", "3d0b0989dd2d066db006158de7220802899a1e5c8cf622abe2d0bd158fd01c2c", "438a3f0e7b681642898fd7993d38e2bf140a2d1eafaf3e89bb626db7f50db355", "5fd214f482ab53f2cea57414c5fb3e58895b17df6e6f5bca5be6a0bb6aea23bb", "73615d3edc84dd7c4aeb212fa3748fb83217e00d201875a47327f55363cef2df", "7bd355ad7496f4ce1d235e9814ec81ee3d28308d591c067ce92e49f745ba2c2f", "7d077f2976b8f3de08a0dcf5d72083f4af5411e8fddacd662aae27baa2601196", "a4092682778dc48093e8bda8d26ee8360153e2047826f95a3f5eae09f0ae3abf", "b458de8624c9f6034af492372eb2fee41a8e605f03f4732f43fc099e227858b2", "e70fc8ff03a961f13363c2c95ef8285e0cf6a720f8271836f852cc0fa64e97c8", "ee8e9d7cad5fe6dde50ede0d2e978d81eafeaa6233fb0b8719f60214cf226578", "f4a4f6aba148858a5a5d546a99280f71f5ee6ec8182a7d195af1a914195b21a2"] pandocfilters = ["b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"] parso = ["63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc", "666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c"] pexpect = ["2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1", "9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"] diff --git a/pyproject.toml b/pyproject.toml index 1cd387e..1e69fc5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,7 @@ license = "MIT" [tool.poetry.dependencies] python = "^3.6" jupyter = "^1.0" +numpy = "^1.17" [tool.poetry.dev-dependencies] black = {version = "^18.3-alpha.0", allows-prereleases = true} diff --git a/requirements.txt b/requirements.txt index 41753e1..28405c8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -30,6 +30,7 @@ mistune==0.8.4 nbconvert==5.6.0 nbformat==4.4.0 notebook==6.0.1 +numpy==1.17.2 pandocfilters==1.4.2 parso==0.5.1 pexpect==4.7.0 From 22a85bc2ba80d5818b66a4ce4163c7a03067b1ac Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Sun, 22 Sep 2019 20:24:15 +0200 Subject: [PATCH 013/242] Streamline previous content --- 00_start_up.ipynb | 49 +- 00_start_up_review_and_exercises.ipynb | 2 +- 01_elements_of_a_program.ipynb | 595 ++++++++++-------- ...ts_of_a_program_review_and_exercises.ipynb | 10 +- 4 files changed, 350 insertions(+), 306 deletions(-) diff --git a/00_start_up.ipynb b/00_start_up.ipynb index 7551dcd..506c6ad 100644 --- a/00_start_up.ipynb +++ b/00_start_up.ipynb @@ -19,7 +19,7 @@ } }, "source": [ - "This course is set up to be a *thorough* introduction to programming in [Python](https://www.python.org/).\n", + "This book is set up to be a *thorough* introduction to programming in [Python](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](https://www.python.org/psf/) in the official [language reference](https://docs.python.org/3/reference/index.html) and introduces additions to the language as distributed with the [standard library](https://docs.python.org/3/library/index.html) that come with every installation.\n", "\n", @@ -58,10 +58,10 @@ "source": [ "To be suitable for *total beginners*, there are *no* formal prerequisites. It is only expected that the student has:\n", "\n", - "- a *solid* understanding of the **English language** (i.e., usage of *technical* terms with *narrow* and *distinct* meanings),\n", - "- knowledge of **basic mathematics** from high school (i.e., addition, subtraction, multiplication, division, and a little bit of calculus and statistics),\n", - "- the ability to **think conceptually** and **reason logically** (i.e., *not* just memorizing), and\n", - "- the willingness to **invest 2-4 hours a day for a month** (cf., \"ABC\"-rule at the end)" + "- a *solid* understanding of the **English language**,\n", + "- knowledge of **basic mathematics** from high school,\n", + "- the ability to **think conceptually** and **reason logically**, and\n", + "- the willingness to **invest 2-4 hours a day for a month**." ] }, { @@ -83,7 +83,7 @@ } }, "source": [ - "The course's **main goal** is to **prepare** the student **for further studies** in the \"field\" of **data science**." + "The **main goal** of this introduction is to **prepare** the student **for further studies** in the \"field\" of **data science**." ] }, { @@ -94,7 +94,7 @@ } }, "source": [ - "This includes but is not limited to more advanced courses on topics such as:\n", + "This includes but is not limited to topics such as:\n", "- linear algebra\n", "- statistics & econometrics\n", "- data cleaning & wrangling\n", @@ -154,7 +154,7 @@ } }, "source": [ - "To follow this course, a working installation of **Python 3.6** or higher is needed.\n", + "To follow this book, a working installation of **Python 3.6** or higher is needed.\n", "\n", "A popular and beginner friendly way is to install the [Anaconda Distribution](https://www.anaconda.com/distribution/) that not only ships Python and the standard library but comes pre-packaged with a lot of third-party libraries from the so-called \"scientific stack\". Just go to the [download](https://www.anaconda.com/download/) page and install the latest version (i.e., *2019-07* with Python 3.7 at the time of this writing) for your operating system.\n", "\n", @@ -180,7 +180,7 @@ } }, "source": [ - "To download the course's materials as a ZIP file, open the accompanying [GitHub repository](https://github.com/webartifex/intro-to-python) in a web browser and click on the green \"Clone or download\" button on the right. Then, unpack the ZIP file into a folder of your choosing (ideally somewhere within your personal user folder so that the files show up right away)." + "To download the materials accompanying this book as a ZIP file, open this [GitHub repository](https://github.com/webartifex/intro-to-python) in a web browser and click on the green \"Clone or download\" button on the right. Then, unpack the ZIP file into a folder of your choosing (ideally somewhere within your personal user folder so that the files show up right away)." ] }, { @@ -252,7 +252,7 @@ "\n", "Furthermore, Jupyter notebooks have become a de-facto standard for communicating and exchanging results in the data science community (both in academia and business) and often provide a more intuitive alternative to terminal based ways of running Python (e.g., the default [Python interpreter](https://docs.python.org/3/tutorial/interpreter.html) as shown above or a more advanced interactive version like [IPython](https://ipython.org/)) or even a full-fledged [Integrated Development Environment](https://en.wikipedia.org/wiki/Integrated_development_environment) (e.g., the commercial [PyCharm](https://www.jetbrains.com/pycharm/) or the free [Spyder](https://github.com/spyder-ide/spyder)).\n", "\n", - "In particular, they allow to mix plain English text with Python code cells. The plain text can be formatted using the [Markdown](https://guides.github.com/features/mastering-markdown/) language and mathematical expressions can be typeset with [LaTeX](https://www.overleaf.com/learn/latex/Free_online_introduction_to_LaTeX_%28part_1%29). Lastly, we can include pictures, plots, and even videos. Because of these features, the notebooks developed for this course come in a self-contained \"tutorial\" style that enables students to learn and review the material on their own." + "In particular, they allow to mix plain English text with Python code cells. The plain text can be formatted using the [Markdown](https://guides.github.com/features/mastering-markdown/) language and mathematical expressions can be typeset with [LaTeX](https://www.overleaf.com/learn/latex/Free_online_introduction_to_LaTeX_%28part_1%29). Lastly, we can include pictures, plots, and even videos. Because of these features, the notebooks developed for this book come in a self-contained \"tutorial\" style that enables students to learn and review the material on their own." ] }, { @@ -295,7 +295,15 @@ "slide_type": "slide" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello world\n" + ] + } + ], "source": [ "print(\"Hello world\")" ] @@ -351,7 +359,8 @@ } }, "source": [ - "For this course *programming* is \"defined\" as\n", + "In this book *programming* is \"defined\" as:\n", + "\n", "- a **structured** way of **problem solving**\n", "- by **expressing** the steps of a **computation / process**\n", "- and thereby **documenting** the process in a formal way\n", @@ -369,7 +378,8 @@ } }, "source": [ - "That is different from *computer science*, which is\n", + "That is different from *computer science*, which is:\n", + "\n", "- a field of study comparable to (applied) **mathematics** that\n", "- asks **abstract** questions (\"Is something computable at all?\"),\n", "- develops and analyses **algorithms** and **data structures**,\n", @@ -419,6 +429,8 @@ } }, "source": [ + "Here is a brief history of and some background on Python:\n", + "\n", "- [Guido van Rossum](https://en.wikipedia.org/wiki/Guido_van_Rossum) (Python’s **[Benevolent Dictator for Life](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", @@ -427,17 +439,6 @@ "- the language is named after the sketch comedy group [Monty Python](https://en.wikipedia.org/wiki/Monty_Python)" ] }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "#### Summary" - ] - }, { "cell_type": "markdown", "metadata": { diff --git a/00_start_up_review_and_exercises.ipynb b/00_start_up_review_and_exercises.ipynb index 26eb93c..e1a435f 100644 --- a/00_start_up_review_and_exercises.ipynb +++ b/00_start_up_review_and_exercises.ipynb @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Read chapter 0 of the book. Then work through the ten review questions." + "Read Chapter 0 of the book. Then work through the ten review questions." ] }, { diff --git a/01_elements_of_a_program.ipynb b/01_elements_of_a_program.ipynb index 66c8e64..c771c87 100644 --- a/01_elements_of_a_program.ipynb +++ b/01_elements_of_a_program.ipynb @@ -120,7 +120,7 @@ "\n", "Lastly, the `average` is calculated as the ratio of the final **values** of `total` and `count`. Overall, we divide the sum of all even numbers by the count of all even numbers, which is exactly what we are looking for.\n", "\n", - "We also observe how the lines of code \"within\" the `for` and `if` **statements** are *indented* and *aligned* with multiples of four spaces: This shows immediately how the lines relate to each other." + "We also observe how the lines of code \"within\" the `for` and `if` **statements** are *indented* and *aligned* with multiples of **four spaces**: This shows immediately how the lines relate to each other." ] }, { @@ -152,7 +152,7 @@ } }, "source": [ - "Yet, we do not see any **output** and obtain the value of `average` by simply referencing it again." + "We do not see any **output** yet but can obtain the value of `average` by simply referencing it again." ] }, { @@ -200,9 +200,7 @@ "source": [ "Note how only two of the previous four code cells generate an **output** while two remained \"silent\" (i.e., there is no \"**Out[...]**\" after running the cell).\n", "\n", - "By default, Jupyter notebooks show the value of a cell's last so-called **expression**. This output can be suppressed by ending the last line with a semicolon.\n", - "\n", - "To visualize something before the end of the cell, we can use the [print()](https://docs.python.org/3/library/functions.html#print) built-in function." + "By default, Jupyter notebooks show the value of a cell's last so-called **expression**. This output can be suppressed by ending the last line with a semicolon `;`." ] }, { @@ -243,6 +241,17 @@ "\"I am invisible!\";" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To visualize something before the end of the cell, we can use the [print()](https://docs.python.org/3/library/functions.html#print) built-in **function**." + ] + }, { "cell_type": "code", "execution_count": 7, @@ -266,6 +275,39 @@ "print(\"I am feeling great :-)\")" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Outside Jupyter notebooks, the semicolon `;` is actually used as a **seperator** between several statements that would otherwise have to be on a line on their own. However, it as *not* considered good practice to use it as it makes code less readable." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello, World!\n", + "I am feeling great :-)\n" + ] + } + ], + "source": [ + "print(\"Hello, World!\"); print(\"I am feeling great :-)\")" + ] + }, { "cell_type": "markdown", "metadata": { @@ -285,7 +327,7 @@ } }, "source": [ - "Python comes with basic mathematical operators built in. **Operators** are built-in **tokens** that have a special meaning to the Python interpreter. Most operators either \"operate\" with the object immediately following them (= **unary** operators; e.g., negation) or somehow \"process\" the two objects \"around\" them (= **binary** operators; e.g., addition). But we will see some exceptions from that as well.\n", + "Python comes with basic mathematical operators built in. **[Operators](https://docs.python.org/3/reference/lexical_analysis.html#operators)** are built-in **tokens** that have a special meaning to the Python interpreter. Most operators either \"operate\" with the object immediately following them (= **unary** operators; e.g., negation) or somehow \"process\" the two objects \"around\" them (= **binary** operators; e.g., addition). But we will see some exceptions from that as well.\n", "\n", "By definition, operators have **no** permanent **side effects** in the computer's memory. Although the code cells in this section do indeed lead to *new* objects being created in memory, they are immediately \"forgotten\" as they are not stored in a **variable** (like `numbers` above). We will revisit this idea further below when we compare **expressions** with **statements**.\n", "\n", @@ -294,7 +336,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": { "slideshow": { "slide_type": "slide" @@ -307,7 +349,7 @@ "90" ] }, - "execution_count": 8, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -318,7 +360,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": { "slideshow": { "slide_type": "-" @@ -331,7 +373,7 @@ "8" ] }, - "execution_count": 9, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -353,7 +395,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": { "slideshow": { "slide_type": "fragment" @@ -366,7 +408,7 @@ "-1" ] }, - "execution_count": 10, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -388,7 +430,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": { "slideshow": { "slide_type": "slide" @@ -401,7 +443,7 @@ "42" ] }, - "execution_count": 11, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -412,7 +454,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "metadata": { "slideshow": { "slide_type": "-" @@ -425,7 +467,7 @@ "42.0" ] }, - "execution_count": 12, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -447,7 +489,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "metadata": { "slideshow": { "slide_type": "slide" @@ -460,7 +502,7 @@ "42" ] }, - "execution_count": 13, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -471,7 +513,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "metadata": { "slideshow": { "slide_type": "-" @@ -484,7 +526,7 @@ "42" ] }, - "execution_count": 14, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -506,7 +548,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "metadata": { "slideshow": { "slide_type": "slide" @@ -519,7 +561,7 @@ "1" ] }, - "execution_count": 15, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -541,7 +583,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "metadata": { "slideshow": { "slide_type": "fragment" @@ -554,7 +596,7 @@ "0" ] }, - "execution_count": 16, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -576,7 +618,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "metadata": { "slideshow": { "slide_type": "fragment" @@ -589,7 +631,7 @@ "3" ] }, - "execution_count": 17, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -600,7 +642,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "metadata": { "slideshow": { "slide_type": "-" @@ -613,7 +655,7 @@ "23" ] }, - "execution_count": 18, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } @@ -630,12 +672,12 @@ } }, "source": [ - "The [divmod()](https://docs.python.org/3/library/functions.html#divmod) built-in **function** combines the integer and modulo divisions into one operation. However, this is not an operator any more (but a function). Also observe that [divmod()](https://docs.python.org/3/library/functions.html#divmod) returns a \"pair\" of integers." + "The [divmod()](https://docs.python.org/3/library/functions.html#divmod) built-in function combines the integer and modulo divisions into one operation. However, this is not an operator any more (but a function). Also observe that [divmod()](https://docs.python.org/3/library/functions.html#divmod) returns a \"pair\" of integers." ] }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "metadata": { "slideshow": { "slide_type": "fragment" @@ -648,7 +690,7 @@ "(4, 2)" ] }, - "execution_count": 19, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -670,7 +712,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 21, "metadata": { "slideshow": { "slide_type": "slide" @@ -683,7 +725,7 @@ "8" ] }, - "execution_count": 20, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } @@ -705,7 +747,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 22, "metadata": { "slideshow": { "slide_type": "fragment" @@ -718,7 +760,7 @@ "18" ] }, - "execution_count": 21, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } @@ -740,7 +782,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 23, "metadata": { "slideshow": { "slide_type": "-" @@ -753,7 +795,7 @@ "18" ] }, - "execution_count": 22, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -764,7 +806,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 24, "metadata": { "slideshow": { "slide_type": "-" @@ -777,7 +819,7 @@ "81" ] }, - "execution_count": 23, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -799,7 +841,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 25, "metadata": { "slideshow": { "slide_type": "skip" @@ -812,7 +854,7 @@ "18" ] }, - "execution_count": 24, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } @@ -840,7 +882,7 @@ } }, "source": [ - "## Objects vs. Values vs. Types" + "## Objects vs. Types vs. Values" ] }, { @@ -860,7 +902,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 26, "metadata": { "slideshow": { "slide_type": "slide" @@ -897,7 +939,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 27, "metadata": { "slideshow": { "slide_type": "slide" @@ -907,31 +949,7 @@ { "data": { "text/plain": [ - "140382247181072" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "id(a)" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "140382247352144" + "139867825605456" ] }, "execution_count": 27, @@ -940,7 +958,7 @@ } ], "source": [ - "id(b)" + "id(a)" ] }, { @@ -955,7 +973,7 @@ { "data": { "text/plain": [ - "140382247028656" + "139867825776440" ] }, "execution_count": 28, @@ -963,6 +981,30 @@ "output_type": "execute_result" } ], + "source": [ + "id(b)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139867825460912" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "id(c)" ] @@ -975,12 +1017,12 @@ } }, "source": [ - "These addresses are really not that meaningful for anything other than checking if two variables actually point at the same object. This may be helpful as different objects can of course have the same value." + "These addresses are really not that meaningful for anything other than checking if two variables actually **point** at the same object. This may be helpful as different objects can of course have the same value." ] }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 30, "metadata": { "slideshow": { "slide_type": "slide" @@ -1004,7 +1046,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 31, "metadata": { "slideshow": { "slide_type": "-" @@ -1017,7 +1059,7 @@ "True" ] }, - "execution_count": 30, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } @@ -1039,7 +1081,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 32, "metadata": { "slideshow": { "slide_type": "-" @@ -1052,7 +1094,7 @@ "False" ] }, - "execution_count": 31, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -1080,12 +1122,12 @@ } }, "source": [ - "The [type()](https://docs.python.org/3/library/functions.html#type) built-in function shows an object's type. For example, `a` is an integer (`int`) while `b` is a so-called [floating-point number](https://en.wikipedia.org/wiki/Floating-point_arithmetic) (`float`)." + "The [type()](https://docs.python.org/3/library/functions.html#type) built-in function shows an object's type. For example, `a` is an integer (i.e., `int`) while `b` is a so-called [floating-point number](https://en.wikipedia.org/wiki/Floating-point_arithmetic) (i.e., `float`)." ] }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 33, "metadata": { "slideshow": { "slide_type": "slide" @@ -1098,7 +1140,7 @@ "int" ] }, - "execution_count": 32, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" } @@ -1109,7 +1151,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 34, "metadata": { "slideshow": { "slide_type": "-" @@ -1122,7 +1164,7 @@ "float" ] }, - "execution_count": 33, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } @@ -1148,7 +1190,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 35, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1161,7 +1203,7 @@ "True" ] }, - "execution_count": 34, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } @@ -1183,7 +1225,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 36, "metadata": { "slideshow": { "slide_type": "-" @@ -1197,7 +1239,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0ma\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mis_integer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0ma\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mis_integer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mAttributeError\u001b[0m: 'int' object has no attribute 'is_integer'" ] } @@ -1214,12 +1256,12 @@ } }, "source": [ - "The `c` object is a so-called **string** type `str`, which we can view as Python's way of representing \"text\". Strings also come with their own behaviors, for example, to convert a text to lower or upper case." + "The `c` object is a so-called **string** type (i.e., `str`), which we can view as Python's way of representing \"text\". Strings also come with their own behaviors, for example, to convert a text to lower or upper case." ] }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 37, "metadata": { "slideshow": { "slide_type": "slide" @@ -1232,7 +1274,7 @@ "str" ] }, - "execution_count": 36, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } @@ -1243,7 +1285,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 38, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1256,7 +1298,7 @@ "'python rocks'" ] }, - "execution_count": 37, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } @@ -1267,7 +1309,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 39, "metadata": { "slideshow": { "slide_type": "-" @@ -1280,7 +1322,7 @@ "'PYTHON ROCKS'" ] }, - "execution_count": 38, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } @@ -1291,7 +1333,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 40, "metadata": { "slideshow": { "slide_type": "skip" @@ -1304,7 +1346,7 @@ "'Python Rocks'" ] }, - "execution_count": 39, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } @@ -1339,7 +1381,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 41, "metadata": { "slideshow": { "slide_type": "slide" @@ -1352,7 +1394,7 @@ "789" ] }, - "execution_count": 40, + "execution_count": 41, "metadata": {}, "output_type": "execute_result" } @@ -1363,7 +1405,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 42, "metadata": { "slideshow": { "slide_type": "-" @@ -1376,7 +1418,7 @@ "42.0" ] }, - "execution_count": 41, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } @@ -1393,12 +1435,12 @@ } }, "source": [ - "In this book, we follow the convention of creating strings with **double quotes** `\"` instead of the **single quotes** `'` to which Python defaults in its literal output. Both types of quotes can be used interchangebly." + "In this book, we follow the convention of creating strings with **double quotes** `\"` instead of the **single quotes** `'` to which Python defaults in its literal output for `str` objects. Both types of quotes can be used interchangebly." ] }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 43, "metadata": { "slideshow": { "slide_type": "-" @@ -1411,13 +1453,13 @@ "'Python rocks'" ] }, - "execution_count": 42, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "c" + "c # we defined c = \"Python rocks\" with double quotes \" above" ] }, { @@ -1463,14 +1505,14 @@ } }, "source": [ - "If we do not follow the rules, the code cannot be **parsed** correctly, i.e., the program does not even start to run but raises a **syntax error** (indicated as `SyntaxError`). Computers are very dumb in the sense that the slightest syntax error leads to the machine not understanding our code.\n", + "If we do not follow the rules, the code cannot be **parsed** correctly, i.e., the program does not even start to run but **raises** a **syntax error** indicated as `SyntaxError` in the output. Computers are very dumb in the sense that the slightest syntax error leads to the machine not understanding our code.\n", "\n", "For example, if we wanted to write an accounting program that adds up currencies, we would have to model dollar prices as `float` objects as the dollar symbol cannot be read by Python." ] }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 44, "metadata": { "slideshow": { "slide_type": "slide" @@ -1479,10 +1521,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "invalid syntax (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 3.99 $ + 10.40 $\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 3.99 $ + 10.40 $\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], @@ -1503,7 +1545,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 45, "metadata": { "slideshow": { "slide_type": "slide" @@ -1512,10 +1554,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "invalid syntax (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m for number in numbers\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m for number in numbers\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], @@ -1532,12 +1574,12 @@ } }, "source": [ - "... and relies on whitespace / indentation unlike many other programming languages. A `IndentationError` is just a special type of a `SyntaxError`." + "... and relies on whitespace (i.e., indentation) unlike many other programming languages. An `IndentationError` is just a special type of a `SyntaxError`." ] }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 46, "metadata": { "slideshow": { "slide_type": "slide" @@ -1546,10 +1588,10 @@ "outputs": [ { "ename": "IndentationError", - "evalue": "expected an indented block (, line 2)", + "evalue": "expected an indented block (, line 2)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m2\u001b[0m\n\u001b[0;31m print(number)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mIndentationError\u001b[0m\u001b[0;31m:\u001b[0m expected an indented block\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m2\u001b[0m\n\u001b[0;31m print(number)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mIndentationError\u001b[0m\u001b[0;31m:\u001b[0m expected an indented block\n" ] } ], @@ -1579,14 +1621,14 @@ "source": [ "Syntax errors as above are easy to find as the code will not even run to begin with.\n", "\n", - "However, there are also so-called **runtime errors** (also called **exceptions**) that occur if the code would run given correct input.\n", + "However, there are also so-called **runtime errors**, often called **exceptions**, that occur whenever otherwise (i.e., syntactically) correct code does not run because of invalid input.\n", "\n", "This example does not work because just like in the \"real\" world, Python does not know how to divide by $0$. The syntactically correct code leads to a `ZeroDivisionError`." ] }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 47, "metadata": { "slideshow": { "slide_type": "slide" @@ -1600,7 +1642,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m1\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m1\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" ] } @@ -1628,14 +1670,14 @@ } }, "source": [ - "So-called **semantic errors**, on the contrary, can be very hard to spot as they do *not* crash the program. The only way to find such errors is to run the program with test input for which we know the answer already and can then verify it. However, testing software is a whole discipline on its own and often very hard to do in practice.\n", + "So-called **semantic errors**, on the contrary, can be very hard to spot as they do *not* crash the program. The only way to find such errors is to run a program with test input for which we know the answer already and can thus check the output. However, testing software is a whole discipline on its own and often very hard to do in practice.\n", "\n", - "The cell below copies our introductory example from above with a \"tiny\" error." + "The cell below copies our introductory example from above with a \"tiny\" error. How fast could you have spotted it without the comment?" ] }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 48, "metadata": { "code_folding": [], "slideshow": { @@ -1657,7 +1699,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 49, "metadata": { "slideshow": { "slide_type": "-" @@ -1670,7 +1712,7 @@ "3.0" ] }, - "execution_count": 48, + "execution_count": 49, "metadata": {}, "output_type": "execute_result" } @@ -1687,7 +1729,7 @@ } }, "source": [ - "Finding errors is is called **debugging**. For the history of the term, check this [link](https://en.wikipedia.org/wiki/Debugging)." + "Finding errors in a systematic way is called **debugging**. For the history of the term, see this [article](https://en.wikipedia.org/wiki/Debugging)." ] }, { @@ -1709,14 +1751,14 @@ } }, "source": [ - "Adhering to just syntax rules is therefore *never* enough. Over time, **best practices** and common **style guides** were created to make it less likely for a developer to mess up a program and also to allow \"onboarding\" him as a contributor to an established code base (often called **legacy code**) faster. These rules are not enforced by Python itself: Badly styled and un-readable code will still run. At the very least, Python programs should be styled according to [PEP 8](https://www.python.org/dev/peps/pep-0008/) and documented \"inline\" (i.e., in the code itself) according to [PEP 257](https://www.python.org/dev/peps/pep-0257/).\n", + "Adhering to just syntax rules is therefore *never* enough. Over time, **best practices** and common **style guides** were created to make it less likely for a developer to mess up a program and also to allow \"onboarding\" him as a contributor to an established code base, often called **legacy code**, faster. These rules are not enforced by Python itself: Badly styled and un-readable code will still run. At the very least, Python programs should be styled according to [PEP 8](https://www.python.org/dev/peps/pep-0008/) and documented \"inline\" (i.e., in the code itself) according to [PEP 257](https://www.python.org/dev/peps/pep-0257/).\n", "\n", "An easier to read version of PEP 8 can be found [here](https://pep8.org/). The video below features a well known \"[Pythonista](https://en.wiktionary.org/wiki/Pythonista)\" talking about the importance of code style." ] }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 50, "metadata": { "slideshow": { "slide_type": "skip" @@ -1738,10 +1780,10 @@ " " ], "text/plain": [ - "" + "" ] }, - "execution_count": 49, + "execution_count": 50, "metadata": {}, "output_type": "execute_result" } @@ -1759,12 +1801,12 @@ } }, "source": [ - "For example, while the above code to calculate the average of the even numbers from 1 through 10 is correct, a Pythonista would re-write it in a more \"Pythonic\" way and use the [sum()](https://docs.python.org/3/library/functions.html#sum) and [len()](https://docs.python.org/3/library/functions.html#len) (= \"length\") built-in functions. Pythonic code runs faster in many cases and is less error prone." + "For example, while the above code to calculate the average of the even numbers from 1 through 10 is correct, a Pythonista would re-write it in a more \"Pythonic\" way and use the [sum()](https://docs.python.org/3/library/functions.html#sum) and [len()](https://docs.python.org/3/library/functions.html#len) (= \"length\") built-in functions (cf., Chapter 2) as well as a so-called **list comprehension** (cf., Chapter 7). Pythonic code runs faster in many cases and is less error prone." ] }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 51, "metadata": { "slideshow": { "slide_type": "slide" @@ -1777,7 +1819,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 52, "metadata": { "slideshow": { "slide_type": "-" @@ -1785,12 +1827,12 @@ }, "outputs": [], "source": [ - "evens = [n for n in numbers if n % 2 == 0] # example for a so-called list comprehension" + "evens = [n for n in numbers if n % 2 == 0] # example of a so-called list comprehension" ] }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 53, "metadata": { "slideshow": { "slide_type": "-" @@ -1803,7 +1845,7 @@ "[2, 4, 6, 8, 10]" ] }, - "execution_count": 52, + "execution_count": 53, "metadata": {}, "output_type": "execute_result" } @@ -1814,7 +1856,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 54, "metadata": { "slideshow": { "slide_type": "-" @@ -1827,7 +1869,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 55, "metadata": { "slideshow": { "slide_type": "-" @@ -1840,7 +1882,7 @@ "6.0" ] }, - "execution_count": 54, + "execution_count": 55, "metadata": {}, "output_type": "execute_result" } @@ -1862,7 +1904,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 56, "metadata": { "slideshow": { "slide_type": "slide" @@ -1961,7 +2003,7 @@ "\n", "At the same time, for a beginner's course it is often easier to just code in a linear fashion.\n", "\n", - "In real data science projects one would probably employ a mixed approach and put re-usable code into so-called Python modules (= \\*.py files) and then use Jupyter notebooks to built up a linear report or story line for a business argument to be made." + "In real data science projects one would probably employ a mixed approach and put re-usable code into so-called Python modules (i.e., *.py* files; cf., Chapter 2) and then use Jupyter notebooks to built up a linear report or story line for a business argument to be made." ] }, { @@ -1972,7 +2014,7 @@ } }, "source": [ - "## Variables / Names" + "## Variables vs. Names vs. Identifiers" ] }, { @@ -1983,12 +2025,14 @@ } }, "source": [ - "**Variables** are created with the **assignment statement** `=`. As its name suggests, it is *not* an operator, mainly because of its side effect of making a **name** \"point\" to an object in memory." + "**Variables** are created with the **[assignment statement](https://docs.python.org/3/reference/simple_stmts.html#assignment-statements)** `=`, which is *not* an operator, mainly because of its side effect of making a **[name](https://docs.python.org/3/reference/lexical_analysis.html#identifiers)** point to an object in memory.\n", + "\n", + "We will read the terms **variable**, **name**, and **identifier** used interchangebly in many Python related texts. In this book, we adopt the following convention: First, we treat *name* and *identifier* as perfect synonyms but only use the term *name* in the text for clarity. Second, whereas *name* only refers to a string of letters, numbers, and some other symbols, a *variable* refers to the combination of a *name* and a *pointer* to some object in memory." ] }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 57, "metadata": { "slideshow": { "slide_type": "slide" @@ -2008,14 +2052,12 @@ } }, "source": [ - "When referenced, a variable just evaluates to the value of the object it points to. Colloquially, we could say that `a` evaluates to `20.0` here but this would not be an accurate description of what is really going on in memory.\n", - "\n", - "We will see some more colloquial jargons in this section but should always remind ourselves what we better said instead." + "When referenced, a variable evaluates to the value of the object it points to. Colloquially, we could say that `a` evaluates to `20.0` here but this would not be a full description of what is really going on in memory. We will see some more colloquial jargons in this section but should always relate this to what Python actually does in memory." ] }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 58, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2028,7 +2070,7 @@ "20.0" ] }, - "execution_count": 57, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } @@ -2050,7 +2092,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 59, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2058,12 +2100,12 @@ }, "outputs": [], "source": [ - "a = 20" + "a = 20 # this makes a point to an object of a different type" ] }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 60, "metadata": { "slideshow": { "slide_type": "-" @@ -2076,7 +2118,7 @@ "20" ] }, - "execution_count": 59, + "execution_count": 60, "metadata": {}, "output_type": "execute_result" } @@ -2093,12 +2135,12 @@ } }, "source": [ - "If we want to re-assign a variable while referencing its \"old\" (i.e., current) object, we can also **update** it using a so-called **augmented assignment statement** (*not* operator). This implicitly inserts the current \"value\" as the first token on the right-hand side." + "If we want to re-assign a variable while referencing its \"old\" (i.e., current) object, we can also **update** it using a so-called **[augmented assignment statement](https://docs.python.org/3/reference/simple_stmts.html#augmented-assignment-statements)** (*not* operator), originally introduced with [PEP 203](https://www.python.org/dev/peps/pep-0203/). This implicitly inserts the currently mapped object as the first operand on the right-hand side." ] }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 61, "metadata": { "slideshow": { "slide_type": "slide" @@ -2111,7 +2153,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 62, "metadata": { "slideshow": { "slide_type": "-" @@ -2124,7 +2166,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 63, "metadata": { "slideshow": { "slide_type": "-" @@ -2137,7 +2179,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 64, "metadata": { "slideshow": { "slide_type": "-" @@ -2150,7 +2192,7 @@ "42" ] }, - "execution_count": 63, + "execution_count": 64, "metadata": {}, "output_type": "execute_result" } @@ -2167,12 +2209,12 @@ } }, "source": [ - "Variables can be **de-referenced** (i.e., \"deleted\") with the `del` statement. This does *not* \"delete\" the object to which a variable points to. It merely removes the variable from the \"list of all variables\"." + "Variables can be **[de-referenced](https://docs.python.org/3/reference/simple_stmts.html#the-del-statement)** (i.e., \"deleted\") with the `del` statement. This does *not* delete the object to which a variable points to. It merely removes the variable from the \"list of all variables\"." ] }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 65, "metadata": { "slideshow": { "slide_type": "slide" @@ -2185,7 +2227,7 @@ "789" ] }, - "execution_count": 64, + "execution_count": 65, "metadata": {}, "output_type": "execute_result" } @@ -2196,7 +2238,7 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 66, "metadata": { "slideshow": { "slide_type": "-" @@ -2215,12 +2257,12 @@ } }, "source": [ - "If we refer to an unknown name, a runtime exception occurs, namely a `NameError`." + "If we refer to an unknown name, a runtime error occurs, namely a `NameError`." ] }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 67, "metadata": { "slideshow": { "slide_type": "-" @@ -2234,7 +2276,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mb\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mb\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'b' is not defined" ] } @@ -2251,12 +2293,12 @@ } }, "source": [ - "Some names magically exist when we start Python. In this introductory book, we can safely ignore such variables." + "Some variables magically exist when we start Python. In this introductory book, we can safely ignore them." ] }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 68, "metadata": { "slideshow": { "slide_type": "skip" @@ -2269,7 +2311,7 @@ "'__main__'" ] }, - "execution_count": 67, + "execution_count": 68, "metadata": {}, "output_type": "execute_result" } @@ -2291,7 +2333,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 69, "metadata": { "slideshow": { "slide_type": "slide" @@ -2321,15 +2363,15 @@ " '_22',\n", " '_23',\n", " '_24',\n", - " '_26',\n", + " '_25',\n", " '_27',\n", " '_28',\n", - " '_30',\n", + " '_29',\n", " '_31',\n", " '_32',\n", " '_33',\n", " '_34',\n", - " '_36',\n", + " '_35',\n", " '_37',\n", " '_38',\n", " '_39',\n", @@ -2337,17 +2379,17 @@ " '_40',\n", " '_41',\n", " '_42',\n", - " '_48',\n", + " '_43',\n", " '_49',\n", " '_5',\n", - " '_52',\n", - " '_54',\n", - " '_57',\n", - " '_59',\n", - " '_63',\n", + " '_50',\n", + " '_53',\n", + " '_55',\n", + " '_58',\n", + " '_60',\n", " '_64',\n", - " '_67',\n", - " '_8',\n", + " '_65',\n", + " '_68',\n", " '_9',\n", " '__',\n", " '___',\n", @@ -2425,6 +2467,7 @@ " '_i66',\n", " '_i67',\n", " '_i68',\n", + " '_i69',\n", " '_i7',\n", " '_i8',\n", " '_i9',\n", @@ -2447,7 +2490,7 @@ " 'total']" ] }, - "execution_count": 68, + "execution_count": 69, "metadata": {}, "output_type": "execute_result" } @@ -2477,20 +2520,7 @@ "source": [ "It is important to understand that *several* variables can point to the *same* object in memory. This can be counter-intuitive in the beginning and lead to many hard to track down bugs.\n", "\n", - "This makes `b` point to whatever object `a` points to." - ] - }, - { - "cell_type": "code", - "execution_count": 69, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [], - "source": [ - "b = a # this is different from b == a" + "This makes `b` point to whatever object `a` is pointing to." ] }, { @@ -2498,23 +2528,12 @@ "execution_count": 70, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "slide" } }, - "outputs": [ - { - "data": { - "text/plain": [ - "42" - ] - }, - "execution_count": 70, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "a" + "b = a" ] }, { @@ -2537,6 +2556,30 @@ "output_type": "execute_result" } ], + "source": [ + "a" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "b" ] @@ -2549,14 +2592,14 @@ } }, "source": [ - "For \"simple\" types like `int` or `float` this will never cause confusion.\n", + "For \"simple\" types like `int` or `float` this will never cause troubles.\n", "\n", "Let's \"change the value\" of `a`. Really, let's create a *new* `123` object and make `a` point to it." ] }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 73, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2569,7 +2612,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 74, "metadata": { "slideshow": { "slide_type": "-" @@ -2582,7 +2625,7 @@ "123" ] }, - "execution_count": 73, + "execution_count": 74, "metadata": {}, "output_type": "execute_result" } @@ -2604,7 +2647,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 75, "metadata": { "slideshow": { "slide_type": "-" @@ -2617,7 +2660,7 @@ "42" ] }, - "execution_count": 74, + "execution_count": 75, "metadata": {}, "output_type": "execute_result" } @@ -2634,12 +2677,12 @@ } }, "source": [ - "However, if a name points to an object of a more \"complex\" object, for example, of type `list`, \"weird\" things can happen." + "However, if a variable points to an object of a more \"complex\" type (e.g., `list`), \"weird\" things can happen." ] }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 76, "metadata": { "slideshow": { "slide_type": "slide" @@ -2652,7 +2695,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 77, "metadata": { "slideshow": { "slide_type": "skip" @@ -2665,7 +2708,7 @@ "list" ] }, - "execution_count": 76, + "execution_count": 77, "metadata": {}, "output_type": "execute_result" } @@ -2676,7 +2719,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 78, "metadata": { "slideshow": { "slide_type": "-" @@ -2697,14 +2740,14 @@ "source": [ "Let's change the first element of `x`.\n", "\n", - "Chapter 7 discusses lists in more depth. For now, let's just view a list as some sort of **container** object that holds an arbitrary number of pointers to other objects and treat the brackets `[...]` attached to `x` as just another operator, called the **indexing operator**. `x[0]` instructs Python to first \"follow\" the pointer from the \"global\" directory of all names to the list object. Then, it follows the first pointer it finds there to the `1` object. The indexing operator must be an operator as we merely read the first element an do not change anything in memory.\n", + "Chapter 7 discusses lists in more depth. For now, let's just view a `list` object as some sort of **container** that holds an arbitrary number of pointers to other objects and treat brackets `[]` attached to it as just another operator, called the **indexing operator**. `x[0]` instructs Python to first follow the pointer from the \"global\" directory of all names to the `x` object. Then, it follows the first pointer it finds there to the `1` object. The indexing operator must be an operator as we merely read the first element and do not change anything in memory.\n", "\n", - "Note how Python **begins counting at 0**. This is not the case for many other languages, for example, MATLAB, R, or Stata. To understand why this makes sense, see this short [note](https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html) by one of the all-time greats in computer science, the late [Edsger Dijkstra](https://en.wikipedia.org/wiki/Edsger_W._Dijkstra)." + "Note how Python **begins counting at 0**. This is not the case for many other languages, for example, [MATLAB](https://en.wikipedia.org/wiki/MATLAB), [R](https://en.wikipedia.org/wiki/R_%28programming_language%29), or [Stata](https://en.wikipedia.org/wiki/Stata). To understand why this makes sense, see this short [note](https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html) by one of the all-time greats in computer science, the late [Edsger Dijkstra](https://en.wikipedia.org/wiki/Edsger_W._Dijkstra)." ] }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 79, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2717,7 +2760,7 @@ "1" ] }, - "execution_count": 78, + "execution_count": 79, "metadata": {}, "output_type": "execute_result" } @@ -2734,12 +2777,12 @@ } }, "source": [ - "To change the first entry in the list, we use the assignment statement `=` again. Here, this does actually *not* create a *new* variable but only changes the object to which the first pointer in the `x` list points to. As we only change parts of the `x` list, we say that we change its **state**." + "To change the first entry in the list, we use the assignment statement `=` again. Here, this does actually *not* create a *new* variable (or overwrite an existing one) but only changes the object to which the first pointer in the `x` list points to. As we only change parts of the `x` object, we say that we **mutate** (i.e., change) its **state**." ] }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 80, "metadata": { "slideshow": { "slide_type": "slide" @@ -2752,7 +2795,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 81, "metadata": { "slideshow": { "slide_type": "-" @@ -2765,7 +2808,7 @@ "[99, 2, 3]" ] }, - "execution_count": 80, + "execution_count": 81, "metadata": {}, "output_type": "execute_result" } @@ -2782,12 +2825,12 @@ } }, "source": [ - "The changes made to `x` can also be seen through the `y` variable." + "The changes made to the object `x` is pointing to can also be seen through the `y` variable!" ] }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 82, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2800,7 +2843,7 @@ "[99, 2, 3]" ] }, - "execution_count": 81, + "execution_count": 82, "metadata": {}, "output_type": "execute_result" } @@ -2823,7 +2866,7 @@ "\n", "In the second case, `x[0] = 99` creates a *new* integer object `99` and merely changes the first pointer in the `x` list.\n", "\n", - "In general, the assignment statement (re-)creates a variable and makes it point to whatever object is on the right-hand side *if* the left-hand side is a *pure* variable name. Otherwise, it changes some object on the left-hand side (this is strictly not a must but we should expect it).\n", + "In general, the assignment statement creates (or overwrites) a variable and makes it point to whatever object is on the right-hand side *only if* the left-hand side is a *pure* name (i.e., it contains no operators like the indexing operator in the example). Otherwise, it mutates some already existing object. And we always have to expect that the latter might have more than one variable pointing at it.\n", "\n", "In the beginning, visualizing the memory with a tool like [PythonTutor](http://pythontutor.com/visualize.html#code=x%20%3D%20%5B1,%202,%203%5D%0Ay%20%3D%20x%0Ax%5B0%5D%20%3D%2099%0Adel%20x,%20y%0Ax%20%3D%20%5B1,%202,%203%5D%0Ay%20%3D%20x.copy%28%29%0Ax%5B0%5D%20%3D%2099&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) will assist in understanding what is going on." ] @@ -2880,7 +2923,7 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 83, "metadata": { "slideshow": { "slide_type": "slide" @@ -2893,7 +2936,7 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 84, "metadata": { "slideshow": { "slide_type": "-" @@ -2906,7 +2949,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 85, "metadata": { "slideshow": { "slide_type": "-" @@ -2919,7 +2962,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 86, "metadata": { "slideshow": { "slide_type": "-" @@ -2943,7 +2986,7 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 87, "metadata": { "slideshow": { "slide_type": "skip" @@ -2956,7 +2999,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 88, "metadata": { "slideshow": { "slide_type": "skip" @@ -2969,7 +3012,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 89, "metadata": { "slideshow": { "slide_type": "skip" @@ -2977,12 +3020,12 @@ }, "outputs": [], "source": [ - "name = \"Alexander\" # name of what ?" + "name = \"Alexander\" # name of what?" ] }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 90, "metadata": { "slideshow": { "slide_type": "skip" @@ -2991,10 +3034,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "can't assign to operator (, line 1)", + "evalue": "can't assign to operator (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m address@work = \"Burgplatz 2, Vallendar\"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m can't assign to operator\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m address@work = \"Burgplatz 2, Vallendar\"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m can't assign to operator\n" ] } ], @@ -3015,7 +3058,7 @@ }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 91, "metadata": { "slideshow": { "slide_type": "skip" @@ -3034,12 +3077,12 @@ } }, "source": [ - "Variables with leading and trailing double underscores (referred to as **dunder** in Python \"slang\") are used for important built-in variables. Do *not* use this style for custom variables!" + "Variables with leading and trailing double underscores, referred to as **dunder** in Python \"slang\", are used for important built-in variables. Do *not* use this style for custom variables!" ] }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 92, "metadata": { "slideshow": { "slide_type": "skip" @@ -3052,7 +3095,7 @@ "'__main__'" ] }, - "execution_count": 91, + "execution_count": 92, "metadata": {}, "output_type": "execute_result" } @@ -3085,7 +3128,7 @@ }, { "cell_type": "code", - "execution_count": 92, + "execution_count": 93, "metadata": { "slideshow": { "slide_type": "skip" @@ -3107,10 +3150,10 @@ " " ], "text/plain": [ - "" + "" ] }, - "execution_count": 92, + "execution_count": 93, "metadata": {}, "output_type": "execute_result" } @@ -3139,18 +3182,18 @@ } }, "source": [ - "An **expression** is any syntactically correct **combination** of **variables** and **literals** with **operators**. See the [language reference](https://docs.python.org/3/reference/expressions.html) for a full list.\n", + "An **[expression](https://docs.python.org/3/reference/expressions.html)** is any syntactically correct **combination** of **variables** and **literals** with **operators**.\n", "\n", "In simple words, anything that can be used on the right-hand side of an assignment statement without creating a `SyntaxError` is an expression.\n", "\n", - "What we said about individual operators above (namely that they have *no* side effects) should have been put here to begin with. The examples in the section on operators were actually all expressions.\n", + "What we said about individual operators above, namely that they have *no* side effects, should have been put here to begin with. The examples in the section on operators above were actually all expressions!\n", "\n", "The simplest possible expression contains only one variable (or literal)." ] }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 94, "metadata": { "slideshow": { "slide_type": "slide" @@ -3163,7 +3206,7 @@ "123" ] }, - "execution_count": 93, + "execution_count": 94, "metadata": {}, "output_type": "execute_result" } @@ -3174,7 +3217,7 @@ }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 95, "metadata": { "slideshow": { "slide_type": "-" @@ -3187,7 +3230,7 @@ "165" ] }, - "execution_count": 94, + "execution_count": 95, "metadata": {}, "output_type": "execute_result" } @@ -3204,12 +3247,12 @@ } }, "source": [ - "The definition of an expression is **recursive**. So here the sub-expression `a + b` is combined with the literal `3` by the operator `**` to form another expression." + "The definition of an expression is **recursive**. So here the sub-expression `a + b` is combined with the literal `3` by the operator `**` to form the full expression." ] }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 96, "metadata": { "slideshow": { "slide_type": "-" @@ -3222,7 +3265,7 @@ "4492125" ] }, - "execution_count": 95, + "execution_count": 96, "metadata": {}, "output_type": "execute_result" } @@ -3239,12 +3282,12 @@ } }, "source": [ - "As before, the bracket operator `[...]` can be used for indexing." + "Here, the variable `y` is combined with the literal `2` by the indexing operator `[]`. The resulting expression evaluates to the " ] }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 97, "metadata": { "slideshow": { "slide_type": "-" @@ -3257,7 +3300,7 @@ "3" ] }, - "execution_count": 96, + "execution_count": 97, "metadata": {}, "output_type": "execute_result" } @@ -3274,12 +3317,12 @@ } }, "source": [ - "When not used as a **delimiter**, parentheses also constitute an operator, namely the **call operator** `(...)`. We have seen this syntax above when we \"called\" (i.e., executed) built-in functions and methods." + "When not used as a **delimiter**, parentheses also constitute an operator, namely the **call operator** `()`. We have seen this syntax above when we \"called\" (i.e., executed) built-in functions and methods." ] }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 98, "metadata": { "slideshow": { "slide_type": "-" @@ -3292,7 +3335,7 @@ "104" ] }, - "execution_count": 97, + "execution_count": 98, "metadata": {}, "output_type": "execute_result" } @@ -3325,7 +3368,7 @@ }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 99, "metadata": { "slideshow": { "slide_type": "slide" @@ -3339,7 +3382,7 @@ }, { "cell_type": "code", - "execution_count": 99, + "execution_count": 100, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3352,7 +3395,7 @@ "'Hi class'" ] }, - "execution_count": 99, + "execution_count": 100, "metadata": {}, "output_type": "execute_result" } @@ -3374,7 +3417,7 @@ }, { "cell_type": "code", - "execution_count": 100, + "execution_count": 101, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3387,7 +3430,7 @@ "'Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi '" ] }, - "execution_count": 100, + "execution_count": 101, "metadata": {}, "output_type": "execute_result" } @@ -3415,14 +3458,14 @@ } }, "source": [ - "A **statement** is anything that changes the state of the program's memory or has some other side effect. Statements do not just evaluate to a value like expressions; instead, they create or change values. See the [language reference](https://docs.python.org/3/reference/simple_stmts.html) for a full list.\n", + "A **[statement](https://docs.python.org/3/reference/simple_stmts.html)** is anything that changes the state of the program's memory or has some other side effect. Statements do not just evaluate to a value like expressions; instead, they create or change values.\n", "\n", "Most notably of course are the `=` and `del` statements." ] }, { "cell_type": "code", - "execution_count": 101, + "execution_count": 102, "metadata": { "slideshow": { "slide_type": "slide" @@ -3435,7 +3478,7 @@ }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 103, "metadata": { "slideshow": { "slide_type": "-" @@ -3459,7 +3502,7 @@ }, { "cell_type": "code", - "execution_count": 103, + "execution_count": 104, "metadata": { "slideshow": { "slide_type": "skip" @@ -3506,7 +3549,7 @@ }, { "cell_type": "code", - "execution_count": 104, + "execution_count": 105, "metadata": { "slideshow": { "slide_type": "slide" @@ -3534,7 +3577,7 @@ }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 106, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3547,7 +3590,7 @@ }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 107, "metadata": { "slideshow": { "slide_type": "-" @@ -3655,7 +3698,7 @@ "\n", "- flow control (cf., Chapter 3)\n", " - expression of **logic** or an **algorithm**\n", - " - conditional execution of a small **branch** within a program (i.e., `if`-statements)\n", + " - conditional execution of a small **branch** within a program (i.e., `if` statements)\n", " - repetitive execution of parts of a program (i.e., `for`-loops and `while`-loops)" ] } diff --git a/01_elements_of_a_program_review_and_exercises.ipynb b/01_elements_of_a_program_review_and_exercises.ipynb index 088001b..2207ccc 100644 --- a/01_elements_of_a_program_review_and_exercises.ipynb +++ b/01_elements_of_a_program_review_and_exercises.ipynb @@ -19,7 +19,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Read chapter 1 of the book. Then work through the ten review questions." + "Read Chapter 1 of the book. Then work through the ten review questions." ] }, { @@ -180,7 +180,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q10**: [PEP 8](https://www.python.org/dev/peps/pep-0008/) suggests that developers use **$8$ spaces** per level of indentation." + "**Q10**: [PEP 8](https://www.python.org/dev/peps/pep-0008/) suggests that developers use **8 spaces** per level of indentation." ] }, { @@ -263,7 +263,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q11.3**: Lastly, what does the `end=\"\\n\"` mean in the documentation? Use it in the `for`-loop to print the numbers $1$ through $10$ in just one line." + "**Q11.3**: Lastly, what does the `end=\"\\n\"` mean in the documentation? Use it in the `for`-loop to print the numbers 1 through 10 in just one line." ] }, { @@ -298,7 +298,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q11.1**: 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()](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()](https://docs.python.org/3/library/functions.html#func-range) a `list` object, you have to \"wrap\" it with the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in (i.e., `list(range(...))`)." + "**Q11.1**: 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()](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()](https://docs.python.org/3/library/functions.html#func-range) a `list` object, you have to \"wrap\" it with the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in (i.e., `list(range(...))`)." ] }, { @@ -335,7 +335,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q11.3**: Create a loop that prints out either the number or any of the Fizz Buzz substitutes. Do it in such a way that we do not end up with $100$ lines of output here." + "**Q11.3**: Create a loop that prints out either the number or any of the Fizz Buzz substitutes. Do it in such a way that we do not end up with 100 lines of output here." ] }, { From c3fc760e700cc3196f9aef186cdce0ea3f8fb12e Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Sun, 22 Sep 2019 20:25:40 +0200 Subject: [PATCH 014/242] Add sample_module.py to the project The file sample_module.py serves as an example in notebook 02 as to how to create a local module and then import it. --- sample_module.py | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 sample_module.py diff --git a/sample_module.py b/sample_module.py new file mode 100644 index 0000000..2efcda4 --- /dev/null +++ b/sample_module.py @@ -0,0 +1,75 @@ +"""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 the computation to an internal +utility function _scaled_average() that contains all the logic common to the +three functions. Also, we define one _default_scalar variable that is used as +the default for the scalar parameter in each of the functions. + +While this example is stylized, it shows how Python modules are often +designed. +""" + +_default_scalar = 1 + + +def _scaled_average(numbers, scalar): + """Internal utility function to calculate scaled averages.""" + average = sum(numbers) / len(numbers) + return scalar * average + + +def average(numbers, *, scalar=_default_scalar): + """Calculate the average of all numbers in a list. + + Args: + numbers (list): list of numbers; may be integers or floats + scalar (float, optional): the scalar that multiplies the + average of the even numbers + + Returns: + float: (scaled) average + """ + return _scaled_average(numbers, scalar) + + +def average_evens(numbers, *, scalar=_default_scalar): + """Calculate the average of all even numbers in a list. + + Args: + numbers (list): list of numbers; may be integers or floats + scalar (float, optional): the scalar that multiplies the + average of the even numbers + + Returns: + float: (scaled) average + """ + return _scaled_average([n for n in numbers if n % 2 == 0], scalar) + + +def average_odds(numbers, *, scalar=_default_scalar): + """Calculate the average of all odd numbers in a list. + + Args: + numbers (list): list of numbers; may be integers or floats + scalar (float, optional): the scalar that multiplies the + average of the even numbers + + Returns: + float: (scaled) average + """ + return _scaled_average([n for n in numbers if n % 2 != 0], scalar) From ec3097e220ba4a4562aa198d014bf5967229eed3 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Sun, 22 Sep 2019 20:26:58 +0200 Subject: [PATCH 015/242] Add the sample_package folder to the project The sample_package folder serves as an example in notebook 02 as to what a Python package looks like. Its contents actually belong to the chapter on object-orientation. --- sample_package/__init__.py | 5 + sample_package/matrix.py | 254 +++++++++++++++++++++++++++++++++++++ sample_package/utils.py | 14 ++ sample_package/vector.py | 141 ++++++++++++++++++++ 4 files changed, 414 insertions(+) create mode 100644 sample_package/__init__.py create mode 100644 sample_package/matrix.py create mode 100644 sample_package/utils.py create mode 100644 sample_package/vector.py diff --git a/sample_package/__init__.py b/sample_package/__init__.py new file mode 100644 index 0000000..76b1b09 --- /dev/null +++ b/sample_package/__init__.py @@ -0,0 +1,5 @@ +"""This package provides Vectors and Matrices.""" + +from .matrix import Matrix +from .utils import norm +from .vector import Vector diff --git a/sample_package/matrix.py b/sample_package/matrix.py new file mode 100644 index 0000000..c6b982f --- /dev/null +++ b/sample_package/matrix.py @@ -0,0 +1,254 @@ +"""This module defines a Matrix class.""" + +class Matrix: + """A standard m-by-n-dimensional matrix from linear algebra. + + The class is designed for sub-classing in such a way that + the user can adapt the typing class attribute to change, + for example, how the entries are stored (e.g., as integers). + + Attributes: + storage (callable): must return an iterable that is used + to store the entries of the matrix; defaults to tuple + typing (callable): type casting applied to all vector + entries upon creation; defaults to float + zero_threshold (float): maximum difference allowed when + comparing an entry to zero; defaults to 1e-12 + """ + + storage = tuple + typing = float + zero_threshold = 1e-12 + + def __init__(self, data): + """Initiate a new matrix. + + Args: + data (iterable of iterables): the matrix's entries; + must be provided with rows first, then column; + the number of column entries must be consistent across rows + where the first row sets the standard; + must have at least one element in total + + Raises: + ValueError: + - if the number of columns is inconsistent across the rows + - if the provided data do not have enough entries + """ + 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("each row must have the same number of entries") + if len(self) == 0: + raise ValueError("the matrix must have at least one entry") + + @classmethod + def from_columns(cls, data): + """Initiate a new matrix. + + This is an alternative constructor for data provided in column-major order. + + Args: + data (iterable of iterables): the matrix's entries in column-major order; + the number of column entries must be consistent per row + while the first row sets the correct number; + must have at least one element in total + + Raises: + ValueError: + - if the number of columns is inconsistent across the rows + - if the provided data do not have enough entries + """ + return cls(data).transpose() + + def __repr__(self): + name = self.__class__.__name__ + args = ", ".join( + "(" + ", ".join(f"{c:.3f}" for c in r) + ",)" for r in self._entries + ) + return f"{name}(({args}))" + + def __str__(self): + name = self.__class__.__name__ + first, last, m, n = self[0], self[-1], self.n_rows, self.n_cols + return f"{name}(({first:.1f}, ...), ..., (..., {last:.1f}))[{m:d}x{n:d}]" + + @property + def n_rows(self): + """Number of rows in the matrix.""" + return len(self._entries) + + @property + def n_cols(self): + """Number of columns in the matrix.""" + return len(self._entries[0]) + + def __len__(self): + return self.n_rows * self.n_cols + + def __getitem__(self, index): + 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] + 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 integer or a tuple of two integers") + + def rows(self): + """Iterate over the rows of the matrix. + + Returns: + rows (Generator): produces Vector instances + representing individual rows of the matrix + """ + return (Vector(r) for r in self._entries) + + def cols(self): + """Iterate over the columns of the matrix. + + Returns: + columns (Generator): produces Vector instances + representing individual columns of the matrix + """ + return ( + Vector(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): + """Iterate over the entries of the matrix in flat fashion. + + Args: + reverse (bool): flag to iterate backwards; defaults to False + row_major (bool): flag to iterate in row major order; defaults to False + + Returns: + entries (Generator): produces the entries rows of the matrix + in the type set in the typing class variable + """ + if reverse: + rows, cols = range(self.n_rows - 1, -1, -1), 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): + return self.entries() + + def __reversed__(self): + return self.entries(reverse=True) + + def __add__(self, other): + if isinstance(other, self.__class__): + if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols): + raise ValueError("matrices need to be of 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) + ) + 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): + if isinstance(other, Vector): + raise TypeError("vectors and matrices cannot be added") + return self + other + + def __sub__(self, other): + return self + (-other) + + def __rsub__(self, other): + if isinstance(other, Vector): + raise TypeError("vectors and matrices cannot be subtracted") + return (-self) + other + + def _matrix_multiply(self, other): + if self.n_cols != other.n_rows: + raise ValueError("matrices need to have compatible dimensions") + return self.__class__((rv * cv for cv in other.cols()) for rv in self.rows()) + + def __mul__(self, other): + if isinstance(other, numbers.Number): + return self.__class__((x * other for x in r) for r in self._entries) + elif isinstance(other, Vector): + return self._matrix_multiply(other.as_matrix()).as_vector() + elif isinstance(other, self.__class__): + return self._matrix_multiply(other) + return NotImplemented + + def __rmul__(self, other): + if isinstance(other, numbers.Number): + return self * other + elif isinstance(other, Vector): + return other.as_matrix(column=False)._matrix_multiply(self).as_vector() + return NotImplemented + + def __truediv__(self, other): + if isinstance(other, numbers.Number): + return self * (1 / other) + return NotImplemented + + def __eq__(self, other): + if isinstance(other, self.__class__): + if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols): + raise ValueError("matrices need to be of the same dimensions") + for x, y in zip(self, other): + if abs(x - y) > self.zero_threshold: + return False + return True + return NotImplemented + + def __pos__(self): + return self + + def __neg__(self): + return self.__class__((-x for x in r) for r in self._entries) + + def __abs__(self): + return norm(self) + + def __bool__(self): + return bool(abs(self)) + + def __float__(self): + 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): + """Cast the matrix as a one-dimensional vector. + + Returns: + vector (Vector) + + Raises: + RuntimeError: if not one of the two dimensions is 1 + """ + if not (self.n_rows == 1 or self.n_cols == 1): + raise RuntimeError("one dimension (m or n) must be 1") + return Vector(x for x in self) + + def transpose(self): + """Transpose the rows and columns of the matrix. + + Returns: + matrix (Matrix) + """ + return self.__class__(zip(*self._entries)) + + +from .vector import Vector diff --git a/sample_package/utils.py b/sample_package/utils.py new file mode 100644 index 0000000..7929de2 --- /dev/null +++ b/sample_package/utils.py @@ -0,0 +1,14 @@ +"""This module provides utility functions.""" + + +def norm(vector_or_matrix): + """Calculate the Frobenius or Euclidean norm of a matrix or vector. + + Args: + vector_or_matrix (Vector/Matrix): the entries whose squares + are to be summed up + + Returns: + norm (float) + """ + return math.sqrt(sum(x ** 2 for x in vector_or_matrix)) diff --git a/sample_package/vector.py b/sample_package/vector.py new file mode 100644 index 0000000..39d87f6 --- /dev/null +++ b/sample_package/vector.py @@ -0,0 +1,141 @@ +"""This module defines a Vector class.""" + +from .matrix import Matrix + + +class Vector: + """A standard one-dimensional vector from linear algebra. + + The class is designed for sub-classing in such a way that + the user can adapt the typing class attribute to change, + for example, how the entries are stored (e.g., as integers). + + Attributes: + storage (callable): must return an iterable that is used + to store the entries of the vector; defaults to tuple + typing (callable): type casting applied to all vector + entries upon creation; defaults to float + zero_threshold (float): maximum difference allowed when + comparing an entry to zero; defaults to 1e-12 + """ + + storage = tuple + typing = float + zero_threshold = 1e-12 + + def __init__(self, data): + """Initiate a new vector. + + Args: + data (iterable): the vector's entries; + must have at least one element + + Raises: + ValueError: if the provided data do not have enough entries + """ + self._entries = self.storage(self.typing(x) for x in data) + if len(self) == 0: + raise ValueError("the vector must have at least one entry") + + def __repr__(self): + name, args = self.__class__.__name__, ", ".join(f"{x:.3f}" for x in self) + return f"{name}(({args}))" + + def __str__(self): + name, first, last, entries = ( + self.__class__.__name__, + self[0], + self[-1], + len(self), + ) + return f"{name}({first:.1f}, ..., {last:.1f})[{entries:d}]" + + def __len__(self): + return len(self._entries) + + def __getitem__(self, index): + if not isinstance(index, int): + raise TypeError("index must be an integer") + return self._entries[index] + + def __iter__(self): + return iter(self._entries) + + def __reversed__(self): + return reversed(self._entries) + + def __add__(self, other): + if isinstance(other, self.__class__): + if len(self) != len(other): + raise ValueError("vectors need to be of the same length") + return self.__class__(x + y for (x, y) in zip(self, other)) + elif isinstance(other, numbers.Number): + return self.__class__(x + other for x in self) + return NotImplemented + + def __radd__(self, other): + return self + other + + def __sub__(self, other): + return self + (-other) + + def __rsub__(self, other): + return (-self) + other + + def __mul__(self, other): + if isinstance(other, self.__class__): + if len(self) != len(other): + raise ValueError("vectors need to be of the same length") + return sum(x * y for (x, y) in zip(self, other)) + elif isinstance(other, numbers.Number): + return self.__class__(x * other for x in self) + return NotImplemented + + def __rmul__(self, other): + return self * other + + def __truediv__(self, other): + if isinstance(other, numbers.Number): + return self * (1 / other) + return NotImplemented + + def __eq__(self, other): + if isinstance(other, self.__class__): + if len(self) != len(other): + raise ValueError("vectors need to be of the same length") + for x, y in zip(self, other): + if abs(x - y) > self.zero_threshold: + return False + return True + return NotImplemented + + def __pos__(self): + return self + + def __neg__(self): + return self.__class__(-x for x in self) + + def __abs__(self): + return norm(self) + + def __bool__(self): + return bool(abs(self)) + + def __float__(self): + 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): + """Convert the vector into a matrix. + + Args: + column (bool): if the vector should be interpreted as + as a column vector or not; defaults to True + + Returns: + matrix (Matrix) + """ + if column: + return Matrix([x] for x in self) + return Matrix([(x for x in self)]) From 72db877c457d592eb76c519eff235b0d14eece14 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Sun, 22 Sep 2019 20:30:13 +0200 Subject: [PATCH 016/242] Add initial version of notebook 02 --- 02_functions.ipynb | 3478 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3478 insertions(+) create mode 100644 02_functions.ipynb diff --git a/02_functions.ipynb b/02_functions.ipynb new file mode 100644 index 0000000..95646a8 --- /dev/null +++ b/02_functions.ipynb @@ -0,0 +1,3478 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 2: Functions & Modularization" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In Chapter 1 we typed the **business logic** of our little program to calculate the mean of a subset of a list of numbers right into the code cells. Then, we executed them one after another. We had no way of **re-using** the code except for either re-executing the cells or copying and pasting their contents into other cells. And whenever we find ourselves doing repetitive manual work, we can be sure that there must be a way of automating what we are doing.\n", + "\n", + "At the same time, we executed built-in functions (e.g., [print()](https://docs.python.org/3/library/functions.html#print), [sum()](https://docs.python.org/3/library/functions.html#sum), [len()](https://docs.python.org/3/library/functions.html#len), [id()](https://docs.python.org/3/library/functions.html#id), or [type()](https://docs.python.org/3/library/functions.html#type)) that obviously must be re-using the same parts inside core Python every time we use them.\n", + "\n", + "This chapter shows how Python offers language constructs that let us **define** our own functions that we can then **call** just like the built-in ones." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Function Definition" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "So-called **[user-defined functions](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)** can be created with the `def` statement. To extend an already familiar example, we re-use the introductory example from Chapter 1 in its final Pythonic version and transform it into the function `average_evens()` below. \n", + "\n", + "A function's **name** must be chosen according to the same naming rules as for ordinary variables. In fact, Python manages function names just like variables. In this book, we further adopt the convention of ending function names with parentheses \"`()`\" in text cells for faster comprehension when reading (i.e., `average_evens()` vs. `average_evens`). These are not actually part of the name but must always be written out in the `def` statement for syntactic reasons.\n", + "\n", + "Functions may define an arbitrary number of **parameters** as inputs that can then be referenced within the indented **code block**: They are simply listed within the parentheses in the `def` statement (i.e., `numbers` below). \n", + "\n", + "The code block is often also called a function's **body** while the first line with the `def` in it is the **header** and must end with a colon.\n", + "\n", + "Together, the name and the list of parameters are also referred to as the function's **[signature](https://en.wikipedia.org/wiki/Type_signature)** (i.e., `average_evens(numbers)` below).\n", + "\n", + "A function may come with an *explicit* **[return value](https://docs.python.org/3/reference/simple_stmts.html#the-return-statement)** (i.e., \"result\" or \"output\") specified with the `return` statement: Functions that have one are considered **fruitful**; otherwise, they are **void**. Functions of the latter kind are still useful because of their **side effects** (e.g., the [print()](https://docs.python.org/3/library/functions.html#print) built-in). Strictly speaking, they also have an *implicit* return value of `None` that is different from the `False` we saw in Chapter 1.\n", + "\n", + "To maintain good coding practices, a function should define a **docstring** that describes what it does in a short subject line, what parameters it expects (i.e., their types), and what it returns (if anything). A docstring is a syntactically valid multi-line string (i.e., type `str`) defined with **triple-double quotes** (strings are covered in depth in Chapter 6). Good standards as to how to format a docstring are [PEP 257](https://www.python.org/dev/peps/pep-0257/) and section 3.8 in [Google's Python Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def average_evens(numbers):\n", + " \"\"\"Calculate the average of all even numbers in a list.\n", + "\n", + " Args:\n", + " numbers (list): a list of numbers; may be integers or floats\n", + "\n", + " Returns:\n", + " float: average\n", + " \"\"\"\n", + " evens = [n for n in numbers if n % 2 == 0]\n", + " average = sum(evens) / len(evens)\n", + " return average" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Once defined, a function can be referenced just like any other variable by its name (i.e., *without* the parenthesis). Its value might seem awkward at first: It consists of the location where we defined the function (i.e., `__main__`, which is Python's way of saying \"in this notebook\") and the signature." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A function is an **object** on its own with an **identity** (i.e., memory location) and a **type**, namely `function`." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140620654002656" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(average_evens)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "function" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(average_evens)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The built-in [help()](https://docs.python.org/3/library/functions.html#help) function shows a function's docstring.\n", + "\n", + "Whenever we use code to analyze or obtain information on an object, we say that we **[introspect](https://en.wikipedia.org/wiki/Type_introspection)** it." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on function average_evens in module __main__:\n", + "\n", + "average_evens(numbers)\n", + " Calculate the average of all even numbers in a list.\n", + " \n", + " Args:\n", + " numbers (list): a list of numbers; may be integers or floats\n", + " \n", + " Returns:\n", + " float: average\n", + "\n" + ] + } + ], + "source": [ + "help(average_evens)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In a Jupyter notebook, we can just as well add a question mark to a function's name to achieve the same. Then, a small tab opens in our browser." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "average_evens?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Two questions marks even show a function's source code." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "average_evens??" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Function Calls" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Once defined we can **call** (i.e., \"execute\") a function with the **call operator** `()`. The formal parameters are filled in by passing variables or expressions as **arguments** to the function within the parentheses." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6.0" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens(nums)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The return value is usually assigned to a new variable for later reference. Otherwise we would loose access to it in memory right away." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "avg = average_evens(nums)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6.0" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "avg" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Scoping Rules" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Local Scope disappears" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Notice how the parameters listed in a function's definition (i.e., `numbers`) and variables created inside it during execution (i.e., `evens` and `average`) are **local** to that function. That means they are only mapped to an object in memory while the function is being executed and de-referenced immediately when the function returns. We say they **go out of scope** once the function terminates." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'numbers' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnumbers\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mNameError\u001b[0m: name 'numbers' is not defined" + ] + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'evens' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mevens\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mNameError\u001b[0m: name 'evens' is not defined" + ] + } + ], + "source": [ + "evens" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'average' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0maverage\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mNameError\u001b[0m: name 'average' is not defined" + ] + } + ], + "source": [ + "average" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Global Scope is everywhere" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "On the contrary, while a function is being executed, it can \"see\" the variables of the **enclosing scope** (i.e., \"outside\" of it). This is a common source of *semantic* errors. Consider the following stylized (and incorrect) example `average_wrong()`. The error is hard to spot with eyes: The function never references the `numbers` parameter but the `nums` variable in the **global scope** instead." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def average_wrong(numbers):\n", + " \"\"\"Calculate the average of all even numbers in a list.\n", + "\n", + " Args:\n", + " numbers (list): a list of numbers; may be integers or floats\n", + "\n", + " Returns:\n", + " float: average\n", + " \"\"\"\n", + " evens = [n for n in nums if n % 2 == 0] # should reference numbers, not nums\n", + " average = sum(evens) / len(evens)\n", + " return average" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nums" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6.0" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_wrong(nums) # the result is correct by accident!" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6.0" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_wrong([123, 456, 789])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Also, observe how both `average_evens()` and `average_wrong()` use the same names for their respective parameters and variables internally. For sure, Python is smart enough to not mix them up. This is because each function call creates a temporary **[namespace](https://en.wikipedia.org/wiki/Namespace)** that *isolates* the local scope's names for usage only from within the function. As we saw in the [Zen of Python](https://www.python.org/dev/peps/pep-0020/), \"namespaces are one honking great idea\" (cf., `import this`)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Shadowing" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Code gets even more confusing when variables by the same name from different scopes collide. In particular, what should we expect to happen if a function changes a globally defined variable internally?\n", + "\n", + "`average_odds()` below works like `average_evens()` above except that it **[casts](https://en.wikipedia.org/wiki/Type_conversion)** (i.e., \"converts\") the elements of `numbers` as objects of type `int` with the [int()](https://docs.python.org/3/library/functions.html#int) built-in first before filtering and averaging them. In doing so, it introduces an *internal* variable `nums` whose name collides with the one in the global scope." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def average_odds(numbers):\n", + " \"\"\"Calculate the average of all odd numbers in a list.\n", + "\n", + " Args:\n", + " numbers (list): a list of numbers; must be integers\n", + "\n", + " Returns:\n", + " float: average\n", + " \"\"\"\n", + " nums = [int(n) for n in numbers] # cast all numbers as integers first\n", + " odds = [n for n in nums if n % 2 != 0] # before filtering them\n", + " average = sum(odds) / len(odds)\n", + " return average" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`nums` in the global scope is of course the same list from above." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nums" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As good practice, let's first use inputs for which we can calculate the answer in our heads to verify that `average_odds()` is correct." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3.0" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_odds([1, 100, 3, 100, 5]) # verify the function's correctness with predictable inputs" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To make the confusion even bigger, let's also pass the global `nums` as an argument to `average_odds()`." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "5.0" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_odds(nums)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Python, however, is again smart enough to keep the two `nums` variables apart. So the global `nums` is still pointing to the very same list object as before." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nums" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The reason why everything works just fine is that *every time* we (re-)assign an object to a variable inside a function with the `=` statement, this is done in the local scope by default. There are ways to change variables existing in an outer scope from within a function but we save that for a later chapter.\n", + "\n", + "Variables whose names collide with the ones of variables in enclosing scopes - and the global scope is just the most enclosing scope - are said to **shadow** them.\n", + "\n", + "While this is not a problem for Python as we have observed, it may lead to less readable code for us humans and should be avoided if possible. But, as we have also heard, \"[naming things](https://skeptics.stackexchange.com/questions/19836/has-phil-karlton-ever-said-there-are-only-two-hard-things-in-computer-science)\" is often considered hard as well and we have to be prepared to encounter shadowing variables." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Built-in Functions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Python comes with plenty of useful functions built in, some of which we have already seen before. The [documentation](https://docs.python.org/3/library/functions.html) has the full list. Just as core Python itself, they are implemented in C and thus very fast.\n", + "\n", + "[len()](https://docs.python.org/3/library/functions.html#len) counts the number of elements in a container object while [sum()](https://docs.python.org/3/library/functions.html#sum) adds up all the elements." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(nums)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "55" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(nums)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We can cast certain objects as a different type. For example, to \"convert\" a float or a text into an integer, we use the [int()](https://docs.python.org/3/library/functions.html#int) built-in. This actually creates a *new* object of type `int` from the provided `avg` or `\"6\"` objects who continue to exist in memory unchanged." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6.0" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "avg" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(avg)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(\"6\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Observe that casting as an integer is different from rounding with the [round()](https://docs.python.org/3/library/functions.html#round) built-in function." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(7.99)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "8" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "round(7.99)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Not all conversions are valid and *runtime* errors can occur as the `ValueError` shows." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "invalid literal for int() with base 10: 'six'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"six\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: invalid literal for int() with base 10: 'six'" + ] + } + ], + "source": [ + "int(\"six\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We can also go in the other direction with the [float()](https://docs.python.org/3/library/functions.html#float) built-in function." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42.0" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(42)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Positional vs. Keyword Arguments" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "So far we have only specified one parameter in each of our user-defined functions. In Chapter 1, however, we saw the built-in function [divmod()](https://docs.python.org/3/library/functions.html#divmod) taking two arguments. Obviously, the order of the numbers passed in mattered. Whenever we call a function and list its arguments in a comma seperated manner, we say that we pass in the arguments by position or refer to them as **positional arguments**." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(4, 2)" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "divmod(42, 10)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(0, 10)" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "divmod(10, 42)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For many functions there is a natural order to the arguments. But what if this is not the case? For example, let's create a close relative of the above `average_evens()` function that also scales the resulting average by a factor. What is more natural? Passing in `numbers` first? Or `scalar`? There is no obvious way and we continue with the first alternative for no real reason." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def scaled_average_evens(numbers, scalar):\n", + " \"\"\"Calculate a scaled average of all even numbers in a list.\n", + "\n", + " Args:\n", + " numbers (list): a list of numbers; may be integers or floats\n", + " scalar (float): the scalar that multiplies the average\n", + " of the even numbers\n", + "\n", + " Returns:\n", + " float: scaled average\n", + " \"\"\"\n", + " evens = [n for n in numbers if n % 2 == 0]\n", + " average = sum(evens) / len(evens)\n", + " return scalar * average" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As with [divmod()](https://docs.python.org/3/library/functions.html#divmod), we can pass in the arguments by position." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12.0" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scaled_average_evens(nums, 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, now the function call is a bit harder to comprehend as we need to always remember what the `2` means. This becomes even harder the more parameters we specify.\n", + "\n", + "Luckily, we can also reference the formal parameter names as **keyword arguments**. We can even combine positional and keyword arguments in the same function call. Each of the following does the exact same thing." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12.0" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scaled_average_evens(nums, scalar=2)" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12.0" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scaled_average_evens(numbers=nums, scalar=2)" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12.0" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scaled_average_evens(scalar=2, numbers=nums)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Unfortunately, there are ways to screw this up with a `SyntaxError`: If positional and keyword arguments are mixed, the keyword arguments *must* come last." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "positional argument follows keyword argument (, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m scaled_average_evens(numbers=nums, 2)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m positional argument follows keyword argument\n" + ] + } + ], + "source": [ + "scaled_average_evens(numbers=nums, 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Default Argument Values" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Defining both `average_evens()` and `scaled_average_evens()` is also kind of repetitive as most of their code is the same. Such a redundancy will make a code base harder to maintain in the long run as whenever we change the logic in one function we must *not* forget to do so for the other function as well.\n", + "\n", + "A better way is to design related functions in a **modular** fashion such that they re-use each other's logic.\n", + "\n", + "For example, as not scaling an average is just a special case of scaling it with `1`, we could re-define the two functions like below: In this setting, the function resembling the *special* case (i.e., `average_evens()`) simply **forwards** the call to the more *general* function (i.e., `scaled_average_evens()`) using a `scalar=1` argument." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def scaled_average_evens(numbers, scalar):\n", + " \"\"\"Calculate a scaled average of all even numbers in a list.\n", + "\n", + " ...\n", + " \"\"\"\n", + " evens = [n for n in numbers if n % 2 == 0]\n", + " average = sum(evens) / len(evens)\n", + " return scalar * average" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "def average_evens(numbers):\n", + " \"\"\"Calculate the average of all even numbers in a list.\n", + "\n", + " ...\n", + " \"\"\"\n", + " return scaled_average_evens(numbers, scalar=1) # refactored to use the logic in scaled_average_evens()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The outcome of `average_evens(nums)` is of course still `6.0`." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6.0" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens(nums)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we *assume* that scaling the average occurs rarely, we could also handle both cases in *one* function definition by providing a **default value** for the `scalar` parameter." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def average_evens(numbers, scalar=1):\n", + " \"\"\"Calculate the average of all even numbers in a list.\n", + "\n", + " Args:\n", + " numbers (list): list of numbers; may be integers or floats\n", + " scalar (float, optional): the scalar that multiplies the\n", + " average of the even numbers\n", + "\n", + " Returns:\n", + " float: (scaled) average\n", + " \"\"\"\n", + " evens = [n for n in numbers if n % 2 == 0]\n", + " average = sum(evens) / len(evens)\n", + " return scalar * average" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Now we can call the function either with or without the `scalar` argument.\n", + "\n", + "If `scalar` is passed in, this can be done as either a positional or a keyword argument. Which of the two versions where `scalar` is `2` is easier to comprehend in a large program?" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6.0" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens(nums)" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12.0" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens(nums, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12.0" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens(nums, scalar=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Keyword-only Arguments" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Since we *assumed* that scaling will occur *rarely*, we'd prefer that our new version of `average_evens()` be called with a *keyword argument* whenever `scalar` is passed in explicitly. Then, the second argument is never ambiguous as we could always read its name.\n", + "\n", + "Luckily, Python offers a **keyword-only** syntax, where all we need to do is to place the arguments for which we require *explicit* keyword use after an asterix `*`." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def average_evens(numbers, *, scalar=1):\n", + " \"\"\"Calculate the average of all even numbers in a list.\n", + "\n", + " Args:\n", + " numbers (list): list of numbers; may be integers or floats\n", + " scalar (float, optional): the scalar that multiplies the\n", + " average of the even numbers\n", + "\n", + " Returns:\n", + " float: (scaled) average\n", + " \"\"\"\n", + " evens = [n for n in numbers if n % 2 == 0]\n", + " average = sum(evens) / len(evens)\n", + " return scalar * average" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we now call the function with a `scalar` argument passed in, we *must* use keyword notation." + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6.0" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens(nums)" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12.0" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens(nums, scalar=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we pass in `scalar` as a positional argument instead, we obtain a `TypeError`." + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "average_evens() takes 1 positional argument but 2 were given", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0maverage_evens\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnums\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: average_evens() takes 1 positional argument but 2 were given" + ] + } + ], + "source": [ + "average_evens(nums, 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Anonymous Functions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `def` statement is a statement because of its side effect of creating a *new* name that points to a *new* `function` object in memory.\n", + "\n", + "We can thus think of it as doing *two* things at once (i.e., either both of them happen or none). First, a `function` object is created that contains the concrete $0$s and $1$s that resemble the instructions we put into the function's body. In the context of a function, these $0$s and $1$s are also called **[byte code](https://en.wikipedia.org/wiki/Bytecode)**. Then, a name is created pointing at the new `function` object.\n", + "\n", + "Only this second aspect makes `def` a statement: Merely creating a new object in memory without making it accessible for later reference does *not* constitute a side effect. This is because the state the program is *not* changed. After all, if we cannot reference an object, how do we know that it is actually existing?\n", + "\n", + "Python provides a so-called **[lambda expression](https://docs.python.org/3/reference/expressions.html#lambda)** syntax that allows us to *only* create a `function` object in memory *without* making a name point to it.\n", + "\n", + "It starts with the keyword `lambda` followed by an optional comma seperated enumeration of parameters, a mandatory colon, and *one* expression that also is the resulting `function` object's return value.\n", + "\n", + "Because it does not create a name pointing to the object, we effectively create \"anonymous\" functions with it. In the example, we create a `function` object that adds `3` to the only argument passed in as the parameter `x` and returns that sum." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(x)>" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "lambda x: x + 3" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If you think this is a rather pointless thing to do, you are absolutely correct!\n", + "\n", + "We created a `function` object, dit *not* call it, and Python immediately forgot about it. So what's the point?\n", + "\n", + "Just to prove that the `lambda` expression really creates a callable `function` object, we use the simple `=` statement to assign it to the variable `add_three`, which is really `add_three()` as per our convention from above." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "add_three = lambda x: x + 3 # we could and should use def instead" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Now we can call `add_three()` as if we defined it with the `def` statement to begin with." + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "13" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "add_three(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Alternatively, we could call any anonymous `function` object created with an `lambda` expression right away (i.e. without assigning it to a variable), which looks really weird for now as we need *two* pairs of parentheses: The first is just a delimiter whereas the second the call operator." + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(lambda x: x + 3)(39) # this looks weird but will become very useful" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The main point of having functions without a name is to use them in a situation where we know ahead of time that we will use the function only once.\n", + "\n", + "Very popular contexts where we will apply lambda expressions are with the **map-filter-reduce** paradigm in Chapter 7 or when we do \"number crunching\" with **arrays** and **data frames** in Chapter 9." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Extending Core Python" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "So far, we have only used what we refer to as **core** Python in this book. By this, we mean all the syntactical rules as specified in the [language reference](https://docs.python.org/3/reference/) and a minimal set of about 50 built-in [functions](https://docs.python.org/3/library/functions.html). With this we could already implement any algorithm or business logic we can think of!\n", + "\n", + "However, after our first couple of programs, we would already start seeing recurring patterns in the code we write. In other words, we would constantly be \"re-inventing the wheel\" in each new project.\n", + "\n", + "Would it not be smarter to pull out the re-usable components from our programs and put them into some project independent **library** of generically useful functionalities? Then we would only need a way of including these **utilities** in our projects.\n", + "\n", + "As all programmers across all languages face this very same issue, most programming languages come with a so-called **[standard library](https://en.wikipedia.org/wiki/Standard_library)** that provides utilities to accomplish common tasks without a lot of code. Examples are making an HTTP request to some website, open and read popular file types (e.g., CSV or Excel files), do something on a computer's file system, and many more." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Standard Library" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Python also comes with its own [standard library](https://docs.python.org/3/library/index.html) that is structured into coherent modules and packages for given topics: A **module** is just a plain text file with the file extension *.py* that contains Python code while a **package** is a folder that groups several related modules.\n", + "\n", + "The code in the [standard library](https://docs.python.org/3/library/index.html) is contributed and maintained by many volunteers around the world. In contrast to so-called \"third-party\" packages (cf., the next section below), the Python core development team closely monitors and tests the code in the [standard library](https://docs.python.org/3/library/index.html). Consequently, we can be reasonably sure that anything provided by it works correctly independent of our computer's operating system and will most likely also be there in the next Python versions. Parts in the [standard library](https://docs.python.org/3/library/index.html) that are computationally expensive are often re-written in C and therefore much faster than anything we could code in Python ourselves. So, whenever we can solve a problem with the help of the [standard library](https://docs.python.org/3/library/index.html), it is almost always the best way to do so as well.\n", + "\n", + "The [standard library](https://docs.python.org/3/library/index.html) has grown very big over the years and we refer to the website [PYMOTW](https://pymotw.com/3/index.html) (i.e., \"Python Module of the Week\") that features well written introductory tutorials and how-to guides to most parts of the library. The same author also published a [book](https://www.amazon.com/Python-Standard-Library-Example-Developers/dp/0134291050/ref=as_li_ss_tl?ie=UTF8&qid=1493563121&sr=8-1&keywords=python+3+standard+library+by+example) that many Pythonistas keep on their shelf for reference. Knowing what is in the [standard library](https://docs.python.org/3/library/index.html) is quite valuable for solving real world tasks quickly.\n", + "\n", + "Throughout this book we will look at many modules and packages from the [standard library](https://docs.python.org/3/library/index.html) in more depth, starting with the [math](https://docs.python.org/3/library/math.html) and [random](https://docs.python.org/3/library/random.html) modules in this chapter." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### The [math](https://docs.python.org/3/library/math.html) Module" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [math](https://docs.python.org/3/library/math.html) module provides non-trivial mathematical functions like $sin(x)$ and constants like $\\pi$ or $\\text{e}$.\n", + "\n", + "To make functions and variables defined \"somewhere else\" available in our current program, we must first **[import](https://docs.python.org/3/reference/simple_stmts.html#import)** them with the `import` statement. " + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import math" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This creates the variable `math` that points to a **[module object](https://docs.python.org/3/glossary.html#term-module)** (i.e., type `module`) in memory." + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140620734513624" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(math)" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "module" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(math)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`module` objects serve as namespaces to organize the names inside a module. In this context, a namespace is nothing but a prefix that avoids collision with the variables already defined at the location where we import the module into.\n", + "\n", + "Let's see what we can do with the `math` module.\n", + "\n", + "The [dir()](https://docs.python.org/3/library/functions.html#dir) built-in function can also be used with an argument passed in. Ignoring the dunder-style names, `math` offers quite a lot of ... names. As we cannot know at this point in time if a listed name refers to a function or an ordinary variable, we use the more generic term **attribute** to mean either one of them." + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['__doc__',\n", + " '__file__',\n", + " '__loader__',\n", + " '__name__',\n", + " '__package__',\n", + " '__spec__',\n", + " 'acos',\n", + " 'acosh',\n", + " 'asin',\n", + " 'asinh',\n", + " 'atan',\n", + " 'atan2',\n", + " 'atanh',\n", + " 'ceil',\n", + " 'copysign',\n", + " 'cos',\n", + " 'cosh',\n", + " 'degrees',\n", + " 'e',\n", + " 'erf',\n", + " 'erfc',\n", + " 'exp',\n", + " 'expm1',\n", + " 'fabs',\n", + " 'factorial',\n", + " 'floor',\n", + " 'fmod',\n", + " 'frexp',\n", + " 'fsum',\n", + " 'gamma',\n", + " 'gcd',\n", + " 'hypot',\n", + " 'inf',\n", + " 'isclose',\n", + " 'isfinite',\n", + " 'isinf',\n", + " 'isnan',\n", + " 'ldexp',\n", + " 'lgamma',\n", + " 'log',\n", + " 'log10',\n", + " 'log1p',\n", + " 'log2',\n", + " 'modf',\n", + " 'nan',\n", + " 'pi',\n", + " 'pow',\n", + " 'radians',\n", + " 'remainder',\n", + " 'sin',\n", + " 'sinh',\n", + " 'sqrt',\n", + " 'tan',\n", + " 'tanh',\n", + " 'tau',\n", + " 'trunc']" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dir(math)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Common mathematical constants and functions are now available via the dot operator `.` on the `math` object. This operator is sometimes also called the **attribute access operator**, in line with the just introduced term." + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3.141592653589793" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.pi" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2.718281828459045" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.e" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.sqrt" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on built-in function sqrt in module math:\n", + "\n", + "sqrt(x, /)\n", + " Return the square root of x.\n", + "\n" + ] + } + ], + "source": [ + "help(math.sqrt)" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1.4142135623730951" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.sqrt(2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Observe how the arguments passed to functions do not need to be just variables or simple literals. Instead, we can pass in any *expression* that evaluates to a *new* object of the type the function expects.\n", + "\n", + "So just as a reminder from the expression vs. statement discussion in Chapter 1: An expression is *any* syntactically correct combination of variables and literals with operators. And the call operator `()` is just ... well another operator. So both of the next two code cells are just expressions! They have no permanent side effect in memory. We can execute them as often as we want *without* changing the state of the program (i.e., this Jupyter notebook).\n", + "\n", + "So, regarding the very next cell in particular: Although the `2 ** 2` creates a *new* object `4` in memory that is then immediately passed into the [math.sqrt()](https://docs.python.org/3/library/math.html#math.sqrt) function, once that function call returns, \"all is lost\" and the newly created `4` object is forgotten again, as well as the return value of [math.sqrt()](https://docs.python.org/3/library/math.html#math.sqrt)." + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2.0" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.sqrt(2 ** 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Even the **composition** of several function calls only constitutes another expression." + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10.0" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.sqrt(average_evens([99, 100, 101]))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we only need one particular function from a module, we can also use the alternative `from ... import ...` syntax.\n", + "\n", + "This does *not* create a module object but only makes a variable in our current location point to an object defined inside a module directly." + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "from math import sqrt" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4.0" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sqrt(16)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### The [random](https://docs.python.org/3/library/random.html) Module" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Often times, we need a random variable, for example, when we want to build a simulation program. The [random](https://docs.python.org/3/library/random.html) module in the [standard library](https://docs.python.org/3/library/index.html) often suffices for that." + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import random" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "random" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Besides the usual dunder-style attributes, the [dir()](https://docs.python.org/3/library/functions.html#dir) built-in function lists some attributes in an upper case naming convention and many others starting with a single underscore \"\\_\". To understand the former, we have to wait until Chapter 10 while the latter are explained further below." + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['BPF',\n", + " 'LOG4',\n", + " 'NV_MAGICCONST',\n", + " 'RECIP_BPF',\n", + " 'Random',\n", + " 'SG_MAGICCONST',\n", + " 'SystemRandom',\n", + " 'TWOPI',\n", + " '_BuiltinMethodType',\n", + " '_MethodType',\n", + " '_Sequence',\n", + " '_Set',\n", + " '__all__',\n", + " '__builtins__',\n", + " '__cached__',\n", + " '__doc__',\n", + " '__file__',\n", + " '__loader__',\n", + " '__name__',\n", + " '__package__',\n", + " '__spec__',\n", + " '_acos',\n", + " '_bisect',\n", + " '_ceil',\n", + " '_cos',\n", + " '_e',\n", + " '_exp',\n", + " '_inst',\n", + " '_itertools',\n", + " '_log',\n", + " '_os',\n", + " '_pi',\n", + " '_random',\n", + " '_sha512',\n", + " '_sin',\n", + " '_sqrt',\n", + " '_test',\n", + " '_test_generator',\n", + " '_urandom',\n", + " '_warn',\n", + " 'betavariate',\n", + " 'choice',\n", + " 'choices',\n", + " 'expovariate',\n", + " 'gammavariate',\n", + " 'gauss',\n", + " 'getrandbits',\n", + " 'getstate',\n", + " 'lognormvariate',\n", + " 'normalvariate',\n", + " 'paretovariate',\n", + " 'randint',\n", + " 'random',\n", + " 'randrange',\n", + " 'sample',\n", + " 'seed',\n", + " 'setstate',\n", + " 'shuffle',\n", + " 'triangular',\n", + " 'uniform',\n", + " 'vonmisesvariate',\n", + " 'weibullvariate']" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dir(random)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [random.random()](https://docs.python.org/3/library/random.html#random.random) function generates a uniformly distributed `float` number between $0$ (including) and $1$ (excluding)." + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "random.random" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on built-in function random:\n", + "\n", + "random(...) method of random.Random instance\n", + " random() -> x in the interval [0, 1).\n", + "\n" + ] + } + ], + "source": [ + "help(random.random)" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.4028601552264015" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "random.random()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While we could build some conditional logic with an `if` statement to map the number generated by [random.random()](https://docs.python.org/3/library/random.html#random.random) to a finite set of elements manually, the [random.choice()](https://docs.python.org/3/library/random.html#random.choice) function provides a lot more **convenience** for us. We simply call it with, for example, the `nums` list and it draws one element out of it with equal chance." + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + ">" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "random.choice" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on method choice in module random:\n", + "\n", + "choice(seq) method of random.Random instance\n", + " Choose a random element from a non-empty sequence.\n", + "\n" + ] + } + ], + "source": [ + "help(random.choice)" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "8" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "random.choice(nums)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In order to re-produce the same random numbers in a simulation each time we run it, we can set the **[random seed](https://en.wikipedia.org/wiki/Random_seed)**. It is good practice to do this at the beginning of a program or notebook. Then every time we re-start the program, we will get the exact same random numbers again. This becomes very important, for example, when we employ certain machine learning algorithms that rely on randomization, like the infamous [Random Forest](https://en.wikipedia.org/wiki/Random_forest), and want to obtain **re-producable** results.\n", + "\n", + "The [random](https://docs.python.org/3/library/random.html) module provides the [random.seed()](https://docs.python.org/3/library/random.html#random.seed) function to do that." + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "random.seed(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.6394267984578837" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "random.random()" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "random.seed(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.6394267984578837" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "random.random()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Third-party Packages" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As the Python community is based around open source, many developers publish their code, for example, on the Python Package Index [PyPI](https://pypi.org) from where anyone can download and install it for free using command line based tools like [pip](https://pip.pypa.io/en/stable/) or [conda](https://conda.io/en/latest/). This way, we can always customize our Python installation even more. Managing many such packages is actually quite a deep topic on its own, sometimes fearfully called **[dependency hell](https://en.wikipedia.org/wiki/Dependency_hell)**.\n", + "\n", + "The difference between the [standard library](https://docs.python.org/3/library/index.html) and such **third-party** packages is that in the first case the code goes through a much more formalized review process and is officially endorsed by the Python core developers. Yet, many third-party projects also offer the highest quality standards and a lot of such software is actually also relied on by many businesses and researchers.\n", + "\n", + "Throughout this book, we will look at many third-party libraries, mostly from Python's [scientific stack](https://scipy.org/about.html), a tightly coupled set of third-party libraries for storing **big data** efficiently ([numpy](http://www.numpy.org/)), \"wrangling\" ([pandas](https://pandas.pydata.org/)) and visualizing them ([matplotlib](https://matplotlib.org/) and [seaborn](https://seaborn.pydata.org/)), fitting classical statistical models ([statsmodels](http://www.statsmodels.org/)), training machine learning models ([sklearn](http://scikit-learn.org/)), and much more.\n", + "\n", + "Below, we briefly show how to install third-party libraries." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### The [numpy](http://www.numpy.org/) Library" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[numpy](http://www.numpy.org/) is the de-facto standard in the Python world for handling **array-like** data. That is a fancy word for data that can be put into a matrix or vector format. We will look at it in depth in Chapter 9.\n", + "\n", + "As [numpy](http://www.numpy.org/) is *not* in the [standard library](https://docs.python.org/3/library/index.html), it must be *manually* installed, for example, with the [pip](https://pip.pypa.io/en/stable/) tool. As mentioned in Chapter 0, to execute terminal commands from within a Jupyter notebook, we just need to start a code cell with an exclamation mark.\n", + "\n", + "If you are running this notebook with an installation of the [Anaconda Distribution](https://www.anaconda.com/distribution/), then [numpy](http://www.numpy.org/) is probably already installed. Running the cell below, will just confirm that." + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: numpy in /home/webartifex/.pyenv/versions/anaconda3-2019.07/lib/python3.7/site-packages (1.16.4)\r\n" + ] + } + ], + "source": [ + "!pip install numpy" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[numpy](http://www.numpy.org/) is conventionally imported with the shorter **idiomatic** name `np`. The `as` in the import statement just changes the resulting variable name. It is a shortcut for the three lines `import numpy`, `np = numpy`, and `del numpy`." + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`np` can be used in the same way as `math` or `random` above." + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 85, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's convert the above `nums` list into a vector-like object of type `numpy.ndarray`." + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "vec = np.array(nums)" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])" + ] + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "vec" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "numpy.ndarray" + ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(vec)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[numpy](http://www.numpy.org/) somehow magically adds new behavior to Python's built-in arithmetic operators. For example, we can now [scalar-multiply](https://en.wikipedia.org/wiki/Scalar_multiplication) `vec`.\n", + "\n", + "[numpy](http://www.numpy.org/)'s functions are implemented in highly optimized C code and therefore fast, especially when it comes to big data." + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20])" + ] + }, + "execution_count": 89, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "2 * vec" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This scalar multiplication would \"fail\" if we used a plain `list` object like `nums` instead of an `numpy.ndarray` object like `vec`. The two types exhibit different **behavior** when used with the same operator, another example of **operator overloading**." + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" + ] + }, + "execution_count": 90, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "2 * nums # surprise, surprise" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[numpy](http://www.numpy.org/)'s `numpy.ndarray` objects integrate nicely with Python's built-in functions (e.g., [sum()](https://docs.python.org/3/library/functions.html#sum)) or functions from the [standard library](https://docs.python.org/3/library/index.html) (e.g., [random.choice()](https://docs.python.org/3/library/random.html#random.choice))." + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "55" + ] + }, + "execution_count": 91, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(vec)" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 92, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "random.choice(vec)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Local Modules and Packages" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For sure, we can create our own modules and packages. In the repository's main directory, there is a [*sample_module.py*](https://github.com/webartifex/intro-to-python/blob/master/sample_module.py) file that contains, among others, a function equivalent to the final version of `average_evens()`. To be realistic, this sample module is structured in a modular manner with several functions building on each other. It is best to skim over it *now* before reading on.\n", + "\n", + "To make code we put into a *.py* file available in our program, we import it as a module just as we did above with modules in the [standard library](https://docs.python.org/3/library/index.html) or third-party packages.\n", + "\n", + "The *name* to be imported is the file's name except for the *.py* part. In order for this to work, the file's name *must* adhere to the *same* rules as hold for [variable names](https://docs.python.org/3/reference/lexical_analysis.html#identifiers) in general.\n", + "\n", + "What happens during an import is conceptually as follows. When Python sees the `import sample_module` part, it first creates a *new* object of type `module` in memory. This is effectively an *empty* namespace. Then, it executes the imported file's code from top to bottom. Whatever variables are still defined at the end of this, are put into the module's namespace. Only if the file's code does *not* raise an error, will Python make a variable in our current location (i.e., `mod` here) point to the created `module` object. Otherwise, it is discarded. In essence, it is as if we copied and pasted the file's code in place of the import statement. If we import an already imported module again, Python is smart enough to avoid doing all this work all over and does nothing." + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import sample_module as mod" + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 94, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mod" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Disregarding the dunder-style attributes, `mod` defines the five attributes `_default_scalar`, `_scaled_average`, `average`, `average_evens`, and `average_odds`, which are exactly the ones we would expect from reading the [*sample_module.py*](https://github.com/webartifex/intro-to-python/blob/master/sample_module.py) file.\n", + "\n", + "An important convention when working with imported code is to *disregard* any attributes starting with an underscore \"\\_\". These are considered **private** and constitute **implementation details** the author of the imported code might change in a future version of his software. We *must* not rely on them in any way.\n", + "\n", + "In contrast, the three remaining **public** attributes are the functions `average()`, `average_evens()`, and `average_odds()` that we may use after the import." + ] + }, + { + "cell_type": "code", + "execution_count": 95, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['__builtins__',\n", + " '__cached__',\n", + " '__doc__',\n", + " '__file__',\n", + " '__loader__',\n", + " '__name__',\n", + " '__package__',\n", + " '__spec__',\n", + " '_default_scalar',\n", + " '_scaled_average',\n", + " 'average',\n", + " 'average_evens',\n", + " 'average_odds']" + ] + }, + "execution_count": 95, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dir(mod)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We can use the imported `mod.average_evens()` just like `average_evens()` defined above. The advantage we get from **modularization** with *.py* files is that we can now easily re-use functions across different Jupyter notebooks without re-defining them again and again. Also, we can \"source out\" code that distracts from the storyline told in a notebook." + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on function average_evens in module sample_module:\n", + "\n", + "average_evens(numbers, *, scalar=1)\n", + " Calculate the average of all even numbers in a list.\n", + " \n", + " Args:\n", + " numbers (list): list of numbers; may be integers or floats\n", + " scalar (float, optional): the scalar that multiplies the\n", + " average of the even numbers\n", + " \n", + " Returns:\n", + " float: (scaled) average\n", + "\n" + ] + } + ], + "source": [ + "help(mod.average_evens)" + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6.0" + ] + }, + "execution_count": 97, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mod.average_evens(nums)" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12.0" + ] + }, + "execution_count": 98, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mod.average_evens(nums, scalar=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Packages are a generalization of modules and we will look at one in Chapter 10 in detail. You can, however, already look at a [sample package](https://github.com/webartifex/intro-to-python/tree/master/sample_package) in the repository, which is nothing but a folder with *.py* files in it.\n", + "\n", + "As a further references on modules, we refer to the [official tutorial](https://docs.python.org/3/tutorial/modules.html)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## TL;DR" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A **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 **re-use of code**.\n", + "\n", + "Functions are **defined** once with the `def` statement. Then, they can 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 can be passed in by **position** or **keyword**. Some functions may even require **keyword-only** arguments.\n", + "\n", + "**Lambda expressions** create anonymous functions.\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.7.3" + }, + "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": 2 +} From 0eb4b70a994a5014e752e09b158c8678cc2ee26e Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Sun, 22 Sep 2019 20:31:17 +0200 Subject: [PATCH 017/242] Add review and exercises for notebook 02 --- 02_functions_review_and_exercises.ipynb | 397 ++++++++++++++++++++++++ 1 file changed, 397 insertions(+) create mode 100644 02_functions_review_and_exercises.ipynb diff --git a/02_functions_review_and_exercises.ipynb b/02_functions_review_and_exercises.ipynb new file mode 100644 index 0000000..647e47f --- /dev/null +++ b/02_functions_review_and_exercises.ipynb @@ -0,0 +1,397 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 2: Functions & Modularization" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Content Review" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read Chapter 2 of the book. Then work through the ten review questions." + ] + }, + { + "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**: What property 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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: What does it mean for a variable to **go out of scope**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: How can a **global** variable be **shadowed**? Is this good or bad?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: Explain the concept of **forwarding** a function **call**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: What are **keyword-only arguments** and when is it appropriate to use them?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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**: A mere function **call** is just an **expression**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9:** Functions always have a name by which we can call them." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: The [standard library](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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Coding Exercises" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Volume of a Sphere" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11.1**: The [volume of a sphere](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. Use the [standard library](https://docs.python.org/3/library/index.html) to obtain a good approximation of $\\pi$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11.2**: 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. Document your work appropriately in a docstring according to [Google's Python Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11.3**: 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": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11.4**: What observation do you make?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11.5**: Using the [range()](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 seperate line.\n", + "\n", + "Note: This is the first task where you actually need to use the [print()](https://docs.python.org/3/library/functions.html#print) built-in function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11.6**: What important lesson did you learn about the `float` type?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "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": 2 +} From 464392787bac3b9de08e9a1d277fa8c768845661 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Sun, 22 Sep 2019 20:35:03 +0200 Subject: [PATCH 018/242] Adjust content overview in README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 78bdff9..b664a32 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ As such they can be viewed in a plain web browser: - [00 - Start up](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb) - [01 - Elements of a Program](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_of_a_program.ipynb) +- [02 - Functions & Modularization](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) However, it is recommended that students **install Python and Jupyter locally** and run the code in the notebooks on their own. From 514b2dac40a380bb4ef82e8d33635386690d04f0 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 24 Sep 2019 19:44:39 +0200 Subject: [PATCH 019/242] Streamline previous content This includes shortening the filename of notebook 01 --- 00_start_up.ipynb | 19 ++++++++--- ...ts_of_a_program.ipynb => 01_elements.ipynb | 34 +++++++++---------- ... => 01_elements_review_and_exercises.ipynb | 0 02_functions.ipynb | 12 +++---- 4 files changed, 38 insertions(+), 27 deletions(-) rename 01_elements_of_a_program.ipynb => 01_elements.ipynb (97%) rename 01_elements_of_a_program_review_and_exercises.ipynb => 01_elements_review_and_exercises.ipynb (100%) diff --git a/00_start_up.ipynb b/00_start_up.ipynb index 506c6ad..29175db 100644 --- a/00_start_up.ipynb +++ b/00_start_up.ipynb @@ -429,7 +429,7 @@ } }, "source": [ - "Here is a brief history of and some background on Python:\n", + "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](https://en.wikipedia.org/wiki/Guido_van_Rossum) (Python’s **[Benevolent Dictator for Life](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", @@ -439,6 +439,17 @@ "- the language is named after the sketch comedy group [Monty Python](https://en.wikipedia.org/wiki/Monty_Python)" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### Summary" + ] + }, { "cell_type": "markdown", "metadata": { @@ -447,7 +458,7 @@ } }, "source": [ - "Python is a **general-purpose** programming language that allows for **fast development**, is **easy to comprehend**, **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**." + "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**." ] }, { @@ -770,7 +781,7 @@ " 1. Elements of a Program\n", " 2. Functions & Modularization\n", "- What is the flow of execution? How can we form sentences from words?\n", - " 3. Boolean Logic & Conditionals\n", + " 3. Conditionals & Exceptions\n", " 4. Recursion & Looping" ] }, @@ -813,7 +824,7 @@ } }, "source": [ - "As with every good lecture, there has to be a [xkcd](https://xkcd.com/353/) comic somewhere." + "As with every good book, there has to be a [xkcd](https://xkcd.com/353/) comic somewhere." ] }, { diff --git a/01_elements_of_a_program.ipynb b/01_elements.ipynb similarity index 97% rename from 01_elements_of_a_program.ipynb rename to 01_elements.ipynb index c771c87..e1d8b09 100644 --- a/01_elements_of_a_program.ipynb +++ b/01_elements.ipynb @@ -742,7 +742,7 @@ } }, "source": [ - "The normal order of precedence from mathematics applies (i.e., \"PEMDAS\" rule) but parentheses help avoid confusion." + "The normal [order of precedence](https://docs.python.org/3/reference/expressions.html#operator-precedence) from mathematics applies (i.e., \"PEMDAS\" rule) but parentheses help avoid confusion." ] }, { @@ -949,7 +949,7 @@ { "data": { "text/plain": [ - "139867825605456" + "139940106427216" ] }, "execution_count": 27, @@ -973,7 +973,7 @@ { "data": { "text/plain": [ - "139867825776440" + "139940106602344" ] }, "execution_count": 28, @@ -997,7 +997,7 @@ { "data": { "text/plain": [ - "139867825460912" + "139940105769456" ] }, "execution_count": 29, @@ -1041,7 +1041,7 @@ } }, "source": [ - "`a` and `d` indeed have the same value as can be checked with the **equality operator** `==`. The resulting `True` (and the `False` below) is yet another data type, a so-called **boolean**." + "`a` and `d` indeed have the same value as can be checked with the **equality operator** `==`. The resulting `True` (and the `False` below) is yet another data type, a so-called **boolean**. We will look into that closely in Chapter 3." ] }, { @@ -1780,7 +1780,7 @@ " " ], "text/plain": [ - "" + "" ] }, "execution_count": 50, @@ -2087,7 +2087,7 @@ } }, "source": [ - "A variable can be **re-assigned** as often as we wish. Thereby, we could also assign an object of a different type. Because this is allowed, Python is said to be a **dynamically typed** language. On the contrary, a **statically typed** language like C also allows re-assignment but only with objects of the same type. This subtle distinction is one reason why Python is slower at execution than C: As it runs a program, it needs to figure out an object's type each time it is referenced. But as mentioned before, this can be mitigated with third-party libraries." + "A variable can be **re-assigned** as often as we wish. Thereby, we could also assign an object of a *different* type. Because this is allowed, Python is said to be a **dynamically typed** language. On the contrary, a **statically typed** language like C also allows re-assignment but only with objects of the *same* type. This subtle distinction is one reason why Python is slower at execution than C: As it runs a program, it needs to figure out an object's type each time it is referenced. But as mentioned before, this can be mitigated with third-party libraries." ] }, { @@ -2209,7 +2209,7 @@ } }, "source": [ - "Variables can be **[de-referenced](https://docs.python.org/3/reference/simple_stmts.html#the-del-statement)** (i.e., \"deleted\") with the `del` statement. This does *not* delete the object to which a variable points to. It merely removes the variable from the \"list of all variables\"." + "Variables can be **[de-referenced](https://docs.python.org/3/reference/simple_stmts.html#the-del-statement)** (i.e., \"deleted\") with the `del` statement. This does *not* delete the object to which a variable points to. It merely removes the variable's name from the \"global list of all names\"." ] }, { @@ -2257,7 +2257,7 @@ } }, "source": [ - "If we refer to an unknown name, a runtime error occurs, namely a `NameError`." + "If we refer to an unknown name, a *runtime* error occurs, namely a `NameError`. The `Name` in `NameError` gives a hint as to why we prefer the term *name* over *identifier*: Python just uses it more often in its error messages." ] }, { @@ -2293,7 +2293,7 @@ } }, "source": [ - "Some variables magically exist when we start Python. In this introductory book, we can safely ignore them." + "Some variables magically exist when we start a Python process or are added by Jupyter. We can safely ignore the former until Chapter 10 and the latter for good." ] }, { @@ -2740,7 +2740,7 @@ "source": [ "Let's change the first element of `x`.\n", "\n", - "Chapter 7 discusses lists in more depth. For now, let's just view a `list` object as some sort of **container** that holds an arbitrary number of pointers to other objects and treat brackets `[]` attached to it as just another operator, called the **indexing operator**. `x[0]` instructs Python to first follow the pointer from the \"global\" directory of all names to the `x` object. Then, it follows the first pointer it finds there to the `1` object. The indexing operator must be an operator as we merely read the first element and do not change anything in memory.\n", + "Chapter 7 discusses lists in more depth. For now, let's just view a `list` object as some sort of **container** that holds an arbitrary number of pointers to other objects and treat the brackets `[]` attached to it as just another operator, called the **indexing operator**. `x[0]` instructs Python to first follow the pointer from the global list of all names to the `x` object. Then, it follows the first pointer it finds there to the `1` object. The indexing operator must be an operator as we merely read the first element and do not change anything in memory.\n", "\n", "Note how Python **begins counting at 0**. This is not the case for many other languages, for example, [MATLAB](https://en.wikipedia.org/wiki/MATLAB), [R](https://en.wikipedia.org/wiki/R_%28programming_language%29), or [Stata](https://en.wikipedia.org/wiki/Stata). To understand why this makes sense, see this short [note](https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html) by one of the all-time greats in computer science, the late [Edsger Dijkstra](https://en.wikipedia.org/wiki/Edsger_W._Dijkstra)." ] @@ -2777,7 +2777,7 @@ } }, "source": [ - "To change the first entry in the list, we use the assignment statement `=` again. Here, this does actually *not* create a *new* variable (or overwrite an existing one) but only changes the object to which the first pointer in the `x` list points to. As we only change parts of the `x` object, we say that we **mutate** (i.e., change) its **state**." + "To change the first entry in the list, we use the assignment statement `=` again. Here, this does actually *not* create a *new* variable (or overwrite an existing one) but only changes the object to which the first pointer in the `x` list points to. As we only change parts of the `x` object, we say that we **mutate** (i.e., \"change\") its **state**. To use the bag analogy from above, we keep the same bag but \"flip\" some of the $0$s into $1$s and some of the $1$s into $0$s." ] }, { @@ -2890,9 +2890,9 @@ } }, "source": [ - "**[Phil Karlton](https://skeptics.stackexchange.com/questions/19836/has-phil-karlton-ever-said-there-are-only-two-hard-things-in-computer-science)** famously noted (during his time at [Netscape](https://en.wikipedia.org/wiki/Netscape)):\n", + "[Phil Karlton](https://skeptics.stackexchange.com/questions/19836/has-phil-karlton-ever-said-there-are-only-two-hard-things-in-computer-science) famously noted during his time at [Netscape](https://en.wikipedia.org/wiki/Netscape):\n", "\n", - "> \"There are two hard problems in computer science: naming things and cache invalidation ... and off-by-one errors.\"" + "> \"There are *two* hard problems in computer science: *naming things* and *cache invalidation* ... and *off-by-one* errors.\"" ] }, { @@ -3077,7 +3077,7 @@ } }, "source": [ - "Variables with leading and trailing double underscores, referred to as **dunder** in Python \"slang\", are used for important built-in variables. Do *not* use this style for custom variables!" + "Variables with leading and trailing double underscores, referred to as **dunder** in Python jargon, are used for important built-in functionalities. Do *not* use this style for custom variables unless you know exactly what you are doing!" ] }, { @@ -3123,7 +3123,7 @@ } }, "source": [ - "This PyCon talk by [Ned Batchelder](https://nedbatchelder.com/) (a software engineer at [edX](https://www.edx.org/) and the organizer of the [Python User Group](https://www.meetup.com/bostonpython/) in Boston) summarizes all situations where some sort of variable assignment is done in Python. The content is intermediate and therefore it is ok if you do not understand everything at this point. However, the contents should be known by everyone claiming to be a Pythonista." + "This PyCon talk by [Ned Batchelder](https://nedbatchelder.com/), a well-known Pythonista and the organizer of the [Python User Group](https://www.meetup.com/bostonpython/) in Boston, summarizes all situations where some sort of variable assignment is done in Python. The content is intermediate and therefore it is ok if you do not understand everything at this point. However, the contents should be known by everyone claiming to be proficient in Python." ] }, { @@ -3150,7 +3150,7 @@ " " ], "text/plain": [ - "" + "" ] }, "execution_count": 93, diff --git a/01_elements_of_a_program_review_and_exercises.ipynb b/01_elements_review_and_exercises.ipynb similarity index 100% rename from 01_elements_of_a_program_review_and_exercises.ipynb rename to 01_elements_review_and_exercises.ipynb diff --git a/02_functions.ipynb b/02_functions.ipynb index 95646a8..2c284f9 100644 --- a/02_functions.ipynb +++ b/02_functions.ipynb @@ -142,7 +142,7 @@ { "data": { "text/plain": [ - "140620654002656" + "139655430681056" ] }, "execution_count": 3, @@ -635,7 +635,7 @@ "source": [ "Code gets even more confusing when variables by the same name from different scopes collide. In particular, what should we expect to happen if a function changes a globally defined variable internally?\n", "\n", - "`average_odds()` below works like `average_evens()` above except that it **[casts](https://en.wikipedia.org/wiki/Type_conversion)** (i.e., \"converts\") the elements of `numbers` as objects of type `int` with the [int()](https://docs.python.org/3/library/functions.html#int) built-in first before filtering and averaging them. In doing so, it introduces an *internal* variable `nums` whose name collides with the one in the global scope." + "`average_odds()` below works like `average_evens()` above except that it **[casts](https://en.wikipedia.org/wiki/Type_conversion)** (i.e., \"converts\") the elements of `numbers` as objects of type `int` with the [int()](https://docs.python.org/3/library/functions.html#int) built-in first before filtering and averaging them. In doing so, it introduces an *internal* variable `nums` whose name collides with the one in the global scope. The **inequality operator** `!=` is just the **reversed** version of `==`." ] }, { @@ -658,7 +658,7 @@ " float: average\n", " \"\"\"\n", " nums = [int(n) for n in numbers] # cast all numbers as integers first\n", - " odds = [n for n in nums if n % 2 != 0] # before filtering them\n", + " odds = [n for n in nums if n % 2 != 0] # before filtering for odd numbers\n", " average = sum(odds) / len(odds)\n", " return average" ] @@ -2036,7 +2036,7 @@ { "data": { "text/plain": [ - "140620734513624" + "139655519584648" ] }, "execution_count": 58, @@ -2649,7 +2649,7 @@ { "data": { "text/plain": [ - "0.4028601552264015" + "0.12717011866176486" ] }, "execution_count": 75, @@ -2684,7 +2684,7 @@ { "data": { "text/plain": [ - ">" + ">" ] }, "execution_count": 76, From 4e2b329fd1bf78e86efeadfd7a95281bf9bf5015 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 24 Sep 2019 19:45:56 +0200 Subject: [PATCH 020/242] Add initial version of notebook 03 --- 03_conditionals.ipynb | 1805 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1805 insertions(+) create mode 100644 03_conditionals.ipynb diff --git a/03_conditionals.ipynb b/03_conditionals.ipynb new file mode 100644 index 0000000..acd509d --- /dev/null +++ b/03_conditionals.ipynb @@ -0,0 +1,1805 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 3: Conditionals & Exceptions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We analyzed every aspect of the `average_evens()` function in Chapter 2 except for the `if` part. While it seems to intuitively do what we expect it to, there is a whole lot more to be learned from taking it apart. In particular, the `if` can occur within both a **statement** as in our introductory example in Chapter 1 but also an **expression** as in `average_evens()`. This is analogous as to how a noun in a natural language is *either* the subject of *or* an object in a sentence. What is common to both versions of the `if` is that it leads to code being executed for *parts* of the input only. It is our first way of **controlling** the **flow of execution** in a program.\n", + "\n", + "After deconstructing `if` in the first part of this chapter, we take a close look at a similar concept, namely handling and raising **exceptions**." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Boolean Expressions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Any expression that is either true or not is called a **boolean expression**. If you think such expressions are boring or just not so useful, read a bit on [propositional logic](https://en.wikipedia.org/wiki/Propositional_calculus) and you will quickly realize how mathematicians and originally philosophers base their rules of how to prove or disprove a conclusion on simple true-or-false \"statements\" about the world. It is the underlying principle of all of reasoning.\n", + "\n", + "A trivial example involves the equality operator `==` that evaluates to either `True` or `False` depending on its operands \"comparing equal\" or not." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 == 42" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 == 123" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Observe how `==` can handle objects of *different* type. This shows how it implements a notion of equality in line with how we humans think of things being equal or not. After all, `42` and `42.0` are totally different $0$s and $1$s for a computer and many programming languages would actually say `False` here! Technically, this is yet another example of operator overloading." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 == 42.0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "There are, however, cases where even well-behaved Python does not make us happy. Chapter 5 will provide more insights on that." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 == 42.000000000000001" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`True` and `False` are special built-in *objects* of type `bool`." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "94709180875744" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(True)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "94709180875712" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(False)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "bool" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(True)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "bool" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(False)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's not confuse the boolean `False` with `None`, another special built-in object! We saw the latter before in Chapter 2 as the *implicit* return value of a function without a `return` statement.\n", + "\n", + "We might think of `None` in a boolean context indicating a \"maybe\" or even an \"unknown\" answer. But for Python, there are no \"maybe\" or \"unknown\" objects as we will see further below!\n", + "\n", + "Whereas `False` is of type `bool`, `None` is of type `NoneType`. So, they are totally unrelated. On the contrary, as both `True` and `False` are of the same type, we could call them \"siblings\"." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "None" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "94709180862704" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(None)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NoneType" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(None)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`True`, `False`, and `None` have the property that they each exist in memory only *once*. Objects designed this way are so-called **singletons**. This **[design pattern](https://en.wikipedia.org/wiki/Design_Patterns)** was originally developed to keep a program's memory usage at a minimum. It may only be employed in situations where we know that an object will never mutate its value in place (i.e., to re-use the bag analogy from Chapter 1, no flipping of $0$s and $1$s in the bag is allowed). In languages \"closer\" to the memory like C we would have to code this singleton logic ourselves but Python has this already built in for *some* types.\n", + "\n", + "We can verify this with either the `is` operator or by comparing memory addresses." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "True is True" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(True) == id(True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "So the following expression regards *four* objects in memory: *One* `list` object holding ten pointers to *three* other objects." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[True, False, None, None, None, True, False, None, None, None]" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[True, False, None, None, None, True, False, None, None, None]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Relational Operators" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The equality operator is only one of several **relational (i.e., \"comparison\") operators** who all evaluate to a boolean object." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 == 123" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 != 123 # = \"not equal to\"; other programming languages sometimes use \"<>\" instead" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The \"less than\" `<` or \"greater than\" `>` operators on their own mean \"strictly less than\" or \"strictly greater than\" but can be combined with the equality operator into just `<=` and `>=`. This is a shortcut for using the logical `or` operator as described in the next section." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 < 123" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 <= 123 # same as 42 < 123 or 42 == 123; cf., next section" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 > 123" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 >= 123 # same as 42 > 123 or 42 == 123; cf., next section" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Logical Operators" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Boolean expressions can be combined or negated with the **logical operators** `and`, `or`, and `not` to form new boolean expressions. Of course, this may be done *recursively* as well to obtain boolean expressions of arbitrary complexity.\n", + "\n", + "Their usage is similar to how the equivalent words are used in plain English:\n", + "\n", + "- `and` evaluates to `True` if *both* sub-expressions evaluate to `True` and `False` otherwise,\n", + "- `or` evaluates to `True` if either one *or* both sub-expressions evaluate to `True` and `False` otherwise, and\n", + "- `not` evaluates to `True` if its *only* sub-expression evaluates to `False` and vice versa." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "x = 42\n", + "y = 87" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Relational operators have a *higher precedence* over logical operators (cf., the [reference](https://docs.python.org/3/reference/expressions.html#operator-precedence)). So the following expression means what we intuitively think it does." + ] + }, + { + "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": [ + "x > 5 and y <= 100" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, sometimes it is good to use *parentheses* around each sub-expression for clarity." + ] + }, + { + "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": [ + "(x > 5) and (y <= 100)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This is especially useful when several logical operators are combined." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x <= 5 or not y > 100" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(x <= 5) or not (y > 100)" + ] + }, + { + "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": [ + "(x <= 5) or (not (y > 100)) # but no need to \"over do\" it" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For even better readability, [some practitioner](https://llewellynfalco.blogspot.com/2016/02/dont-use-greater-than-sign-in.html) suggest to never use the `>` and `>=` operators (note that the included example is written in [Java](https://en.wikipedia.org/wiki/Java_%28programming_language%29) and `&&` means `and` and `||` means `or`).\n", + "\n", + "Python allows **chaining** relational operators that are combined with the `and` operator. For example, the following two cells implement the same logic where the second is a lot easier to read." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(5 < x) and (x < 21)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "5 < x < 21" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Truthy vs. Falsy" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The operands of the logical operators do not actually have to be *boolean* expressions as defined above but may be *any* kind of expression. If a sub-expression does *not* evaluate to an object of type `bool`, Python automatically casts the resulting object as such.\n", + "\n", + "For example, any non-zero numeric object effectively becomes `True`. While this behavior allows writing more concise and thus \"beautiful\" code, it is also a common source of confusion." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(x - 9) and (y < 100) # = 33 and (y < 100)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Whenever we are unsure as to how Python will evaluate a non-boolean expression in a boolean context, the [bool()](https://docs.python.org/3/library/functions.html#bool) built-in allows us to check it ourselves." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bool(x - 9) # = bool(33)" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bool(x - 42) # = bool(0)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Keep in mind that negative numbers also evaluate to `True`." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bool(x - 99) # = bool(-57)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In a boolean context `None` is casted as `False`. So, `None` is really *not* a \"maybe\" answer but a \"no\"." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bool(None)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Another good rule to know is that container types (e.g., `list`) evaluate to `True` whenever they are not empty and `False` otherwise." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bool([])" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bool([False])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Pythonistas often use the terms **truthy** or **falsy** to describe a non-boolean expression's behavior when used in place of a boolean one." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Conditional Statements" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In order to write useful programs, we need to control the flow of execution, for example, to react to user input.\n", + "\n", + "One major language construct to do so is the **conditional statement** or `if` **statement** (cf., the [reference](https://docs.python.org/3/reference/compound_stmts.html#the-if-statement)). It consists of:\n", + "\n", + "- *one* mandatory `if`-clause,\n", + "- an *arbitrary* number of `elif`-clauses (i.e. \"else if\"), and\n", + "- an *optional* `else`-clause.\n", + "\n", + "The `if`- and `elif`-clauses each specify one *boolean* expression, also called **condition**, while the `else`-clause serves as a \"catch everything else\" case.\n", + "\n", + "In terms of syntax, the header lines end with a colon and the code blocks are indented.\n", + "\n", + "In contrast to our intuitive interpretation in natural languages, only the code in *one* of the alternatives, also called **branches**, is executed. To be precise, it is always the code in the first branch whose condition evaluates to `True`." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "z = 101" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z is positive but odd\n" + ] + } + ], + "source": [ + "if (z % 2 == 0) and (z > 0):\n", + " print(\"z is even and positive\")\n", + "elif z % 2 == 0:\n", + " print(\"z is even but negative\")\n", + "elif z > 0:\n", + " print(\"z is positive but odd\")\n", + "else:\n", + " print(\"z is neither even nor positive\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In many situations, we only need a reduced form of the `if` statement.\n", + "\n", + "We could **inject** code only at random to, for example, implement some sort of [A/B testing](https://en.wikipedia.org/wiki/A/B_testing)." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import random" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "You will read this just as often as you see heads when tossing a coin\n" + ] + } + ], + "source": [ + "if random.random() > 0.5:\n", + " print(\"You will read this just as often as you see heads when tossing a coin\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "More often than not, we might model a binary choice." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z is positive\n" + ] + } + ], + "source": [ + "if z > 0:\n", + " print(\"z is positive\")\n", + "else:\n", + " print(\"z is negative\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We may **nest** `if` statements to control the flow of execution in a more granular way. Every additional layer, however, makes the code less readable, in particular, if we have more than one line per code block." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z is odd\n" + ] + } + ], + "source": [ + "if random.random() > 0.5:\n", + " if z % 2: # no need to write out the \"== 0\"\n", + " print(\"z is odd\")\n", + " else:\n", + " print(\"z is even\")\n", + "else:\n", + " if z > 0:\n", + " print(\"z is positive\")\n", + " else:\n", + " print(\"z is negative\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A good way to make this code more readable is to introduce **temporary variables** *in combination* with using the `and` operator to **flatten** the branching logic. The `if` statement then reads almost like plain English. In contrast to many other languages, creating variables is a computationally *cheap* operation in Python and also helps to document the code *inline*. Without temporary variables, the `and` flattening could actually lead to more sub-expressions in the conditions be evaluated than necessary. Do you see why?" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "z is positive\n" + ] + } + ], + "source": [ + "check_oddness = (random.random() > 0.5)\n", + "is_odd = (z % 2)\n", + "is_positive = (z > 0)\n", + "\n", + "if check_oddness and is_odd:\n", + " print(\"z is odd\")\n", + "elif check_oddness and not is_odd:\n", + " print(\"z is even\")\n", + "elif not check_oddness and is_positive:\n", + " print(\"z is positive\")\n", + "else:\n", + " print(\"z is negative\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Conditional Expressions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When all we do with an `if` statement is to assign an object to a variable with respect to a single true-or-false condition (cf., binary choice above), there is a shortcut for that: We could simply assign the result of a so-called **conditional expression** or `if` expression to the variable.\n", + "\n", + "Think of a situation where we evaluate a piece-wise functional relationship $y = f(x)$ at a given $x$, for example:" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "$\n", + "y = f(x) =\n", + "\\begin{cases}\n", + "0, \\text{ if } x \\le 0 \\\\\n", + "x^2, \\text{ otherwise}\n", + "\\end{cases}\n", + "$" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "x = 3" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Of course, we could use an `if` statement as above to do the job. Yet, this is rather lengthy." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "if x <= 0:\n", + " y = 0\n", + "else:\n", + " y = x ** 2" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "9" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "y" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "On the contrary, the `if` expression fits into one line. The main downside here is a potential loss in readability, in particular, if the functional relationship is not that simple." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "y = 0 if x <= 0 else x ** 2" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "9" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "y" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In this concrete example, however, the most elegant solution would be to use the built-in [max()](https://docs.python.org/3/library/functions.html#max) function." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "y = max(0, x) ** 2" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "9" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "y" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Conditional expressions may not only be used in the way described in this section. We already saw them as part of a list comprehension that is introduced in Chapter 7." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Exceptions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In the previous two chapters we already encountered a couple of *runtime* errors. A natural urge we might have after reading about conditional statements is to write code that somehow reacts to the occurence of such exceptions. All we need for that is a way to formulate a condition for that.\n", + "\n", + "For sure, this is such a common thing to do that Python provides its own language construct for it, namely the `try` statement (cf., the [reference](https://docs.python.org/3/reference/compound_stmts.html#the-try-statement)).\n", + "\n", + "In its simplest form, it comes with just two branches: `try` and `except`. The following basically tells Python to execute the code in the `try`-branch and if *anything* goes wrong, continue in the `except`-branch instead of **raising** an error to us. Of course, if nothing goes wrong, the `except`-branch is *not* executed." + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "user_input = 0" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Something went wrong\n" + ] + } + ], + "source": [ + "try:\n", + " 1 / user_input\n", + "except:\n", + " print(\"Something went wrong\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, it is good practise to *not* **handle** *any* possible exception but only the ones we may expect from the code in the `try`-branch. The reasoning why this is done is a bit involved. We only remark here that the code base becomes easier to understand as we clearly communicate to any human reader what could go wrong during execution. Python comes with a lot of [built-in exceptions](https://docs.python.org/3/library/exceptions.html#concrete-exceptions) that we should familiarize ourselves with.\n", + "\n", + "Another good practise is to always keep the code in the `try`-branch short so as to not accidently handle an exception we do not want to handle.\n", + "\n", + "In the example, we are dividing numbers and may therefore expect a `ZeroDivisionError`." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Something went wrong\n" + ] + } + ], + "source": [ + "try:\n", + " 1 / user_input\n", + "except ZeroDivisionError:\n", + " print(\"Something went wrong\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Often, we must have some code run independent of an exception occuring (e.g., to close a connection to a database). To achieve that, we can add an optional `finally`-branch to the `try` statement.\n", + "\n", + "Similarly, we might have some code that must be run exactly when no exception occurs but we do not want to put it in the `try`-branch as per the good practice mentioned. To achieve that, we can add an optional `else`-branch to the `try` statement.\n", + "\n", + "To showcase everything together, we look at one last example. To spice it up a bit, we randomize the input. So run the cell several times and see for yourself. It's actually quite easy." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Yes, division worked smoothly.\n", + "I am always printed\n" + ] + } + ], + "source": [ + "divisor = random.choice([0, 1])\n", + "\n", + "try:\n", + " 1 / divisor\n", + "except ZeroDivisionError:\n", + " print(\"Oops. Division by 0. How does that work?\")\n", + "else:\n", + " print(\"Yes, division worked smoothly.\")\n", + "finally:\n", + " print(\"I am always printed\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## TL;DR" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "- **boolean expressions** evaluate either to `True` or `False`\n", + "- **relational operators** compare operands according to \"human\" interpretations\n", + "- **logical operators** combine boolean sub-expressions to more \"complex\" expressions\n", + "- the **conditional statement** is a *major* concept to **control** 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.7.3" + }, + "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": 2 +} From e1845113cf5ebd1d80b4e0934f8cad8afd292fce Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 24 Sep 2019 19:46:44 +0200 Subject: [PATCH 021/242] Adjust content overview in README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b664a32..951bf75 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +**Important**: The notebooks are being added throughout the fall semester of 2019! + # An Introduction to Python and Programming The purpose of this repository is to serve as an interactive "book" for a @@ -13,8 +15,9 @@ science professionals and researchers. As such they can be viewed in a plain web browser: - [00 - Start up](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb) -- [01 - Elements of a Program](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_of_a_program.ipynb) +- [01 - Elements of a Program](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) - [02 - Functions & Modularization](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) +- [03 - Conditionals & Exceptions](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb) However, it is recommended that students **install Python and Jupyter locally** and run the code in the notebooks on their own. From 0b067ec4b54fdad8d7db20775688a2c96cb382f6 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 25 Sep 2019 09:08:31 +0200 Subject: [PATCH 022/242] Add review and exercises for notebook 03 --- 03_conditionals_review_and_exercises.ipynb | 389 +++++++++++++++++++++ 1 file changed, 389 insertions(+) create mode 100644 03_conditionals_review_and_exercises.ipynb diff --git a/03_conditionals_review_and_exercises.ipynb b/03_conditionals_review_and_exercises.ipynb new file mode 100644 index 0000000..f60bc2b --- /dev/null +++ b/03_conditionals_review_and_exercises.ipynb @@ -0,0 +1,389 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "# Chapter 3: Conditionals & Exceptions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Content Review" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read Chapter 3 of the book. Then work through the seven review questions." + ] + }, + { + "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**: What is the **singleton** design pattern? How many objects does the expression `[True, False, True, False]` generate in memory?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: What do we mean when we talk about **truthy** and **falsy** expressions?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: Why is the use of **temporary variables** encouraged in Python?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: What does the `finally`-branch enforce in this code snippet? How can a `try` statement be useful *without* an `except`-branch?" + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + "**Q6**: 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: The `try` statement is useful for handling **syntax** errors." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Coding Exercises" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Discounting Customer Orders" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8.1**: 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": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8.2**: 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": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8.3**: Re-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": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " (your observation)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8.4**: Looking at the `if`-`else`-logic in the function, why do you think the four example line items in **Q8.2** were chosen as they were?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fizz Buzz revisited" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When you worked on the Fizz Buzz exercise in Chapter 1, you actually did not know about the `elif` and `else` keywords yet. Well, now you do." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "numbers = list(range(1, 101))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: Copy and paste your answer to **Q11.2** in Chapter 1 here and instead of three consecutive `if` statements re-write it with *one* compound `if` statement.\n", + "\n", + "This code will then be a lot more robust as the order of the three `if` statements cannot be screwed up." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\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.7.3" + }, + "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": 2 +} From 84e08d06f49e57d8bed6b4630bb2e56067d421c4 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 25 Sep 2019 14:27:03 +0200 Subject: [PATCH 023/242] Move FizzBuzz exercise from chapter 01 to 03 --- 01_elements_review_and_exercises.ipynb | 72 ------------------ 03_conditionals_review_and_exercises.ipynb | 88 +++++++++++++++------- 2 files changed, 61 insertions(+), 99 deletions(-) diff --git a/01_elements_review_and_exercises.ipynb b/01_elements_review_and_exercises.ipynb index 2207ccc..7e43b77 100644 --- a/01_elements_review_and_exercises.ipynb +++ b/01_elements_review_and_exercises.ipynb @@ -275,78 +275,6 @@ "for number in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:\n", " print(...)" ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Fizz Buzz" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The kids game [Fizz Buzz](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 actually is ([source](https://news.ycombinator.com/item?id=16446774)).\n", - "\n", - "In its simplest form, a group of people start 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.\n", - "\n", - "With just Chapter 1, we actually do not yet know all of Python's language constructs we need to write an implementation of Fizz Buzz in a Pythonic way. Yet, we will tweak what we know a bit and make it work." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q11.1**: 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()](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()](https://docs.python.org/3/library/functions.html#func-range) a `list` object, you have to \"wrap\" it with the [list()](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": [ - "**Q11.2**: 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 `=`. In the chapter we saw that Python starts indexing with `0` as the first element. So in each iteration of the `for`-loop you have to determine the *index* as well as checking the actual `number`.\n", - "\n", - "Also note that for numbers divisible by both $3$ and $5$ we need some sort of a \"third\" condition check: As we only know about the `if` statement so far (and have not heard about `elif` and `else` from Chapter 3), there will be three `if` statements in total within the loop. And the order of them matters!\n", - "\n", - "Hint: Is there a single condition that checks for both $3$ and $5$?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for number in numbers:\n", - " ..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q11.3**: Create a loop that prints out either the number or any of the Fizz Buzz substitutes. Do it in such a way that we do not end up with 100 lines of output here." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for number in numbers:\n", - " ..." - ] } ], "metadata": { diff --git a/03_conditionals_review_and_exercises.ipynb b/03_conditionals_review_and_exercises.ipynb index f60bc2b..f152b30 100644 --- a/03_conditionals_review_and_exercises.ipynb +++ b/03_conditionals_review_and_exercises.ipynb @@ -211,11 +211,6 @@ "\n", "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "\n", "\n" ] }, @@ -235,28 +230,36 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "discounted_price(...)" + ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "discounted_price(...)" + ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "discounted_price(...)" + ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "discounted_price(...)" + ] }, { "cell_type": "markdown", @@ -270,14 +273,18 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "discounted_price(...)" + ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "discounted_price(...)" + ] }, { "cell_type": "markdown", @@ -304,14 +311,23 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Fizz Buzz revisited" + "### Fizz Buzz" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "When you worked on the Fizz Buzz exercise in Chapter 1, you actually did not know about the `elif` and `else` keywords yet. Well, now you do." + "The kids game [Fizz Buzz](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 actually is ([source](https://news.ycombinator.com/item?id=16446774)).\n", + "\n", + "In its simplest form, a group of people start 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": [ + "**Q9.1**: 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()](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()](https://docs.python.org/3/library/functions.html#func-range) a `list` object, you have to wrap it with the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in (i.e., `list(range(...))`)." ] }, { @@ -320,16 +336,20 @@ "metadata": {}, "outputs": [], "source": [ - "numbers = list(range(1, 101))" + "numbers = ..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "**Q9**: Copy and paste your answer to **Q11.2** in Chapter 1 here and instead of three consecutive `if` statements re-write it with *one* compound `if` statement.\n", + "**Q9.2**: 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", - "This code will then be a lot more robust as the order of the three `if` statements cannot be screwed up." + "In Chapter 1 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 checking the actual `number` for its divisors.\n", + "\n", + "Hint: the order of the conditions is important!" ] }, { @@ -338,17 +358,31 @@ "metadata": {}, "outputs": [], "source": [ - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n" + "for number in numbers:\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9.3**: Create a loop that prints out either the number or any of the Fizz Buzz substitutes. Do it in such a way that we do not end up with 100 lines of output here." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in numbers:\n", + " print(...)" ] } ], From 250aa092093402f89f5ef564b56134d18cfb7a7f Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 1 Oct 2019 17:47:45 +0200 Subject: [PATCH 024/242] Streamline previous content --- 00_start_up_review_and_exercises.ipynb | 2 +- 01_elements.ipynb | 143 +++++++++++++-------- 01_elements_review_and_exercises.ipynb | 2 +- 02_functions.ipynb | 96 ++++++++++---- 02_functions_review_and_exercises.ipynb | 2 +- 03_conditionals.ipynb | 38 +++--- 03_conditionals_review_and_exercises.ipynb | 2 +- 7 files changed, 181 insertions(+), 104 deletions(-) diff --git a/00_start_up_review_and_exercises.ipynb b/00_start_up_review_and_exercises.ipynb index e1a435f..2f4c334 100644 --- a/00_start_up_review_and_exercises.ipynb +++ b/00_start_up_review_and_exercises.ipynb @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Read Chapter 0 of the book. Then work through the ten review questions." + "Read [Chapter 0](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb) of the book. Then work through the ten review questions." ] }, { diff --git a/01_elements.ipynb b/01_elements.ipynb index e1d8b09..382d757 100644 --- a/01_elements.ipynb +++ b/01_elements.ipynb @@ -24,7 +24,7 @@ "\n", "It is intuitively best to take the very mindset of a small child when learing a foreign language and we do so for learning the Python language as well. This first chapter introduces simplistic examples and we better just accept them as they are without knowing any of the \"grammar\" rules yet. Then, we analyze them in parts and slowly build up our understanding.\n", "\n", - "Consequently, if parts of this chapter do not make sense right away, let's not worry too much. Besides introducing some basics (that we need to understand), it also serves as an outlook for what is to come. So, many terms and concepts referenced here will be covered in great detail in following chapters." + "Consequently, if parts of this chapter do not make sense right away, let's not worry too much. Besides introducing some basics that we need to understand, it also serves as an outlook for what is to come. So, many terms and concepts used here will be covered in great detail in following chapters." ] }, { @@ -46,7 +46,7 @@ } }, "source": [ - "As our introductory example, we want to calculate the average of all even numbers from one through ten.\n", + "As our introductory example, we want to calculate the *average* of all *even* numbers from *one* through *ten*.\n", "\n", "While we could come up with an [analytical solution](https://math.stackexchange.com/questions/935405/what-s-the-difference-between-analytical-and-numerical-approaches-to-problems/935446#935446) (i.e., derive some equation with \"pen and paper\" from, e.g., one of [Faulhaber's formulas](https://en.wikipedia.org/wiki/Faulhaber%27s_formula)), we instead solve the task programmatically.\n", "\n", @@ -75,7 +75,7 @@ } }, "source": [ - "To verify that something happened in our computer's memory, we simply **reference** `numbers` and observe that Python indeed knows about." + "To verify that something happened in our computer's memory, we simply **reference** `numbers` and observe that Python indeed knows about it." ] }, { @@ -110,17 +110,17 @@ } }, "source": [ - "So far, so good. Let's see how the desired result could be expressed as a **sequence of instructions** in Python.\n", + "So far, so good. Let's see how the desired **computation** could be expressed as a **sequence of instructions** in Python.\n", "\n", "Intuitively, the line `for number in numbers` describes a \"loop\" over all the numbers in the `numbers` list, one at a time.\n", "\n", "The `if number % 2 == 0` may look disturbing at first sight. Both the `%` and `==` must have an unintuitive meaning here. Luckily, the **comment** in the same line after the `#` symbol has the answer: The program only does something if the current `number` is even.\n", "\n", - "In particular, it increases `count` by $1$ and adds the current `number` onto the [running](https://en.wikipedia.org/wiki/Running_total) `total`. Both `count` and `number` were initially set to $0$ and the single `=` reads as \"... is *set* equal to ...\". It could not indicate a mathematical equation as, for example, `count` is generally not equal to `count + 1`.\n", + "In particular, it increases `count` by $1$ and adds the current `number` onto the [running](https://en.wikipedia.org/wiki/Running_total) `total`. Both `count` and `number` were initially set to $0$ and the single `=` symbol reads as \"... is *set* equal to ...\". It could not indicate a mathematical equation as, for example, `count` is generally not equal to `count + 1`.\n", "\n", "Lastly, the `average` is calculated as the ratio of the final **values** of `total` and `count`. Overall, we divide the sum of all even numbers by the count of all even numbers, which is exactly what we are looking for.\n", "\n", - "We also observe how the lines of code \"within\" the `for` and `if` **statements** are *indented* and *aligned* with multiples of **four spaces**: This shows immediately how the lines relate to each other." + "We also observe how the lines of code \"within\" the `for` and `if` **statements** are **indented** and *aligned* with multiples of **four spaces**: This shows immediately how the lines relate to each other." ] }, { @@ -200,7 +200,7 @@ "source": [ "Note how only two of the previous four code cells generate an **output** while two remained \"silent\" (i.e., there is no \"**Out[...]**\" after running the cell).\n", "\n", - "By default, Jupyter notebooks show the value of a cell's last so-called **expression**. This output can be suppressed by ending the last line with a semicolon `;`." + "By default, Jupyter notebooks show the value of a cell's last **expression**. This output can be suppressed by ending the last line with a semicolon `;`." ] }, { @@ -249,7 +249,7 @@ } }, "source": [ - "To visualize something before the end of the cell, we can use the [print()](https://docs.python.org/3/library/functions.html#print) built-in **function**." + "To visualize something before the end of the cell, we use the [print()](https://docs.python.org/3/library/functions.html#print) built-in **function**." ] }, { @@ -327,11 +327,13 @@ } }, "source": [ - "Python comes with basic mathematical operators built in. **[Operators](https://docs.python.org/3/reference/lexical_analysis.html#operators)** are built-in **tokens** that have a special meaning to the Python interpreter. Most operators either \"operate\" with the object immediately following them (= **unary** operators; e.g., negation) or somehow \"process\" the two objects \"around\" them (= **binary** operators; e.g., addition). But we will see some exceptions from that as well.\n", + "Python comes with many **[operators](https://docs.python.org/3/reference/lexical_analysis.html#operators)** built in: They are **tokens** (i.e., \"symbols\") that have a special meaning to the Python interpreter.\n", "\n", - "By definition, operators have **no** permanent **side effects** in the computer's memory. Although the code cells in this section do indeed lead to *new* objects being created in memory, they are immediately \"forgotten\" as they are not stored in a **variable** (like `numbers` above). We will revisit this idea further below when we compare **expressions** with **statements**.\n", + "The arithmetic operators either \"operate\" with the number immediately following them (= **unary** operators; e.g., negation) or \"process\" the two numbers \"around\" them (= **binary** operators; e.g., addition). But we will see many exceptions from that as well.\n", "\n", - "Let's see some examples of operators. We start with the binary `+` and the `-` operators for addition and subtraction. Binary operators resemble what mathematicians call [infix notation](https://en.wikipedia.org/wiki/Infix_notation) and have the expected meaning." + "By definition, operators have **no** permanent **side effects** in the computer's memory. Although the code cells in this section do indeed create *new* numbers in memory (e.g., `77 + 13` creates `90`), they are immediately \"forgotten\" as they are not stored in a **variable** like `numbers` or `average` above. We will continue this thought further below when we compare **expressions** with **statements**.\n", + "\n", + "Let's see some examples of operators. We start with the binary `+` and the `-` operators for addition and subtraction. Binary operators are designed to resemble what mathematicians call [infix notation](https://en.wikipedia.org/wiki/Infix_notation) and have the expected meaning." ] }, { @@ -425,7 +427,7 @@ } }, "source": [ - "When we compare the output of the `*` and `/` operators for multiplication and division, we note the subtle difference between the $42$ and the $42.0$. This is a first illustration of the concept of a **data type**." + "When we compare the output of the `*` and `/` operators for multiplication and division, we note the subtle *difference* between the $42$ and the $42.0$. This is a first illustration of the concept of a **data type**." ] }, { @@ -543,7 +545,7 @@ } }, "source": [ - "To obtain the remainder of a division, we can use the **modulo operator** `%`." + "To obtain the remainder of a division, we use the **modulo operator** `%`." ] }, { @@ -613,7 +615,7 @@ } }, "source": [ - "Modulo division can be useful if we, for example, need to get the last couple of digits of a large integer." + "Modulo division is also useful if we, for example, need to get the last couple of digits of a large integer." ] }, { @@ -672,7 +674,7 @@ } }, "source": [ - "The [divmod()](https://docs.python.org/3/library/functions.html#divmod) built-in function combines the integer and modulo divisions into one operation. However, this is not an operator any more (but a function). Also observe that [divmod()](https://docs.python.org/3/library/functions.html#divmod) returns a \"pair\" of integers." + "The [divmod()](https://docs.python.org/3/library/functions.html#divmod) built-in function combines the integer and modulo divisions into one operation. However, this is not an operator but a function. Also observe that [divmod()](https://docs.python.org/3/library/functions.html#divmod) returns a \"pair\" of integers." ] }, { @@ -707,7 +709,7 @@ } }, "source": [ - "Raising a number to a power is performed with the **exponentiation operator** `**`. Note that this is different from the `^` operator many other programming languages might use and that also exists in Python with a *different* meaning." + "Raising a number to a power is performed with the **exponentiation operator** `**`. This is different from the `^` operator many other programming languages use and that also exists in Python with a *different* meaning." ] }, { @@ -742,7 +744,7 @@ } }, "source": [ - "The normal [order of precedence](https://docs.python.org/3/reference/expressions.html#operator-precedence) from mathematics applies (i.e., \"PEMDAS\" rule) but parentheses help avoid confusion." + "The normal [order of precedence](https://docs.python.org/3/reference/expressions.html#operator-precedence) from mathematics applies (i.e., \"PEMDAS\" rule)." ] }, { @@ -777,7 +779,7 @@ } }, "source": [ - "The parentheses here serve as a **delimiter**." + "Parentheses help avoid confusion and take the role of a **delimiter** here." ] }, { @@ -871,7 +873,7 @@ } }, "source": [ - "There are plenty more mathematical and non-mathematical operators that are introduced throughout this book together with the concepts they implement or support. Some of these are already shown in the next section." + "There are many more non-mathematical operators that are introduced throughout this book together with the concepts they implement. Some of these are already shown in the next section." ] }, { @@ -949,7 +951,7 @@ { "data": { "text/plain": [ - "139940106427216" + "140658972730512" ] }, "execution_count": 27, @@ -973,7 +975,7 @@ { "data": { "text/plain": [ - "139940106602344" + "140658972907392" ] }, "execution_count": 28, @@ -997,7 +999,7 @@ { "data": { "text/plain": [ - "139940105769456" + "140658972586992" ] }, "execution_count": 29, @@ -1017,7 +1019,7 @@ } }, "source": [ - "These addresses are really not that meaningful for anything other than checking if two variables actually **point** at the same object. This may be helpful as different objects can of course have the same value." + "These addresses are really not that meaningful for anything other than checking if two variables actually **point** at the same object. This may be helpful as, for example, different objects in memory may of course have the same value." ] }, { @@ -1041,7 +1043,7 @@ } }, "source": [ - "`a` and `d` indeed have the same value as can be checked with the **equality operator** `==`. The resulting `True` (and the `False` below) is yet another data type, a so-called **boolean**. We will look into that closely in Chapter 3." + "`a` and `d` indeed have the same value as can be checked with the **equality operator** `==`. The resulting `True` (and the `False` below) is yet another data type, a so-called **boolean**. We will look into that closely in [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb)." ] }, { @@ -1780,7 +1782,7 @@ " " ], "text/plain": [ - "" + "" ] }, "execution_count": 50, @@ -1801,7 +1803,7 @@ } }, "source": [ - "For example, while the above code to calculate the average of the even numbers from 1 through 10 is correct, a Pythonista would re-write it in a more \"Pythonic\" way and use the [sum()](https://docs.python.org/3/library/functions.html#sum) and [len()](https://docs.python.org/3/library/functions.html#len) (= \"length\") built-in functions (cf., Chapter 2) as well as a so-called **list comprehension** (cf., Chapter 7). Pythonic code runs faster in many cases and is less error prone." + "For example, while the above code to calculate the average of the even numbers from 1 through 10 is correct, a Pythonista would re-write it in a more \"Pythonic\" way and use the [sum()](https://docs.python.org/3/library/functions.html#sum) and [len()](https://docs.python.org/3/library/functions.html#len) (= \"length\") built-in functions (cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb)) as well as a so-called **list comprehension** (cf., Chapter 7). Pythonic code runs faster in many cases and is less error prone." ] }, { @@ -2003,7 +2005,7 @@ "\n", "At the same time, for a beginner's course it is often easier to just code in a linear fashion.\n", "\n", - "In real data science projects one would probably employ a mixed approach and put re-usable code into so-called Python modules (i.e., *.py* files; cf., Chapter 2) and then use Jupyter notebooks to built up a linear report or story line for a business argument to be made." + "In real data science projects one would probably employ a mixed approach and put re-usable code into so-called Python modules (i.e., *.py* files; cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb)) and then use Jupyter notebooks to built up a linear report or story line for a business argument to be made." ] }, { @@ -2868,7 +2870,7 @@ "\n", "In general, the assignment statement creates (or overwrites) a variable and makes it point to whatever object is on the right-hand side *only if* the left-hand side is a *pure* name (i.e., it contains no operators like the indexing operator in the example). Otherwise, it mutates some already existing object. And we always have to expect that the latter might have more than one variable pointing at it.\n", "\n", - "In the beginning, visualizing the memory with a tool like [PythonTutor](http://pythontutor.com/visualize.html#code=x%20%3D%20%5B1,%202,%203%5D%0Ay%20%3D%20x%0Ax%5B0%5D%20%3D%2099%0Adel%20x,%20y%0Ax%20%3D%20%5B1,%202,%203%5D%0Ay%20%3D%20x.copy%28%29%0Ax%5B0%5D%20%3D%2099&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) will assist in understanding what is going on." + "Visualizing what is going on in the memory with a tool like [PythonTutor](http://pythontutor.com/visualize.html#code=x%20%3D%20%5B1,%202,%203%5D%0Ay%20%3D%20x%0Ax%5B0%5D%20%3D%2099%0Aprint%28y%5B0%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) might be helpful for a beginner." ] }, { @@ -2903,7 +2905,7 @@ } }, "source": [ - "Variable names may contain upper and lower case letters, numbers, and underscores (\"\\_\") and be as long as we want them to be. However, they must not begin with a number. Also, they must not be any of Python's **[keywords](https://docs.python.org/3/reference/lexical_analysis.html#keywords)**.\n", + "Variable names may contain upper and lower case letters, numbers, and underscores (\"\\_\") and be as long as we want them to be. However, they must not begin with a number. Also, they must not be any of Python's built-in **[keywords](https://docs.python.org/3/reference/lexical_analysis.html#keywords)**.\n", "\n", "Variable names are usually chosen such that they do not need any more documentation and are self-explanatory. A very common convention is to use so-called **[snake\\_case](https://en.wikipedia.org/wiki/Snake_case)**: Keep everything lowercase and use underscores to seperate words.\n", "\n", @@ -3150,7 +3152,7 @@ " " ], "text/plain": [ - "" + "" ] }, "execution_count": 93, @@ -3188,7 +3190,7 @@ "\n", "What we said about individual operators above, namely that they have *no* side effects, should have been put here to begin with. The examples in the section on operators above were actually all expressions!\n", "\n", - "The simplest possible expression contains only one variable (or literal)." + "The simplest possible expressions contain only one variable or literal." ] }, { @@ -3218,6 +3220,33 @@ { "cell_type": "code", "execution_count": 95, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 95, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For sure, we need to include operators to achieve something useful." + ] + }, + { + "cell_type": "code", + "execution_count": 96, "metadata": { "slideshow": { "slide_type": "-" @@ -3230,7 +3259,7 @@ "165" ] }, - "execution_count": 95, + "execution_count": 96, "metadata": {}, "output_type": "execute_result" } @@ -3247,12 +3276,12 @@ } }, "source": [ - "The definition of an expression is **recursive**. So here the sub-expression `a + b` is combined with the literal `3` by the operator `**` to form the full expression." + "The definition of an expression is **recursive**. So, here the sub-expression `a + b` is combined with the literal `3` by the operator `**` to form the full expression." ] }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 97, "metadata": { "slideshow": { "slide_type": "-" @@ -3265,7 +3294,7 @@ "4492125" ] }, - "execution_count": 96, + "execution_count": 97, "metadata": {}, "output_type": "execute_result" } @@ -3282,12 +3311,12 @@ } }, "source": [ - "Here, the variable `y` is combined with the literal `2` by the indexing operator `[]`. The resulting expression evaluates to the " + "Here, the variable `y` is combined with the literal `2` by the indexing operator `[]`. The resulting expression evaluates to the third element in the `y` list." ] }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 98, "metadata": { "slideshow": { "slide_type": "-" @@ -3300,7 +3329,7 @@ "3" ] }, - "execution_count": 97, + "execution_count": 98, "metadata": {}, "output_type": "execute_result" } @@ -3317,12 +3346,12 @@ } }, "source": [ - "When not used as a **delimiter**, parentheses also constitute an operator, namely the **call operator** `()`. We have seen this syntax above when we \"called\" (i.e., executed) built-in functions and methods." + "When not used as a delimiter, parentheses also constitute an operator, namely the **call operator** `()`. We have seen this syntax above when we \"called\" (i.e., executed) built-in functions and methods." ] }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 99, "metadata": { "slideshow": { "slide_type": "-" @@ -3335,7 +3364,7 @@ "104" ] }, - "execution_count": 98, + "execution_count": 99, "metadata": {}, "output_type": "execute_result" } @@ -3368,7 +3397,7 @@ }, { "cell_type": "code", - "execution_count": 99, + "execution_count": 100, "metadata": { "slideshow": { "slide_type": "slide" @@ -3382,7 +3411,7 @@ }, { "cell_type": "code", - "execution_count": 100, + "execution_count": 101, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3395,7 +3424,7 @@ "'Hi class'" ] }, - "execution_count": 100, + "execution_count": 101, "metadata": {}, "output_type": "execute_result" } @@ -3417,7 +3446,7 @@ }, { "cell_type": "code", - "execution_count": 101, + "execution_count": 102, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3430,7 +3459,7 @@ "'Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi '" ] }, - "execution_count": 101, + "execution_count": 102, "metadata": {}, "output_type": "execute_result" } @@ -3458,14 +3487,14 @@ } }, "source": [ - "A **[statement](https://docs.python.org/3/reference/simple_stmts.html)** is anything that changes the state of the program's memory or has some other side effect. Statements do not just evaluate to a value like expressions; instead, they create or change values.\n", + "A **[statement](https://docs.python.org/3/reference/simple_stmts.html)** is anything that changes the state of a program or has some other side effect. Statements do not just evaluate to a value like expressions; instead, they create or change values.\n", "\n", "Most notably of course are the `=` and `del` statements." ] }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 103, "metadata": { "slideshow": { "slide_type": "slide" @@ -3478,7 +3507,7 @@ }, { "cell_type": "code", - "execution_count": 103, + "execution_count": 104, "metadata": { "slideshow": { "slide_type": "-" @@ -3497,12 +3526,12 @@ } }, "source": [ - "The [print()](https://docs.python.org/3/library/functions.html#print) function is regarded a \"statement\" as well. In fact, it used to be a real statement in Python 2 and has all the necessary properties. It is a bit of a corner case as expressions are also \"printed\" in a Jupyter notebook when evaluated last in a code cell." + "The built-in [print()](https://docs.python.org/3/library/functions.html#print) function is regarded a \"statement\" as well. In fact, it used to be a real statement in Python 2 and has all the necessary properties. It is a bit of a corner case as expressions are also \"printed\" in a Jupyter notebook when evaluated last in a code cell." ] }, { "cell_type": "code", - "execution_count": 104, + "execution_count": 105, "metadata": { "slideshow": { "slide_type": "skip" @@ -3549,7 +3578,7 @@ }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 106, "metadata": { "slideshow": { "slide_type": "slide" @@ -3577,7 +3606,7 @@ }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 107, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3590,7 +3619,7 @@ }, { "cell_type": "code", - "execution_count": 107, + "execution_count": 108, "metadata": { "slideshow": { "slide_type": "-" @@ -3690,14 +3719,14 @@ " - ignored by Python\n", "\n", "\n", - "- functions (cf., Chapter 2)\n", + "- functions (cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.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", "\n", "\n", - "- flow control (cf., Chapter 3)\n", - " - expression of **logic** or an **algorithm**\n", + "- flow control (cf., [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb))\n", + " - expression of **business logic** or an **algorithm**\n", " - conditional execution of a small **branch** within a program (i.e., `if` statements)\n", " - repetitive execution of parts of a program (i.e., `for`-loops and `while`-loops)" ] diff --git a/01_elements_review_and_exercises.ipynb b/01_elements_review_and_exercises.ipynb index 7e43b77..8940f24 100644 --- a/01_elements_review_and_exercises.ipynb +++ b/01_elements_review_and_exercises.ipynb @@ -19,7 +19,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Read Chapter 1 of the book. Then work through the ten review questions." + "Read [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) of the book. Then work through the ten review questions." ] }, { diff --git a/02_functions.ipynb b/02_functions.ipynb index 2c284f9..681838a 100644 --- a/02_functions.ipynb +++ b/02_functions.ipynb @@ -19,7 +19,7 @@ } }, "source": [ - "In Chapter 1 we typed the **business logic** of our little program to calculate the mean of a subset of a list of numbers right into the code cells. Then, we executed them one after another. We had no way of **re-using** the code except for either re-executing the cells or copying and pasting their contents into other cells. And whenever we find ourselves doing repetitive manual work, we can be sure that there must be a way of automating what we are doing.\n", + "In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) we typed the **business logic** of our little program to calculate the mean of a subset of a list of numbers right into the code cells. Then, we executed them one after another. We had no way of **re-using** the code except for either re-executing the cells or copying and pasting their contents into other cells. And whenever we find ourselves doing repetitive manual work, we can be sure that there must be a way of automating what we are doing.\n", "\n", "At the same time, we executed built-in functions (e.g., [print()](https://docs.python.org/3/library/functions.html#print), [sum()](https://docs.python.org/3/library/functions.html#sum), [len()](https://docs.python.org/3/library/functions.html#len), [id()](https://docs.python.org/3/library/functions.html#id), or [type()](https://docs.python.org/3/library/functions.html#type)) that obviously must be re-using the same parts inside core Python every time we use them.\n", "\n", @@ -45,7 +45,7 @@ } }, "source": [ - "So-called **[user-defined functions](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)** can be created with the `def` statement. To extend an already familiar example, we re-use the introductory example from Chapter 1 in its final Pythonic version and transform it into the function `average_evens()` below. \n", + "So-called **[user-defined functions](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)** can be created with the `def` statement. To extend an already familiar example, we re-use the introductory example from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) in its final Pythonic version and transform it into the function `average_evens()` below. \n", "\n", "A function's **name** must be chosen according to the same naming rules as for ordinary variables. In fact, Python manages function names just like variables. In this book, we further adopt the convention of ending function names with parentheses \"`()`\" in text cells for faster comprehension when reading (i.e., `average_evens()` vs. `average_evens`). These are not actually part of the name but must always be written out in the `def` statement for syntactic reasons.\n", "\n", @@ -142,7 +142,7 @@ { "data": { "text/plain": [ - "139655430681056" + "140693945143776" ] }, "execution_count": 3, @@ -289,7 +289,7 @@ } }, "source": [ - "Once defined we can **call** (i.e., \"execute\") a function with the **call operator** `()`. The formal parameters are filled in by passing variables or expressions as **arguments** to the function within the parentheses." + "We can **call** (i.e., \"execute\") a function with the **call operator** `()` as often as we wish. The formal parameters are filled in by passing variables or expressions as **arguments** to the function within the parentheses." ] }, { @@ -407,7 +407,7 @@ } }, "source": [ - "Notice how the parameters listed in a function's definition (i.e., `numbers`) and variables created inside it during execution (i.e., `evens` and `average`) are **local** to that function. That means they are only mapped to an object in memory while the function is being executed and de-referenced immediately when the function returns. We say they **go out of scope** once the function terminates." + "Notice how the parameters listed in a function's definition (i.e., `numbers`) and variables created inside it during execution (i.e., `evens` and `average`) are **local** to that function. That means they only point to an object in memory *while* the function is being executed and de-referenced immediately when the function returns. We say they **go out of scope** once the function terminates." ] }, { @@ -485,6 +485,17 @@ "average" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010%5D%0A%0Adef%20average_evens%28numbers%29%3A%0A%20%20%20%20evens%20%3D%20%5Bn%20for%20n%20in%20numbers%20if%20n%20%25%202%20%3D%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28evens%29%20/%20len%28evens%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_evens%28nums%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) visualizes what happens in memory: To be precise, in the exact moment when the function call is initiated and `nums` is passed in as the `numbers` argument, there are *two* pointers to the *same* `list` object (cf., steps 4-5 in the visualization). We also see how Python creates a *new* **frame** that holds the function's local scope (i.e., \"internal names\") in addition to the **global** frame. Frames are nothing but [namespaces](https://en.wikipedia.org/wiki/Namespace) to *isolate* the names of different **scopes** from each other. The list comprehension `[n for n in numbers if n % 2 == 0]` constitutes yet another frame that is in scope as the `list` object assigned to `evens` is *being* created (cf., steps 6-18). When the function returns, only the global frame is left (cf., steps 21-22)." + ] + }, { "cell_type": "markdown", "metadata": { @@ -504,7 +515,7 @@ } }, "source": [ - "On the contrary, while a function is being executed, it can \"see\" the variables of the **enclosing scope** (i.e., \"outside\" of it). This is a common source of *semantic* errors. Consider the following stylized (and incorrect) example `average_wrong()`. The error is hard to spot with eyes: The function never references the `numbers` parameter but the `nums` variable in the **global scope** instead." + "On the contrary, while a function is being executed, it can reference the variables of **enclosing scopes** (i.e., \"outside\" of it). This is a common source of *semantic* errors. Consider the following stylized (and incorrect) example `average_wrong()`. The error is hard to spot with eyes: The function never references the `numbers` parameter but the `nums` variable in the **global scope** instead." ] }, { @@ -526,11 +537,22 @@ " Returns:\n", " float: average\n", " \"\"\"\n", - " evens = [n for n in nums if n % 2 == 0] # should reference numbers, not nums\n", + " evens = [n for n in nums if n % 2 == 0] # should reference numbers not nums\n", " average = sum(evens) / len(evens)\n", " return average" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`nums` in the global scope is of course unchanged." + ] + }, { "cell_type": "code", "execution_count": 16, @@ -555,6 +577,17 @@ "nums" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Sometimes a function might return a correct solution for *some* inputs ..." + ] + }, { "cell_type": "code", "execution_count": 17, @@ -576,7 +609,18 @@ } ], "source": [ - "average_wrong(nums) # the result is correct by accident!" + "average_wrong(nums) # this is correct by accident" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... but still be wrong *in general*." ] }, { @@ -611,7 +655,9 @@ } }, "source": [ - "Also, observe how both `average_evens()` and `average_wrong()` use the same names for their respective parameters and variables internally. For sure, Python is smart enough to not mix them up. This is because each function call creates a temporary **[namespace](https://en.wikipedia.org/wiki/Namespace)** that *isolates* the local scope's names for usage only from within the function. As we saw in the [Zen of Python](https://www.python.org/dev/peps/pep-0020/), \"namespaces are one honking great idea\" (cf., `import this`)." + "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010%5D%0A%0Adef%20average_wrong%28numbers%29%3A%0A%20%20%20%20evens%20%3D%20%5Bn%20for%20n%20in%20nums%20if%20n%20%25%202%20%3D%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28evens%29%20/%20len%28evens%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_wrong%28%5B123,%20456,%20789%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) is again helpful at visualizing the error interactively: Creating the `list` object `evens` eventually points to takes *12* computational steps, namely one for setting up an empty `list` object, *ten* for filling it with elements derived from `nums` in the global scope, and one to make `evens` point at it (cf., steps 6-18).\n", + "\n", + "The frames logic shown by PythonTutor is the mechanism by which Python not only manages the names inside one function call but for potentially many calls occuring simultaneously as we will see in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb). It is the reason why we may re-use the same names for the parameters and variables inside both `average_evens()` and `average_wrong()` without Python mixing them up. So, as we already read in the [Zen of Python](https://www.python.org/dev/peps/pep-0020/), \"namespaces are one honking great idea\" (cf., `import this`) and a frame is just a special kind of namespace." ] }, { @@ -633,9 +679,9 @@ } }, "source": [ - "Code gets even more confusing when variables by the same name from different scopes collide. In particular, what should we expect to happen if a function changes a globally defined variable internally?\n", + "Code gets even more confusing when variables by the *same* name from *different* scopes collide. In particular, what should we expect to happen if a function \"changes\" a globally defined variable internally?\n", "\n", - "`average_odds()` below works like `average_evens()` above except that it **[casts](https://en.wikipedia.org/wiki/Type_conversion)** (i.e., \"converts\") the elements of `numbers` as objects of type `int` with the [int()](https://docs.python.org/3/library/functions.html#int) built-in first before filtering and averaging them. In doing so, it introduces an *internal* variable `nums` whose name collides with the one in the global scope. The **inequality operator** `!=` is just the **reversed** version of `==`." + "`average_odds()` below works like `average_evens()` above except that it **[casts](https://en.wikipedia.org/wiki/Type_conversion)** (i.e., \"converts\") the elements of `numbers` as objects of type `int` with the [int()](https://docs.python.org/3/library/functions.html#int) built-in first before filtering and averaging them. In doing so, it introduces an *internal* variable `nums` whose name collides with the one in the global scope. To filter for odd numbers, we use the **inequality operator** `!=` that is just the **reversed** version of `==`." ] }, { @@ -652,7 +698,7 @@ " \"\"\"Calculate the average of all odd numbers in a list.\n", "\n", " Args:\n", - " numbers (list): a list of numbers; must be integers\n", + " numbers (list): a list of numbers; must be integer-like\n", "\n", " Returns:\n", " float: average\n", @@ -671,7 +717,7 @@ } }, "source": [ - "`nums` in the global scope is of course the same list from above." + "`nums` in the global scope is still unchanged." ] }, { @@ -706,7 +752,7 @@ } }, "source": [ - "As good practice, let's first use inputs for which we can calculate the answer in our heads to verify that `average_odds()` is correct." + "As good practice, let's first use inputs for which we can calculate the answer in our heads to \"verify\" that `average_odds()` is \"correct\"." ] }, { @@ -730,7 +776,7 @@ } ], "source": [ - "average_odds([1, 100, 3, 100, 5]) # verify the function's correctness with predictable inputs" + "average_odds([1.0, 10.0, 3.0, 10.0, 5.0]) # verify correctness with predictable inputs" ] }, { @@ -776,7 +822,7 @@ } }, "source": [ - "Python, however, is again smart enough to keep the two `nums` variables apart. So the global `nums` is still pointing to the very same list object as before." + "Python, however, is again smart enough to keep all the involved `nums` variables apart. So the global `nums` is still pointing to the very same `list` object as before." ] }, { @@ -811,7 +857,9 @@ } }, "source": [ - "The reason why everything works just fine is that *every time* we (re-)assign an object to a variable inside a function with the `=` statement, this is done in the local scope by default. There are ways to change variables existing in an outer scope from within a function but we save that for a later chapter.\n", + "The reason why everything just works is that *every time* we (re-)assign an object to a variable *inside* a function with the `=` statement, this is done in the *local* scope by default. There are ways to change variables existing in an outer scope from within a function but this is a rather advanced topic on its own.\n", + "\n", + "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010%5D%0A%0Adef%20average_odds%28numbers%29%3A%0A%20%20%20%20nums%20%3D%20%5Bint%28n%29%20for%20n%20in%20numbers%5D%0A%20%20%20%20odds%20%3D%20%5Bn%20for%20n%20in%20nums%20if%20n%20%25%202%20!%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28odds%29%20/%20len%28odds%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_odds%28%5B1.0,%2010.0,%203.0,%2010.0,%205.0%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how *two* `nums` variables exist in *different* scopes pointing to *different* objects (cf., steps 14-25) when we execute `average_odds([1.0, 10.0, 3.0, 10.0, 5.0])`.\n", "\n", "Variables whose names collide with the ones of variables in enclosing scopes - and the global scope is just the most enclosing scope - are said to **shadow** them.\n", "\n", @@ -1122,7 +1170,7 @@ } }, "source": [ - "So far we have only specified one parameter in each of our user-defined functions. In Chapter 1, however, we saw the built-in function [divmod()](https://docs.python.org/3/library/functions.html#divmod) taking two arguments. Obviously, the order of the numbers passed in mattered. Whenever we call a function and list its arguments in a comma seperated manner, we say that we pass in the arguments by position or refer to them as **positional arguments**." + "So far we have only specified one parameter in each of our user-defined functions. In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb), however, we saw the built-in function [divmod()](https://docs.python.org/3/library/functions.html#divmod) taking two arguments. Obviously, the order of the numbers passed in mattered. Whenever we call a function and list its arguments in a comma seperated manner, we say that we pass in the arguments by position or refer to them as **positional arguments**." ] }, { @@ -2036,7 +2084,7 @@ { "data": { "text/plain": [ - "139655519584648" + "140694050824664" ] }, "execution_count": 58, @@ -2308,7 +2356,7 @@ "source": [ "Observe how the arguments passed to functions do not need to be just variables or simple literals. Instead, we can pass in any *expression* that evaluates to a *new* object of the type the function expects.\n", "\n", - "So just as a reminder from the expression vs. statement discussion in Chapter 1: An expression is *any* syntactically correct combination of variables and literals with operators. And the call operator `()` is just ... well another operator. So both of the next two code cells are just expressions! They have no permanent side effect in memory. We can execute them as often as we want *without* changing the state of the program (i.e., this Jupyter notebook).\n", + "So just as a reminder from the expression vs. statement discussion in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb): An expression is *any* syntactically correct combination of variables and literals with operators. And the call operator `()` is just ... well another operator. So both of the next two code cells are just expressions! They have no permanent side effect in memory. We can execute them as often as we want *without* changing the state of the program (i.e., this Jupyter notebook).\n", "\n", "So, regarding the very next cell in particular: Although the `2 ** 2` creates a *new* object `4` in memory that is then immediately passed into the [math.sqrt()](https://docs.python.org/3/library/math.html#math.sqrt) function, once that function call returns, \"all is lost\" and the newly created `4` object is forgotten again, as well as the return value of [math.sqrt()](https://docs.python.org/3/library/math.html#math.sqrt)." ] @@ -2649,7 +2697,7 @@ { "data": { "text/plain": [ - "0.12717011866176486" + "0.15268128055183228" ] }, "execution_count": 75, @@ -2684,7 +2732,7 @@ { "data": { "text/plain": [ - ">" + ">" ] }, "execution_count": 76, @@ -2733,7 +2781,7 @@ { "data": { "text/plain": [ - "8" + "2" ] }, "execution_count": 78, @@ -2881,7 +2929,7 @@ "source": [ "[numpy](http://www.numpy.org/) is the de-facto standard in the Python world for handling **array-like** data. That is a fancy word for data that can be put into a matrix or vector format. We will look at it in depth in Chapter 9.\n", "\n", - "As [numpy](http://www.numpy.org/) is *not* in the [standard library](https://docs.python.org/3/library/index.html), it must be *manually* installed, for example, with the [pip](https://pip.pypa.io/en/stable/) tool. As mentioned in Chapter 0, to execute terminal commands from within a Jupyter notebook, we just need to start a code cell with an exclamation mark.\n", + "As [numpy](http://www.numpy.org/) is *not* in the [standard library](https://docs.python.org/3/library/index.html), it must be *manually* installed, for example, with the [pip](https://pip.pypa.io/en/stable/) tool. As mentioned in [Chapter 0](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb), to execute terminal commands from within a Jupyter notebook, we just need to start a code cell with an exclamation mark.\n", "\n", "If you are running this notebook with an installation of the [Anaconda Distribution](https://www.anaconda.com/distribution/), then [numpy](http://www.numpy.org/) is probably already installed. Running the cell below, will just confirm that." ] diff --git a/02_functions_review_and_exercises.ipynb b/02_functions_review_and_exercises.ipynb index 647e47f..dcd0a9c 100644 --- a/02_functions_review_and_exercises.ipynb +++ b/02_functions_review_and_exercises.ipynb @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Read Chapter 2 of the book. Then work through the ten review questions." + "Read [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) of the book. Then work through the ten review questions." ] }, { diff --git a/03_conditionals.ipynb b/03_conditionals.ipynb index acd509d..6d70f07 100644 --- a/03_conditionals.ipynb +++ b/03_conditionals.ipynb @@ -19,7 +19,7 @@ } }, "source": [ - "We analyzed every aspect of the `average_evens()` function in Chapter 2 except for the `if` part. While it seems to intuitively do what we expect it to, there is a whole lot more to be learned from taking it apart. In particular, the `if` can occur within both a **statement** as in our introductory example in Chapter 1 but also an **expression** as in `average_evens()`. This is analogous as to how a noun in a natural language is *either* the subject of *or* an object in a sentence. What is common to both versions of the `if` is that it leads to code being executed for *parts* of the input only. It is our first way of **controlling** the **flow of execution** in a program.\n", + "We analyzed every aspect of the `average_evens()` function in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) except for the `if` part. While it seems to intuitively do what we expect it to, there is a whole lot more to be learned from taking it apart. In particular, the `if` can occur within both a **statement** as in our introductory example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) but also an **expression** as in `average_evens()`. This is analogous as to how a noun in a natural language is *either* the subject of *or* an object in a sentence. What is common to both versions of the `if` is that it leads to code being executed for *parts* of the input only. It is our first way of **controlling** the **flow of execution** in a program.\n", "\n", "After deconstructing `if` in the first part of this chapter, we take a close look at a similar concept, namely handling and raising **exceptions**." ] @@ -139,7 +139,7 @@ } }, "source": [ - "There are, however, cases where even well-behaved Python does not make us happy. Chapter 5 will provide more insights on that." + "There are, however, cases where even well-behaved Python does not make us happy. Chapter 5 will provide more insights on this \"bug\"." ] }, { @@ -189,7 +189,7 @@ { "data": { "text/plain": [ - "94709180875744" + "94163040564192" ] }, "execution_count": 5, @@ -213,7 +213,7 @@ { "data": { "text/plain": [ - "94709180875712" + "94163040564160" ] }, "execution_count": 6, @@ -281,9 +281,9 @@ } }, "source": [ - "Let's not confuse the boolean `False` with `None`, another special built-in object! We saw the latter before in Chapter 2 as the *implicit* return value of a function without a `return` statement.\n", + "Let's not confuse the boolean `False` with `None`, another special built-in object! We saw the latter before in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) as the *implicit* return value of a function without a `return` statement.\n", "\n", - "We might think of `None` in a boolean context indicating a \"maybe\" or even an \"unknown\" answer. But for Python, there are no \"maybe\" or \"unknown\" objects as we will see further below!\n", + "We might think of `None` in a boolean context indicating a \"maybe\" or even an \"unknown\" answer; however, for Python, there are no \"maybe\" or \"unknown\" objects as we will see further below!\n", "\n", "Whereas `False` is of type `bool`, `None` is of type `NoneType`. So, they are totally unrelated. On the contrary, as both `True` and `False` are of the same type, we could call them \"siblings\"." ] @@ -313,7 +313,7 @@ { "data": { "text/plain": [ - "94709180862704" + "94163040551152" ] }, "execution_count": 10, @@ -357,7 +357,7 @@ } }, "source": [ - "`True`, `False`, and `None` have the property that they each exist in memory only *once*. Objects designed this way are so-called **singletons**. This **[design pattern](https://en.wikipedia.org/wiki/Design_Patterns)** was originally developed to keep a program's memory usage at a minimum. It may only be employed in situations where we know that an object will never mutate its value in place (i.e., to re-use the bag analogy from Chapter 1, no flipping of $0$s and $1$s in the bag is allowed). In languages \"closer\" to the memory like C we would have to code this singleton logic ourselves but Python has this already built in for *some* types.\n", + "`True`, `False`, and `None` have the property that they each exist in memory only *once*. Objects designed this way are so-called **singletons**. This **[design pattern](https://en.wikipedia.org/wiki/Design_Patterns)** was originally developed to keep a program's memory usage at a minimum. It may only be employed in situations where we know that an object will *not* mutate its value (i.e., to re-use the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb), no flipping of $0$s and $1$s in the bag is allowed). In languages \"closer\" to the memory like C we would have to code this singleton logic ourselves but Python has this already built in for *some* types.\n", "\n", "We can verify this with either the `is` operator or by comparing memory addresses." ] @@ -512,7 +512,7 @@ } ], "source": [ - "42 != 123 # = \"not equal to\"; other programming languages sometimes use \"<>\" instead" + "42 != 123 # = \"not equal to\"; other languages may use \"<>\"" ] }, { @@ -672,7 +672,7 @@ } }, "source": [ - "Relational operators have a *higher precedence* over logical operators (cf., the [reference](https://docs.python.org/3/reference/expressions.html#operator-precedence)). So the following expression means what we intuitively think it does." + "Relational operators have a **[higher precedence](https://docs.python.org/3/reference/expressions.html#operator-precedence)** over logical operators. So the following expression means what we intuitively think it does." ] }, { @@ -825,7 +825,7 @@ } }, "source": [ - "For even better readability, [some practitioner](https://llewellynfalco.blogspot.com/2016/02/dont-use-greater-than-sign-in.html) suggest to never use the `>` and `>=` operators (note that the included example is written in [Java](https://en.wikipedia.org/wiki/Java_%28programming_language%29) and `&&` means `and` and `||` means `or`).\n", + "For even better readability, [some practitioners](https://llewellynfalco.blogspot.com/2016/02/dont-use-greater-than-sign-in.html) suggest to *never* use the `>` and `>=` operators (note that the included example is written in [Java](https://en.wikipedia.org/wiki/Java_%28programming_language%29) and `&&` means `and` and `||` means `or`).\n", "\n", "Python allows **chaining** relational operators that are combined with the `and` operator. For example, the following two cells implement the same logic where the second is a lot easier to read." ] @@ -899,7 +899,7 @@ "source": [ "The operands of the logical operators do not actually have to be *boolean* expressions as defined above but may be *any* kind of expression. If a sub-expression does *not* evaluate to an object of type `bool`, Python automatically casts the resulting object as such.\n", "\n", - "For example, any non-zero numeric object effectively becomes `True`. While this behavior allows writing more concise and thus \"beautiful\" code, it is also a common source of confusion." + "For example, any non-zero numeric object becomes `True`. While this behavior allows writing conciser and thus more \"beautiful\" code, it is also a common source of confusion." ] }, { @@ -1028,7 +1028,7 @@ } }, "source": [ - "In a boolean context `None` is casted as `False`. So, `None` is really *not* a \"maybe\" answer but a \"no\"." + "In a boolean context, `None` is casted as `False`! So, `None` is really *not* a \"maybe\" answer but a \"no\"." ] }, { @@ -1144,9 +1144,9 @@ } }, "source": [ - "In order to write useful programs, we need to control the flow of execution, for example, to react to user input.\n", + "In order to write useful programs, we need to control the flow of execution, for example, to react to user input. The logic by which a program does that is referred to as **business logic**.\n", "\n", - "One major language construct to do so is the **conditional statement** or `if` **statement** (cf., the [reference](https://docs.python.org/3/reference/compound_stmts.html#the-if-statement)). It consists of:\n", + "One major language construct to do so is the **[conditional statement](https://docs.python.org/3/reference/compound_stmts.html#the-if-statement)** or `if` statement. It consists of:\n", "\n", "- *one* mandatory `if`-clause,\n", "- an *arbitrary* number of `elif`-clauses (i.e. \"else if\"), and\n", @@ -1350,7 +1350,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "z is positive\n" + "z is odd\n" ] } ], @@ -1602,7 +1602,7 @@ "source": [ "In the previous two chapters we already encountered a couple of *runtime* errors. A natural urge we might have after reading about conditional statements is to write code that somehow reacts to the occurence of such exceptions. All we need for that is a way to formulate a condition for that.\n", "\n", - "For sure, this is such a common thing to do that Python provides its own language construct for it, namely the `try` statement (cf., the [reference](https://docs.python.org/3/reference/compound_stmts.html#the-try-statement)).\n", + "For sure, this is such a common thing to do that Python provides its own language construct for it, namely the `try` [statement](https://docs.python.org/3/reference/compound_stmts.html#the-try-statement).\n", "\n", "In its simplest form, it comes with just two branches: `try` and `except`. The following basically tells Python to execute the code in the `try`-branch and if *anything* goes wrong, continue in the `except`-branch instead of **raising** an error to us. Of course, if nothing goes wrong, the `except`-branch is *not* executed." ] @@ -1711,7 +1711,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Yes, division worked smoothly.\n", + "Oops. Division by 0. How does that work?\n", "I am always printed\n" ] } @@ -1748,7 +1748,7 @@ } }, "source": [ - "- **boolean expressions** evaluate either to `True` or `False`\n", + "- **boolean expressions** evaluate to either `True` or `False`\n", "- **relational operators** compare operands according to \"human\" interpretations\n", "- **logical operators** combine boolean sub-expressions to more \"complex\" expressions\n", "- the **conditional statement** is a *major* concept to **control** the **flow of execution** depending on some **conditions**\n", diff --git a/03_conditionals_review_and_exercises.ipynb b/03_conditionals_review_and_exercises.ipynb index f152b30..d39c551 100644 --- a/03_conditionals_review_and_exercises.ipynb +++ b/03_conditionals_review_and_exercises.ipynb @@ -19,7 +19,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Read Chapter 3 of the book. Then work through the seven review questions." + "Read [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb) of the book. Then work through the seven review questions." ] }, { From a91aea1f3685b2cb118c36fbe450a6f5febc1c2b Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 1 Oct 2019 17:48:22 +0200 Subject: [PATCH 025/242] Add initial version of notebook 04 --- 04_iteration.ipynb | 6529 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 6529 insertions(+) create mode 100644 04_iteration.ipynb diff --git a/04_iteration.ipynb b/04_iteration.ipynb new file mode 100644 index 0000000..755b4a5 --- /dev/null +++ b/04_iteration.ipynb @@ -0,0 +1,6529 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 4: Iteration" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While controlling the flow of execution with an `if` statement is definitely a must-have building block in any programming language, it alone does not allow us to run a block of code repetitively and we need to be able to do exactly that in order to write useful software.\n", + "\n", + "You might think that the `for` statement shown in some examples before is the missing piece in the puzzle. However, we can actually live without it and postpone its official introduction until the second half of this chapter.\n", + "\n", + "Instead, we dive into the big idea of **iteration** by studying the concept of **recursion** first. This is quite the opposite of many introductory books on programming that only treat the latter as a nice-to-have artifact, if at all. Yet, understanding recursion sharpens one's mind and contributes to seeing problems from a different angle." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Recursion" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A function that calls itself is **recursive** and the process of executing such a function it is called **recursion**.\n", + "\n", + "Recursive functions contain some form of a conditional check (e.g., `if` statement) to identify a **base case** that ends the recursion *when* reached. Otherwise, the function would keep calling itself forever.\n", + "\n", + "The meaning of the word *recursive* is similar to *circular*. However, a truly circular definition is not very helpful and we think of a recursive function as kind of a \"circular function with a way out at the end\"." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Trivial Example: Countdown" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A rather trivial toy example illustrates the important aspects concretely: If called with any positive integer as its `n` argument, `countdown()` just prints that number and calls itself with the *new* `n` being the *old* `n` minus `1`. This continues until `countdown()` is called with `n=0`. Then, the flow of execution hits the base case and the function calls stop." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def countdown(n):\n", + " \"\"\"Print a countdown until the party starts.\n", + "\n", + " Args:\n", + " n (int): seconds until the party begins\n", + " \"\"\"\n", + " if n == 0: # base case\n", + " print(\"Happy new Year!\")\n", + " else:\n", + " print(n)\n", + " countdown(n - 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3\n", + "2\n", + "1\n", + "Happy new Year!\n" + ] + } + ], + "source": [ + "countdown(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As trivial as this seems at first sight, a lot of complexity is hidden in this implementation. In particular, the order in which objects are created and de-referenced in memory might not be obvious right away as [PythonTutor](http://pythontutor.com/visualize.html#code=def%20countdown%28n%29%3A%0A%20%20%20%20if%20n%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20print%28%22Happy%20new%20Year!%22%29%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20print%28n%29%0A%20%20%20%20%20%20%20%20countdown%28n%20-%201%29%0A%0Acountdown%283%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows: Each time `countdown()` is called, Python creates a *new* frame in the part of the memory where it manages all the names. This way Python *isolates* all the different `n` variables from each other. As new frames are created until we reach the base case after which the frames are destroyed in the *reversed* order, this is called a **[stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type))** of frames in computer science terminology. In simple words, a stack is a last-in-first-out (LIFO) task queue. Each frame has a single parent frame, namely the one whose recursive function call created it." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Recursion in Mathematics" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Recursion plays an important role in mathematics as well and we likely know about it from some introductory course, for example, in [combinatorics](https://en.wikipedia.org/wiki/Combinatorics)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Easy Example: [Factorial](https://en.wikipedia.org/wiki/Factorial)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The factorial function, denoted with the symbol $!$, is defined as follows for all non-negative integers:" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "$$0! = 1$$\n", + "$$n! = n*(n-1)!$$" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Whenever we can find a recursive way of formulating an idea, we can immediately translate it into Python in a *naive* way (i.e., we create a *correct* program that may *not* be an *efficient* implementation yet).\n", + "\n", + "Below is a first version of `factorial()`: The `return` statement does not have to be a function's last code line and we can certainly have several `return` statements as well." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def factorial(n):\n", + " \"\"\"Calculate the factorial of a number.\n", + "\n", + " Args:\n", + " n (int): number to calculate the factorial for\n", + "\n", + " Returns:\n", + " factorial (int)\n", + " \"\"\"\n", + " if n == 0:\n", + " return 1\n", + " else:\n", + " recurse = factorial(n - 1)\n", + " result = n * recurse\n", + " return result" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When we read such code, it is often easier not to follow every function call (i.e., `factorial(n - 1)` here) in one's mind but assume we receive a return value as specified in the documentation. Some call this approach **[leap of faith](http://greenteapress.com/thinkpython2/html/thinkpython2007.html#sec75)**. In fact, we practice this anyways whenever we call built-in functions (e.g., [print()](https://docs.python.org/3/library/functions.html#print) or [len()](https://docs.python.org/3/library/functions.html#len)) where we would have to read C code in many cases.\n", + "\n", + "To visualize *all* the computational steps of the examplary `factorial(3)`, we use [PythonTutor](http://pythontutor.com/visualize.html#code=def%20factorial%28n%29%3A%0A%20%20%20%20if%20n%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20recurse%20%3D%20factorial%28n%20-%201%29%0A%20%20%20%20%20%20%20%20result%20%3D%20n%20*%20recurse%0A%20%20%20%20%20%20%20%20return%20result%0A%0Asolution%20%3D%20factorial%283%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false): The recursion again creates a stack of frames in memory. In contrast to the previous trivial example, each frame leaves a return value in memory after it is destroyed. This return value is then assigned to the `recurse` variable within the parent frame and used to compute `result`." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(3)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3628800" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A Pythonista would formulate `factorial()` in a conciser way using the so-called **early exit** pattern: No `else`-clause is needed as reaching a `return` statement immediately ends a function call. Furthermore, we do not really need the temporary variables `recurse` and `result`.\n", + "\n", + "As [PythonTutor](http://pythontutor.com/visualize.html#code=def%20factorial%28n%29%3A%0A%20%20%20%20if%20n%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20return%20n%20*%20factorial%28n%20-%201%29%0A%0Asolution%20%3D%20factorial%283%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows, this implementation is more efficient as it only requires 18 computational steps instead of 24 to calculate `factorial(3)`, an improvement of 25 percent! " + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def factorial(n):\n", + " \"\"\"Calculate the factorial of a number.\n", + "\n", + " Args:\n", + " n (int): number to calculate the factorial for\n", + "\n", + " Returns:\n", + " factorial (int)\n", + " \"\"\"\n", + " if n == 0:\n", + " return 1\n", + " return n * factorial(n - 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(3)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3628800" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Note that the [math](https://docs.python.org/3/library/math.html) module in the standard library provides a [factorial()](https://docs.python.org/3/library/math.html#math.factorial) function as well and we should therefore *never* implement it ourselves in a real code base." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import math" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on built-in function factorial in module math:\n", + "\n", + "factorial(x, /)\n", + " Find x!.\n", + " \n", + " Raise a ValueError if x is negative or non-integral.\n", + "\n" + ] + } + ], + "source": [ + "help(math.factorial)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.factorial(3)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3628800" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.factorial(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### \"Involved\" Example: [Euclid's Algorithm](https://en.wikipedia.org/wiki/Euclidean_algorithm)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As famous philosopher Euclid already shows in his \"Elements\" (ca. 300 BC), the greatest common divisor of two integers, i.e., the largest number that divides both integers without a remainder, can be efficiently computed with the following code. This example illustrates that a recursive solution to a problem is not always easy to understand." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def gcd(a, b):\n", + " \"\"\"Calculate the greatest common divisor of two numbers.\n", + "\n", + " Args:\n", + " a (int): first number\n", + " b (int): second number\n", + "\n", + " Returns:\n", + " gcd (int)\n", + " \"\"\"\n", + " if b == 0:\n", + " return a \n", + " return gcd(b, a % b)" + ] + }, + { + "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": [ + "gcd(12, 4)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Euclid's algorithm is stunningly fast, even for large numbers. Its speed comes from the use of the modulo operation `%`. However, this is *not* true for recusion in general, which can result in very slow programs if not applied correctly." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "9" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gcd(112233445566778899, 987654321)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As expected, for two [prime numbers](https://en.wikipedia.org/wiki/List_of_prime_numbers) the greatest common divisor is of course $1$." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gcd(2, 7919)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [math](https://docs.python.org/3/library/math.html) module in the standard library provides a [gcd()](https://docs.python.org/3/library/math.html#math.gcd) function as well and therefore we should again *never* implement it on our own." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on built-in function gcd in module math:\n", + "\n", + "gcd(x, y, /)\n", + " greatest common divisor of x and y\n", + "\n" + ] + } + ], + "source": [ + "help(math.gcd)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.gcd(12, 4)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "9" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.gcd(112233445566778899, 987654321)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.gcd(2, 7919)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### \"Easy at first Glance\" Example: [Fibonacci Numbers](https://en.wikipedia.org/wiki/Fibonacci_number)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The Fibonacci numbers are an infinite sequence of non-negative integers that are calculated such that every number is the sum of its two predecessors where the first two numbers of the sequence are defined to be $0$ and $1$. For example, the first 13 numbers are:" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "$0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144$" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's write a function `fibonacci()` that calculates the $i$th Fibonacci number where $0$ will be the $0$th number. Looking at the numbers in a **backwards** fashion (i.e., from right to left), we realize that the return value for `fibonacci(i)` can be reduced to the sum of the return values for `fibonacci(i - 1)` and `fibonacci(i - 2)` disregarding the *two* base cases." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def fibonacci(i):\n", + " \"\"\"Calculate the ith Fibonacci number.\n", + "\n", + " Args:\n", + " i (int): index of the Fibonacci number to calculate\n", + "\n", + " Returns:\n", + " ith_fibonacci (int)\n", + " \"\"\"\n", + " if i == 0:\n", + " return 0\n", + " elif i == 1:\n", + " return 1\n", + " return fibonacci(i - 1) + fibonacci(i - 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "144" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fibonacci(12) # = 13th number" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "##### Efficiency of Algorithms" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This implementation is *highly* **inefficient** as small Fibonacci numbers can already take a very long time to compute. The reason for this is **exponential growth** in the number of function calls. As [PythonTutor](http://pythontutor.com/visualize.html#code=def%20fibonacci%28i%29%3A%0A%20%20%20%20if%20i%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%200%0A%20%20%20%20elif%20i%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20return%20fibonacci%28i%20-%201%29%20%2B%20fibonacci%28i%20-%202%29%0A%0Arv%20%3D%20fibonacci%285%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows, `fibonacci()` is called again and again with the same `i` arguments.\n", + "\n", + "To understand this in detail, we would have to study algorithms and data structures (e.g., with [this book](https://www.amazon.de/Introduction-Algorithms-Press-Thomas-Cormen/dp/0262033844/ref=sr_1_1?__mk_de_DE=%C3%85M%C3%85%C5%BD%C3%95%C3%91&crid=1JNE8U0VZGU0O&qid=1569837169&s=gateway&sprefix=algorithms+an%2Caps%2C180&sr=8-1)), a discipline within computer science, and dive into the analysis of **[time complexity of algorithms](https://en.wikipedia.org/wiki/Time_complexity)**.\n", + "\n", + "Luckily, in the Fibonacci case the inefficiency can be resolved with a **caching** (i.e., \"re-use\") strategy from the field of **[dynamic programming](https://en.wikipedia.org/wiki/Dynamic_programming)**, namely **[memoization](https://en.wikipedia.org/wiki/Memoization)**. We will do so in Chapter 8 after introducing the [dictionaries](https://docs.python.org/3/library/stdtypes.html#dict) data type.\n", + "\n", + "Let's measure the average run times for `fibonacci()` and varying `i` arguments with the `%%timeit` [cell magic](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-timeit) that comes with Jupyter." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The slowest run took 5.01 times longer than the fastest. This could mean that an intermediate result is being cached.\n", + "55 µs ± 44.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + ] + } + ], + "source": [ + "%%timeit -n 100\n", + "fibonacci(12)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.63 ms ± 68 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + ] + } + ], + "source": [ + "%%timeit -n 100\n", + "fibonacci(20)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "192 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n 1 -r 1\n", + "fibonacci(30)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2.21 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n 1 -r 1\n", + "fibonacci(35)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5.62 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n 1 -r 1\n", + "fibonacci(37)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Infinite Recursion" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If a recursion does not reach its base case, it will theoretically run forever. Luckily, Python detects that and saves the computer from crashing by raising a `RecursionError`.\n", + "\n", + "The simplest possible infinite recursion is generated like so." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def run_forever():\n", + " \"\"\"Also a pointless function should have a docstring.\"\"\"\n", + " run_forever()" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "RecursionError", + "evalue": "maximum recursion depth exceeded", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mRecursionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mrun_forever\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mrun_forever\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mrun_forever\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\"\"\"Also a pointless function should have a docstring.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mrun_forever\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "... last 1 frames repeated, from the frame below ...\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mrun_forever\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mrun_forever\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\"\"\"Also a pointless function should have a docstring.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mrun_forever\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mRecursionError\u001b[0m: maximum recursion depth exceeded" + ] + } + ], + "source": [ + "run_forever()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, even the trivial `countdown()` function from above is not immune to an infinite recursion. Let's call it with `3.1` instead of `3`. What goes wrong here?" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.1\n", + "2.1\n", + "1.1\n", + "0.10000000000000009\n", + "-0.8999999999999999\n", + "-1.9\n", + "-2.9\n", + "-3.9\n", + "-4.9\n", + "-5.9\n", + "-6.9\n", + "-7.9\n", + "-8.9\n", + "-9.9\n", + "-10.9\n", + "-11.9\n", + "-12.9\n", + "-13.9\n", + "-14.9\n", + "-15.9\n", + "-16.9\n", + "-17.9\n", + "-18.9\n", + "-19.9\n", + "-20.9\n", + "-21.9\n", + "-22.9\n", + "-23.9\n", + "-24.9\n", + "-25.9\n", + "-26.9\n", + "-27.9\n", + "-28.9\n", + "-29.9\n", + "-30.9\n", + "-31.9\n", + "-32.9\n", + "-33.9\n", + "-34.9\n", + "-35.9\n", + "-36.9\n", + "-37.9\n", + "-38.9\n", + "-39.9\n", + "-40.9\n", + "-41.9\n", + "-42.9\n", + "-43.9\n", + "-44.9\n", + "-45.9\n", + "-46.9\n", + "-47.9\n", + "-48.9\n", + "-49.9\n", + "-50.9\n", + "-51.9\n", + "-52.9\n", + "-53.9\n", + "-54.9\n", + "-55.9\n", + "-56.9\n", + "-57.9\n", + "-58.9\n", + "-59.9\n", + "-60.9\n", + "-61.9\n", + "-62.9\n", + "-63.9\n", + "-64.9\n", + "-65.9\n", + "-66.9\n", + "-67.9\n", + "-68.9\n", + "-69.9\n", + "-70.9\n", + "-71.9\n", + "-72.9\n", + "-73.9\n", + "-74.9\n", + "-75.9\n", + "-76.9\n", + "-77.9\n", + "-78.9\n", + "-79.9\n", + "-80.9\n", + "-81.9\n", + "-82.9\n", + "-83.9\n", + "-84.9\n", + "-85.9\n", + "-86.9\n", + "-87.9\n", + "-88.9\n", + "-89.9\n", + "-90.9\n", + "-91.9\n", + "-92.9\n", + "-93.9\n", + "-94.9\n", + "-95.9\n", + "-96.9\n", + "-97.9\n", + "-98.9\n", + "-99.9\n", + "-100.9\n", + "-101.9\n", + "-102.9\n", + "-103.9\n", + "-104.9\n", + "-105.9\n", + "-106.9\n", + "-107.9\n", + "-108.9\n", + "-109.9\n", + "-110.9\n", + "-111.9\n", + "-112.9\n", + "-113.9\n", + "-114.9\n", + "-115.9\n", + "-116.9\n", + "-117.9\n", + "-118.9\n", + "-119.9\n", + "-120.9\n", + "-121.9\n", + "-122.9\n", + "-123.9\n", + "-124.9\n", + "-125.9\n", + "-126.9\n", + "-127.9\n", + "-128.9\n", + "-129.9\n", + "-130.9\n", + "-131.9\n", + "-132.9\n", + "-133.9\n", + "-134.9\n", + "-135.9\n", + "-136.9\n", + "-137.9\n", + "-138.9\n", + "-139.9\n", + "-140.9\n", + "-141.9\n", + "-142.9\n", + "-143.9\n", + "-144.9\n", + "-145.9\n", + "-146.9\n", + "-147.9\n", + "-148.9\n", + "-149.9\n", + "-150.9\n", + "-151.9\n", + "-152.9\n", + "-153.9\n", + "-154.9\n", + "-155.9\n", + "-156.9\n", + "-157.9\n", + "-158.9\n", + "-159.9\n", + "-160.9\n", + "-161.9\n", + "-162.9\n", + "-163.9\n", + "-164.9\n", + "-165.9\n", + "-166.9\n", + "-167.9\n", + "-168.9\n", + "-169.9\n", + "-170.9\n", + "-171.9\n", + "-172.9\n", + "-173.9\n", + "-174.9\n", + "-175.9\n", + "-176.9\n", + "-177.9\n", + "-178.9\n", + "-179.9\n", + "-180.9\n", + "-181.9\n", + "-182.9\n", + "-183.9\n", + "-184.9\n", + "-185.9\n", + "-186.9\n", + "-187.9\n", + "-188.9\n", + "-189.9\n", + "-190.9\n", + "-191.9\n", + "-192.9\n", + "-193.9\n", + "-194.9\n", + "-195.9\n", + "-196.9\n", + "-197.9\n", + "-198.9\n", + "-199.9\n", + "-200.9\n", + "-201.9\n", + "-202.9\n", + "-203.9\n", + "-204.9\n", + "-205.9\n", + "-206.9\n", + "-207.9\n", + "-208.9\n", + "-209.9\n", + "-210.9\n", + "-211.9\n", + "-212.9\n", + "-213.9\n", + "-214.9\n", + "-215.9\n", + "-216.9\n", + "-217.9\n", + "-218.9\n", + "-219.9\n", + "-220.9\n", + "-221.9\n", + "-222.9\n", + "-223.9\n", + "-224.9\n", + "-225.9\n", + "-226.9\n", + "-227.9\n", + "-228.9\n", + "-229.9\n", + "-230.9\n", + "-231.9\n", + "-232.9\n", + "-233.9\n", + "-234.9\n", + "-235.9\n", + "-236.9\n", + "-237.9\n", + "-238.9\n", + "-239.9\n", + "-240.9\n", + "-241.9\n", + "-242.9\n", + "-243.9\n", + "-244.9\n", + "-245.9\n", + "-246.9\n", + "-247.9\n", + "-248.9\n", + "-249.9\n", + "-250.9\n", + "-251.9\n", + "-252.9\n", + "-253.9\n", + "-254.9\n", + "-255.9\n", + "-256.9\n", + "-257.9\n", + "-258.9\n", + "-259.9\n", + "-260.9\n", + "-261.9\n", + "-262.9\n", + "-263.9\n", + "-264.9\n", + "-265.9\n", + "-266.9\n", + "-267.9\n", + "-268.9\n", + "-269.9\n", + "-270.9\n", + "-271.9\n", + "-272.9\n", + "-273.9\n", + "-274.9\n", + "-275.9\n", + "-276.9\n", + "-277.9\n", + "-278.9\n", + "-279.9\n", + "-280.9\n", + "-281.9\n", + "-282.9\n", + "-283.9\n", + "-284.9\n", + "-285.9\n", + "-286.9\n", + "-287.9\n", + "-288.9\n", + "-289.9\n", + "-290.9\n", + "-291.9\n", + "-292.9\n", + "-293.9\n", + "-294.9\n", + "-295.9\n", + "-296.9\n", + "-297.9\n", + "-298.9\n", + "-299.9\n", + "-300.9\n", + "-301.9\n", + "-302.9\n", + "-303.9\n", + "-304.9\n", + "-305.9\n", + "-306.9\n", + "-307.9\n", + "-308.9\n", + "-309.9\n", + "-310.9\n", + "-311.9\n", + "-312.9\n", + "-313.9\n", + "-314.9\n", + "-315.9\n", + "-316.9\n", + "-317.9\n", + "-318.9\n", + "-319.9\n", + "-320.9\n", + "-321.9\n", + "-322.9\n", + "-323.9\n", + "-324.9\n", + "-325.9\n", + "-326.9\n", + "-327.9\n", + "-328.9\n", + "-329.9\n", + "-330.9\n", + "-331.9\n", + "-332.9\n", + "-333.9\n", + "-334.9\n", + "-335.9\n", + "-336.9\n", + "-337.9\n", + "-338.9\n", + "-339.9\n", + "-340.9\n", + "-341.9\n", + "-342.9\n", + "-343.9\n", + "-344.9\n", + "-345.9\n", + "-346.9\n", + "-347.9\n", + "-348.9\n", + "-349.9\n", + "-350.9\n", + "-351.9\n", + "-352.9\n", + "-353.9\n", + "-354.9\n", + "-355.9\n", + "-356.9\n", + "-357.9\n", + "-358.9\n", + "-359.9\n", + "-360.9\n", + "-361.9\n", + "-362.9\n", + "-363.9\n", + "-364.9\n", + "-365.9\n", + "-366.9\n", + "-367.9\n", + "-368.9\n", + "-369.9\n", + "-370.9\n", + "-371.9\n", + "-372.9\n", + "-373.9\n", + "-374.9\n", + "-375.9\n", + "-376.9\n", + "-377.9\n", + "-378.9\n", + "-379.9\n", + "-380.9\n", + "-381.9\n", + "-382.9\n", + "-383.9\n", + "-384.9\n", + "-385.9\n", + "-386.9\n", + "-387.9\n", + "-388.9\n", + "-389.9\n", + "-390.9\n", + "-391.9\n", + "-392.9\n", + "-393.9\n", + "-394.9\n", + "-395.9\n", + "-396.9\n", + "-397.9\n", + "-398.9\n", + "-399.9\n", + "-400.9\n", + "-401.9\n", + "-402.9\n", + "-403.9\n", + "-404.9\n", + "-405.9\n", + "-406.9\n", + "-407.9\n", + "-408.9\n", + "-409.9\n", + "-410.9\n", + "-411.9\n", + "-412.9\n", + "-413.9\n", + "-414.9\n", + "-415.9\n", + "-416.9\n", + "-417.9\n", + "-418.9\n", + "-419.9\n", + "-420.9\n", + "-421.9\n", + "-422.9\n", + "-423.9\n", + "-424.9\n", + "-425.9\n", + "-426.9\n", + "-427.9\n", + "-428.9\n", + "-429.9\n", + "-430.9\n", + "-431.9\n", + "-432.9\n", + "-433.9\n", + "-434.9\n", + "-435.9\n", + "-436.9\n", + "-437.9\n", + "-438.9\n", + "-439.9\n", + "-440.9\n", + "-441.9\n", + "-442.9\n", + "-443.9\n", + "-444.9\n", + "-445.9\n", + "-446.9\n", + "-447.9\n", + "-448.9\n", + "-449.9\n", + "-450.9\n", + "-451.9\n", + "-452.9\n", + "-453.9\n", + "-454.9\n", + "-455.9\n", + "-456.9\n", + "-457.9\n", + "-458.9\n", + "-459.9\n", + "-460.9\n", + "-461.9\n", + "-462.9\n", + "-463.9\n", + "-464.9\n", + "-465.9\n", + "-466.9\n", + "-467.9\n", + "-468.9\n", + "-469.9\n", + "-470.9\n", + "-471.9\n", + "-472.9\n", + "-473.9\n", + "-474.9\n", + "-475.9\n", + "-476.9\n", + "-477.9\n", + "-478.9\n", + "-479.9\n", + "-480.9\n", + "-481.9\n", + "-482.9\n", + "-483.9\n", + "-484.9\n", + "-485.9\n", + "-486.9\n", + "-487.9\n", + "-488.9\n", + "-489.9\n", + "-490.9\n", + "-491.9\n", + "-492.9\n", + "-493.9\n", + "-494.9\n", + "-495.9\n", + "-496.9\n", + "-497.9\n", + "-498.9\n", + "-499.9\n", + "-500.9\n", + "-501.9\n", + "-502.9\n", + "-503.9\n", + "-504.9\n", + "-505.9\n", + "-506.9\n", + "-507.9\n", + "-508.9\n", + "-509.9\n", + "-510.9\n", + "-511.9\n", + "-512.9\n", + "-513.9\n", + "-514.9\n", + "-515.9\n", + "-516.9\n", + "-517.9\n", + "-518.9\n", + "-519.9\n", + "-520.9\n", + "-521.9\n", + "-522.9\n", + "-523.9\n", + "-524.9\n", + "-525.9\n", + "-526.9\n", + "-527.9\n", + "-528.9\n", + "-529.9\n", + "-530.9\n", + "-531.9\n", + "-532.9\n", + "-533.9\n", + "-534.9\n", + "-535.9\n", + "-536.9\n", + "-537.9\n", + "-538.9\n", + "-539.9\n", + "-540.9\n", + "-541.9\n", + "-542.9\n", + "-543.9\n", + "-544.9\n", + "-545.9\n", + "-546.9\n", + "-547.9\n", + "-548.9\n", + "-549.9\n", + "-550.9\n", + "-551.9\n", + "-552.9\n", + "-553.9\n", + "-554.9\n", + "-555.9\n", + "-556.9\n", + "-557.9\n", + "-558.9\n", + "-559.9\n", + "-560.9\n", + "-561.9\n", + "-562.9\n", + "-563.9\n", + "-564.9\n", + "-565.9\n", + "-566.9\n", + "-567.9\n", + "-568.9\n", + "-569.9\n", + "-570.9\n", + "-571.9\n", + "-572.9\n", + "-573.9\n", + "-574.9\n", + "-575.9\n", + "-576.9\n", + "-577.9\n", + "-578.9\n", + "-579.9\n", + "-580.9\n", + "-581.9\n", + "-582.9\n", + "-583.9\n", + "-584.9\n", + "-585.9\n", + "-586.9\n", + "-587.9\n", + "-588.9\n", + "-589.9\n", + "-590.9\n", + "-591.9\n", + "-592.9\n", + "-593.9\n", + "-594.9\n", + "-595.9\n", + "-596.9\n", + "-597.9\n", + "-598.9\n", + "-599.9\n", + "-600.9\n", + "-601.9\n", + "-602.9\n", + "-603.9\n", + "-604.9\n", + "-605.9\n", + "-606.9\n", + "-607.9\n", + "-608.9\n", + "-609.9\n", + "-610.9\n", + "-611.9\n", + "-612.9\n", + "-613.9\n", + "-614.9\n", + "-615.9\n", + "-616.9\n", + "-617.9\n", + "-618.9\n", + "-619.9\n", + "-620.9\n", + "-621.9\n", + "-622.9\n", + "-623.9\n", + "-624.9\n", + "-625.9\n", + "-626.9\n", + "-627.9\n", + "-628.9\n", + "-629.9\n", + "-630.9\n", + "-631.9\n", + "-632.9\n", + "-633.9\n", + "-634.9\n", + "-635.9\n", + "-636.9\n", + "-637.9\n", + "-638.9\n", + "-639.9\n", + "-640.9\n", + "-641.9\n", + "-642.9\n", + "-643.9\n", + "-644.9\n", + "-645.9\n", + "-646.9\n", + "-647.9\n", + "-648.9\n", + "-649.9\n", + "-650.9\n", + "-651.9\n", + "-652.9\n", + "-653.9\n", + "-654.9\n", + "-655.9\n", + "-656.9\n", + "-657.9\n", + "-658.9\n", + "-659.9\n", + "-660.9\n", + "-661.9\n", + "-662.9\n", + "-663.9\n", + "-664.9\n", + "-665.9\n", + "-666.9\n", + "-667.9\n", + "-668.9\n", + "-669.9\n", + "-670.9\n", + "-671.9\n", + "-672.9\n", + "-673.9\n", + "-674.9\n", + "-675.9\n", + "-676.9\n", + "-677.9\n", + "-678.9\n", + "-679.9\n", + "-680.9\n", + "-681.9\n", + "-682.9\n", + "-683.9\n", + "-684.9\n", + "-685.9\n", + "-686.9\n", + "-687.9\n", + "-688.9\n", + "-689.9\n", + "-690.9\n", + "-691.9\n", + "-692.9\n", + "-693.9\n", + "-694.9\n", + "-695.9\n", + "-696.9\n", + "-697.9\n", + "-698.9\n", + "-699.9\n", + "-700.9\n", + "-701.9\n", + "-702.9\n", + "-703.9\n", + "-704.9\n", + "-705.9\n", + "-706.9\n", + "-707.9\n", + "-708.9\n", + "-709.9\n", + "-710.9\n", + "-711.9\n", + "-712.9\n", + "-713.9\n", + "-714.9\n", + "-715.9\n", + "-716.9\n", + "-717.9\n", + "-718.9\n", + "-719.9\n", + "-720.9\n", + "-721.9\n", + "-722.9\n", + "-723.9\n", + "-724.9\n", + "-725.9\n", + "-726.9\n", + "-727.9\n", + "-728.9\n", + "-729.9\n", + "-730.9\n", + "-731.9\n", + "-732.9\n", + "-733.9\n", + "-734.9\n", + "-735.9\n", + "-736.9\n", + "-737.9\n", + "-738.9\n", + "-739.9\n", + "-740.9\n", + "-741.9\n", + "-742.9\n", + "-743.9\n", + "-744.9\n", + "-745.9\n", + "-746.9\n", + "-747.9\n", + "-748.9\n", + "-749.9\n", + "-750.9\n", + "-751.9\n", + "-752.9\n", + "-753.9\n", + "-754.9\n", + "-755.9\n", + "-756.9\n", + "-757.9\n", + "-758.9\n", + "-759.9\n", + "-760.9\n", + "-761.9\n", + "-762.9\n", + "-763.9\n", + "-764.9\n", + "-765.9\n", + "-766.9\n", + "-767.9\n", + "-768.9\n", + "-769.9\n", + "-770.9\n", + "-771.9\n", + "-772.9\n", + "-773.9\n", + "-774.9\n", + "-775.9\n", + "-776.9\n", + "-777.9\n", + "-778.9\n", + "-779.9\n", + "-780.9\n", + "-781.9\n", + "-782.9\n", + "-783.9\n", + "-784.9\n", + "-785.9\n", + "-786.9\n", + "-787.9\n", + "-788.9\n", + "-789.9\n", + "-790.9\n", + "-791.9\n", + "-792.9\n", + "-793.9\n", + "-794.9\n", + "-795.9\n", + "-796.9\n", + "-797.9\n", + "-798.9\n", + "-799.9\n", + "-800.9\n", + "-801.9\n", + "-802.9\n", + "-803.9\n", + "-804.9\n", + "-805.9\n", + "-806.9\n", + "-807.9\n", + "-808.9\n", + "-809.9\n", + "-810.9\n", + "-811.9\n", + "-812.9\n", + "-813.9\n", + "-814.9\n", + "-815.9\n", + "-816.9\n", + "-817.9\n", + "-818.9\n", + "-819.9\n", + "-820.9\n", + "-821.9\n", + "-822.9\n", + "-823.9\n", + "-824.9\n", + "-825.9\n", + "-826.9\n", + "-827.9\n", + "-828.9\n", + "-829.9\n", + "-830.9\n", + "-831.9\n", + "-832.9\n", + "-833.9\n", + "-834.9\n", + "-835.9\n", + "-836.9\n", + "-837.9\n", + "-838.9\n", + "-839.9\n", + "-840.9\n", + "-841.9\n", + "-842.9\n", + "-843.9\n", + "-844.9\n", + "-845.9\n", + "-846.9\n", + "-847.9\n", + "-848.9\n", + "-849.9\n", + "-850.9\n", + "-851.9\n", + "-852.9\n", + "-853.9\n", + "-854.9\n", + "-855.9\n", + "-856.9\n", + "-857.9\n", + "-858.9\n", + "-859.9\n", + "-860.9\n", + "-861.9\n", + "-862.9\n", + "-863.9\n", + "-864.9\n", + "-865.9\n", + "-866.9\n", + "-867.9\n", + "-868.9\n", + "-869.9\n", + "-870.9\n", + "-871.9\n", + "-872.9\n", + "-873.9\n", + "-874.9\n", + "-875.9\n", + "-876.9\n", + "-877.9\n", + "-878.9\n", + "-879.9\n", + "-880.9\n", + "-881.9\n", + "-882.9\n", + "-883.9\n", + "-884.9\n", + "-885.9\n", + "-886.9\n", + "-887.9\n", + "-888.9\n", + "-889.9\n", + "-890.9\n", + "-891.9\n", + "-892.9\n", + "-893.9\n", + "-894.9\n", + "-895.9\n", + "-896.9\n", + "-897.9\n", + "-898.9\n", + "-899.9\n", + "-900.9\n", + "-901.9\n", + "-902.9\n", + "-903.9\n", + "-904.9\n", + "-905.9\n", + "-906.9\n", + "-907.9\n", + "-908.9\n", + "-909.9\n", + "-910.9\n", + "-911.9\n", + "-912.9\n", + "-913.9\n", + "-914.9\n", + "-915.9\n", + "-916.9\n", + "-917.9\n", + "-918.9\n", + "-919.9\n", + "-920.9\n", + "-921.9\n", + "-922.9\n", + "-923.9\n", + "-924.9\n", + "-925.9\n", + "-926.9\n", + "-927.9\n", + "-928.9\n", + "-929.9\n", + "-930.9\n", + "-931.9\n", + "-932.9\n", + "-933.9\n", + "-934.9\n", + "-935.9\n", + "-936.9\n", + "-937.9\n", + "-938.9\n", + "-939.9\n", + "-940.9\n", + "-941.9\n", + "-942.9\n", + "-943.9\n", + "-944.9\n", + "-945.9\n", + "-946.9\n", + "-947.9\n", + "-948.9\n", + "-949.9\n", + "-950.9\n", + "-951.9\n", + "-952.9\n", + "-953.9\n", + "-954.9\n", + "-955.9\n", + "-956.9\n", + "-957.9\n", + "-958.9\n", + "-959.9\n", + "-960.9\n", + "-961.9\n", + "-962.9\n", + "-963.9\n", + "-964.9\n", + "-965.9\n", + "-966.9\n", + "-967.9\n", + "-968.9\n", + "-969.9\n", + "-970.9\n", + "-971.9\n", + "-972.9\n", + "-973.9\n", + "-974.9\n", + "-975.9\n", + "-976.9\n", + "-977.9\n", + "-978.9\n", + "-979.9\n", + "-980.9\n", + "-981.9\n", + "-982.9\n", + "-983.9\n", + "-984.9\n", + "-985.9\n", + "-986.9\n", + "-987.9\n", + "-988.9\n", + "-989.9\n", + "-990.9\n", + "-991.9\n", + "-992.9\n", + "-993.9\n", + "-994.9\n", + "-995.9\n", + "-996.9\n", + "-997.9\n", + "-998.9\n", + "-999.9\n", + "-1000.9\n", + "-1001.9\n", + "-1002.9\n", + "-1003.9\n", + "-1004.9\n", + "-1005.9\n", + "-1006.9\n", + "-1007.9\n", + "-1008.9\n", + "-1009.9\n", + "-1010.9\n", + "-1011.9\n", + "-1012.9\n", + "-1013.9\n", + "-1014.9\n", + "-1015.9\n", + "-1016.9\n", + "-1017.9\n", + "-1018.9\n", + "-1019.9\n", + "-1020.9\n", + "-1021.9\n", + "-1022.9\n", + "-1023.9\n", + "-1024.9\n", + "-1025.9\n", + "-1026.9\n", + "-1027.9\n", + "-1028.9\n", + "-1029.9\n", + "-1030.9\n", + "-1031.9\n", + "-1032.9\n", + "-1033.9\n", + "-1034.9\n", + "-1035.9\n", + "-1036.9\n", + "-1037.9\n", + "-1038.9\n", + "-1039.9\n", + "-1040.9\n", + "-1041.9\n", + "-1042.9\n", + "-1043.9\n", + "-1044.9\n", + "-1045.9\n", + "-1046.9\n", + "-1047.9\n", + "-1048.9\n", + "-1049.9\n", + "-1050.9\n", + "-1051.9\n", + "-1052.9\n", + "-1053.9\n", + "-1054.9\n", + "-1055.9\n", + "-1056.9\n", + "-1057.9\n", + "-1058.9\n", + "-1059.9\n", + "-1060.9\n", + "-1061.9\n", + "-1062.9\n", + "-1063.9\n", + "-1064.9\n", + "-1065.9\n", + "-1066.9\n", + "-1067.9\n", + "-1068.9\n", + "-1069.9\n", + "-1070.9\n", + "-1071.9\n", + "-1072.9\n", + "-1073.9\n", + "-1074.9\n", + "-1075.9\n", + "-1076.9\n", + "-1077.9\n", + "-1078.9\n", + "-1079.9\n", + "-1080.9\n", + "-1081.9\n", + "-1082.9\n", + "-1083.9\n", + "-1084.9\n", + "-1085.9\n", + "-1086.9\n", + "-1087.9\n", + "-1088.9\n", + "-1089.9\n", + "-1090.9\n", + "-1091.9\n", + "-1092.9\n", + "-1093.9\n", + "-1094.9\n", + "-1095.9\n", + "-1096.9\n", + "-1097.9\n", + "-1098.9\n", + "-1099.9\n", + "-1100.9\n", + "-1101.9\n", + "-1102.9\n", + "-1103.9\n", + "-1104.9\n", + "-1105.9\n", + "-1106.9\n", + "-1107.9\n", + "-1108.9\n", + "-1109.9\n", + "-1110.9\n", + "-1111.9\n", + "-1112.9\n", + "-1113.9\n", + "-1114.9\n", + "-1115.9\n", + "-1116.9\n", + "-1117.9\n", + "-1118.9\n", + "-1119.9\n", + "-1120.9\n", + "-1121.9\n", + "-1122.9\n", + "-1123.9\n", + "-1124.9\n", + "-1125.9\n", + "-1126.9\n", + "-1127.9\n", + "-1128.9\n", + "-1129.9\n", + "-1130.9\n", + "-1131.9\n", + "-1132.9\n", + "-1133.9\n", + "-1134.9\n", + "-1135.9\n", + "-1136.9\n", + "-1137.9\n", + "-1138.9\n", + "-1139.9\n", + "-1140.9\n", + "-1141.9\n", + "-1142.9\n", + "-1143.9\n", + "-1144.9\n", + "-1145.9\n", + "-1146.9\n", + "-1147.9\n", + "-1148.9\n", + "-1149.9\n", + "-1150.9\n", + "-1151.9\n", + "-1152.9\n", + "-1153.9\n", + "-1154.9\n", + "-1155.9\n", + "-1156.9\n", + "-1157.9\n", + "-1158.9\n", + "-1159.9\n", + "-1160.9\n", + "-1161.9\n", + "-1162.9\n", + "-1163.9\n", + "-1164.9\n", + "-1165.9\n", + "-1166.9\n", + "-1167.9\n", + "-1168.9\n", + "-1169.9\n", + "-1170.9\n", + "-1171.9\n", + "-1172.9\n", + "-1173.9\n", + "-1174.9\n", + "-1175.9\n", + "-1176.9\n", + "-1177.9\n", + "-1178.9\n", + "-1179.9\n", + "-1180.9\n", + "-1181.9\n", + "-1182.9\n", + "-1183.9\n", + "-1184.9\n", + "-1185.9\n", + "-1186.9\n", + "-1187.9\n", + "-1188.9\n", + "-1189.9\n", + "-1190.9\n", + "-1191.9\n", + "-1192.9\n", + "-1193.9\n", + "-1194.9\n", + "-1195.9\n", + "-1196.9\n", + "-1197.9\n", + "-1198.9\n", + "-1199.9\n", + "-1200.9\n", + "-1201.9\n", + "-1202.9\n", + "-1203.9\n", + "-1204.9\n", + "-1205.9\n", + "-1206.9\n", + "-1207.9\n", + "-1208.9\n", + "-1209.9\n", + "-1210.9\n", + "-1211.9\n", + "-1212.9\n", + "-1213.9\n", + "-1214.9\n", + "-1215.9\n", + "-1216.9\n", + "-1217.9\n", + "-1218.9\n", + "-1219.9\n", + "-1220.9\n", + "-1221.9\n", + "-1222.9\n", + "-1223.9\n", + "-1224.9\n", + "-1225.9\n", + "-1226.9\n", + "-1227.9\n", + "-1228.9\n", + "-1229.9\n", + "-1230.9\n", + "-1231.9\n", + "-1232.9\n", + "-1233.9\n", + "-1234.9\n", + "-1235.9\n", + "-1236.9\n", + "-1237.9\n", + "-1238.9\n", + "-1239.9\n", + "-1240.9\n", + "-1241.9\n", + "-1242.9\n", + "-1243.9\n", + "-1244.9\n", + "-1245.9\n", + "-1246.9\n", + "-1247.9\n", + "-1248.9\n", + "-1249.9\n", + "-1250.9\n", + "-1251.9\n", + "-1252.9\n", + "-1253.9\n", + "-1254.9\n", + "-1255.9\n", + "-1256.9\n", + "-1257.9\n", + "-1258.9\n", + "-1259.9\n", + "-1260.9\n", + "-1261.9\n", + "-1262.9\n", + "-1263.9\n", + "-1264.9\n", + "-1265.9\n", + "-1266.9\n", + "-1267.9\n", + "-1268.9\n", + "-1269.9\n", + "-1270.9\n", + "-1271.9\n", + "-1272.9\n", + "-1273.9\n", + "-1274.9\n", + "-1275.9\n", + "-1276.9\n", + "-1277.9\n", + "-1278.9\n", + "-1279.9\n", + "-1280.9\n", + "-1281.9\n", + "-1282.9\n", + "-1283.9\n", + "-1284.9\n", + "-1285.9\n", + "-1286.9\n", + "-1287.9\n", + "-1288.9\n", + "-1289.9\n", + "-1290.9\n", + "-1291.9\n", + "-1292.9\n", + "-1293.9\n", + "-1294.9\n", + "-1295.9\n", + "-1296.9\n", + "-1297.9\n", + "-1298.9\n", + "-1299.9\n", + "-1300.9\n", + "-1301.9\n", + "-1302.9\n", + "-1303.9\n", + "-1304.9\n", + "-1305.9\n", + "-1306.9\n", + "-1307.9\n", + "-1308.9\n", + "-1309.9\n", + "-1310.9\n", + "-1311.9\n", + "-1312.9\n", + "-1313.9\n", + "-1314.9\n", + "-1315.9\n", + "-1316.9\n", + "-1317.9\n", + "-1318.9\n", + "-1319.9\n", + "-1320.9\n", + "-1321.9\n", + "-1322.9\n", + "-1323.9\n", + "-1324.9\n", + "-1325.9\n", + "-1326.9\n", + "-1327.9\n", + "-1328.9\n", + "-1329.9\n", + "-1330.9\n", + "-1331.9\n", + "-1332.9\n", + "-1333.9\n", + "-1334.9\n", + "-1335.9\n", + "-1336.9\n", + "-1337.9\n", + "-1338.9\n", + "-1339.9\n", + "-1340.9\n", + "-1341.9\n", + "-1342.9\n", + "-1343.9\n", + "-1344.9\n", + "-1345.9\n", + "-1346.9\n", + "-1347.9\n", + "-1348.9\n", + "-1349.9\n", + "-1350.9\n", + "-1351.9\n", + "-1352.9\n", + "-1353.9\n", + "-1354.9\n", + "-1355.9\n", + "-1356.9\n", + "-1357.9\n", + "-1358.9\n", + "-1359.9\n", + "-1360.9\n", + "-1361.9\n", + "-1362.9\n", + "-1363.9\n", + "-1364.9\n", + "-1365.9\n", + "-1366.9\n", + "-1367.9\n", + "-1368.9\n", + "-1369.9\n", + "-1370.9\n", + "-1371.9\n", + "-1372.9\n", + "-1373.9\n", + "-1374.9\n", + "-1375.9\n", + "-1376.9\n", + "-1377.9\n", + "-1378.9\n", + "-1379.9\n", + "-1380.9\n", + "-1381.9\n", + "-1382.9\n", + "-1383.9\n", + "-1384.9\n", + "-1385.9\n", + "-1386.9\n", + "-1387.9\n", + "-1388.9\n", + "-1389.9\n", + "-1390.9\n", + "-1391.9\n", + "-1392.9\n", + "-1393.9\n", + "-1394.9\n", + "-1395.9\n", + "-1396.9\n", + "-1397.9\n", + "-1398.9\n", + "-1399.9\n", + "-1400.9\n", + "-1401.9\n", + "-1402.9\n", + "-1403.9\n", + "-1404.9\n", + "-1405.9\n", + "-1406.9\n", + "-1407.9\n", + "-1408.9\n", + "-1409.9\n", + "-1410.9\n", + "-1411.9\n", + "-1412.9\n", + "-1413.9\n", + "-1414.9\n", + "-1415.9\n", + "-1416.9\n", + "-1417.9\n", + "-1418.9\n", + "-1419.9\n", + "-1420.9\n", + "-1421.9\n", + "-1422.9\n", + "-1423.9\n", + "-1424.9\n", + "-1425.9\n", + "-1426.9\n", + "-1427.9\n", + "-1428.9\n", + "-1429.9\n", + "-1430.9\n", + "-1431.9\n", + "-1432.9\n", + "-1433.9\n", + "-1434.9\n", + "-1435.9\n", + "-1436.9\n", + "-1437.9\n", + "-1438.9\n", + "-1439.9\n", + "-1440.9\n", + "-1441.9\n", + "-1442.9\n", + "-1443.9\n", + "-1444.9\n", + "-1445.9\n", + "-1446.9\n", + "-1447.9\n", + "-1448.9\n", + "-1449.9\n", + "-1450.9\n", + "-1451.9\n", + "-1452.9\n", + "-1453.9\n", + "-1454.9\n", + "-1455.9\n", + "-1456.9\n", + "-1457.9\n", + "-1458.9\n", + "-1459.9\n", + "-1460.9\n", + "-1461.9\n", + "-1462.9\n", + "-1463.9\n", + "-1464.9\n", + "-1465.9\n", + "-1466.9\n", + "-1467.9\n", + "-1468.9\n", + "-1469.9\n", + "-1470.9\n", + "-1471.9\n", + "-1472.9\n", + "-1473.9\n", + "-1474.9\n", + "-1475.9\n", + "-1476.9\n", + "-1477.9\n", + "-1478.9\n", + "-1479.9\n", + "-1480.9\n", + "-1481.9\n", + "-1482.9\n", + "-1483.9\n", + "-1484.9\n", + "-1485.9\n", + "-1486.9\n", + "-1487.9\n", + "-1488.9\n", + "-1489.9\n", + "-1490.9\n", + "-1491.9\n", + "-1492.9\n", + "-1493.9\n", + "-1494.9\n", + "-1495.9\n", + "-1496.9\n", + "-1497.9\n", + "-1498.9\n", + "-1499.9\n", + "-1500.9\n", + "-1501.9\n", + "-1502.9\n", + "-1503.9\n", + "-1504.9\n", + "-1505.9\n", + "-1506.9\n", + "-1507.9\n", + "-1508.9\n", + "-1509.9\n", + "-1510.9\n", + "-1511.9\n", + "-1512.9\n", + "-1513.9\n", + "-1514.9\n", + "-1515.9\n", + "-1516.9\n", + "-1517.9\n", + "-1518.9\n", + "-1519.9\n", + "-1520.9\n", + "-1521.9\n", + "-1522.9\n", + "-1523.9\n", + "-1524.9\n", + "-1525.9\n", + "-1526.9\n", + "-1527.9\n", + "-1528.9\n", + "-1529.9\n", + "-1530.9\n", + "-1531.9\n", + "-1532.9\n", + "-1533.9\n", + "-1534.9\n", + "-1535.9\n", + "-1536.9\n", + "-1537.9\n", + "-1538.9\n", + "-1539.9\n", + "-1540.9\n", + "-1541.9\n", + "-1542.9\n", + "-1543.9\n", + "-1544.9\n", + "-1545.9\n", + "-1546.9\n", + "-1547.9\n", + "-1548.9\n", + "-1549.9\n", + "-1550.9\n", + "-1551.9\n", + "-1552.9\n", + "-1553.9\n", + "-1554.9\n", + "-1555.9\n", + "-1556.9\n", + "-1557.9\n", + "-1558.9\n", + "-1559.9\n", + "-1560.9\n", + "-1561.9\n", + "-1562.9\n", + "-1563.9\n", + "-1564.9\n", + "-1565.9\n", + "-1566.9\n", + "-1567.9\n", + "-1568.9\n", + "-1569.9\n", + "-1570.9\n", + "-1571.9\n", + "-1572.9\n", + "-1573.9\n", + "-1574.9\n", + "-1575.9\n", + "-1576.9\n", + "-1577.9\n", + "-1578.9\n", + "-1579.9\n", + "-1580.9\n", + "-1581.9\n", + "-1582.9\n", + "-1583.9\n", + "-1584.9\n", + "-1585.9\n", + "-1586.9\n", + "-1587.9\n", + "-1588.9\n", + "-1589.9\n", + "-1590.9\n", + "-1591.9\n", + "-1592.9\n", + "-1593.9\n", + "-1594.9\n", + "-1595.9\n", + "-1596.9\n", + "-1597.9\n", + "-1598.9\n", + "-1599.9\n", + "-1600.9\n", + "-1601.9\n", + "-1602.9\n", + "-1603.9\n", + "-1604.9\n", + "-1605.9\n", + "-1606.9\n", + "-1607.9\n", + "-1608.9\n", + "-1609.9\n", + "-1610.9\n", + "-1611.9\n", + "-1612.9\n", + "-1613.9\n", + "-1614.9\n", + "-1615.9\n", + "-1616.9\n", + "-1617.9\n", + "-1618.9\n", + "-1619.9\n", + "-1620.9\n", + "-1621.9\n", + "-1622.9\n", + "-1623.9\n", + "-1624.9\n", + "-1625.9\n", + "-1626.9\n", + "-1627.9\n", + "-1628.9\n", + "-1629.9\n", + "-1630.9\n", + "-1631.9\n", + "-1632.9\n", + "-1633.9\n", + "-1634.9\n", + "-1635.9\n", + "-1636.9\n", + "-1637.9\n", + "-1638.9\n", + "-1639.9\n", + "-1640.9\n", + "-1641.9\n", + "-1642.9\n", + "-1643.9\n", + "-1644.9\n", + "-1645.9\n", + "-1646.9\n", + "-1647.9\n", + "-1648.9\n", + "-1649.9\n", + "-1650.9\n", + "-1651.9\n", + "-1652.9\n", + "-1653.9\n", + "-1654.9\n", + "-1655.9\n", + "-1656.9\n", + "-1657.9\n", + "-1658.9\n", + "-1659.9\n", + "-1660.9\n", + "-1661.9\n", + "-1662.9\n", + "-1663.9\n", + "-1664.9\n", + "-1665.9\n", + "-1666.9\n", + "-1667.9\n", + "-1668.9\n", + "-1669.9\n", + "-1670.9\n", + "-1671.9\n", + "-1672.9\n", + "-1673.9\n", + "-1674.9\n", + "-1675.9\n", + "-1676.9\n", + "-1677.9\n", + "-1678.9\n", + "-1679.9\n", + "-1680.9\n", + "-1681.9\n", + "-1682.9\n", + "-1683.9\n", + "-1684.9\n", + "-1685.9\n", + "-1686.9\n", + "-1687.9\n", + "-1688.9\n", + "-1689.9\n", + "-1690.9\n", + "-1691.9\n", + "-1692.9\n", + "-1693.9\n", + "-1694.9\n", + "-1695.9\n", + "-1696.9\n", + "-1697.9\n", + "-1698.9\n", + "-1699.9\n", + "-1700.9\n", + "-1701.9\n", + "-1702.9\n", + "-1703.9\n", + "-1704.9\n", + "-1705.9\n", + "-1706.9\n", + "-1707.9\n", + "-1708.9\n", + "-1709.9\n", + "-1710.9\n", + "-1711.9\n", + "-1712.9\n", + "-1713.9\n", + "-1714.9\n", + "-1715.9\n", + "-1716.9\n", + "-1717.9\n", + "-1718.9\n", + "-1719.9\n", + "-1720.9\n", + "-1721.9\n", + "-1722.9\n", + "-1723.9\n", + "-1724.9\n", + "-1725.9\n", + "-1726.9\n", + "-1727.9\n", + "-1728.9\n", + "-1729.9\n", + "-1730.9\n", + "-1731.9\n", + "-1732.9\n", + "-1733.9\n", + "-1734.9\n", + "-1735.9\n", + "-1736.9\n", + "-1737.9\n", + "-1738.9\n", + "-1739.9\n", + "-1740.9\n", + "-1741.9\n", + "-1742.9\n", + "-1743.9\n", + "-1744.9\n", + "-1745.9\n", + "-1746.9\n", + "-1747.9\n", + "-1748.9\n", + "-1749.9\n", + "-1750.9\n", + "-1751.9\n", + "-1752.9\n", + "-1753.9\n", + "-1754.9\n", + "-1755.9\n", + "-1756.9\n", + "-1757.9\n", + "-1758.9\n", + "-1759.9\n", + "-1760.9\n", + "-1761.9\n", + "-1762.9\n", + "-1763.9\n", + "-1764.9\n", + "-1765.9\n", + "-1766.9\n", + "-1767.9\n", + "-1768.9\n", + "-1769.9\n", + "-1770.9\n", + "-1771.9\n", + "-1772.9\n", + "-1773.9\n", + "-1774.9\n", + "-1775.9\n", + "-1776.9\n", + "-1777.9\n", + "-1778.9\n", + "-1779.9\n", + "-1780.9\n", + "-1781.9\n", + "-1782.9\n", + "-1783.9\n", + "-1784.9\n", + "-1785.9\n", + "-1786.9\n", + "-1787.9\n", + "-1788.9\n", + "-1789.9\n", + "-1790.9\n", + "-1791.9\n", + "-1792.9\n", + "-1793.9\n", + "-1794.9\n", + "-1795.9\n", + "-1796.9\n", + "-1797.9\n", + "-1798.9\n", + "-1799.9\n", + "-1800.9\n", + "-1801.9\n", + "-1802.9\n", + "-1803.9\n", + "-1804.9\n", + "-1805.9\n", + "-1806.9\n", + "-1807.9\n", + "-1808.9\n", + "-1809.9\n", + "-1810.9\n", + "-1811.9\n", + "-1812.9\n", + "-1813.9\n", + "-1814.9\n", + "-1815.9\n", + "-1816.9\n", + "-1817.9\n", + "-1818.9\n", + "-1819.9\n", + "-1820.9\n", + "-1821.9\n", + "-1822.9\n", + "-1823.9\n", + "-1824.9\n", + "-1825.9\n", + "-1826.9\n", + "-1827.9\n", + "-1828.9\n", + "-1829.9\n", + "-1830.9\n", + "-1831.9\n", + "-1832.9\n", + "-1833.9\n", + "-1834.9\n", + "-1835.9\n", + "-1836.9\n", + "-1837.9\n", + "-1838.9\n", + "-1839.9\n", + "-1840.9\n", + "-1841.9\n", + "-1842.9\n", + "-1843.9\n", + "-1844.9\n", + "-1845.9\n", + "-1846.9\n", + "-1847.9\n", + "-1848.9\n", + "-1849.9\n", + "-1850.9\n", + "-1851.9\n", + "-1852.9\n", + "-1853.9\n", + "-1854.9\n", + "-1855.9\n", + "-1856.9\n", + "-1857.9\n", + "-1858.9\n", + "-1859.9\n", + "-1860.9\n", + "-1861.9\n", + "-1862.9\n", + "-1863.9\n", + "-1864.9\n", + "-1865.9\n", + "-1866.9\n", + "-1867.9\n", + "-1868.9\n", + "-1869.9\n", + "-1870.9\n", + "-1871.9\n", + "-1872.9\n", + "-1873.9\n", + "-1874.9\n", + "-1875.9\n", + "-1876.9\n", + "-1877.9\n", + "-1878.9\n", + "-1879.9\n", + "-1880.9\n", + "-1881.9\n", + "-1882.9\n", + "-1883.9\n", + "-1884.9\n", + "-1885.9\n", + "-1886.9\n", + "-1887.9\n", + "-1888.9\n", + "-1889.9\n", + "-1890.9\n", + "-1891.9\n", + "-1892.9\n", + "-1893.9\n", + "-1894.9\n", + "-1895.9\n", + "-1896.9\n", + "-1897.9\n", + "-1898.9\n", + "-1899.9\n", + "-1900.9\n", + "-1901.9\n", + "-1902.9\n", + "-1903.9\n", + "-1904.9\n", + "-1905.9\n", + "-1906.9\n", + "-1907.9\n", + "-1908.9\n", + "-1909.9\n", + "-1910.9\n", + "-1911.9\n", + "-1912.9\n", + "-1913.9\n", + "-1914.9\n", + "-1915.9\n", + "-1916.9\n", + "-1917.9\n", + "-1918.9\n", + "-1919.9\n", + "-1920.9\n", + "-1921.9\n", + "-1922.9\n", + "-1923.9\n", + "-1924.9\n", + "-1925.9\n", + "-1926.9\n", + "-1927.9\n", + "-1928.9\n", + "-1929.9\n", + "-1930.9\n", + "-1931.9\n", + "-1932.9\n", + "-1933.9\n", + "-1934.9\n", + "-1935.9\n", + "-1936.9\n", + "-1937.9\n", + "-1938.9\n", + "-1939.9\n", + "-1940.9\n", + "-1941.9\n", + "-1942.9\n", + "-1943.9\n", + "-1944.9\n", + "-1945.9\n", + "-1946.9\n", + "-1947.9\n", + "-1948.9\n", + "-1949.9\n", + "-1950.9\n", + "-1951.9\n", + "-1952.9\n", + "-1953.9\n", + "-1954.9\n", + "-1955.9\n", + "-1956.9\n", + "-1957.9\n", + "-1958.9\n", + "-1959.9\n", + "-1960.9\n", + "-1961.9\n", + "-1962.9\n", + "-1963.9\n", + "-1964.9\n", + "-1965.9\n", + "-1966.9\n", + "-1967.9\n", + "-1968.9\n", + "-1969.9\n", + "-1970.9\n", + "-1971.9\n", + "-1972.9\n", + "-1973.9\n", + "-1974.9\n", + "-1975.9\n", + "-1976.9\n", + "-1977.9\n", + "-1978.9\n", + "-1979.9\n", + "-1980.9\n", + "-1981.9\n", + "-1982.9\n", + "-1983.9\n", + "-1984.9\n", + "-1985.9\n", + "-1986.9\n", + "-1987.9\n", + "-1988.9\n", + "-1989.9\n", + "-1990.9\n", + "-1991.9\n", + "-1992.9\n", + "-1993.9\n", + "-1994.9\n", + "-1995.9\n", + "-1996.9\n", + "-1997.9\n", + "-1998.9\n", + "-1999.9\n", + "-2000.9\n", + "-2001.9\n", + "-2002.9\n", + "-2003.9\n", + "-2004.9\n", + "-2005.9\n", + "-2006.9\n", + "-2007.9\n", + "-2008.9\n", + "-2009.9\n", + "-2010.9\n", + "-2011.9\n", + "-2012.9\n", + "-2013.9\n", + "-2014.9\n", + "-2015.9\n", + "-2016.9\n", + "-2017.9\n", + "-2018.9\n", + "-2019.9\n", + "-2020.9\n", + "-2021.9\n", + "-2022.9\n", + "-2023.9\n", + "-2024.9\n", + "-2025.9\n", + "-2026.9\n", + "-2027.9\n", + "-2028.9\n", + "-2029.9\n", + "-2030.9\n", + "-2031.9\n", + "-2032.9\n", + "-2033.9\n", + "-2034.9\n", + "-2035.9\n", + "-2036.9\n", + "-2037.9\n", + "-2038.9\n", + "-2039.9\n", + "-2040.9\n", + "-2041.9\n", + "-2042.9\n", + "-2043.9\n", + "-2044.9\n", + "-2045.9\n", + "-2046.9\n", + "-2047.9\n", + "-2048.9\n", + "-2049.9\n", + "-2050.9\n", + "-2051.9\n", + "-2052.9\n", + "-2053.9\n", + "-2054.9\n", + "-2055.9\n", + "-2056.9\n", + "-2057.9\n", + "-2058.9\n", + "-2059.9\n", + "-2060.9\n", + "-2061.9\n", + "-2062.9\n", + "-2063.9\n", + "-2064.9\n", + "-2065.9\n", + "-2066.9\n", + "-2067.9\n", + "-2068.9\n", + "-2069.9\n", + "-2070.9\n", + "-2071.9\n", + "-2072.9\n", + "-2073.9\n", + "-2074.9\n", + "-2075.9\n", + "-2076.9\n", + "-2077.9\n", + "-2078.9\n", + "-2079.9\n", + "-2080.9\n", + "-2081.9\n", + "-2082.9\n", + "-2083.9\n", + "-2084.9\n", + "-2085.9\n", + "-2086.9\n", + "-2087.9\n", + "-2088.9\n", + "-2089.9\n", + "-2090.9\n", + "-2091.9\n", + "-2092.9\n", + "-2093.9\n", + "-2094.9\n", + "-2095.9\n", + "-2096.9\n", + "-2097.9\n", + "-2098.9\n", + "-2099.9\n", + "-2100.9\n", + "-2101.9\n", + "-2102.9\n", + "-2103.9\n", + "-2104.9\n", + "-2105.9\n", + "-2106.9\n", + "-2107.9\n", + "-2108.9\n", + "-2109.9\n", + "-2110.9\n", + "-2111.9\n", + "-2112.9\n", + "-2113.9\n", + "-2114.9\n", + "-2115.9\n", + "-2116.9\n", + "-2117.9\n", + "-2118.9\n", + "-2119.9\n", + "-2120.9\n", + "-2121.9\n", + "-2122.9\n", + "-2123.9\n", + "-2124.9\n", + "-2125.9\n", + "-2126.9\n", + "-2127.9\n", + "-2128.9\n", + "-2129.9\n", + "-2130.9\n", + "-2131.9\n", + "-2132.9\n", + "-2133.9\n", + "-2134.9\n", + "-2135.9\n", + "-2136.9\n", + "-2137.9\n", + "-2138.9\n", + "-2139.9\n", + "-2140.9\n", + "-2141.9\n", + "-2142.9\n", + "-2143.9\n", + "-2144.9\n", + "-2145.9\n", + "-2146.9\n", + "-2147.9\n", + "-2148.9\n", + "-2149.9\n", + "-2150.9\n", + "-2151.9\n", + "-2152.9\n", + "-2153.9\n", + "-2154.9\n", + "-2155.9\n", + "-2156.9\n", + "-2157.9\n", + "-2158.9\n", + "-2159.9\n", + "-2160.9\n", + "-2161.9\n", + "-2162.9\n", + "-2163.9\n", + "-2164.9\n", + "-2165.9\n", + "-2166.9\n", + "-2167.9\n", + "-2168.9\n", + "-2169.9\n", + "-2170.9\n", + "-2171.9\n", + "-2172.9\n", + "-2173.9\n", + "-2174.9\n", + "-2175.9\n", + "-2176.9\n", + "-2177.9\n", + "-2178.9\n", + "-2179.9\n", + "-2180.9\n", + "-2181.9\n", + "-2182.9\n", + "-2183.9\n", + "-2184.9\n", + "-2185.9\n", + "-2186.9\n", + "-2187.9\n", + "-2188.9\n", + "-2189.9\n", + "-2190.9\n", + "-2191.9\n", + "-2192.9\n", + "-2193.9\n", + "-2194.9\n", + "-2195.9\n", + "-2196.9\n", + "-2197.9\n", + "-2198.9\n", + "-2199.9\n", + "-2200.9\n", + "-2201.9\n", + "-2202.9\n", + "-2203.9\n", + "-2204.9\n", + "-2205.9\n", + "-2206.9\n", + "-2207.9\n", + "-2208.9\n", + "-2209.9\n", + "-2210.9\n", + "-2211.9\n", + "-2212.9\n", + "-2213.9\n", + "-2214.9\n", + "-2215.9\n", + "-2216.9\n", + "-2217.9\n", + "-2218.9\n", + "-2219.9\n", + "-2220.9\n", + "-2221.9\n", + "-2222.9\n", + "-2223.9\n", + "-2224.9\n", + "-2225.9\n", + "-2226.9\n", + "-2227.9\n", + "-2228.9\n", + "-2229.9\n", + "-2230.9\n", + "-2231.9\n", + "-2232.9\n", + "-2233.9\n", + "-2234.9\n", + "-2235.9\n", + "-2236.9\n", + "-2237.9\n", + "-2238.9\n", + "-2239.9\n", + "-2240.9\n", + "-2241.9\n", + "-2242.9\n", + "-2243.9\n", + "-2244.9\n", + "-2245.9\n", + "-2246.9\n", + "-2247.9\n", + "-2248.9\n", + "-2249.9\n", + "-2250.9\n", + "-2251.9\n", + "-2252.9\n", + "-2253.9\n", + "-2254.9\n", + "-2255.9\n", + "-2256.9\n", + "-2257.9\n", + "-2258.9\n", + "-2259.9\n", + "-2260.9\n", + "-2261.9\n", + "-2262.9\n", + "-2263.9\n", + "-2264.9\n", + "-2265.9\n", + "-2266.9\n", + "-2267.9\n", + "-2268.9\n", + "-2269.9\n", + "-2270.9\n", + "-2271.9\n", + "-2272.9\n", + "-2273.9\n", + "-2274.9\n", + "-2275.9\n", + "-2276.9\n", + "-2277.9\n", + "-2278.9\n", + "-2279.9\n", + "-2280.9\n", + "-2281.9\n", + "-2282.9\n", + "-2283.9\n", + "-2284.9\n", + "-2285.9\n", + "-2286.9\n", + "-2287.9\n", + "-2288.9\n", + "-2289.9\n", + "-2290.9\n", + "-2291.9\n", + "-2292.9\n", + "-2293.9\n", + "-2294.9\n", + "-2295.9\n", + "-2296.9\n", + "-2297.9\n", + "-2298.9\n", + "-2299.9\n", + "-2300.9\n", + "-2301.9\n", + "-2302.9\n", + "-2303.9\n", + "-2304.9\n", + "-2305.9\n", + "-2306.9\n", + "-2307.9\n", + "-2308.9\n", + "-2309.9\n", + "-2310.9\n", + "-2311.9\n", + "-2312.9\n", + "-2313.9\n", + "-2314.9\n", + "-2315.9\n", + "-2316.9\n", + "-2317.9\n", + "-2318.9\n", + "-2319.9\n", + "-2320.9\n", + "-2321.9\n", + "-2322.9\n", + "-2323.9\n", + "-2324.9\n", + "-2325.9\n", + "-2326.9\n", + "-2327.9\n", + "-2328.9\n", + "-2329.9\n", + "-2330.9\n", + "-2331.9\n", + "-2332.9\n", + "-2333.9\n", + "-2334.9\n", + "-2335.9\n", + "-2336.9\n", + "-2337.9\n", + "-2338.9\n", + "-2339.9\n", + "-2340.9\n", + "-2341.9\n", + "-2342.9\n", + "-2343.9\n", + "-2344.9\n", + "-2345.9\n", + "-2346.9\n", + "-2347.9\n", + "-2348.9\n", + "-2349.9\n", + "-2350.9\n", + "-2351.9\n", + "-2352.9\n", + "-2353.9\n", + "-2354.9\n", + "-2355.9\n", + "-2356.9\n", + "-2357.9\n", + "-2358.9\n", + "-2359.9\n", + "-2360.9\n", + "-2361.9\n", + "-2362.9\n", + "-2363.9\n", + "-2364.9\n", + "-2365.9\n", + "-2366.9\n", + "-2367.9\n", + "-2368.9\n", + "-2369.9\n", + "-2370.9\n", + "-2371.9\n", + "-2372.9\n", + "-2373.9\n", + "-2374.9\n", + "-2375.9\n", + "-2376.9\n", + "-2377.9\n", + "-2378.9\n", + "-2379.9\n", + "-2380.9\n", + "-2381.9\n", + "-2382.9\n", + "-2383.9\n", + "-2384.9\n", + "-2385.9\n", + "-2386.9\n", + "-2387.9\n", + "-2388.9\n", + "-2389.9\n", + "-2390.9\n", + "-2391.9\n", + "-2392.9\n", + "-2393.9\n", + "-2394.9\n", + "-2395.9\n", + "-2396.9\n", + "-2397.9\n", + "-2398.9\n", + "-2399.9\n", + "-2400.9\n", + "-2401.9\n", + "-2402.9\n", + "-2403.9\n", + "-2404.9\n", + "-2405.9\n", + "-2406.9\n", + "-2407.9\n", + "-2408.9\n", + "-2409.9\n", + "-2410.9\n", + "-2411.9\n", + "-2412.9\n", + "-2413.9\n", + "-2414.9\n", + "-2415.9\n", + "-2416.9\n", + "-2417.9\n", + "-2418.9\n", + "-2419.9\n", + "-2420.9\n", + "-2421.9\n", + "-2422.9\n", + "-2423.9\n", + "-2424.9\n", + "-2425.9\n", + "-2426.9\n", + "-2427.9\n", + "-2428.9\n", + "-2429.9\n", + "-2430.9\n", + "-2431.9\n", + "-2432.9\n", + "-2433.9\n", + "-2434.9\n", + "-2435.9\n", + "-2436.9\n", + "-2437.9\n", + "-2438.9\n", + "-2439.9\n", + "-2440.9\n", + "-2441.9\n", + "-2442.9\n", + "-2443.9\n", + "-2444.9\n", + "-2445.9\n", + "-2446.9\n", + "-2447.9\n", + "-2448.9\n", + "-2449.9\n", + "-2450.9\n", + "-2451.9\n", + "-2452.9\n", + "-2453.9\n", + "-2454.9\n", + "-2455.9\n", + "-2456.9\n", + "-2457.9\n", + "-2458.9\n", + "-2459.9\n", + "-2460.9\n", + "-2461.9\n", + "-2462.9\n", + "-2463.9\n", + "-2464.9\n", + "-2465.9\n", + "-2466.9\n", + "-2467.9\n", + "-2468.9\n", + "-2469.9\n", + "-2470.9\n", + "-2471.9\n", + "-2472.9\n", + "-2473.9\n", + "-2474.9\n", + "-2475.9\n", + "-2476.9\n", + "-2477.9\n", + "-2478.9\n", + "-2479.9\n", + "-2480.9\n", + "-2481.9\n", + "-2482.9\n", + "-2483.9\n", + "-2484.9\n", + "-2485.9\n", + "-2486.9\n", + "-2487.9\n", + "-2488.9\n", + "-2489.9\n", + "-2490.9\n", + "-2491.9\n", + "-2492.9\n", + "-2493.9\n", + "-2494.9\n", + "-2495.9\n", + "-2496.9\n", + "-2497.9\n", + "-2498.9\n", + "-2499.9\n", + "-2500.9\n", + "-2501.9\n", + "-2502.9\n", + "-2503.9\n", + "-2504.9\n", + "-2505.9\n", + "-2506.9\n", + "-2507.9\n", + "-2508.9\n", + "-2509.9\n", + "-2510.9\n", + "-2511.9\n", + "-2512.9\n", + "-2513.9\n", + "-2514.9\n", + "-2515.9\n", + "-2516.9\n", + "-2517.9\n", + "-2518.9\n", + "-2519.9\n", + "-2520.9\n", + "-2521.9\n", + "-2522.9\n", + "-2523.9\n", + "-2524.9\n", + "-2525.9\n", + "-2526.9\n", + "-2527.9\n", + "-2528.9\n", + "-2529.9\n", + "-2530.9\n", + "-2531.9\n", + "-2532.9\n", + "-2533.9\n", + "-2534.9\n", + "-2535.9\n", + "-2536.9\n", + "-2537.9\n", + "-2538.9\n", + "-2539.9\n", + "-2540.9\n", + "-2541.9\n", + "-2542.9\n", + "-2543.9\n", + "-2544.9\n", + "-2545.9\n", + "-2546.9\n", + "-2547.9\n", + "-2548.9\n", + "-2549.9\n", + "-2550.9\n", + "-2551.9\n", + "-2552.9\n", + "-2553.9\n", + "-2554.9\n", + "-2555.9\n", + "-2556.9\n", + "-2557.9\n", + "-2558.9\n", + "-2559.9\n", + "-2560.9\n", + "-2561.9\n", + "-2562.9\n", + "-2563.9\n", + "-2564.9\n", + "-2565.9\n", + "-2566.9\n", + "-2567.9\n", + "-2568.9\n", + "-2569.9\n", + "-2570.9\n", + "-2571.9\n", + "-2572.9\n", + "-2573.9\n", + "-2574.9\n", + "-2575.9\n", + "-2576.9\n", + "-2577.9\n", + "-2578.9\n", + "-2579.9\n", + "-2580.9\n", + "-2581.9\n", + "-2582.9\n", + "-2583.9\n", + "-2584.9\n", + "-2585.9\n", + "-2586.9\n", + "-2587.9\n", + "-2588.9\n", + "-2589.9\n", + "-2590.9\n", + "-2591.9\n", + "-2592.9\n", + "-2593.9\n", + "-2594.9\n", + "-2595.9\n", + "-2596.9\n", + "-2597.9\n", + "-2598.9\n", + "-2599.9\n", + "-2600.9\n", + "-2601.9\n", + "-2602.9\n", + "-2603.9\n", + "-2604.9\n", + "-2605.9\n", + "-2606.9\n", + "-2607.9\n", + "-2608.9\n", + "-2609.9\n", + "-2610.9\n", + "-2611.9\n", + "-2612.9\n", + "-2613.9\n", + "-2614.9\n", + "-2615.9\n", + "-2616.9\n", + "-2617.9\n", + "-2618.9\n", + "-2619.9\n", + "-2620.9\n", + "-2621.9\n", + "-2622.9\n", + "-2623.9\n", + "-2624.9\n", + "-2625.9\n", + "-2626.9\n", + "-2627.9\n", + "-2628.9\n", + "-2629.9\n", + "-2630.9\n", + "-2631.9\n", + "-2632.9\n", + "-2633.9\n", + "-2634.9\n", + "-2635.9\n", + "-2636.9\n", + "-2637.9\n", + "-2638.9\n", + "-2639.9\n", + "-2640.9\n", + "-2641.9\n", + "-2642.9\n", + "-2643.9\n", + "-2644.9\n", + "-2645.9\n", + "-2646.9\n", + "-2647.9\n", + "-2648.9\n", + "-2649.9\n", + "-2650.9\n", + "-2651.9\n", + "-2652.9\n", + "-2653.9\n", + "-2654.9\n", + "-2655.9\n", + "-2656.9\n", + "-2657.9\n", + "-2658.9\n", + "-2659.9\n", + "-2660.9\n", + "-2661.9\n", + "-2662.9\n", + "-2663.9\n", + "-2664.9\n", + "-2665.9\n", + "-2666.9\n", + "-2667.9\n", + "-2668.9\n", + "-2669.9\n", + "-2670.9\n", + "-2671.9\n", + "-2672.9\n", + "-2673.9\n", + "-2674.9\n", + "-2675.9\n", + "-2676.9\n", + "-2677.9\n", + "-2678.9\n", + "-2679.9\n", + "-2680.9\n", + "-2681.9\n", + "-2682.9\n", + "-2683.9\n", + "-2684.9\n", + "-2685.9\n", + "-2686.9\n", + "-2687.9\n", + "-2688.9\n", + "-2689.9\n", + "-2690.9\n", + "-2691.9\n", + "-2692.9\n", + "-2693.9\n", + "-2694.9\n", + "-2695.9\n", + "-2696.9\n", + "-2697.9\n", + "-2698.9\n", + "-2699.9\n", + "-2700.9\n", + "-2701.9\n", + "-2702.9\n", + "-2703.9\n", + "-2704.9\n", + "-2705.9\n", + "-2706.9\n", + "-2707.9\n", + "-2708.9\n", + "-2709.9\n", + "-2710.9\n", + "-2711.9\n", + "-2712.9\n", + "-2713.9\n", + "-2714.9\n", + "-2715.9\n", + "-2716.9\n", + "-2717.9\n", + "-2718.9\n", + "-2719.9\n", + "-2720.9\n", + "-2721.9\n", + "-2722.9\n", + "-2723.9\n", + "-2724.9\n", + "-2725.9\n", + "-2726.9\n", + "-2727.9\n", + "-2728.9\n", + "-2729.9\n", + "-2730.9\n", + "-2731.9\n", + "-2732.9\n", + "-2733.9\n", + "-2734.9\n", + "-2735.9\n", + "-2736.9\n", + "-2737.9\n", + "-2738.9\n", + "-2739.9\n", + "-2740.9\n", + "-2741.9\n", + "-2742.9\n", + "-2743.9\n", + "-2744.9\n", + "-2745.9\n", + "-2746.9\n", + "-2747.9\n", + "-2748.9\n", + "-2749.9\n", + "-2750.9\n", + "-2751.9\n", + "-2752.9\n", + "-2753.9\n", + "-2754.9\n", + "-2755.9\n", + "-2756.9\n", + "-2757.9\n", + "-2758.9\n", + "-2759.9\n", + "-2760.9\n", + "-2761.9\n", + "-2762.9\n", + "-2763.9\n", + "-2764.9\n", + "-2765.9\n", + "-2766.9\n", + "-2767.9\n", + "-2768.9\n", + "-2769.9\n", + "-2770.9\n", + "-2771.9\n", + "-2772.9\n", + "-2773.9\n", + "-2774.9\n", + "-2775.9\n", + "-2776.9\n", + "-2777.9\n", + "-2778.9\n", + "-2779.9\n", + "-2780.9\n", + "-2781.9\n", + "-2782.9\n", + "-2783.9\n", + "-2784.9\n", + "-2785.9\n", + "-2786.9\n", + "-2787.9\n", + "-2788.9\n", + "-2789.9\n", + "-2790.9\n", + "-2791.9\n", + "-2792.9\n", + "-2793.9\n", + "-2794.9\n", + "-2795.9\n", + "-2796.9\n", + "-2797.9\n", + "-2798.9\n", + "-2799.9\n", + "-2800.9\n", + "-2801.9\n", + "-2802.9\n", + "-2803.9\n", + "-2804.9\n", + "-2805.9\n", + "-2806.9\n", + "-2807.9\n", + "-2808.9\n", + "-2809.9\n", + "-2810.9\n", + "-2811.9\n", + "-2812.9\n", + "-2813.9\n", + "-2814.9\n", + "-2815.9\n", + "-2816.9\n", + "-2817.9\n", + "-2818.9\n", + "-2819.9\n", + "-2820.9\n", + "-2821.9\n", + "-2822.9\n", + "-2823.9\n", + "-2824.9\n", + "-2825.9\n", + "-2826.9\n", + "-2827.9\n", + "-2828.9\n", + "-2829.9\n", + "-2830.9\n", + "-2831.9\n", + "-2832.9\n", + "-2833.9\n", + "-2834.9\n", + "-2835.9\n", + "-2836.9\n", + "-2837.9\n", + "-2838.9\n", + "-2839.9\n", + "-2840.9\n", + "-2841.9\n", + "-2842.9\n", + "-2843.9\n", + "-2844.9\n", + "-2845.9\n", + "-2846.9\n", + "-2847.9\n", + "-2848.9\n", + "-2849.9\n", + "-2850.9\n", + "-2851.9\n", + "-2852.9\n", + "-2853.9\n", + "-2854.9\n", + "-2855.9\n", + "-2856.9\n", + "-2857.9\n", + "-2858.9\n", + "-2859.9\n", + "-2860.9\n", + "-2861.9\n", + "-2862.9\n", + "-2863.9\n", + "-2864.9\n", + "-2865.9\n", + "-2866.9\n", + "-2867.9\n", + "-2868.9\n", + "-2869.9\n", + "-2870.9\n", + "-2871.9\n", + "-2872.9\n", + "-2873.9\n", + "-2874.9\n", + "-2875.9\n", + "-2876.9\n", + "-2877.9\n", + "-2878.9\n", + "-2879.9\n", + "-2880.9\n", + "-2881.9\n", + "-2882.9\n", + "-2883.9\n", + "-2884.9\n", + "-2885.9\n", + "-2886.9\n", + "-2887.9\n", + "-2888.9\n", + "-2889.9\n", + "-2890.9\n", + "-2891.9\n", + "-2892.9\n", + "-2893.9\n", + "-2894.9\n", + "-2895.9\n", + "-2896.9\n", + "-2897.9\n", + "-2898.9\n", + "-2899.9\n", + "-2900.9\n", + "-2901.9\n", + "-2902.9\n", + "-2903.9\n", + "-2904.9\n", + "-2905.9\n", + "-2906.9\n", + "-2907.9\n", + "-2908.9\n", + "-2909.9\n", + "-2910.9\n", + "-2911.9\n", + "-2912.9\n", + "-2913.9\n", + "-2914.9\n", + "-2915.9\n", + "-2916.9\n", + "-2917.9\n", + "-2918.9\n", + "-2919.9\n", + "-2920.9\n", + "-2921.9\n", + "-2922.9\n", + "-2923.9\n", + "-2924.9\n", + "-2925.9\n", + "-2926.9\n", + "-2927.9\n", + "-2928.9\n", + "-2929.9\n", + "-2930.9\n", + "-2931.9\n", + "-2932.9\n", + "-2933.9\n", + "-2934.9\n", + "-2935.9\n", + "-2936.9\n", + "-2937.9\n", + "-2938.9\n", + "-2939.9\n", + "-2940.9\n", + "-2941.9\n", + "-2942.9\n", + "-2943.9\n", + "-2944.9\n", + "-2945.9\n", + "-2946.9\n", + "-2947.9\n", + "-2948.9\n", + "-2949.9\n" + ] + }, + { + "ename": "RecursionError", + "evalue": "maximum recursion depth exceeded while calling a Python object", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mRecursionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mcountdown\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mcountdown\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 11\u001b[0;31m \u001b[0mcountdown\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "... last 1 frames repeated, from the frame below ...\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mcountdown\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 11\u001b[0;31m \u001b[0mcountdown\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mRecursionError\u001b[0m: maximum recursion depth exceeded while calling a Python object" + ] + } + ], + "source": [ + "countdown(3.1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In the same way, a `RecursionError` occurs if we call `factorial()` with `3.1` instead of `3`." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "RecursionError", + "evalue": "maximum recursion depth exceeded in comparison", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mRecursionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 12\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "... last 1 frames repeated, from the frame below ...\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 12\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mRecursionError\u001b[0m: maximum recursion depth exceeded in comparison" + ] + } + ], + "source": [ + "factorial(3.1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The infinite recursions could easily be avoided by replacing `n == 0` with `n <= 0` in both functions and thereby **generalizing** them. But even then, calling either `countdown()` or `factorial()` with a non-integer number is *semantically* wrong and therefore we better leave the base cases unchanged.\n", + "\n", + "Errors as above are a symptom of missing **type checking**: By design, Python allows us to pass in not only integers but objects of any type as arguments to the `countdown()` and `factorial()` functions. As long as the arguments \"behave\" like integers, we will not encounter any *runtime* errors. This is the case here as the two example functions only use the `-` and `*` operators internally and in this context a `float` object behaves exactly like an `int` object. So, the functions keep calling themselves until Python decides with a built-in heuristic that the recursion is likely not going to end and aborts the computations with a `RecursionError`. Stricly speaking, this is of course a *runtime* error as well. The missing type checking is 100% intentional and considered a feature of rather than a bug in Python.\n", + "\n", + "Pythonistas often use the term **[duck typing](https://en.wikipedia.org/wiki/Duck_typing)** when refering to the same idea and the colloquial saying goes \"If it walks like a duck and it quacks like a duck, it must be a duck\". For example, we could call `factorial()` with the `float` object `3.0` and the recursion works out fine. So, as long as the `3.0` \"walks\" like a `3` and \"quacks\" like a `3`, it \"must be\" a `3`.\n", + "\n", + "We see a similar behavior when we mix objects of types `int` and `float` with arithmetic operators. For example, `1 + 2.0` works because Python implicitly views the `1` as a `1.0` at runtime and then knows how to do floating-point arithmetic: Here, the `int` \"walks\" and \"quacks\" like a `float`. Strictly speaking, this is yet another example of operator overloading whereas duck typing refers to the same behavior when passing arguments to function calls.\n", + "\n", + "The important lesson is that we must expect our functions to be called with objects of *any* type at runtime, as opposed to the one type we had in mind when we defined the function.\n", + "\n", + "Duck typing is possible because Python is a dynamically typed language. On the contrary, in statically typed languages like C we have to declare (i.e., \"specify\") the data type of every parameter in a function definition. Then, a `RecursionError` as for `countdown(3.1)` or `factorial(3.1)` above could not occur. For example, if we declared the `countdown()` and `factorial()` functions to only accept `int` objects, calling the functions with a `float` argument would immediately fail *syntactically*. As a downside, we would then lose the ability to call `factorial()` with `3.0`, which is *semantically* correct nevertheless.\n", + "\n", + "So, there is no black or white answer as to which of the two language designs is better. Yet, most professional programmers have very strong opinions with respect to duck typing reaching from \"love\" to \"hate\". This is another example as to how programming is a subjective art and not an \"objective\" science. Probably, Python is regarded more beginner friendly as `3` and `3.0` should intuitively be interchangeable." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Type Checking & Input Validation" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We can use the built-in [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function to make sure `factorial()` is called with an `int` object passed in. We further **validate the input** by verifying that the integer is non-negative.\n", + "\n", + "Meanwhile, we also see how we can manually raise exceptions with the `raise` [statement](https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement), another way of controlling the flow of execution.\n", + "\n", + "The first two branches in the revised `factorial()` function act as **guardians** ensuring that the code does not produce *unexpected* runtime errors: Errors can certainly be expected when mentioned in the docstring.\n", + "\n", + "Forcing `n` to be an `int` is a very puritan way of handling the issues discussed above. A more relaxed approach could be to also accept a `float` and use its [is_integer()](https://docs.python.org/3/library/stdtypes.html#float.is_integer) method to check if `n` could be casted as an `int`. After all, by being too puritan we can not take advantage of duck typing.\n", + "\n", + "So in essence, we are doing *two* things here. Besides checking for the correct type, we are also enforcing **domain-specific** (i.e., mathematical) rules with respect to the non-negativity of `n`." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def factorial(n):\n", + " \"\"\"Calculate the factorial of a number.\n", + "\n", + " Args:\n", + " n (int): number to calculate the factorial for; must be positive\n", + "\n", + " Returns:\n", + " factorial (int)\n", + "\n", + " Raises:\n", + " TypeError: if n is not an integer\n", + " ValueError: if n is negative\n", + " \"\"\"\n", + " if not isinstance(n, int):\n", + " raise TypeError(\"Factorial is only defined for integers\")\n", + " elif n < 0:\n", + " raise ValueError(\"Factorial is not defined for negative integers\")\n", + " elif n == 0:\n", + " return 1\n", + " return n * factorial(n - 1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The revised `factorial()` function works like the old one." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3628800" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Instead of running into an infinite recursion, we now receive very specifc error messages." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "Factorial is only defined for integers", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 13\u001b[0m \"\"\"\n\u001b[1;32m 14\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 15\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 16\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is not defined for negative integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: Factorial is only defined for integers" + ] + } + ], + "source": [ + "factorial(3.1)" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "Factorial is not defined for negative integers", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m42\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 17\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is not defined for negative integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 18\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: Factorial is not defined for negative integers" + ] + } + ], + "source": [ + "factorial(-42)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### Theory of Computation" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With everything *officially* introduced so far (i.e., without the introductary example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) that only served as an overview), Python would be what is called **[Turing complete](https://en.wikipedia.org/wiki/Turing_completeness)**. That means that anything that could be formulated as an algorithm could be expressed with all the language features we have seen. Note that, in particular, we have *not* yet formally *introduced* the `for` and `while` statements!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Looping" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### The `while` Statement" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Whereas functions combined with `if` statements suffice to model any type of iteration with a recursion, Python comes with a `while` [statement](https://docs.python.org/3/reference/compound_stmts.html#the-while-statement) that often makes it easier to implement iterative ideas.\n", + "\n", + "It consists of a header line with a boolean expression followed by an indented code block. Before the first and after every execution of the code block, the boolean expression is evaluated and if it is (still) equal to `True`, the code block runs (again). Eventually, some variable referenced in the boolean expression is changed in the code block such that the condition becomes `False`.\n", + "\n", + "If the condition is `False` before the first iteration, the entire code block is *never* executed. As the flow of control keeps **looping** (or **iterating**) back to the beginning of the code block, this concept is also called a `while`-loop and each pass through the loop an **iteration**." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Trivial Example: Countdown (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's rewrite the `countdown()` example in an iterative style. We also build in **input validation** by allowing the function to only be called with strictly positive integers. As any positive integer will hit $0$ at some point in time when iteratively decremented by $1$, `countdown()` is guaranteed to **terminate**. Note also that the base case is now handled at the end of the function, which commonly happens with iterative solutions to problems." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def countdown(n):\n", + " \"\"\"Print a countdown until the party starts.\n", + "\n", + " Args:\n", + " n (int): seconds until the party begins; must be positive\n", + "\n", + " Raises:\n", + " TypeError: if n is not of an integer\n", + " ValueError: if n is not positive\n", + " \"\"\"\n", + " if not isinstance(n, int):\n", + " raise TypeError(\"Can only count down with whole numbers\")\n", + " elif n <= 0:\n", + " raise ValueError(\"n must be stricly positive\")\n", + "\n", + " while n != 0:\n", + " print(n)\n", + " n -= 1\n", + "\n", + " print(\"Happy new Year!\") # = base case" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3\n", + "2\n", + "1\n", + "Happy new Year!\n" + ] + } + ], + "source": [ + "countdown(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As [PythonTutor](http://pythontutor.com/visualize.html#code=def%20countdown%28n%29%3A%0A%20%20%20%20if%20not%20isinstance%28n,%20int%29%3A%0A%20%20%20%20%20%20%20%20raise%20TypeError%28%22...%22%29%0A%20%20%20%20elif%20n%20%3C%3D%200%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError%28%22...%22%29%0A%0A%20%20%20%20while%20n%20!%3D%200%3A%0A%20%20%20%20%20%20%20%20print%28n%29%0A%20%20%20%20%20%20%20%20n%20-%3D%201%0A%0A%20%20%20%20print%28%22Happy%20new%20Year!%22%29%0A%0Acountdown%283%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows, there is a subtle but important difference in the way a `while` statement is treated in memory: In short, `while` statements can *not* run into a `RecursionError` as only *one* frame is needed to manage the names. After all, there is only *one* function call to be made. For common day-to-day applications this difference is, however, not important." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### \"Still involved\" Example: [Euclid's Algorithm](https://en.wikipedia.org/wiki/Euclidean_algorithm) (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Finding the greatest common divisor of two numbers is still not so obvious when using a `while`-loop instead of a recursion.\n", + "\n", + "The iterative implementation of `gcd()` below accepts any two strictly positive integers. As in any iteration through the loop the smaller number is subtracted from the larger one, the two decremented values of `a` and `b` will eventually be equal. Thus, this algorithm is also guaranteed to terminate. If one of the two numbers were negative or $0$ to begin with, `gcd()` would run forever and not even Python could detect this. Try this out by removing the input validation and running the function with negative arguments!" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def gcd(a, b):\n", + " \"\"\"Calculate the greatest common divisor of two numbers.\n", + "\n", + " Args:\n", + " a (int): first number; must be positive\n", + " b (int): second number; must be positive\n", + "\n", + " Returns:\n", + " gcd (int)\n", + "\n", + " Raises:\n", + " TypeError: if a or b are not of an integer type\n", + " ValueError: if a or b are not positive\n", + " \"\"\"\n", + " if not isinstance(a, int) or not isinstance(b, int):\n", + " raise TypeError(\"Greatest common divisor is only defined for two integers\")\n", + " elif a <= 0 or b <= 0:\n", + " raise ValueError(\"a and b must be strictly positive\")\n", + "\n", + " while a != b:\n", + " if a > b:\n", + " a -= b\n", + " else:\n", + " b -= a\n", + "\n", + " return a" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gcd(12, 4)" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gcd(2, 7919)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "##### Efficiency of Algorithms (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We can also see that this implementation is a lot *less* efficient than its recursive counterpart which solves `gcd()` for the same two numbers $112233445566778899$ and $987654321$ within microseconds." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4.76 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n 1 -r 1\n", + "gcd(112233445566778899, 987654321)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Infinite Loops" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As with recursion, we must ensure that the iteration ends. For the above `countdown()` and `gcd()` examples we could \"prove\" (i.e., at least argue in favor) that some pre-defined **termination criterion** will be reached eventually. However, this cannot be done in all cases as the following example shows." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### \"Mystery\" Example: [Collatz Conjecture](https://en.wikipedia.org/wiki/Collatz_conjecture)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Let's play the following game:\n", + "- think of any positive integer $n$\n", + "- if $n$ is even, the next $n$ is half the old $n$\n", + "- if $n$ is odd, multiply the old $n$ by $3$ and add $1$ to obtain the next $n$\n", + "- repeat these steps until you reach $1$\n", + "\n", + "**Do we always reach the final $1$?**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The function below implements this game. Does it always reach $1$? No one has proven it so far! We include some input validation as before because `collatz()` would definitely not terminate if we called it with a negative number. Further, the Collatz sequence also works for real numbers but then we would have to study fractals (cf., [this](https://en.wikipedia.org/wiki/Collatz_conjecture#Iterating_on_real_or_complex_numbers)). So we restrict our example to integers only." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def collatz(n):\n", + " \"\"\"Print a Collatz sequence in descending order.\n", + "\n", + " Given a positive integer n, modify it according to these rules:\n", + " - if n is even, the next n is half the previous one\n", + " - if n is odd, the next n is 3 times the previous one plus 1\n", + " - if n is 1, stop the iteration\n", + "\n", + " Args:\n", + " n (int): a positive number to start the Collatz sequence at\n", + "\n", + " Raises:\n", + " TypeError: if n is not of an integer\n", + " ValueError: if n is not positive\n", + " \"\"\"\n", + " if not isinstance(n, int):\n", + " raise TypeError(\"non-integers require some advanced math\")\n", + " elif n <= 0:\n", + " raise ValueError(\"n must be stricly positive\")\n", + "\n", + " while n != 1:\n", + " print(n, end=\" \")\n", + " if n % 2 == 0:\n", + " n //= 2 # //= so that n remains an int\n", + " else:\n", + " n = 3 * n + 1\n", + "\n", + " print(n) # = base case" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Collatz sequences do not necessarily become longer with a larger initial `n`." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "100 50 25 76 38 19 58 29 88 44 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1\n" + ] + } + ], + "source": [ + "collatz(100)" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1000 500 250 125 376 188 94 47 142 71 214 107 322 161 484 242 121 364 182 91 274 137 412 206 103 310 155 466 233 700 350 175 526 263 790 395 1186 593 1780 890 445 1336 668 334 167 502 251 754 377 1132 566 283 850 425 1276 638 319 958 479 1438 719 2158 1079 3238 1619 4858 2429 7288 3644 1822 911 2734 1367 4102 2051 6154 3077 9232 4616 2308 1154 577 1732 866 433 1300 650 325 976 488 244 122 61 184 92 46 23 70 35 106 53 160 80 40 20 10 5 16 8 4 2 1\n" + ] + } + ], + "source": [ + "collatz(1000)" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "10000 5000 2500 1250 625 1876 938 469 1408 704 352 176 88 44 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1\n" + ] + } + ], + "source": [ + "collatz(10000)" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "100000 50000 25000 12500 6250 3125 9376 4688 2344 1172 586 293 880 440 220 110 55 166 83 250 125 376 188 94 47 142 71 214 107 322 161 484 242 121 364 182 91 274 137 412 206 103 310 155 466 233 700 350 175 526 263 790 395 1186 593 1780 890 445 1336 668 334 167 502 251 754 377 1132 566 283 850 425 1276 638 319 958 479 1438 719 2158 1079 3238 1619 4858 2429 7288 3644 1822 911 2734 1367 4102 2051 6154 3077 9232 4616 2308 1154 577 1732 866 433 1300 650 325 976 488 244 122 61 184 92 46 23 70 35 106 53 160 80 40 20 10 5 16 8 4 2 1\n" + ] + } + ], + "source": [ + "collatz(100000)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### The `for` Statement" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Recursion and the `while` statement are two sides of the same coin. Disregarding that in the case of recursion Python internally faces some additional burden for managing the stack of frames in memory, both approaches lead to the *same* computational steps in memory. More importantly, we can re-formulate a recursive implementation in an iterative way and vice verca despite one of the two ways often \"feeling\" a lot more natural given a particular problem.\n", + "\n", + "So how does the `for` [statement](https://docs.python.org/3/reference/compound_stmts.html#the-for-statement) we saw in the very first example in this book fit into this picture? It is really a *redundant* language construct to provide a *shorter* and more *convenient* syntax for common applications of the `while` statement. In programming, such additions to a language are called **syntactic sugar**. Sugar makes a cup of tea taste better but we can drink tea without sugar too.\n", + "\n", + "Consider the following `numbers` list. Without the `for` statement, we would have to keep track of a temporary **index variable** `i` to loop over all its elements and also obtain the individual elements with the `[]` operator in each iteration of the loop." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers = [5, 6, 7, 8, 9]" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5 6 7 8 9 " + ] + } + ], + "source": [ + "i = 0\n", + "while i < len(numbers):\n", + " number = numbers[i]\n", + " print(number, end=\" \")\n", + " i += 1" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `for` statement, on the contrary, makes the actual business logic more apparent by stripping all the boilerplate code away." + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5 6 7 8 9 " + ] + } + ], + "source": [ + "for number in numbers:\n", + " print(number, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For sequences of integers the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in makes the `for` statement even more convenient: It creates a list-like object of type `range` that generates integers \"on the fly\" and we will look closely at the underlying data types in Chapter 7." + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 1 2 3 4 " + ] + } + ], + "source": [ + "for number in [0, 1, 2, 3, 4]:\n", + " print(number, end=\" \")" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 1 2 3 4 " + ] + } + ], + "source": [ + "for number in range(5):\n", + " print(number, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's quickly verify that `range(5)` creates an object of type `range`." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "range" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(range(5))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[range()](https://docs.python.org/3/library/functions.html#func-range) takes optional `start` and `step` arguments that we can use to customize the sequence of integers even more." + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 3 5 7 9 " + ] + } + ], + "source": [ + "for number in [1, 3, 5, 7, 9]:\n", + " print(number, end=\" \")" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 3 5 7 9 " + ] + } + ], + "source": [ + "for number in range(1, 10, 2):\n", + " print(number, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Containers vs. Iterables" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The important difference between the `list` objects (i.e., `[0, 1, 2, 3, 4]` and `[1, 3, 5, 7, 9]`) and the `range` objects (i.e., `range(5)` and `range(1, 10, 2)`) is that in the former case *six* objects are created in memory, one `list` holding pointers to *five* `int` objects, whereas in the latter case only *one* `range` object exists in memory that **generates** `int` objects as we ask for it.\n", + "\n", + "However, we can iterate over both of them. So a natural question to ask is why Python treats objects of *different* types in the *same* way when used with a `for` statement.\n", + "\n", + "So far, the overarching storyline in this book goes like this: In Python, *everything* is an object. Besides its *identity* and *value*, every object is characterized by belonging to *one* data type that determines how the object behaves and what we can do with it.\n", + "\n", + "Now, just as we classify objects by their types, we also classify these **concrete data types** (e.g., `int`, `float`, or `str`) into **abstract concepts**.\n", + "\n", + "We have actually done this in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) already when we described a `list` object as \"some sort of container that holds [...] pointers to other objects\". So, abstractly speaking, **containers** are any objects that are \"composed\" of other objects and also \"manage\" how these objects are organized. `list` objects, for example, have the property that they model an inherent order associated with its elements. There exist, however, many other container types, many of which do *not* come with an order. So, containers primarily \"contain\" other objects and have *nothing* to do with looping.\n", + "\n", + "On the contrary, the abstract concept of **iterables** is all about looping: Any object that we can loop over is by definition an iterable. So, `range` objects, for example, are iterables taht do *not* contain any other objects. Moreover, looping does *not* have to occur in any particular order although this is the case for both `list` and `range` objects.\n", + "\n", + "Typically, containers are iterable and iterables are containers. Yet, only because these two concepts coincide often, we must not think of them as the same. Chapter 10 will finally give an explanation as to how abstract concepts are implemented and play together.\n", + "\n", + "`list` objects like `first_names` below are iterable containers. They actually implement even more abstract concepts as we will see in Chapter 7." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "first_names = [\"Achim\", \"Berthold\", \"Carl\", \"Diedrich\", \"Eckardt\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The characteristic operator associated with a container type is the `in` operator which checks if a given object evaluates equal to any of the objects in the container. Colloquially, it checks if an object is \"contained\" in the container. This operation is also called **membership testing**." + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Achim\" in first_names" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Alexander\" in first_names" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This shows the exact workings of the `in` operator: Although `7.0` is *not* in `numbers`, `7.0` evaluates equal to the `7` that is in it, which is why the following expression evaluates to `True`. So, while we could colloquially say that `numbers` \"contains\" `7.0`, it actually does not." + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "7.0 in numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Similarly, the characteristic operation of an iterable type is that it supports iteration, for example, with a `for`-loop." + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Achim Berthold Carl Diedrich Eckardt " + ] + } + ], + "source": [ + "for name in first_names:\n", + " print(name, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we need to have an index variable in the loop's body, we use the [enumerate()](https://docs.python.org/3/library/functions.html#enumerate) built-in that takes an iterable as its argument and then generates a stream of \"pairs\" consisting of an index variable and an object provided by the iterable. There is *no* need to ever revert back to the `while` statement to loop over an iterable object." + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 > Achim 1 > Berthold 2 > Carl 3 > Diedrich 4 > Eckardt " + ] + } + ], + "source": [ + "for i, name in enumerate(first_names):\n", + " print(i, name, sep=\" > \", end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[enumerate()](https://docs.python.org/3/library/functions.html#enumerate) takes an optional `start` argument." + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 > Achim 2 > Berthold 3 > Carl 4 > Diedrich 5 > Eckardt " + ] + } + ], + "source": [ + "for i, name in enumerate(first_names, start=1):\n", + " print(i, name, sep=\" > \", end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [zip()](https://docs.python.org/3/library/functions.html#zip) built-in allows us to combine the elements of two or more iterables in a *pairwise* fashion: It conceptually works like a zipper for a jacket." + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "last_names = [\"MĂĽller\", \"Meyer\", \"Mayer\", \"Schmitt\", \"Schmidt\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Achim MĂĽller\n", + "Berthold Meyer\n", + "Carl Mayer\n", + "Diedrich Schmitt\n", + "Eckardt Schmidt\n" + ] + } + ], + "source": [ + "for first_name, last_name in zip(first_names, last_names):\n", + " print(first_name, last_name)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### \"Hard at first Glance\" Example: [Fibonacci Numbers](https://en.wikipedia.org/wiki/Fibonacci_number) (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In contrast to its recursive counterpart, the iterative `fibonacci()` function below is somewhat harder to read. For example, it is not so obvious as to how many iterations through the `for`-loop we need to make when implementing it. There is an increased risk of making an *off-by-one* error. Moreover, we need to track a `temp` variable along, at least until we have worked through Chapter 7. Do you understand what `temp` does?\n", + "\n", + "However, one advantage of calculating Fibonacci numbers in a **forwards** fashion with a `for` statement is that we could list the entire sequence in ascending order as we calculate the desired number. To show this, we added `print()` statements in `fibonacci()` below.\n", + "\n", + "We do *not* need to store the index variable in the `for`-loop's header line: That is what the underscore \"\\_\" indicates; we \"throw it away\". Also, we do not need to explicitly check if `i` is of type `int` as the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in raises a `TypeError` if used with anything other than an `int`." + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def fibonacci(i):\n", + " \"\"\"Calculate the ith Fibonacci number.\n", + "\n", + " Args:\n", + " i (int): index of the Fibonacci number to calculate\n", + "\n", + " Returns:\n", + " ith_fibonacci (int)\n", + "\n", + " Raises:\n", + " TypeError: if i is not of an integer type\n", + " ValueError: if i is not positive\n", + " \"\"\"\n", + " # no need to check if i is an integer as range() does that\n", + " if i < 0:\n", + " raise ValueError(\"i must be non-negative\")\n", + "\n", + " a = 0\n", + " b = 1\n", + " print(a, b, sep=\" \", end=\" \") # added for didactical purposes\n", + " for _ in range(i - 1): # the index variable is not needed\n", + " temp = a + b\n", + " a = b\n", + " b = temp\n", + " print(b, end=\" \") # added for didactical purposes\n", + "\n", + " return b" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 1 1 2 3 5 8 13 21 34 55 89 144 " + ] + }, + { + "data": { + "text/plain": [ + "144" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fibonacci(12) # = 13th number" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "##### Efficiency of Algorithms (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Another more important advantage is that now we can calculate even big Fibonacci numbers *efficiently*." + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465 14930352 24157817 39088169 63245986 102334155 165580141 267914296 433494437 701408733 1134903170 1836311903 2971215073 4807526976 7778742049 12586269025 20365011074 32951280099 53316291173 86267571272 139583862445 225851433717 365435296162 591286729879 956722026041 1548008755920 2504730781961 4052739537881 6557470319842 10610209857723 17167680177565 27777890035288 44945570212853 72723460248141 117669030460994 190392490709135 308061521170129 498454011879264 806515533049393 1304969544928657 2111485077978050 3416454622906707 5527939700884757 8944394323791464 14472334024676221 23416728348467685 37889062373143906 61305790721611591 99194853094755497 160500643816367088 259695496911122585 420196140727489673 679891637638612258 1100087778366101931 1779979416004714189 2880067194370816120 4660046610375530309 7540113804746346429 12200160415121876738 19740274219868223167 31940434634990099905 51680708854858323072 83621143489848422977 135301852344706746049 218922995834555169026 " + ] + }, + { + "data": { + "text/plain": [ + "218922995834555169026" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fibonacci(99) # = 100th number" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Easy Example: [Factorial](https://en.wikipedia.org/wiki/Factorial) (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The iterative `factorial()` function is comparable to its recursive counterpart when it comes to readability. One advantage of calculating the factorial in a forwards fashion is that we could track the intermediate `product` as it grows." + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def factorial(n):\n", + " \"\"\"Calculate the factorial of a number.\n", + "\n", + " Args:\n", + " n (int): number to calculate the factorial for, must be positive\n", + "\n", + " Returns:\n", + " factorial (int)\n", + "\n", + " Raises:\n", + " TypeError: if n is not an integer\n", + " ValueError: if n is negative\n", + " \"\"\"\n", + " if not isinstance(n, int):\n", + " raise TypeError(\"Factorial is only defined for integers\")\n", + " elif n < 0:\n", + " raise ValueError(\"Factorial is not defined for negative integers\")\n", + "\n", + " product = 1 # because 0! = 1\n", + " for i in range(1, n + 1): # loop starts at 1 as 0! is already covered\n", + " product *= i\n", + " print(product, end=\" \") # added for didactical purposes\n", + "\n", + " return product" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 2 6 24 120 720 5040 40320 362880 3628800 " + ] + }, + { + "data": { + "text/plain": [ + "3628800" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### The `break` Statement" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The remainder of this chapter introduces more syntactic sugar. None of the language constructs introduced below are actually needed but contribute to making Python the expressive and easy to read language that it is." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Example: Searching an Iterable" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's say we have a list of numbers and we want to check if the square of at least one of its numbers is above a `threshold` of `100`." + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers = [3, 7, 2, 9, 11, 4, 7, 9, 4, 5]" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "threshold = 100 # is the square of an element in numbers greater than this?" + ] + }, + { + "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` to `True` once we encounter an element satisfying our search condition." + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3 7 2 9 11 4 7 9 4 5 => at least one number's square is above 100\n" + ] + } + ], + "source": [ + "is_above = False\n", + "\n", + "for number in numbers:\n", + " print(number, end=\" \") # added for didactical purposes\n", + " if number ** 2 > threshold:\n", + " is_above = True\n", + "\n", + "if is_above:\n", + " print(\"=> at least one number's square is above\", threshold)\n", + "else:\n", + " print(\"=> no number's square is above\", threshold)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This implementation is *inefficient* as even if the *first* number in `numbers` has a square greater than `100`, we loop until the last element, which 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 seperate from it to check for the final result. The actual business logic is not clear right away.\n", + "\n", + "Luckily, Python provides a `break` [statement](https://docs.python.org/3/reference/simple_stmts.html#the-break-statement) that let's us stop the `for`-loop in any iteration of the loop. Conceptually, it is yet another means of controlling the flow of execution." + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3 7 2 9 11 => at least one number's square is above 100\n" + ] + } + ], + "source": [ + "is_above = False\n", + "\n", + "for number in numbers:\n", + " print(number, end=\" \") # added for didactical purposes\n", + " if number ** 2 > threshold:\n", + " is_above = True\n", + " break\n", + "\n", + "if is_above:\n", + " print(\"=> at least one number's square is above\", threshold)\n", + "else:\n", + " print(\"=> no number's square is above\", threshold)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This is a computational improvement. However, the code can still be split into *three* groups: initialization, the `for`-loop, and some finalizing logic. We would prefer to convey the program's idea in *one* compound statement." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### The `for`-`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. The `else`-branch is only executed if the body in the `for`-branch is *not* stopped with a `break` statement before reaching the last iteration in the loop. The word \"else\" implies a rather unintuitive meaning and it had better been named a `then`-clause. In most scenarios, however, the `else`-clause logically goes together with some `if` statement within the `for`-loop's body.\n", + "\n", + "Overall, the expressive power of our code increases. Not many programming languages support a `for`-`else`-branching, which turns out to be very useful in practice." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### Example: Searching an Iterable (revisited)" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3 7 2 9 11 => at least one number's square is above 100\n" + ] + } + ], + "source": [ + "for number in numbers:\n", + " print(number, end=\" \") # added for didactical purposes\n", + " if number ** 2 > threshold:\n", + " is_above = True\n", + " break\n", + "else:\n", + " is_above = False\n", + "\n", + "if is_above:\n", + " print(\"=> at least one number's square is above\", threshold)\n", + "else:\n", + " print(\"=> no number's square is above\", threshold)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Lastly, we incorporate the `if`-`else` logic at the end into the `for`-loop and avoid the `is_above` variable alltogether." + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3 7 2 9 11 => at least one number's square is above 100\n" + ] + } + ], + "source": [ + "for number in numbers:\n", + " print(number, end=\" \") # added for didactical purposes\n", + " if number ** 2 > threshold:\n", + " print(\"=> at least one number's square is above\", threshold)\n", + " break\n", + "else:\n", + " print(\"=> no number's square is above\", threshold)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Of course, if we set the `threshold` a number's square has to pass higher, for example to `200`, we have to loop through the entire `numbers` list. There is no way to optimize this **[linear search](https://en.wikipedia.org/wiki/Linear_search)**, at least as long as we model the list of numbers with a `list` object. More advanced data types, however, exist that mitigate that downside." + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3 7 2 9 11 4 7 9 4 5 => no number's square is above 200\n" + ] + } + ], + "source": [ + "threshold = 200\n", + "\n", + "for number in numbers:\n", + " print(number, end=\" \") # added for didactical purposes\n", + " if number ** 2 > threshold:\n", + " print(\"=> at least one number's square is above\", threshold)\n", + " break\n", + "else:\n", + " print(\"=> no number's square is above\", threshold)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### The `continue` Statement" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Often times, we process some iterable with numeric data, for example, a list of numbers as in the introductory example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) or, more realistically, data from a CSV file with many rows and columns.\n", + "\n", + "Processing numeric data usually comes down to operations that can be grouped into one of the following three categories:\n", + "\n", + "- **mapping**: transform an observation according to some functional relationship $y = f(x)$\n", + "- **filtering**: throw away individual observations (e.g., statistical outliers)\n", + "- **reducing**: collect individual observations into summary statistics\n", + "\n", + "We will study this **map-filter-reduce** paradigm extensively in Chapter 7 after introducing more advanced data types that are needed to work with \"big\" data.\n", + "\n", + "In this section, we focus on *filtering out* some samples within 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 from $1$ through $12$ 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": 77, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2 > 5 4 > 17 6 > 37 8 > 65 10 > 101 12 > 145 " + ] + }, + { + "data": { + "text/plain": [ + "370" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "total = 0\n", + "\n", + "for x in numbers:\n", + " if x % 2 == 0: # only keep even numbers\n", + " y = (x ** 2) + 1\n", + " print(x, y, sep=\" > \", end=\" \") # added for didactical purposes\n", + " total += y\n", + "\n", + "total" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The above code is still rather easy to read as it only involves 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 complient with [PEP 8](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: Nested Filters" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Calculate the sum of every third and even number from $1$ through $12$ 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": 79, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6 > 37 12 > 145 " + ] + }, + { + "data": { + "text/plain": [ + "182" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "total = 0\n", + "\n", + "for i, x in enumerate(numbers, start=1):\n", + " if i % 3 == 0: # only keep every third number\n", + " if x % 2 == 0: # only keep even numbers\n", + " y = (x ** 2) + 1\n", + " print(x, y, sep=\" > \", end=\" \") # added for didactical purposes \n", + " total += y\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. Of course, one could combine the two `if` statements with the logical `and` operator as shown in [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb). However, then we trade off horizontal space against a more \"complex\" `if` logic and this is not a real improvement.\n", + "\n", + "A Pythonista would instead make use of the `continue` [statement](https://docs.python.org/3/reference/simple_stmts.html#the-continue-statement) that causes a loop to jump right into the next iteration skipping the rest of the code block.\n", + "\n", + "The revised code fragement below occupies more vertical space and less horizontal space. A good trade-off." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### Example: Nested Filters (revisited)" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6 > 37 12 > 145 " + ] + }, + { + "data": { + "text/plain": [ + "182" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "total = 0\n", + "\n", + "for i, x in enumerate(numbers, start=1):\n", + " if i % 3 != 0: # only keep every third number\n", + " continue\n", + " elif x % 2 != 0: # only keep even numbers\n", + " continue\n", + "\n", + " y = (x ** 2) + 1\n", + " print(x, y, sep=\" > \", end=\" \") # added for didactical purposes \n", + " total += y\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 fragments do *exactly* the *same* with *identical* time complexity. However, arguably the latter it a lot easier to read for a human, at least when the business logic grows beyond more than just two nested filters.\n", + "\n", + "The idea behind the `continue` statement is conceptually similar to the early exit pattern we saw in the context of function definitions in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb).\n", + "\n", + "The two examples can be modeled in an even better way as we will see in Chapter 7.\n", + "\n", + "Both the `break` and `continue` statements as well as the optional `else`-clause are not only supported within `for`-loops but also `while`-loops." + ] + }, + { + "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 can *not* 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()](https://docs.python.org/3/library/functions.html#input) function that prints a message to the user, called the **prompt**, and reads in what the user typed in response as a `str` object. We use it to process the user's \"unpredictable\" input to our program (i.e., a user might type in some invalid response). Further, we use the [random()](https://docs.python.org/3/library/random.html#random.random) function in the [random](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 certain event occurs, we `break` out of the loop.\n", + "\n", + "Let's look at a first naive implementation." + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import random" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "random.seed(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Guess if the coin comes up as heads or tails: Tails\n", + "Ooops, it was tails\n", + "Guess if the coin comes up as heads or tails: heads\n", + "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 has two *severe* aspects where we should improve on:\n", + "\n", + "1. If a user enters something other than \"heads\" or \"tails\", for example, \"Heads\" or \"Tails\", the program keeps running without the user knowing about the mistake!\n", + "2. It intermingles the coin tossing with the comparison against the user's input: Mixing unrelated business logic in the same code fragment makes a program harder to read and 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* and *comprehendable*.\n", + "\n", + "First, we divide the 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()](https://docs.python.org/3/library/stdtypes.html?highlight=__contains__#str.strip) and [lower()](https://docs.python.org/3/library/stdtypes.html?highlight=__contains__#str.lower) methods remove preceeding and trailing whitespace and lower case the input ensuring that the user can 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": 85, + "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()` is a simple function that models a fair coin toss when called with default arguments." + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "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 explictly handle the case where `get_guess()` returns `None`: Whenever the user enters something invalid, a warning is shown, and another try is granted. Observe how we use the `is` operator and not the `==` operator as `None` is a singleton object.\n", + "\n", + "The `while` statement itself takes on the role of **glue code** that only manages how other parts of the program interact with each other." + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Guess if the coin comes up as heads or tails: invalid\n", + "Make sure to enter your guess correctly!\n", + "Guess if the coin comes up as heads or tails: Heads\n", + "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, our little program's business logic is a lot easier to comprehend. More importantly, we can now easily make changes to the program. 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()](https://docs.python.org/3/library/random.html#random.random) function with another one). In general, a modular architecture leads to improved software maintenance." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## 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 of achieving that.\n", + "\n", + "First, we can 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 **backwards** reasoning might not be intuitive but it turns out to be a very useful 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 **forwards** reasoning. The `for` statement is **syntactic sugar** that allows to rewrite common occurences of the `while` statement in a concise way. 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\" pointers to other objects in memory. On the contrary, the `range` type does not point to any other objects but rather creates new `int` objects \"on the fly\"." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "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": 2 +} From 2c8ff65f57ea3efa01e327e4c9c14451db9de096 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 1 Oct 2019 17:50:58 +0200 Subject: [PATCH 026/242] Adjust content overview in README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 951bf75..4561afb 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ As such they can be viewed in a plain web browser: - [01 - Elements of a Program](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) - [02 - Functions & Modularization](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) - [03 - Conditionals & Exceptions](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb) +- [04 - Recursion & Looping](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb) However, it is recommended that students **install Python and Jupyter locally** and run the code in the notebooks on their own. From 6385153e38219106efdbaced296f3175d20cc7de Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 1 Oct 2019 23:21:05 +0200 Subject: [PATCH 027/242] Add review and exercises for notebook 04 --- 04_iteration_review_and_exercises.ipynb | 941 ++++++++++++++++++++++++ static/towers_of_hanoi.gif | Bin 0 -> 197609 bytes 2 files changed, 941 insertions(+) create mode 100644 04_iteration_review_and_exercises.ipynb create mode 100644 static/towers_of_hanoi.gif diff --git a/04_iteration_review_and_exercises.ipynb b/04_iteration_review_and_exercises.ipynb new file mode 100644 index 0000000..0e937b7 --- /dev/null +++ b/04_iteration_review_and_exercises.ipynb @@ -0,0 +1,941 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "# Chapter 4: Iteration" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Content Review" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb) of the book. Then work through the fourteen review questions." + ] + }, + { + "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**: What is so \"special\" about the number **7919**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: Solving a problem with a **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 **backwards** fashion!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: Explain what **duck typing** means! Why can it cause problems? Why it is [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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: What is **syntactic sugar**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: What is the conceptual difference between a **container** and a **list**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: What is a good use case for the `for`-loop's optional `else`-clause?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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**: When a **recursion** does **not** reach the base case, this is an example of the **early exit** strategy." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: Any programming language **without** looping constructs like the `for` or `while` statements is **not** Turing complete." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: A **recursive** formulation is the same as a **circular** one: The terms are **synonyms**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: Formulating a computational problem as a **recursion** results in an **efficient** implementation." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q12**: Whereas a **recursion** may accidently result in a **never ending** program, `while`-loops and `for`-loops are guaranteed to **terminate**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q13**: Before writing **any** kind of **loop**, we **always** need to think about a **stopping criterion** ahead of time." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q14**: **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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Coding Exercises" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Towers of Hanoi" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A popular example for a problem that is solved with a recursion art the **[Towers of Hanoi](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](https://en.wikipedia.org/wiki/Tower_of_Hanoi)** are a **classic** example, introduced by the mathematician [Édouard Lucas](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": [ + "" + ] + }, + { + "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 Initative](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 in order 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": [ + "**Q15.1**: Explain for the $n = 3$ case why it can be solved as a **recursion**!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.2**: 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 **Q15.1**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.3**: The **[Towers of Hanoi](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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.4**: 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 **Q15.1**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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 progam'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.\n", + "\n", + "While the first number of $Sol(\\cdot)$ is the number of `disks` $n$, the second and third \"numbers\" are actually the **labels** for the three spots. Instead of spots `1`, `2`, and `3` we could also call them `\"left\"`, `\"center\"`, and `\"right\"` in our Python implementation. When \"passed\" to the $Sol(\\cdot)$ \"function\" they take on the role of an `origin` (= $o$) and `destination` (= $d$) pair.\n", + "\n", + "So, the expression $Sol(4, 1, 3)$ is the same as $Sol(4, \\text{\"left\"}, \\text{\"right\"})$ and describes the problem of moving a tower consisting of $n = 4$ disks from `origin` `1` / `\"left\"` to `destination` `3` / `right`. As we have seen in the video, we need some `intermediate` (= $i$) spot." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In summary, the generalized functional relationship can be expressed as:\n", + "\n", + "$Sol(n, o, d) = Sol(n-1, o, i) ~ \\bigoplus ~ Sol(1, o, d) ~ \\bigoplus ~ Sol(n-1, i, d)$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In words, this means that in order to move a tower consisting of $n$ disks from an `origin` $o$ to a `destination` $d$, we three steps must be executed:\n", + "\n", + "1. Move the top most $n - 1$ disks of the tower temporarily from $o$ to $i$ (= sub-problem 1)\n", + "2. Move the remaining and largest disk from $o$ to $d$\n", + "3. Move the the $n - 1$ disks from the temporary spot $i$ to $d$ (= sub-problem 2)\n", + "\n", + "The two sub-problems can be solved via the same recursive logic." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$Sol(\\cdot)$ can be written in Python as a function `sol()` that takes three arguments `disks`, `origin`, and `destination` that mirror $n$, $o$, and $d$.\n", + "\n", + "Assume that all arguments to `sol()` will be `int` objects!\n", + "\n", + "Once completed, `sol()` should print out all the moves in the correct order. With **printing a move**, we simply mean a line like \"1 -> 3\", short for \"Move the top-most disk from spot 1 to spot 3\".\n", + "\n", + "Write your answers to **Q15.5** to **Q15.7** into the subsequent code cell and finalize `sol()`! No need to write a docstring or validate the input here." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def sol(disks, origin, destination):\n", + "\n", + " # answer to Q15.5\n", + " # ...\n", + "\n", + " # answer to Q15.6\n", + " # ...\n", + " # ...\n", + " # ...\n", + " # ...\n", + " # ...\n", + " # ...\n", + " # ...\n", + " # ...\n", + " # ...\n", + " # ...\n", + " # ...\n", + "\n", + " # answer to Q15.7\n", + " # ...\n", + " # ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.5**: What is the `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!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.6**: If not in the base case, `sol()` needs to determine 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 sets a variable `intermediate` to the correct temporary spot. **How many** branches will there be?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.7**: `sol()` needs to call itself **two more times** with the correct 2-pairs chosen from the three available spots `origin`, `intermediate`, and `destination`.\n", + "\n", + "In between the two recursive function calls, write a `print()` statement that prints out from where to where the \"remaining and largest\" disk has to be moved!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.8**: Execute the code cells below and confirm that the printed 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 Re-Factoring" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The previous `sol()` implementation does the job but the conditional statement needed in unnecessarily tedious. \n", + "\n", + "Let's create a more concise `hanoi()` function that in addition to a positional `disks` argument takes three keyword-only arguments `origin`, `intermediate`, and `destination` with default values `\"left\"`, `\"center\"`, and `\"right\"`.\n", + "\n", + "Write your answers to **Q15.9** and **Q15.10** into the subsequent code cell and finalize `hanoi()`! No need to write a docstring or validate the input here." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def hanoi(disks, *, origin=\"left\", intermediate=\"center\", destination=\"right\"):\n", + "\n", + " # answer to Q15.9\n", + " # ...\n", + "\n", + " # answer to Q15.10\n", + " # ...\n", + " # ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.9**: Copy the base case from `sol()`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.10**: 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!\n", + "\n", + "Also, write a `print()` statement analogous to the one in `sol()` in between the two recursive function calls. Is it ok to just copy and paste it?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.11**: Execute the code cells below and confirm that the printed 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 \"up\" the Recursion Tree" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's say, we did not know about the **analytical formula** for the number of **minimal moves** given $n$.\n", + "\n", + "In such cases, we could modify a recursive function to return a count value to be passed up the recursion tree.\n", + "\n", + "In fact, this is similar to what we do in the recursive versions of `factorial()` and `fibonacci()` in [Chapter 4](https://github.com/webartifex/intro-to-python/blob/master/04_iteration.ipynb) where we pass up an intermediate result.\n", + "\n", + "Let's create a `hanoi_moves()` function that follows the same internal logic as `hanoi()` but instead of printing out the moves returns the number of steps done so far in the recursion.\n", + "\n", + "Write your answers to **Q15.12** to **Q15.14** into the subsequent code cell and finalize `hanoi_moves()`! No need to write a docstring or validate the input here." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def hanoi_moves(disks, *, origin=\"left\", intermediate=\"center\", destination=\"right\"):\n", + "\n", + " # answer to Q15.12\n", + " # ...\n", + "\n", + " moves = ... # <- answer to Q15.13\n", + " moves += hanoi_moves(...) # <- answer to Q15.14 between the ()\n", + " moves += hanoi_moves(...) # <- answer to Q15.14 between the ()\n", + "\n", + " return moves" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.12**: Copy the base case from `hanoi()`! What count should be returned when it is reached?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.13**: Initialize the variable `moves` with an appropriate count! This is the number of moves that corresponds to **one** recursive function call." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.14**: `moves` is updated with the counts passed up from the two recursive calls.\n", + "\n", + "Complete the two recursive function calls with the same arguments as in `hanoi()`!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.15**: Write a `for`-loop that prints out the **minimum number** of moves needed to solve Towers of Hanoi for any number of `disks` from `1` through `20` to confirm your answer to **Q15.2**." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Time Complexity" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Observe how quickly the `hanoi_moves()` function slows down for increasing `disks` arguments.\n", + "\n", + "With `disks` in the range from `24` through `26` the computation time roughly doubles for each increase of `disks` by 1.\n", + "\n", + "**Q15.16**: Execute the code cells below and see for yourself!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%timeit -n 1 -r 1\n", + "print(\"Number of moves:\", hanoi_moves(24))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%timeit -n 1 -r 1\n", + "print(\"Number of moves:\", hanoi_moves(25))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%timeit -n 1 -r 1\n", + "print(\"Number of moves:\", hanoi_moves(26))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Passing a Value \"down\" the Recursion Tree (Advanced)" + ] + }, + { + "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 can be build in by passing on one more argument `offset` down the recursion tree. As the logic gets a bit \"involved\", `hanoi_ordered()` below is almost finished.\n", + "\n", + "Write your answers to **Q15.17** and **Q15.18** into the subsequent code cell and finalize `hanoi_ordered()`! No need to write a docstring or validate the input here." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def hanoi_ordered(disks, *, origin=\"left\", intermediate=\"center\", destination=\"right\", offset=None):\n", + "\n", + " # answer to Q15.17\n", + " # ...\n", + "\n", + " total = (2 ** disks - 1)\n", + " half = (2 ** (disks - 1) - 1)\n", + " count = total - half\n", + "\n", + " if offset is not None:\n", + " count += offset\n", + "\n", + " hanoi_ordered(..., offset=offset) # <- answer to Q15.18 between the ()\n", + " # answer to Q15.18\n", + " hanoi_ordered(..., offset=count) # <- answer to Q15.18 between the ()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.17**: Copy the base case from the original `hanoi()`!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.18**: Complete the two recursive function calls with the same arguments as in `hanoi()` or `hanoi_moves()`! Do not change the already filled in `offset` arguments!\n", + "\n", + "Then, copy the `print()` statement from `hanoi()` and adjust it to print out `count` as well!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.19**: 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 `disks` argument it is easier to collect all the moves first in a list and then add the order number with the [enumerate()](https://docs.python.org/3/library/functions.html#enumerate) built-in." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Open Question" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15.20**: Conducting your own research on the internet (max. 15 minutes), what can you say about generalizing the **[Towers of Hanoi](https://en.wikipedia.org/wiki/Tower_of_Hanoi)** problem to a setting with **more than three** landing spots?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "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": 2 +} diff --git a/static/towers_of_hanoi.gif b/static/towers_of_hanoi.gif new file mode 100644 index 0000000000000000000000000000000000000000..7ab13d43bcb28bf830b0d864cb72062ed393ec2d GIT binary patch literal 197609 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®usM#|8+M$%bN-%|4p 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 Hy8QnDu6(%i literal 0 HcmV?d00001 From 3b28b8d507a260fb62d879907467acdc28adf319 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 7 Oct 2019 22:31:06 +0200 Subject: [PATCH 028/242] Streamline previous content --- 00_start_up.ipynb | 12 +- 01_elements.ipynb | 523 +++++++++++++----------- 02_functions.ipynb | 66 +-- 03_conditionals.ipynb | 68 ++- 04_iteration.ipynb | 316 +++++++------- 04_iteration_review_and_exercises.ipynb | 4 +- 6 files changed, 493 insertions(+), 496 deletions(-) diff --git a/00_start_up.ipynb b/00_start_up.ipynb index 29175db..1cfaf91 100644 --- a/00_start_up.ipynb +++ b/00_start_up.ipynb @@ -224,7 +224,7 @@ } }, "source": [ - "For more \"couragous\" beginners wanting to learn how to accomplish this, here is a rough sketch of the aspects to know. First, all Python realeases are available for free on the official [download](https://www.python.org/downloads/) page for any supported operating system. Choose the one you want, then download and install it (cf., the [instruction notes](https://wiki.python.org/moin/BeginnersGuide/Download)). As this only includes core Python and the standard library, the beginner then needs to learn about the [pip](https://pip.pypa.io/en/stable/) module, which is to be used inside a terminal. With the command `python -m pip install jupyter`, all necessary third-party libraries can be installed (cf., more background [here](https://jupyter.readthedocs.io/en/latest/install.html)). However, this would be done in a *system-wide* fashion and is not recommended. Instead, the best practice is to create a so-called **virtual environment** with the [venv](https://docs.python.org/3/library/venv.html) module with which the installed third-party packages can be *isolated* on a per-project basis (the command `python -m venv env-name` creates a virtual enviroment called \"env-name\"). This tactic is employed to avoid a situation known as **[dependency hell](https://en.wikipedia.org/wiki/Dependency_hell)** Once created, the virtual environment must then be activated each time before resuming work in each terminal (with the command `source env-name/bin/activate`). While there exist convenience tools that automate parts of this (e.g., [poetry](https://poetry.eustace.io/docs/) or [virtualenvwrapper](https://virtualenvwrapper.readthedocs.io/en/latest/)), it only distracts a beginner from actually studying the Python language. Yet, it is still worthwhile to have heard about these terms and concepts as many online resources often implicitly assume the user to know about them." + "For more \"couragous\" beginners wanting to learn how to accomplish this, here is a rough sketch of the aspects to know. First, all Python realeases are available for free on the official [download](https://www.python.org/downloads/) page for any supported operating system. Choose the one you want, then download and install it (cf., the [instruction notes](https://wiki.python.org/moin/BeginnersGuide/Download)). As this only includes core Python and the standard library, the beginner then needs to learn about the [pip](https://pip.pypa.io/en/stable/) module, which is to be used inside a terminal. With the command `python -m pip install jupyter`, all necessary third-party libraries can be installed (cf., more background [here](https://jupyter.readthedocs.io/en/latest/install.html)). However, this would be done in a *system-wide* fashion and is not recommended. Instead, the best practice is to create a so-called **virtual environment** with the [venv](https://docs.python.org/3/library/venv.html) module with which the installed third-party packages are *isolated* on a per-project basis (the command `python -m venv env-name` creates a virtual enviroment called \"env-name\"). This tactic is employed to avoid a situation known as **[dependency hell](https://en.wikipedia.org/wiki/Dependency_hell)** Once created, the virtual environment must then be activated each time before resuming work in each terminal (with the command `source env-name/bin/activate`). While there exist convenience tools that automate parts of this (e.g., [poetry](https://poetry.eustace.io/docs/) or [virtualenvwrapper](https://virtualenvwrapper.readthedocs.io/en/latest/)), it only distracts a beginner from actually studying the Python language. Yet, it is still worthwhile to have heard about these terms and concepts as many online resources often implicitly assume the user to know about them." ] }, { @@ -778,11 +778,11 @@ "**Part 1: Expressing Logic**\n", "\n", "- What is a programming language? What kind of words exist?\n", - " 1. Elements of a Program\n", - " 2. Functions & Modularization\n", + " 1. [Elements of a Program](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb)\n", + " 2. [Functions & Modularization](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb)\n", "- What is the flow of execution? How can we form sentences from words?\n", - " 3. Conditionals & Exceptions\n", - " 4. Recursion & Looping" + " 3. [Conditionals & Exceptions](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb)\n", + " 4. [Recursion & Looping](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb)" ] }, { @@ -796,7 +796,7 @@ "**Part 2: Managing Data and Memory**\n", "\n", "- How is data stored in memory?\n", - " 5. Numbers\n", + " 5. [Numbers](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb)\n", " 6. Text\n", " 7. Sequences\n", " 8. Mappings & Sets\n", diff --git a/01_elements.ipynb b/01_elements.ipynb index 382d757..658e696 100644 --- a/01_elements.ipynb +++ b/01_elements.ipynb @@ -75,7 +75,7 @@ } }, "source": [ - "To verify that something happened in our computer's memory, we simply **reference** `numbers` and observe that Python indeed knows about it." + "To verify that something happened in our computer's memory, we simply **reference** `numbers`." ] }, { @@ -120,7 +120,7 @@ "\n", "Lastly, the `average` is calculated as the ratio of the final **values** of `total` and `count`. Overall, we divide the sum of all even numbers by the count of all even numbers, which is exactly what we are looking for.\n", "\n", - "We also observe how the lines of code \"within\" the `for` and `if` **statements** are **indented** and *aligned* with multiples of **four spaces**: This shows immediately how the lines relate to each other." + "The lines of code \"within\" the `for` and `if` **statements** are **indented** and *aligned* with multiples of **four spaces**: This shows immediately how the lines relate to each other." ] }, { @@ -152,7 +152,7 @@ } }, "source": [ - "We do not see any **output** yet but can obtain the value of `average` by simply referencing it again." + "We do not see any **output** yet but obtain the value of `average` by simply referencing it again." ] }, { @@ -392,7 +392,7 @@ } }, "source": [ - "The `-` operator can be used as a unary operator as well. Then it just flips the sign of a number." + "The `-` operator is used as a unary operator as well. Then it just flips the sign of a number." ] }, { @@ -486,7 +486,7 @@ } }, "source": [ - "The so-called **floor division operator** `//` always rounds down to the next integer and is thus also called **integer division operator**. This is a first example of an operator we commonly do not know from high school mathematics." + "The so-called **floor division operator** `//` always \"rounds\" to the next integer and is thus also called **integer division operator**. This is a first example of an operator we commonly do not know from high school mathematics." ] }, { @@ -545,12 +545,47 @@ } }, "source": [ - "To obtain the remainder of a division, we use the **modulo operator** `%`." + "Even though it appears that the `//` operator **truncates** (i.e., \"cuts off\") the decimals so as to effectively \"rounding\" down (i.e., the `42.5` became `42` in the previous code cell), this is *not* the case: The result is always \"rounded\" towards minus infinity!" ] }, { "cell_type": "code", "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-43" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "-85 // 2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To obtain the remainder of a division, we use the **modulo operator** `%`." + ] + }, + { + "cell_type": "code", + "execution_count": 17, "metadata": { "slideshow": { "slide_type": "slide" @@ -563,7 +598,7 @@ "1" ] }, - "execution_count": 16, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -580,12 +615,12 @@ } }, "source": [ - "Note that the remainder is $0$ if a number is divisable by another." + "Note that the remainder is $0$ *only if* a number is *divisable* by another." ] }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "metadata": { "slideshow": { "slide_type": "fragment" @@ -598,7 +633,7 @@ "0" ] }, - "execution_count": 17, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -620,7 +655,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "metadata": { "slideshow": { "slide_type": "fragment" @@ -633,7 +668,7 @@ "3" ] }, - "execution_count": 18, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } @@ -644,7 +679,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "metadata": { "slideshow": { "slide_type": "-" @@ -657,7 +692,7 @@ "23" ] }, - "execution_count": 19, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -674,12 +709,12 @@ } }, "source": [ - "The [divmod()](https://docs.python.org/3/library/functions.html#divmod) built-in function combines the integer and modulo divisions into one operation. However, this is not an operator but a function. Also observe that [divmod()](https://docs.python.org/3/library/functions.html#divmod) returns a \"pair\" of integers." + "The [divmod()](https://docs.python.org/3/library/functions.html#divmod) built-in function combines the integer and modulo divisions into one operation. However, this is not an operator but a function. Also, [divmod()](https://docs.python.org/3/library/functions.html#divmod) returns a \"pair\" of integers and not just one object." ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 21, "metadata": { "slideshow": { "slide_type": "fragment" @@ -692,7 +727,7 @@ "(4, 2)" ] }, - "execution_count": 20, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } @@ -714,7 +749,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 22, "metadata": { "slideshow": { "slide_type": "slide" @@ -727,7 +762,7 @@ "8" ] }, - "execution_count": 21, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } @@ -749,7 +784,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 23, "metadata": { "slideshow": { "slide_type": "fragment" @@ -762,7 +797,7 @@ "18" ] }, - "execution_count": 22, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -784,7 +819,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 24, "metadata": { "slideshow": { "slide_type": "-" @@ -797,7 +832,7 @@ "18" ] }, - "execution_count": 23, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -808,7 +843,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 25, "metadata": { "slideshow": { "slide_type": "-" @@ -821,7 +856,7 @@ "81" ] }, - "execution_count": 24, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } @@ -838,12 +873,12 @@ } }, "source": [ - "Some programmers also use \"style\" conventions. For example, we can play with the **whitespace**, which is an umbrella term that refers to any non-printable sign like spaces, tabs, or the like. However, parentheses convey a much clearer picture." + "Some programmers also use \"style\" conventions. For example, we might play with the **whitespace**, which is an umbrella term that refers to any non-printable sign like spaces, tabs, or the like. However, parentheses convey a much clearer picture." ] }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 26, "metadata": { "slideshow": { "slide_type": "skip" @@ -856,7 +891,7 @@ "18" ] }, - "execution_count": 25, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -904,7 +939,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 27, "metadata": { "slideshow": { "slide_type": "slide" @@ -941,7 +976,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 28, "metadata": { "slideshow": { "slide_type": "slide" @@ -951,31 +986,7 @@ { "data": { "text/plain": [ - "140658972730512" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "id(a)" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "140658972907392" + "140098012863472" ] }, "execution_count": 28, @@ -984,7 +995,7 @@ } ], "source": [ - "id(b)" + "id(a)" ] }, { @@ -999,7 +1010,7 @@ { "data": { "text/plain": [ - "140658972586992" + "140098013042536" ] }, "execution_count": 29, @@ -1007,6 +1018,30 @@ "output_type": "execute_result" } ], + "source": [ + "id(b)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140098012714672" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "id(c)" ] @@ -1024,7 +1059,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 31, "metadata": { "slideshow": { "slide_type": "slide" @@ -1043,12 +1078,12 @@ } }, "source": [ - "`a` and `d` indeed have the same value as can be checked with the **equality operator** `==`. The resulting `True` (and the `False` below) is yet another data type, a so-called **boolean**. We will look into that closely in [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb)." + "`a` and `d` indeed have the same value as is checked with the **equality operator** `==`. The resulting `True` (and the `False` below) is yet another data type, a so-called **boolean**. We will look into that closely in [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb)." ] }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 32, "metadata": { "slideshow": { "slide_type": "-" @@ -1061,7 +1096,7 @@ "True" ] }, - "execution_count": 31, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -1078,12 +1113,12 @@ } }, "source": [ - "On the contrary, `a` and `d` are different objects as can be seen with the **identity operator** `is`: they are stored at seperate addresses in the memory." + "On the contrary, `a` and `d` are different objects as the **identity operator** `is` shows: they are stored at seperate addresses in the memory." ] }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 33, "metadata": { "slideshow": { "slide_type": "-" @@ -1096,7 +1131,7 @@ "False" ] }, - "execution_count": 32, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" } @@ -1129,7 +1164,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 34, "metadata": { "slideshow": { "slide_type": "slide" @@ -1142,7 +1177,7 @@ "int" ] }, - "execution_count": 33, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } @@ -1153,7 +1188,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 35, "metadata": { "slideshow": { "slide_type": "-" @@ -1166,7 +1201,7 @@ "float" ] }, - "execution_count": 34, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } @@ -1185,14 +1220,14 @@ "source": [ "Different types imply different behaviors for the objects. The `b` object, for example, can be \"asked\" if it could also be interpreted as an `int` with the [is_integer()](https://docs.python.org/3/library/stdtypes.html#float.is_integer) \"functionality\" that comes with every `float` object.\n", "\n", - "Formally, we call such type-specific functionalities **methods** (to differentiate them from functions) and we will eventually fully introduce them when we talk about object-orientation in Chapter 10. For now, it suffices to know that we can access them using the **dot operator** `.`. Of course `b` could be converted into an `int`, which the boolean value `True` tells us.\n", + "Formally, we call such type-specific functionalities **methods** (to differentiate them from functions) and we will eventually fully introduce them when we talk about object-orientation in Chapter 10. For now, it suffices to know that we access them using the **dot operator** `.`. Of course `b` could be converted into an `int`, which the boolean value `True` tells us.\n", "\n", "Also note how the `.` operator is neiter a unary nor a binary operator as specified above." ] }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 36, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1205,7 +1240,7 @@ "True" ] }, - "execution_count": 35, + "execution_count": 36, "metadata": {}, "output_type": "execute_result" } @@ -1227,7 +1262,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 37, "metadata": { "slideshow": { "slide_type": "-" @@ -1241,7 +1276,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0ma\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mis_integer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0ma\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mis_integer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mAttributeError\u001b[0m: 'int' object has no attribute 'is_integer'" ] } @@ -1258,12 +1293,12 @@ } }, "source": [ - "The `c` object is a so-called **string** type (i.e., `str`), which we can view as Python's way of representing \"text\". Strings also come with their own behaviors, for example, to convert a text to lower or upper case." + "The `c` object is a so-called **string** type (i.e., `str`), which is Python's way of representing \"text\". Strings also come with their own behaviors, for example, to convert a text to lower or upper case." ] }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 38, "metadata": { "slideshow": { "slide_type": "slide" @@ -1276,7 +1311,7 @@ "str" ] }, - "execution_count": 37, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } @@ -1287,7 +1322,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 39, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1300,7 +1335,7 @@ "'python rocks'" ] }, - "execution_count": 38, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } @@ -1311,7 +1346,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 40, "metadata": { "slideshow": { "slide_type": "-" @@ -1324,7 +1359,7 @@ "'PYTHON ROCKS'" ] }, - "execution_count": 39, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } @@ -1335,7 +1370,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 41, "metadata": { "slideshow": { "slide_type": "skip" @@ -1348,7 +1383,7 @@ "'Python Rocks'" ] }, - "execution_count": 40, + "execution_count": 41, "metadata": {}, "output_type": "execute_result" } @@ -1383,7 +1418,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 42, "metadata": { "slideshow": { "slide_type": "slide" @@ -1396,7 +1431,7 @@ "789" ] }, - "execution_count": 41, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } @@ -1407,7 +1442,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 43, "metadata": { "slideshow": { "slide_type": "-" @@ -1420,7 +1455,7 @@ "42.0" ] }, - "execution_count": 42, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } @@ -1437,12 +1472,12 @@ } }, "source": [ - "In this book, we follow the convention of creating strings with **double quotes** `\"` instead of the **single quotes** `'` to which Python defaults in its literal output for `str` objects. Both types of quotes can be used interchangebly." + "In this book, we follow the convention of creating strings with **double quotes** `\"` instead of the **single quotes** `'` to which Python defaults in its literal output for `str` objects. Both types of quotes may be used interchangebly." ] }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 44, "metadata": { "slideshow": { "slide_type": "-" @@ -1455,7 +1490,7 @@ "'Python rocks'" ] }, - "execution_count": 43, + "execution_count": 44, "metadata": {}, "output_type": "execute_result" } @@ -1514,7 +1549,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 45, "metadata": { "slideshow": { "slide_type": "slide" @@ -1523,10 +1558,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "invalid syntax (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 3.99 $ + 10.40 $\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 3.99 $ + 10.40 $\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], @@ -1547,7 +1582,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 46, "metadata": { "slideshow": { "slide_type": "slide" @@ -1556,10 +1591,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "invalid syntax (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m for number in numbers\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m for number in numbers\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], @@ -1581,7 +1616,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 47, "metadata": { "slideshow": { "slide_type": "slide" @@ -1590,10 +1625,10 @@ "outputs": [ { "ename": "IndentationError", - "evalue": "expected an indented block (, line 2)", + "evalue": "expected an indented block (, line 2)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m2\u001b[0m\n\u001b[0;31m print(number)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mIndentationError\u001b[0m\u001b[0;31m:\u001b[0m expected an indented block\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m2\u001b[0m\n\u001b[0;31m print(number)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mIndentationError\u001b[0m\u001b[0;31m:\u001b[0m expected an indented block\n" ] } ], @@ -1630,7 +1665,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 48, "metadata": { "slideshow": { "slide_type": "slide" @@ -1644,7 +1679,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m1\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m1\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" ] } @@ -1672,14 +1707,14 @@ } }, "source": [ - "So-called **semantic errors**, on the contrary, can be very hard to spot as they do *not* crash the program. The only way to find such errors is to run a program with test input for which we know the answer already and can thus check the output. However, testing software is a whole discipline on its own and often very hard to do in practice.\n", + "So-called **semantic errors**, on the contrary, are hard to spot as they do *not* crash the program. The only way to find such errors is to run a program with test input for which we can predict the output. However, testing software is a whole discipline on its own and often very hard to do in practice.\n", "\n", "The cell below copies our introductory example from above with a \"tiny\" error. How fast could you have spotted it without the comment?" ] }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 49, "metadata": { "code_folding": [], "slideshow": { @@ -1701,7 +1736,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 50, "metadata": { "slideshow": { "slide_type": "-" @@ -1714,7 +1749,7 @@ "3.0" ] }, - "execution_count": 49, + "execution_count": 50, "metadata": {}, "output_type": "execute_result" } @@ -1755,12 +1790,12 @@ "source": [ "Adhering to just syntax rules is therefore *never* enough. Over time, **best practices** and common **style guides** were created to make it less likely for a developer to mess up a program and also to allow \"onboarding\" him as a contributor to an established code base, often called **legacy code**, faster. These rules are not enforced by Python itself: Badly styled and un-readable code will still run. At the very least, Python programs should be styled according to [PEP 8](https://www.python.org/dev/peps/pep-0008/) and documented \"inline\" (i.e., in the code itself) according to [PEP 257](https://www.python.org/dev/peps/pep-0257/).\n", "\n", - "An easier to read version of PEP 8 can be found [here](https://pep8.org/). The video below features a well known \"[Pythonista](https://en.wiktionary.org/wiki/Pythonista)\" talking about the importance of code style." + "An easier to read version of PEP 8 is [here](https://pep8.org/). The video below features a well known \"[Pythonista](https://en.wiktionary.org/wiki/Pythonista)\" talking about the importance of code style." ] }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 51, "metadata": { "slideshow": { "slide_type": "skip" @@ -1782,10 +1817,10 @@ " " ], "text/plain": [ - "" + "" ] }, - "execution_count": 50, + "execution_count": 51, "metadata": {}, "output_type": "execute_result" } @@ -1808,7 +1843,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 52, "metadata": { "slideshow": { "slide_type": "slide" @@ -1821,7 +1856,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 53, "metadata": { "slideshow": { "slide_type": "-" @@ -1834,7 +1869,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 54, "metadata": { "slideshow": { "slide_type": "-" @@ -1847,7 +1882,7 @@ "[2, 4, 6, 8, 10]" ] }, - "execution_count": 53, + "execution_count": 54, "metadata": {}, "output_type": "execute_result" } @@ -1858,7 +1893,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 55, "metadata": { "slideshow": { "slide_type": "-" @@ -1871,7 +1906,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 56, "metadata": { "slideshow": { "slide_type": "-" @@ -1884,7 +1919,7 @@ "6.0" ] }, - "execution_count": 55, + "execution_count": 56, "metadata": {}, "output_type": "execute_result" } @@ -1906,7 +1941,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 57, "metadata": { "slideshow": { "slide_type": "slide" @@ -2001,7 +2036,7 @@ } }, "source": [ - "While this book is built with Jupyter notebooks, it is important to understand that \"real\" programs are almost always just \"linear\" (= top to bottom) sequences of instructions but instead can take many different **flows of execution**.\n", + "While this book is built with Jupyter notebooks, it is important to understand that \"real\" programs are almost always just \"linear\" (= top to bottom) sequences of instructions but instead may take many different **flows of execution**.\n", "\n", "At the same time, for a beginner's course it is often easier to just code in a linear fashion.\n", "\n", @@ -2034,7 +2069,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 58, "metadata": { "slideshow": { "slide_type": "slide" @@ -2059,7 +2094,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 59, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2072,7 +2107,7 @@ "20.0" ] }, - "execution_count": 58, + "execution_count": 59, "metadata": {}, "output_type": "execute_result" } @@ -2089,12 +2124,12 @@ } }, "source": [ - "A variable can be **re-assigned** as often as we wish. Thereby, we could also assign an object of a *different* type. Because this is allowed, Python is said to be a **dynamically typed** language. On the contrary, a **statically typed** language like C also allows re-assignment but only with objects of the *same* type. This subtle distinction is one reason why Python is slower at execution than C: As it runs a program, it needs to figure out an object's type each time it is referenced. But as mentioned before, this can be mitigated with third-party libraries." + "A variable may be **re-assigned** as often as we wish. Thereby, we could also assign an object of a *different* type. Because this is allowed, Python is said to be a **dynamically typed** language. On the contrary, a **statically typed** language like C also allows re-assignment but only with objects of the *same* type. This subtle distinction is one reason why Python is slower at execution than C: As it runs a program, it needs to figure out an object's type each time it is referenced. But as mentioned before, this is mitigated with third-party libraries." ] }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 60, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2107,7 +2142,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 61, "metadata": { "slideshow": { "slide_type": "-" @@ -2120,7 +2155,7 @@ "20" ] }, - "execution_count": 60, + "execution_count": 61, "metadata": {}, "output_type": "execute_result" } @@ -2137,12 +2172,12 @@ } }, "source": [ - "If we want to re-assign a variable while referencing its \"old\" (i.e., current) object, we can also **update** it using a so-called **[augmented assignment statement](https://docs.python.org/3/reference/simple_stmts.html#augmented-assignment-statements)** (*not* operator), originally introduced with [PEP 203](https://www.python.org/dev/peps/pep-0203/). This implicitly inserts the currently mapped object as the first operand on the right-hand side." + "If we want to re-assign a variable while referencing its \"old\" (i.e., current) object, we may also **update** it using a so-called **[augmented assignment statement](https://docs.python.org/3/reference/simple_stmts.html#augmented-assignment-statements)** (*not* operator), originally introduced with [PEP 203](https://www.python.org/dev/peps/pep-0203/). This implicitly inserts the currently mapped object as the first operand on the right-hand side." ] }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 62, "metadata": { "slideshow": { "slide_type": "slide" @@ -2155,7 +2190,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 63, "metadata": { "slideshow": { "slide_type": "-" @@ -2168,7 +2203,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 64, "metadata": { "slideshow": { "slide_type": "-" @@ -2181,7 +2216,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 65, "metadata": { "slideshow": { "slide_type": "-" @@ -2194,7 +2229,7 @@ "42" ] }, - "execution_count": 64, + "execution_count": 65, "metadata": {}, "output_type": "execute_result" } @@ -2211,12 +2246,12 @@ } }, "source": [ - "Variables can be **[de-referenced](https://docs.python.org/3/reference/simple_stmts.html#the-del-statement)** (i.e., \"deleted\") with the `del` statement. This does *not* delete the object to which a variable points to. It merely removes the variable's name from the \"global list of all names\"." + "Variables are **[de-referenced](https://docs.python.org/3/reference/simple_stmts.html#the-del-statement)** (i.e., \"deleted\") with the `del` statement. This does *not* delete the object to which a variable points to. It merely removes the variable's name from the \"global list of all names\"." ] }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 66, "metadata": { "slideshow": { "slide_type": "slide" @@ -2229,7 +2264,7 @@ "789" ] }, - "execution_count": 65, + "execution_count": 66, "metadata": {}, "output_type": "execute_result" } @@ -2240,7 +2275,7 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 67, "metadata": { "slideshow": { "slide_type": "-" @@ -2264,7 +2299,7 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 68, "metadata": { "slideshow": { "slide_type": "-" @@ -2278,7 +2313,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mb\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mb\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'b' is not defined" ] } @@ -2295,12 +2330,12 @@ } }, "source": [ - "Some variables magically exist when we start a Python process or are added by Jupyter. We can safely ignore the former until Chapter 10 and the latter for good." + "Some variables magically exist when we start a Python process or are added by Jupyter. We may safely ignore the former until Chapter 10 and the latter for good." ] }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 69, "metadata": { "slideshow": { "slide_type": "skip" @@ -2313,7 +2348,7 @@ "'__main__'" ] }, - "execution_count": 68, + "execution_count": 69, "metadata": {}, "output_type": "execute_result" } @@ -2335,7 +2370,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 70, "metadata": { "slideshow": { "slide_type": "slide" @@ -2366,15 +2401,15 @@ " '_23',\n", " '_24',\n", " '_25',\n", - " '_27',\n", + " '_26',\n", " '_28',\n", " '_29',\n", - " '_31',\n", + " '_30',\n", " '_32',\n", " '_33',\n", " '_34',\n", " '_35',\n", - " '_37',\n", + " '_36',\n", " '_38',\n", " '_39',\n", " '_4',\n", @@ -2382,16 +2417,17 @@ " '_41',\n", " '_42',\n", " '_43',\n", - " '_49',\n", + " '_44',\n", " '_5',\n", " '_50',\n", - " '_53',\n", - " '_55',\n", - " '_58',\n", - " '_60',\n", - " '_64',\n", + " '_51',\n", + " '_54',\n", + " '_56',\n", + " '_59',\n", + " '_61',\n", " '_65',\n", - " '_68',\n", + " '_66',\n", + " '_69',\n", " '_9',\n", " '__',\n", " '___',\n", @@ -2471,6 +2507,7 @@ " '_i68',\n", " '_i69',\n", " '_i7',\n", + " '_i70',\n", " '_i8',\n", " '_i9',\n", " '_ih',\n", @@ -2492,7 +2529,7 @@ " 'total']" ] }, - "execution_count": 69, + "execution_count": 70, "metadata": {}, "output_type": "execute_result" } @@ -2520,14 +2557,14 @@ } }, "source": [ - "It is important to understand that *several* variables can point to the *same* object in memory. This can be counter-intuitive in the beginning and lead to many hard to track down bugs.\n", + "It is important to understand that *several* variables may point to the *same* object in memory. This may be counter-intuitive in the beginning and lead to many hard to track down bugs.\n", "\n", "This makes `b` point to whatever object `a` is pointing to." ] }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 71, "metadata": { "slideshow": { "slide_type": "slide" @@ -2538,30 +2575,6 @@ "b = a" ] }, - { - "cell_type": "code", - "execution_count": 71, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "42" - ] - }, - "execution_count": 71, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a" - ] - }, { "cell_type": "code", "execution_count": 72, @@ -2582,6 +2595,30 @@ "output_type": "execute_result" } ], + "source": [ + "a" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "b" ] @@ -2601,7 +2638,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 74, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2614,7 +2651,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 75, "metadata": { "slideshow": { "slide_type": "-" @@ -2627,7 +2664,7 @@ "123" ] }, - "execution_count": 74, + "execution_count": 75, "metadata": {}, "output_type": "execute_result" } @@ -2649,7 +2686,7 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 76, "metadata": { "slideshow": { "slide_type": "-" @@ -2662,7 +2699,7 @@ "42" ] }, - "execution_count": 75, + "execution_count": 76, "metadata": {}, "output_type": "execute_result" } @@ -2679,12 +2716,12 @@ } }, "source": [ - "However, if a variable points to an object of a more \"complex\" type (e.g., `list`), \"weird\" things can happen." + "However, if a variable points to an object of a more \"complex\" type (e.g., `list`), \"weird\" things happen." ] }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 77, "metadata": { "slideshow": { "slide_type": "slide" @@ -2697,7 +2734,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 78, "metadata": { "slideshow": { "slide_type": "skip" @@ -2710,7 +2747,7 @@ "list" ] }, - "execution_count": 77, + "execution_count": 78, "metadata": {}, "output_type": "execute_result" } @@ -2721,7 +2758,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 79, "metadata": { "slideshow": { "slide_type": "-" @@ -2749,7 +2786,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 80, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2762,7 +2799,7 @@ "1" ] }, - "execution_count": 79, + "execution_count": 80, "metadata": {}, "output_type": "execute_result" } @@ -2784,7 +2821,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 81, "metadata": { "slideshow": { "slide_type": "slide" @@ -2797,7 +2834,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 82, "metadata": { "slideshow": { "slide_type": "-" @@ -2810,7 +2847,7 @@ "[99, 2, 3]" ] }, - "execution_count": 81, + "execution_count": 82, "metadata": {}, "output_type": "execute_result" } @@ -2827,12 +2864,12 @@ } }, "source": [ - "The changes made to the object `x` is pointing to can also be seen through the `y` variable!" + "The changes made to the object `x` is pointing to are also seen through the `y` variable!" ] }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 83, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2845,7 +2882,7 @@ "[99, 2, 3]" ] }, - "execution_count": 82, + "execution_count": 83, "metadata": {}, "output_type": "execute_result" } @@ -2905,7 +2942,7 @@ } }, "source": [ - "Variable names may contain upper and lower case letters, numbers, and underscores (\"\\_\") and be as long as we want them to be. However, they must not begin with a number. Also, they must not be any of Python's built-in **[keywords](https://docs.python.org/3/reference/lexical_analysis.html#keywords)**.\n", + "Variable names may contain upper and lower case letters, numbers, and underscores (i.e., `_`) and be as long as we want them to be. However, they must not begin with a number. Also, they must not be any of Python's built-in **[keywords](https://docs.python.org/3/reference/lexical_analysis.html#keywords)**.\n", "\n", "Variable names are usually chosen such that they do not need any more documentation and are self-explanatory. A very common convention is to use so-called **[snake\\_case](https://en.wikipedia.org/wiki/Snake_case)**: Keep everything lowercase and use underscores to seperate words.\n", "\n", @@ -2925,7 +2962,7 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 84, "metadata": { "slideshow": { "slide_type": "slide" @@ -2938,7 +2975,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 85, "metadata": { "slideshow": { "slide_type": "-" @@ -2951,7 +2988,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 86, "metadata": { "slideshow": { "slide_type": "-" @@ -2964,7 +3001,7 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 87, "metadata": { "slideshow": { "slide_type": "-" @@ -2988,7 +3025,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 88, "metadata": { "slideshow": { "slide_type": "skip" @@ -3001,7 +3038,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 89, "metadata": { "slideshow": { "slide_type": "skip" @@ -3014,7 +3051,7 @@ }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 90, "metadata": { "slideshow": { "slide_type": "skip" @@ -3027,7 +3064,7 @@ }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 91, "metadata": { "slideshow": { "slide_type": "skip" @@ -3036,10 +3073,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "can't assign to operator (, line 1)", + "evalue": "can't assign to operator (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m address@work = \"Burgplatz 2, Vallendar\"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m can't assign to operator\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m address@work = \"Burgplatz 2, Vallendar\"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m can't assign to operator\n" ] } ], @@ -3060,7 +3097,7 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 92, "metadata": { "slideshow": { "slide_type": "skip" @@ -3084,7 +3121,7 @@ }, { "cell_type": "code", - "execution_count": 92, + "execution_count": 93, "metadata": { "slideshow": { "slide_type": "skip" @@ -3097,7 +3134,7 @@ "'__main__'" ] }, - "execution_count": 92, + "execution_count": 93, "metadata": {}, "output_type": "execute_result" } @@ -3130,7 +3167,7 @@ }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 94, "metadata": { "slideshow": { "slide_type": "skip" @@ -3152,10 +3189,10 @@ " " ], "text/plain": [ - "" + "" ] }, - "execution_count": 93, + "execution_count": 94, "metadata": {}, "output_type": "execute_result" } @@ -3186,7 +3223,7 @@ "source": [ "An **[expression](https://docs.python.org/3/reference/expressions.html)** is any syntactically correct **combination** of **variables** and **literals** with **operators**.\n", "\n", - "In simple words, anything that can be used on the right-hand side of an assignment statement without creating a `SyntaxError` is an expression.\n", + "In simple words, anything that may be used on the right-hand side of an assignment statement without creating a `SyntaxError` is an expression.\n", "\n", "What we said about individual operators above, namely that they have *no* side effects, should have been put here to begin with. The examples in the section on operators above were actually all expressions!\n", "\n", @@ -3195,7 +3232,7 @@ }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 95, "metadata": { "slideshow": { "slide_type": "slide" @@ -3208,7 +3245,7 @@ "123" ] }, - "execution_count": 94, + "execution_count": 95, "metadata": {}, "output_type": "execute_result" } @@ -3219,7 +3256,7 @@ }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 96, "metadata": {}, "outputs": [ { @@ -3228,7 +3265,7 @@ "42" ] }, - "execution_count": 95, + "execution_count": 96, "metadata": {}, "output_type": "execute_result" } @@ -3246,7 +3283,7 @@ }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 97, "metadata": { "slideshow": { "slide_type": "-" @@ -3259,7 +3296,7 @@ "165" ] }, - "execution_count": 96, + "execution_count": 97, "metadata": {}, "output_type": "execute_result" } @@ -3281,7 +3318,7 @@ }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 98, "metadata": { "slideshow": { "slide_type": "-" @@ -3294,7 +3331,7 @@ "4492125" ] }, - "execution_count": 97, + "execution_count": 98, "metadata": {}, "output_type": "execute_result" } @@ -3316,7 +3353,7 @@ }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 99, "metadata": { "slideshow": { "slide_type": "-" @@ -3329,7 +3366,7 @@ "3" ] }, - "execution_count": 98, + "execution_count": 99, "metadata": {}, "output_type": "execute_result" } @@ -3351,7 +3388,7 @@ }, { "cell_type": "code", - "execution_count": 99, + "execution_count": 100, "metadata": { "slideshow": { "slide_type": "-" @@ -3364,7 +3401,7 @@ "104" ] }, - "execution_count": 99, + "execution_count": 100, "metadata": {}, "output_type": "execute_result" } @@ -3392,12 +3429,12 @@ } }, "source": [ - "Python **overloads** certain operators. For example you can not only add numbers but also strings. This is called **string concatenation**." + "Python **overloads** certain operators. For example, you may not only \"add\" numbers but also strings. This is called **string concatenation**." ] }, { "cell_type": "code", - "execution_count": 100, + "execution_count": 101, "metadata": { "slideshow": { "slide_type": "slide" @@ -3411,7 +3448,7 @@ }, { "cell_type": "code", - "execution_count": 101, + "execution_count": 102, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3424,7 +3461,7 @@ "'Hi class'" ] }, - "execution_count": 101, + "execution_count": 102, "metadata": {}, "output_type": "execute_result" } @@ -3446,7 +3483,7 @@ }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 103, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3459,7 +3496,7 @@ "'Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi '" ] }, - "execution_count": 102, + "execution_count": 103, "metadata": {}, "output_type": "execute_result" } @@ -3487,14 +3524,14 @@ } }, "source": [ - "A **[statement](https://docs.python.org/3/reference/simple_stmts.html)** is anything that changes the state of a program or has some other side effect. Statements do not just evaluate to a value like expressions; instead, they create or change values.\n", + "A **[statement](https://docs.python.org/3/reference/simple_stmts.html)** is anything that *changes* the *state of a program* or has some other *side effect*. Statements do not just evaluate to a value like expressions; instead, they create or change values.\n", "\n", "Most notably of course are the `=` and `del` statements." ] }, { "cell_type": "code", - "execution_count": 103, + "execution_count": 104, "metadata": { "slideshow": { "slide_type": "slide" @@ -3507,7 +3544,7 @@ }, { "cell_type": "code", - "execution_count": 104, + "execution_count": 105, "metadata": { "slideshow": { "slide_type": "-" @@ -3531,7 +3568,7 @@ }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 106, "metadata": { "slideshow": { "slide_type": "skip" @@ -3569,16 +3606,16 @@ } }, "source": [ - "We can use the `#` symbol to write comments in plain English right into the code.\n", + "We use the `#` symbol to write comments in plain English right into the code.\n", "\n", "As a good practice, comments should not describe what happens (this should be evident by reading the code, otherwise it is most likely badly written code) but why something happens.\n", "\n", - "Comments can be either added at the end of a line of code (by convention seperated with two spaces) or be on a line on their own." + "Comments may be added either at the end of a line of code, by convention seperated with two spaces, or on a line on their own." ] }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 107, "metadata": { "slideshow": { "slide_type": "slide" @@ -3606,7 +3643,7 @@ }, { "cell_type": "code", - "execution_count": 107, + "execution_count": 108, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3619,7 +3656,7 @@ }, { "cell_type": "code", - "execution_count": 108, + "execution_count": 109, "metadata": { "slideshow": { "slide_type": "-" @@ -3649,7 +3686,7 @@ } }, "source": [ - "We end each chapter with a summary of the main points. The essence in this first chapter is that just like a sentence in a real language like English can be decomposed into its parts (subject, predicate, objects, ...) the same can be done with programming languages." + "We end each chapter with a summary of the main points. The essence in this first chapter is that just like a sentence in a real language like English may be decomposed into its parts (subject, predicate, objects, ...) the same may be done with programming languages." ] }, { @@ -3727,8 +3764,8 @@ "\n", "- flow control (cf., [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb))\n", " - expression of **business logic** or an **algorithm**\n", - " - conditional execution of a small **branch** within a program (i.e., `if` statements)\n", - " - repetitive execution of parts of a program (i.e., `for`-loops and `while`-loops)" + " - conditional execution of a small **branch** within a program (e.g., `if` statements)\n", + " - repetitive execution of parts of a program (e.g., `for`-loops)" ] } ], diff --git a/02_functions.ipynb b/02_functions.ipynb index 681838a..03452c7 100644 --- a/02_functions.ipynb +++ b/02_functions.ipynb @@ -23,7 +23,7 @@ "\n", "At the same time, we executed built-in functions (e.g., [print()](https://docs.python.org/3/library/functions.html#print), [sum()](https://docs.python.org/3/library/functions.html#sum), [len()](https://docs.python.org/3/library/functions.html#len), [id()](https://docs.python.org/3/library/functions.html#id), or [type()](https://docs.python.org/3/library/functions.html#type)) that obviously must be re-using the same parts inside core Python every time we use them.\n", "\n", - "This chapter shows how Python offers language constructs that let us **define** our own functions that we can then **call** just like the built-in ones." + "This chapter shows how Python offers language constructs that let us **define** our own functions that we may then **call** just like the built-in ones." ] }, { @@ -45,11 +45,11 @@ } }, "source": [ - "So-called **[user-defined functions](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)** can be created with the `def` statement. To extend an already familiar example, we re-use the introductory example from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) in its final Pythonic version and transform it into the function `average_evens()` below. \n", + "So-called **[user-defined functions](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)** may be created with the `def` statement. To extend an already familiar example, we re-use the introductory example from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) in its final Pythonic version and transform it into the function `average_evens()` below. \n", "\n", "A function's **name** must be chosen according to the same naming rules as for ordinary variables. In fact, Python manages function names just like variables. In this book, we further adopt the convention of ending function names with parentheses \"`()`\" in text cells for faster comprehension when reading (i.e., `average_evens()` vs. `average_evens`). These are not actually part of the name but must always be written out in the `def` statement for syntactic reasons.\n", "\n", - "Functions may define an arbitrary number of **parameters** as inputs that can then be referenced within the indented **code block**: They are simply listed within the parentheses in the `def` statement (i.e., `numbers` below). \n", + "Functions may define an arbitrary number of **parameters** as inputs that are then referenced within the indented **code block**: They are simply listed within the parentheses in the `def` statement (i.e., `numbers` below). \n", "\n", "The code block is often also called a function's **body** while the first line with the `def` in it is the **header** and must end with a colon.\n", "\n", @@ -92,7 +92,7 @@ } }, "source": [ - "Once defined, a function can be referenced just like any other variable by its name (i.e., *without* the parenthesis). Its value might seem awkward at first: It consists of the location where we defined the function (i.e., `__main__`, which is Python's way of saying \"in this notebook\") and the signature." + "Once defined, a function may be referenced just like any other variable by its name (i.e., *without* the parenthesis). Its value might seem awkward at first: It consists of the location where we defined the function (i.e., `__main__`, which is Python's way of saying \"in this notebook\") and the signature." ] }, { @@ -142,7 +142,7 @@ { "data": { "text/plain": [ - "140693945143776" + "139925407773152" ] }, "execution_count": 3, @@ -289,7 +289,7 @@ } }, "source": [ - "We can **call** (i.e., \"execute\") a function with the **call operator** `()` as often as we wish. The formal parameters are filled in by passing variables or expressions as **arguments** to the function within the parentheses." + "We **call** (i.e., \"execute\") a function with the **call operator** `()` as often as we wish. The formal parameters are filled in by passing variables or expressions as **arguments** to the function within the parentheses." ] }, { @@ -863,7 +863,7 @@ "\n", "Variables whose names collide with the ones of variables in enclosing scopes - and the global scope is just the most enclosing scope - are said to **shadow** them.\n", "\n", - "While this is not a problem for Python as we have observed, it may lead to less readable code for us humans and should be avoided if possible. But, as we have also heard, \"[naming things](https://skeptics.stackexchange.com/questions/19836/has-phil-karlton-ever-said-there-are-only-two-hard-things-in-computer-science)\" is often considered hard as well and we have to be prepared to encounter shadowing variables." + "While this is not a problem for Python, it may lead to less readable code for us humans and should be avoided if possible. But, as we have also heard, \"[naming things](https://skeptics.stackexchange.com/questions/19836/has-phil-karlton-ever-said-there-are-only-two-hard-things-in-computer-science)\" is often considered hard as well and we have to be prepared to encounter shadowing variables." ] }, { @@ -946,7 +946,7 @@ } }, "source": [ - "We can cast certain objects as a different type. For example, to \"convert\" a float or a text into an integer, we use the [int()](https://docs.python.org/3/library/functions.html#int) built-in. This actually creates a *new* object of type `int` from the provided `avg` or `\"6\"` objects who continue to exist in memory unchanged." + "We may cast certain objects as a different type. For example, to \"convert\" a float or a text into an integer, we use the [int()](https://docs.python.org/3/library/functions.html#int) built-in. This actually creates a *new* object of type `int` from the provided `avg` or `\"6\"` objects who continue to exist in memory unchanged." ] }, { @@ -1088,7 +1088,7 @@ } }, "source": [ - "Not all conversions are valid and *runtime* errors can occur as the `ValueError` shows." + "Not all conversions are valid and *runtime* errors may occur as the `ValueError` shows." ] }, { @@ -1124,7 +1124,7 @@ } }, "source": [ - "We can also go in the other direction with the [float()](https://docs.python.org/3/library/functions.html#float) built-in function." + "We may also go in the other direction with the [float()](https://docs.python.org/3/library/functions.html#float) built-in function." ] }, { @@ -1266,7 +1266,7 @@ } }, "source": [ - "As with [divmod()](https://docs.python.org/3/library/functions.html#divmod), we can pass in the arguments by position." + "As with [divmod()](https://docs.python.org/3/library/functions.html#divmod), we pass in the arguments by position." ] }, { @@ -1303,7 +1303,7 @@ "source": [ "However, now the function call is a bit harder to comprehend as we need to always remember what the `2` means. This becomes even harder the more parameters we specify.\n", "\n", - "Luckily, we can also reference the formal parameter names as **keyword arguments**. We can even combine positional and keyword arguments in the same function call. Each of the following does the exact same thing." + "Luckily, we may also reference the formal parameter names as **keyword arguments**. We can even combine positional and keyword arguments in the same function call. Each of the following does the exact same thing." ] }, { @@ -1555,9 +1555,9 @@ } }, "source": [ - "Now we can call the function either with or without the `scalar` argument.\n", + "Now we call the function either with or without the `scalar` argument.\n", "\n", - "If `scalar` is passed in, this can be done as either a positional or a keyword argument. Which of the two versions where `scalar` is `2` is easier to comprehend in a large program?" + "If `scalar` is passed in, this may be done as either a positional or a keyword argument. Which of the two versions where `scalar` is `2` is easier to comprehend in a large program?" ] }, { @@ -1869,7 +1869,7 @@ } }, "source": [ - "Now we can call `add_three()` as if we defined it with the `def` statement to begin with." + "Now we call `add_three()` as if we defined it with the `def` statement to begin with." ] }, { @@ -2084,7 +2084,7 @@ { "data": { "text/plain": [ - "140694050824664" + "139925472068136" ] }, "execution_count": 58, @@ -2132,7 +2132,7 @@ "\n", "Let's see what we can do with the `math` module.\n", "\n", - "The [dir()](https://docs.python.org/3/library/functions.html#dir) built-in function can also be used with an argument passed in. Ignoring the dunder-style names, `math` offers quite a lot of ... names. As we cannot know at this point in time if a listed name refers to a function or an ordinary variable, we use the more generic term **attribute** to mean either one of them." + "The [dir()](https://docs.python.org/3/library/functions.html#dir) built-in function may also be used with an argument passed in. Ignoring the dunder-style names, `math` offers quite a lot of ... names. As we cannot know at this point in time if a listed name refers to a function or an ordinary variable, we use the more generic term **attribute** to mean either one of them." ] }, { @@ -2354,9 +2354,9 @@ } }, "source": [ - "Observe how the arguments passed to functions do not need to be just variables or simple literals. Instead, we can pass in any *expression* that evaluates to a *new* object of the type the function expects.\n", + "Observe how the arguments passed to functions do not need to be just variables or simple literals. Instead, we may pass in any *expression* that evaluates to a *new* object of the type the function expects.\n", "\n", - "So just as a reminder from the expression vs. statement discussion in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb): An expression is *any* syntactically correct combination of variables and literals with operators. And the call operator `()` is just ... well another operator. So both of the next two code cells are just expressions! They have no permanent side effect in memory. We can execute them as often as we want *without* changing the state of the program (i.e., this Jupyter notebook).\n", + "So just as a reminder from the expression vs. statement discussion in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb): An expression is *any* syntactically correct combination of variables and literals with operators. And the call operator `()` is just ... well another operator. So both of the next two code cells are just expressions! They have no permanent side effect in memory. We may execute them as often as we want *without* changing the state of the program (i.e., this Jupyter notebook).\n", "\n", "So, regarding the very next cell in particular: Although the `2 ** 2` creates a *new* object `4` in memory that is then immediately passed into the [math.sqrt()](https://docs.python.org/3/library/math.html#math.sqrt) function, once that function call returns, \"all is lost\" and the newly created `4` object is forgotten again, as well as the return value of [math.sqrt()](https://docs.python.org/3/library/math.html#math.sqrt)." ] @@ -2428,7 +2428,7 @@ } }, "source": [ - "If we only need one particular function from a module, we can also use the alternative `from ... import ...` syntax.\n", + "If we only need one particular function from a module, we may also use the alternative `from ... import ...` syntax.\n", "\n", "This does *not* create a module object but only makes a variable in our current location point to an object defined inside a module directly." ] @@ -2537,7 +2537,7 @@ } }, "source": [ - "Besides the usual dunder-style attributes, the [dir()](https://docs.python.org/3/library/functions.html#dir) built-in function lists some attributes in an upper case naming convention and many others starting with a single underscore \"\\_\". To understand the former, we have to wait until Chapter 10 while the latter are explained further below." + "Besides the usual dunder-style attributes, the [dir()](https://docs.python.org/3/library/functions.html#dir) built-in function lists some attributes in an upper case naming convention and many others starting with a single underscore `_`. To understand the former, we have to wait until Chapter 10 while the latter are explained further below." ] }, { @@ -2697,7 +2697,7 @@ { "data": { "text/plain": [ - "0.15268128055183228" + "0.270353921677863" ] }, "execution_count": 75, @@ -2732,7 +2732,7 @@ { "data": { "text/plain": [ - ">" + ">" ] }, "execution_count": 76, @@ -2781,7 +2781,7 @@ { "data": { "text/plain": [ - "2" + "7" ] }, "execution_count": 78, @@ -2801,7 +2801,7 @@ } }, "source": [ - "In order to re-produce the same random numbers in a simulation each time we run it, we can set the **[random seed](https://en.wikipedia.org/wiki/Random_seed)**. It is good practice to do this at the beginning of a program or notebook. Then every time we re-start the program, we will get the exact same random numbers again. This becomes very important, for example, when we employ certain machine learning algorithms that rely on randomization, like the infamous [Random Forest](https://en.wikipedia.org/wiki/Random_forest), and want to obtain **re-producable** results.\n", + "In order to re-produce the same random numbers in a simulation each time we run it, we set the **[random seed](https://en.wikipedia.org/wiki/Random_seed)**. It is good practice to do this at the beginning of a program or notebook. Then every time we re-start the program, we will get the exact same random numbers again. This becomes very important, for example, when we employ certain machine learning algorithms that rely on randomization, like the infamous [Random Forest](https://en.wikipedia.org/wiki/Random_forest), and want to obtain **re-producable** results.\n", "\n", "The [random](https://docs.python.org/3/library/random.html) module provides the [random.seed()](https://docs.python.org/3/library/random.html#random.seed) function to do that." ] @@ -2899,7 +2899,7 @@ } }, "source": [ - "As the Python community is based around open source, many developers publish their code, for example, on the Python Package Index [PyPI](https://pypi.org) from where anyone can download and install it for free using command line based tools like [pip](https://pip.pypa.io/en/stable/) or [conda](https://conda.io/en/latest/). This way, we can always customize our Python installation even more. Managing many such packages is actually quite a deep topic on its own, sometimes fearfully called **[dependency hell](https://en.wikipedia.org/wiki/Dependency_hell)**.\n", + "As the Python community is based around open source, many developers publish their code, for example, on the Python Package Index [PyPI](https://pypi.org) from where anyone may download and install it for free using command line based tools like [pip](https://pip.pypa.io/en/stable/) or [conda](https://conda.io/en/latest/). This way, we can always customize our Python installation even more. Managing many such packages is actually quite a deep topic on its own, sometimes fearfully called **[dependency hell](https://en.wikipedia.org/wiki/Dependency_hell)**.\n", "\n", "The difference between the [standard library](https://docs.python.org/3/library/index.html) and such **third-party** packages is that in the first case the code goes through a much more formalized review process and is officially endorsed by the Python core developers. Yet, many third-party projects also offer the highest quality standards and a lot of such software is actually also relied on by many businesses and researchers.\n", "\n", @@ -2987,7 +2987,7 @@ } }, "source": [ - "`np` can be used in the same way as `math` or `random` above." + "`np` is used in the same way as `math` or `random` above." ] }, { @@ -3094,7 +3094,7 @@ } }, "source": [ - "[numpy](http://www.numpy.org/) somehow magically adds new behavior to Python's built-in arithmetic operators. For example, we can now [scalar-multiply](https://en.wikipedia.org/wiki/Scalar_multiplication) `vec`.\n", + "[numpy](http://www.numpy.org/) somehow magically adds new behavior to Python's built-in arithmetic operators. For example, we may now [scalar-multiply](https://en.wikipedia.org/wiki/Scalar_multiplication) `vec`.\n", "\n", "[numpy](http://www.numpy.org/)'s functions are implemented in highly optimized C code and therefore fast, especially when it comes to big data." ] @@ -3292,7 +3292,7 @@ "source": [ "Disregarding the dunder-style attributes, `mod` defines the five attributes `_default_scalar`, `_scaled_average`, `average`, `average_evens`, and `average_odds`, which are exactly the ones we would expect from reading the [*sample_module.py*](https://github.com/webartifex/intro-to-python/blob/master/sample_module.py) file.\n", "\n", - "An important convention when working with imported code is to *disregard* any attributes starting with an underscore \"\\_\". These are considered **private** and constitute **implementation details** the author of the imported code might change in a future version of his software. We *must* not rely on them in any way.\n", + "An important convention when working with imported code is to *disregard* any attributes starting with an underscore `_`. These are considered **private** and constitute **implementation details** the author of the imported code might change in a future version of his software. We *must* not rely on them in any way.\n", "\n", "In contrast, the three remaining **public** attributes are the functions `average()`, `average_evens()`, and `average_odds()` that we may use after the import." ] @@ -3341,7 +3341,7 @@ } }, "source": [ - "We can use the imported `mod.average_evens()` just like `average_evens()` defined above. The advantage we get from **modularization** with *.py* files is that we can now easily re-use functions across different Jupyter notebooks without re-defining them again and again. Also, we can \"source out\" code that distracts from the storyline told in a notebook." + "We use the imported `mod.average_evens()` just like `average_evens()` defined above. The advantage we get from **modularization** with *.py* files is that we can now easily re-use functions across different Jupyter notebooks without re-defining them again and again. Also, we can \"source out\" code that distracts from the storyline told in a notebook." ] }, { @@ -3433,7 +3433,7 @@ } }, "source": [ - "Packages are a generalization of modules and we will look at one in Chapter 10 in detail. You can, however, already look at a [sample package](https://github.com/webartifex/intro-to-python/tree/master/sample_package) in the repository, which is nothing but a folder with *.py* files in it.\n", + "Packages are a generalization of modules and we will look at one in Chapter 10 in detail. You may, however, already look at a [sample package](https://github.com/webartifex/intro-to-python/tree/master/sample_package) in the repository, which is nothing but a folder with *.py* files in it.\n", "\n", "As a further references on modules, we refer to the [official tutorial](https://docs.python.org/3/tutorial/modules.html)." ] @@ -3464,11 +3464,11 @@ "- 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 **re-use of code**.\n", "\n", - "Functions are **defined** once with the `def` statement. Then, they can be **called** many times with the call operator `()`.\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 can be passed in by **position** or **keyword**. Some functions may even require **keyword-only** arguments.\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", diff --git a/03_conditionals.ipynb b/03_conditionals.ipynb index 6d70f07..f350397 100644 --- a/03_conditionals.ipynb +++ b/03_conditionals.ipynb @@ -19,9 +19,9 @@ } }, "source": [ - "We analyzed every aspect of the `average_evens()` function in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) except for the `if` part. While it seems to intuitively do what we expect it to, there is a whole lot more to be learned from taking it apart. In particular, the `if` can occur within both a **statement** as in our introductory example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) but also an **expression** as in `average_evens()`. This is analogous as to how a noun in a natural language is *either* the subject of *or* an object in a sentence. What is common to both versions of the `if` is that it leads to code being executed for *parts* of the input only. It is our first way of **controlling** the **flow of execution** in a program.\n", + "We analyzed every aspect of the `average_evens()` function in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) except for the `if` related parts. While it seems to intuitively do what we expect it to, there is a whole lot more to be learned from taking it apart. In particular, the `if` may occur within both a **statement** as in our introductory example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) but also an **expression** as in `average_evens()`. This is analogous as to how a noun in a natural language is *either* the subject of *or* an object in a sentence. What is common to both versions of the `if` is that it leads to code being executed for *parts* of the input only. It is our first way of **controlling** the **flow of execution** in a program.\n", "\n", - "After deconstructing `if` in the first part of this chapter, we take a close look at a similar concept, namely handling and raising **exceptions**." + "After deconstructing `if` in the first part of this chapter, we take a close look at a similar concept, namely handling **exceptions**." ] }, { @@ -104,7 +104,7 @@ } }, "source": [ - "Observe how `==` can handle objects of *different* type. This shows how it implements a notion of equality in line with how we humans think of things being equal or not. After all, `42` and `42.0` are totally different $0$s and $1$s for a computer and many programming languages would actually say `False` here! Technically, this is yet another example of operator overloading." + "The `==` operator handles objects of *different* type. This shows how it implements a notion of equality in line with how we humans think of things being equal or not. After all, `42` and `42.0` are totally different $0$s and $1$s for a computer and many programming languages would actually say `False` here! Technically, this is yet another example of operator overloading." ] }, { @@ -139,7 +139,7 @@ } }, "source": [ - "There are, however, cases where even well-behaved Python does not make us happy. Chapter 5 will provide more insights on this \"bug\"." + "There are, however, cases where even well-behaved Python does not make us happy. [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb) will provide more insights on this \"bug\"." ] }, { @@ -189,7 +189,7 @@ { "data": { "text/plain": [ - "94163040564192" + "94697002906592" ] }, "execution_count": 5, @@ -213,7 +213,7 @@ { "data": { "text/plain": [ - "94163040564160" + "94697002906560" ] }, "execution_count": 6, @@ -313,7 +313,7 @@ { "data": { "text/plain": [ - "94163040551152" + "94697002893552" ] }, "execution_count": 10, @@ -359,7 +359,7 @@ "source": [ "`True`, `False`, and `None` have the property that they each exist in memory only *once*. Objects designed this way are so-called **singletons**. This **[design pattern](https://en.wikipedia.org/wiki/Design_Patterns)** was originally developed to keep a program's memory usage at a minimum. It may only be employed in situations where we know that an object will *not* mutate its value (i.e., to re-use the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb), no flipping of $0$s and $1$s in the bag is allowed). In languages \"closer\" to the memory like C we would have to code this singleton logic ourselves but Python has this already built in for *some* types.\n", "\n", - "We can verify this with either the `is` operator or by comparing memory addresses." + "We verify this with either the `is` operator or by comparing memory addresses." ] }, { @@ -523,7 +523,7 @@ } }, "source": [ - "The \"less than\" `<` or \"greater than\" `>` operators on their own mean \"strictly less than\" or \"strictly greater than\" but can be combined with the equality operator into just `<=` and `>=`. This is a shortcut for using the logical `or` operator as described in the next section." + "The \"less than\" `<` or \"greater than\" `>` operators on their own mean \"strictly less than\" or \"strictly greater than\" but may be combined with the equality operator into just `<=` and `>=`. This is a shortcut for using the logical `or` operator as described in the next section." ] }, { @@ -641,7 +641,7 @@ } }, "source": [ - "Boolean expressions can be combined or negated with the **logical operators** `and`, `or`, and `not` to form new boolean expressions. Of course, this may be done *recursively* as well to obtain boolean expressions of arbitrary complexity.\n", + "Boolean expressions may be combined or negated with the **logical operators** `and`, `or`, and `not` to form new boolean expressions. Of course, this may be done *recursively* as well to obtain boolean expressions of arbitrary complexity.\n", "\n", "Their usage is similar to how the equivalent words are used in plain English:\n", "\n", @@ -1133,7 +1133,7 @@ } }, "source": [ - "## Conditional Statements" + "## The `if` Statement" ] }, { @@ -1146,13 +1146,13 @@ "source": [ "In order to write useful programs, we need to control the flow of execution, for example, to react to user input. The logic by which a program does that is referred to as **business logic**.\n", "\n", - "One major language construct to do so is the **[conditional statement](https://docs.python.org/3/reference/compound_stmts.html#the-if-statement)** or `if` statement. It consists of:\n", + "One major language construct to do so is the **[conditional statement](https://docs.python.org/3/reference/compound_stmts.html#the-if-statement)**, or `if` statement for short. It consists of:\n", "\n", "- *one* mandatory `if`-clause,\n", "- an *arbitrary* number of `elif`-clauses (i.e. \"else if\"), and\n", "- an *optional* `else`-clause.\n", "\n", - "The `if`- and `elif`-clauses each specify one *boolean* expression, also called **condition**, while the `else`-clause serves as a \"catch everything else\" case.\n", + "The `if`- and `elif`-clauses each specify one *boolean* expression, also called **condition**, while the `else`-clause serves as the \"catch everything else\" case.\n", "\n", "In terms of syntax, the header lines end with a colon and the code blocks are indented.\n", "\n", @@ -1236,15 +1236,7 @@ "slide_type": "-" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "You will read this just as often as you see heads when tossing a coin\n" - ] - } - ], + "outputs": [], "source": [ "if random.random() > 0.5:\n", " print(\"You will read this just as often as you see heads when tossing a coin\")" @@ -1258,7 +1250,7 @@ } }, "source": [ - "More often than not, we might model a binary choice." + "More often than not, we model a **binary choice**." ] }, { @@ -1293,7 +1285,9 @@ } }, "source": [ - "We may **nest** `if` statements to control the flow of execution in a more granular way. Every additional layer, however, makes the code less readable, in particular, if we have more than one line per code block." + "We may **nest** `if` statements to control the flow of execution in a more granular way. Every additional layer, however, makes the code *less* readable, in particular, if we have more than one line per code block.\n", + "\n", + "The code cell below *either* checks if a number is even or odd *or* if it is positive or negative." ] }, { @@ -1334,7 +1328,9 @@ } }, "source": [ - "A good way to make this code more readable is to introduce **temporary variables** *in combination* with using the `and` operator to **flatten** the branching logic. The `if` statement then reads almost like plain English. In contrast to many other languages, creating variables is a computationally *cheap* operation in Python and also helps to document the code *inline*. Without temporary variables, the `and` flattening could actually lead to more sub-expressions in the conditions be evaluated than necessary. Do you see why?" + "A good way to make this code more readable is to introduce **temporary variables** *in combination* with the `and` operator to **flatten** the branching logic. The `if` statement then reads almost like plain English. In contrast to many other languages, creating variables is a computationally *cheap* operation in Python and also helps to document the code *inline* with meaningful variable names.\n", + "\n", + "Flattening the logic *without* temporary variables could actually lead to more sub-expressions in the conditions be evaluated than necessary. Do you see why?" ] }, { @@ -1377,7 +1373,7 @@ } }, "source": [ - "## Conditional Expressions" + "## The `if` Expression" ] }, { @@ -1388,7 +1384,7 @@ } }, "source": [ - "When all we do with an `if` statement is to assign an object to a variable with respect to a single true-or-false condition (cf., binary choice above), there is a shortcut for that: We could simply assign the result of a so-called **conditional expression** or `if` expression to the variable.\n", + "When all we do with an `if` statement is to assign an object to a variable with respect to a single true-or-false condition (i.e., a binary choice), there is a shortcut for that: We could simply assign the result of a so-called **conditional expression**, or `if` expression for short, to the variable.\n", "\n", "Think of a situation where we evaluate a piece-wise functional relationship $y = f(x)$ at a given $x$, for example:" ] @@ -1530,7 +1526,7 @@ } }, "source": [ - "In this concrete example, however, the most elegant solution would be to use the built-in [max()](https://docs.python.org/3/library/functions.html#max) function." + "In this example, however, the most elegant solution would be to use the built-in [max()](https://docs.python.org/3/library/functions.html#max) function." ] }, { @@ -1578,7 +1574,7 @@ } }, "source": [ - "Conditional expressions may not only be used in the way described in this section. We already saw them as part of a list comprehension that is introduced in Chapter 7." + "Conditional expressions may not only be used in the way described in this section. We already saw them as part of a list comprehension in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) and [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb)." ] }, { @@ -1589,7 +1585,7 @@ } }, "source": [ - "## Exceptions" + "## The `try` Statement" ] }, { @@ -1600,7 +1596,7 @@ } }, "source": [ - "In the previous two chapters we already encountered a couple of *runtime* errors. A natural urge we might have after reading about conditional statements is to write code that somehow reacts to the occurence of such exceptions. All we need for that is a way to formulate a condition for that.\n", + "In the previous two chapters we already encountered a couple of *runtime* errors. A natural urge we might have after reading about conditional statements is to write code that somehow reacts to the occurence of such exceptions. All we need is a way to formulate a condition for that.\n", "\n", "For sure, this is such a common thing to do that Python provides its own language construct for it, namely the `try` [statement](https://docs.python.org/3/reference/compound_stmts.html#the-try-statement).\n", "\n", @@ -1652,9 +1648,9 @@ } }, "source": [ - "However, it is good practise to *not* **handle** *any* possible exception but only the ones we may expect from the code in the `try`-branch. The reasoning why this is done is a bit involved. We only remark here that the code base becomes easier to understand as we clearly communicate to any human reader what could go wrong during execution. Python comes with a lot of [built-in exceptions](https://docs.python.org/3/library/exceptions.html#concrete-exceptions) that we should familiarize ourselves with.\n", + "However, it is good practice to *not* **handle** *any* possible exception but only the ones we may expect from the code in the `try`-branch. The reasoning why this is done is a bit involved. We only remark here that the code base becomes easier to understand as we clearly communicate to any human reader what could go wrong during execution. Python comes with a lot of [built-in exceptions](https://docs.python.org/3/library/exceptions.html#concrete-exceptions) that we should familiarize ourselves with.\n", "\n", - "Another good practise is to always keep the code in the `try`-branch short so as to not accidently handle an exception we do not want to handle.\n", + "Another good practice is to always keep the code in the `try`-branch short so as to not accidently handle an exception we do not want to handle.\n", "\n", "In the example, we are dividing numbers and may therefore expect a `ZeroDivisionError`." ] @@ -1691,9 +1687,9 @@ } }, "source": [ - "Often, we must have some code run independent of an exception occuring (e.g., to close a connection to a database). To achieve that, we can add an optional `finally`-branch to the `try` statement.\n", + "Often, we may have to run some code *independent* of an exception occuring, for example, to close a connection to a database. To achieve that, we add a `finally`-branch to the `try` statement.\n", "\n", - "Similarly, we might have some code that must be run exactly when no exception occurs but we do not want to put it in the `try`-branch as per the good practice mentioned. To achieve that, we can add an optional `else`-branch to the `try` statement.\n", + "Similarly, we may have to run some code *only if* no exception occurs but we do not want to put it in the `try`-branch as per the good practice mentioned above. To achieve that, we add an `else`-branch to the `try` statement.\n", "\n", "To showcase everything together, we look at one last example. To spice it up a bit, we randomize the input. So run the cell several times and see for yourself. It's actually quite easy." ] @@ -1711,7 +1707,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Oops. Division by 0. How does that work?\n", + "Yes, division worked smoothly.\n", "I am always printed\n" ] } diff --git a/04_iteration.ipynb b/04_iteration.ipynb index 755b4a5..7ab75c3 100644 --- a/04_iteration.ipynb +++ b/04_iteration.ipynb @@ -197,9 +197,9 @@ } }, "source": [ - "Whenever we can find a recursive way of formulating an idea, we can immediately translate it into Python in a *naive* way (i.e., we create a *correct* program that may *not* be an *efficient* implementation yet).\n", + "Whenever we find a recursive way of formulating an idea, we can immediately translate it into Python in a *naive* way (i.e., we create a *correct* program that may *not* be an *efficient* implementation yet).\n", "\n", - "Below is a first version of `factorial()`: The `return` statement does not have to be a function's last code line and we can certainly have several `return` statements as well." + "Below is a first version of `factorial()`: The `return` statement does not have to be a function's last code line and we may certainly have several `return` statements as well." ] }, { @@ -554,7 +554,7 @@ } }, "source": [ - "Euclid's algorithm is stunningly fast, even for large numbers. Its speed comes from the use of the modulo operation `%`. However, this is *not* true for recusion in general, which can result in very slow programs if not applied correctly." + "Euclid's algorithm is stunningly fast, even for large numbers. Its speed comes from the use of the modulo operator `%`. However, this is *not* true for recusion in general, which may result in very slow programs if not applied correctly." ] }, { @@ -837,7 +837,7 @@ } }, "source": [ - "This implementation is *highly* **inefficient** as small Fibonacci numbers can already take a very long time to compute. The reason for this is **exponential growth** in the number of function calls. As [PythonTutor](http://pythontutor.com/visualize.html#code=def%20fibonacci%28i%29%3A%0A%20%20%20%20if%20i%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%200%0A%20%20%20%20elif%20i%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20return%20fibonacci%28i%20-%201%29%20%2B%20fibonacci%28i%20-%202%29%0A%0Arv%20%3D%20fibonacci%285%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows, `fibonacci()` is called again and again with the same `i` arguments.\n", + "This implementation is *highly* **inefficient** as small Fibonacci numbers already take a very long time to compute. The reason for this is **exponential growth** in the number of function calls. As [PythonTutor](http://pythontutor.com/visualize.html#code=def%20fibonacci%28i%29%3A%0A%20%20%20%20if%20i%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%200%0A%20%20%20%20elif%20i%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20return%20fibonacci%28i%20-%201%29%20%2B%20fibonacci%28i%20-%202%29%0A%0Arv%20%3D%20fibonacci%285%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows, `fibonacci()` is called again and again with the same `i` arguments.\n", "\n", "To understand this in detail, we would have to study algorithms and data structures (e.g., with [this book](https://www.amazon.de/Introduction-Algorithms-Press-Thomas-Cormen/dp/0262033844/ref=sr_1_1?__mk_de_DE=%C3%85M%C3%85%C5%BD%C3%95%C3%91&crid=1JNE8U0VZGU0O&qid=1569837169&s=gateway&sprefix=algorithms+an%2Caps%2C180&sr=8-1)), a discipline within computer science, and dive into the analysis of **[time complexity of algorithms](https://en.wikipedia.org/wiki/Time_complexity)**.\n", "\n", @@ -859,8 +859,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "The slowest run took 5.01 times longer than the fastest. This could mean that an intermediate result is being cached.\n", - "55 µs ± 44.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + "47.3 µs ± 12.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], @@ -882,7 +881,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "1.63 ms ± 68 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + "1.63 ms ± 21.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], @@ -904,7 +903,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "192 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "211 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -926,7 +925,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "2.21 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "2.22 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -948,7 +947,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "5.62 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "5.81 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -4072,17 +4071,19 @@ "source": [ "The infinite recursions could easily be avoided by replacing `n == 0` with `n <= 0` in both functions and thereby **generalizing** them. But even then, calling either `countdown()` or `factorial()` with a non-integer number is *semantically* wrong and therefore we better leave the base cases unchanged.\n", "\n", - "Errors as above are a symptom of missing **type checking**: By design, Python allows us to pass in not only integers but objects of any type as arguments to the `countdown()` and `factorial()` functions. As long as the arguments \"behave\" like integers, we will not encounter any *runtime* errors. This is the case here as the two example functions only use the `-` and `*` operators internally and in this context a `float` object behaves exactly like an `int` object. So, the functions keep calling themselves until Python decides with a built-in heuristic that the recursion is likely not going to end and aborts the computations with a `RecursionError`. Stricly speaking, this is of course a *runtime* error as well. The missing type checking is 100% intentional and considered a feature of rather than a bug in Python.\n", + "Errors as above are a symptom of missing **type checking**: By design, Python allows us to pass in not only integers but objects of any type as arguments to the `countdown()` and `factorial()` functions. As long as the arguments \"behave\" like integers, we will not encounter any *runtime* errors. This is the case here as the two example functions only use the `-` and `*` operators internally and, in the context of arithmetic, a `float` object behaves exactly like an `int` object. So, the functions keep calling themselves until Python decides with a built-in heuristic that the recursion is likely not going to end and aborts the computations with a `RecursionError`. Stricly speaking, a `RecursionError` is of course a *runtime* error as well.\n", "\n", - "Pythonistas often use the term **[duck typing](https://en.wikipedia.org/wiki/Duck_typing)** when refering to the same idea and the colloquial saying goes \"If it walks like a duck and it quacks like a duck, it must be a duck\". For example, we could call `factorial()` with the `float` object `3.0` and the recursion works out fine. So, as long as the `3.0` \"walks\" like a `3` and \"quacks\" like a `3`, it \"must be\" a `3`.\n", + "The missing type checking is *100% intentional* and considered a **[feature of rather than a bug](https://www.urbandictionary.com/define.php?term=It%27s%20not%20a%20bug%2C%20it%27s%20a%20feature)** in Python!\n", + "\n", + "Pythonistas often use the colloquial term **[duck typing](https://en.wikipedia.org/wiki/Duck_typing)** when refering to the same idea and the saying goes \"If it walks like a duck and it quacks like a duck, it must be a duck\". For example, we could call `factorial()` with the `float` object `3.0` and the recursion works out fine. So, as long as the `3.0` \"walks\" like a `3` and \"quacks\" like a `3`, it \"must be\" a `3`.\n", "\n", "We see a similar behavior when we mix objects of types `int` and `float` with arithmetic operators. For example, `1 + 2.0` works because Python implicitly views the `1` as a `1.0` at runtime and then knows how to do floating-point arithmetic: Here, the `int` \"walks\" and \"quacks\" like a `float`. Strictly speaking, this is yet another example of operator overloading whereas duck typing refers to the same behavior when passing arguments to function calls.\n", "\n", - "The important lesson is that we must expect our functions to be called with objects of *any* type at runtime, as opposed to the one type we had in mind when we defined the function.\n", + "The important lesson is that we must expect our functions be called with objects of *any* type at runtime, as opposed to the one type we had in mind when we defined the function.\n", "\n", - "Duck typing is possible because Python is a dynamically typed language. On the contrary, in statically typed languages like C we have to declare (i.e., \"specify\") the data type of every parameter in a function definition. Then, a `RecursionError` as for `countdown(3.1)` or `factorial(3.1)` above could not occur. For example, if we declared the `countdown()` and `factorial()` functions to only accept `int` objects, calling the functions with a `float` argument would immediately fail *syntactically*. As a downside, we would then lose the ability to call `factorial()` with `3.0`, which is *semantically* correct nevertheless.\n", + "Duck typing is possible because Python is a dynamically typed language. On the contrary, in statically typed languages like C we *must* declare (i.e., \"specify\") the data type of every parameter in a function definition. Then, a `RecursionError` as for `countdown(3.1)` or `factorial(3.1)` above could not occur. For example, if we declared the `countdown()` and `factorial()` functions to only accept `int` objects, calling the functions with a `float` argument would immediately fail *syntactically*. As a downside, we would then lose the ability to call `factorial()` with `3.0`, which is *semantically* correct nevertheless.\n", "\n", - "So, there is no black or white answer as to which of the two language designs is better. Yet, most professional programmers have very strong opinions with respect to duck typing reaching from \"love\" to \"hate\". This is another example as to how programming is a subjective art and not an \"objective\" science. Probably, Python is regarded more beginner friendly as `3` and `3.0` should intuitively be interchangeable." + "So, there is no black or white answer as to which of the two language designs is better. Yet, most professional programmers have very strong opinions with respect to duck typing reaching from \"love\" to \"hate\". This is another example as to how programming is a subjective art rather than an \"objective\" science. Python's design is probably more appealing to beginners who intuitively regard `3` and `3.0` as interchangeable." ] }, { @@ -4104,15 +4105,15 @@ } }, "source": [ - "We can use the built-in [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function to make sure `factorial()` is called with an `int` object passed in. We further **validate the input** by verifying that the integer is non-negative.\n", + "We use the built-in [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function to make sure `factorial()` is called with an `int` object as the argument. We further **validate the input** by verifying that the integer is non-negative.\n", "\n", - "Meanwhile, we also see how we can manually raise exceptions with the `raise` [statement](https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement), another way of controlling the flow of execution.\n", + "Meanwhile, we also see how we manually raise exceptions with the `raise` [statement](https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement), another way of controlling the flow of execution.\n", "\n", - "The first two branches in the revised `factorial()` function act as **guardians** ensuring that the code does not produce *unexpected* runtime errors: Errors can certainly be expected when mentioned in the docstring.\n", + "The first two branches in the revised `factorial()` function act as **guardians** ensuring that the code does not produce *unexpected* runtime errors: Errors may certainly be expected when mentioned in the docstring.\n", "\n", - "Forcing `n` to be an `int` is a very puritan way of handling the issues discussed above. A more relaxed approach could be to also accept a `float` and use its [is_integer()](https://docs.python.org/3/library/stdtypes.html#float.is_integer) method to check if `n` could be casted as an `int`. After all, by being too puritan we can not take advantage of duck typing.\n", + "Forcing `n` to be an `int` is a very puritan way of handling the issues discussed above. A more relaxed approach could be to also accept a `float` and use its [is_integer()](https://docs.python.org/3/library/stdtypes.html#float.is_integer) method to check if `n` could be casted as an `int`. After all, being too puritan we cannot take advantage of duck typing.\n", "\n", - "So in essence, we are doing *two* things here. Besides checking for the correct type, we are also enforcing **domain-specific** (i.e., mathematical) rules with respect to the non-negativity of `n`." + "So in essence, we are doing *two* things here: Besides checking the type, we also enforce **domain-specific** (i.e., mathematical here) rules with respect to the non-negativity of `n`." ] }, { @@ -4368,7 +4369,7 @@ " n (int): seconds until the party begins; must be positive\n", "\n", " Raises:\n", - " TypeError: if n is not of an integer\n", + " TypeError: if n is not an integer\n", " ValueError: if n is not positive\n", " \"\"\"\n", " if not isinstance(n, int):\n", @@ -4464,7 +4465,7 @@ " gcd (int)\n", "\n", " Raises:\n", - " TypeError: if a or b are not of an integer type\n", + " TypeError: if a or b are not integers\n", " ValueError: if a or b are not positive\n", " \"\"\"\n", " if not isinstance(a, int) or not isinstance(b, int):\n", @@ -4548,7 +4549,7 @@ } }, "source": [ - "We can also see that this implementation is a lot *less* efficient than its recursive counterpart which solves `gcd()` for the same two numbers $112233445566778899$ and $987654321$ within microseconds." + "We also see that this implementation is a lot *less* efficient than its recursive counterpart which solves `gcd()` for the same two numbers $112233445566778899$ and $987654321$ within microseconds." ] }, { @@ -4657,7 +4658,7 @@ " n (int): a positive number to start the Collatz sequence at\n", "\n", " Raises:\n", - " TypeError: if n is not of an integer\n", + " TypeError: if n is not an integer\n", " ValueError: if n is not positive\n", " \"\"\"\n", " if not isinstance(n, int):\n", @@ -4668,7 +4669,7 @@ " while n != 1:\n", " print(n, end=\" \")\n", " if n % 2 == 0:\n", - " n //= 2 # //= so that n remains an int\n", + " n //= 2 # //= instead of /= so that n remains an int\n", " else:\n", " n = 3 * n + 1\n", "\n", @@ -4791,9 +4792,9 @@ "source": [ "Recursion and the `while` statement are two sides of the same coin. Disregarding that in the case of recursion Python internally faces some additional burden for managing the stack of frames in memory, both approaches lead to the *same* computational steps in memory. More importantly, we can re-formulate a recursive implementation in an iterative way and vice verca despite one of the two ways often \"feeling\" a lot more natural given a particular problem.\n", "\n", - "So how does the `for` [statement](https://docs.python.org/3/reference/compound_stmts.html#the-for-statement) we saw in the very first example in this book fit into this picture? It is really a *redundant* language construct to provide a *shorter* and more *convenient* syntax for common applications of the `while` statement. In programming, such additions to a language are called **syntactic sugar**. Sugar makes a cup of tea taste better but we can drink tea without sugar too.\n", + "So how does the `for` [statement](https://docs.python.org/3/reference/compound_stmts.html#the-for-statement) we saw in the very first example in this book fit into this picture? It is really a *redundant* language construct to provide a *shorter* and more *convenient* syntax for common applications of the `while` statement. In programming, such additions to a language are called **syntactic sugar**. Sugar makes a cup of tea taste better but we may drink tea without sugar too.\n", "\n", - "Consider the following `numbers` list. Without the `for` statement, we would have to keep track of a temporary **index variable** `i` to loop over all its elements and also obtain the individual elements with the `[]` operator in each iteration of the loop." + "Consider the following `numbers` list. Without the `for` statement, we have to manage a temporary **index variable** `i` to loop over all the elements and also obtain the individual elements with the `[]` operator in each iteration of the loop." ] }, { @@ -4806,7 +4807,7 @@ }, "outputs": [], "source": [ - "numbers = [5, 6, 7, 8, 9]" + "numbers = [0, 1, 2, 3, 4]" ] }, { @@ -4822,7 +4823,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "5 6 7 8 9 " + "0 1 2 3 4 " ] } ], @@ -4831,7 +4832,8 @@ "while i < len(numbers):\n", " number = numbers[i]\n", " print(number, end=\" \")\n", - " i += 1" + " i += 1\n", + "del i" ] }, { @@ -4842,7 +4844,7 @@ } }, "source": [ - "The `for` statement, on the contrary, makes the actual business logic more apparent by stripping all the boilerplate code away." + "The `for` statement, on the contrary, makes the actual business logic more apparent by stripping all the **boilerplate code** away." ] }, { @@ -4858,7 +4860,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "5 6 7 8 9 " + "0 1 2 3 4 " ] } ], @@ -4875,34 +4877,12 @@ } }, "source": [ - "For sequences of integers the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in makes the `for` statement even more convenient: It creates a list-like object of type `range` that generates integers \"on the fly\" and we will look closely at the underlying data types in Chapter 7." + "For sequences of integers the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in makes the `for` statement even more convenient: It creates a list-like object of type `range` that generates integers \"on the fly\" and we will look closely at the underlying effects in memory in Chapter 7." ] }, { "cell_type": "code", "execution_count": 51, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0 1 2 3 4 " - ] - } - ], - "source": [ - "for number in [0, 1, 2, 3, 4]:\n", - " print(number, end=\" \")" - ] - }, - { - "cell_type": "code", - "execution_count": 52, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4922,20 +4902,9 @@ " print(number, end=\" \")" ] }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "Let's quickly verify that `range(5)` creates an object of type `range`." - ] - }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 52, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4948,7 +4917,7 @@ "range" ] }, - "execution_count": 53, + "execution_count": 52, "metadata": {}, "output_type": "execute_result" } @@ -4965,12 +4934,12 @@ } }, "source": [ - "[range()](https://docs.python.org/3/library/functions.html#func-range) takes optional `start` and `step` arguments that we can use to customize the sequence of integers even more." + "[range()](https://docs.python.org/3/library/functions.html#func-range) takes optional `start` and `step` arguments that we use to customize the sequence of integers even more." ] }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 53, "metadata": { "slideshow": { "slide_type": "slide" @@ -4992,7 +4961,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 54, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5031,26 +5000,26 @@ } }, "source": [ - "The important difference between the `list` objects (i.e., `[0, 1, 2, 3, 4]` and `[1, 3, 5, 7, 9]`) and the `range` objects (i.e., `range(5)` and `range(1, 10, 2)`) is that in the former case *six* objects are created in memory, one `list` holding pointers to *five* `int` objects, whereas in the latter case only *one* `range` object exists in memory that **generates** `int` objects as we ask for it.\n", + "The important difference between the above `list` objects, `[0, 1, 2, 3, 4]` and `[1, 3, 5, 7, 9]`, and the `range` objects, `range(5)` and `range(1, 10, 2)`, is that in the former case *six* objects are created in memory *before* the `for` statement starts running, *one* `list` holding pointers to *five* `int` objects, whereas in the latter case only *one* `range` object is created that **generates** `int` objects one at a time *while* the `for`-loop runs.\n", "\n", - "However, we can iterate over both of them. So a natural question to ask is why Python treats objects of *different* types in the *same* way when used with a `for` statement.\n", + "However, we can loop over both of them. So a natural question to ask is why Python treats objects of *different* types in the *same* way when used with a `for` statement.\n", "\n", - "So far, the overarching storyline in this book goes like this: In Python, *everything* is an object. Besides its *identity* and *value*, every object is characterized by belonging to *one* data type that determines how the object behaves and what we can do with it.\n", + "So far, the overarching storyline in this book goes like this: In Python, *everything* is an object. Besides its *identity* and *value*, every object is characterized by belonging to *one data type* that determines how the object behaves and what we may do with it.\n", "\n", - "Now, just as we classify objects by their types, we also classify these **concrete data types** (e.g., `int`, `float`, or `str`) into **abstract concepts**.\n", + "Now, just as we classify objects by their types, we also classify these **concrete data types** (e.g., `int`, `float`, `str`, or `list`) into **abstract concepts**.\n", "\n", - "We have actually done this in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) already when we described a `list` object as \"some sort of container that holds [...] pointers to other objects\". So, abstractly speaking, **containers** are any objects that are \"composed\" of other objects and also \"manage\" how these objects are organized. `list` objects, for example, have the property that they model an inherent order associated with its elements. There exist, however, many other container types, many of which do *not* come with an order. So, containers primarily \"contain\" other objects and have *nothing* to do with looping.\n", + "We actually did this already in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) when we described a `list` object as \"some sort of container that holds [...] pointers to other objects\". So, abstractly speaking, **containers** are any objects that are \"composed\" of other objects and also \"manage\" how these objects are organized. `list` objects, for example, have the property that they model an internal order associated with its elements. There exist, however, other container types, many of which do *not* come with an order. So, containers primarily \"contain\" other objects and have *nothing* to do with looping.\n", "\n", - "On the contrary, the abstract concept of **iterables** is all about looping: Any object that we can loop over is by definition an iterable. So, `range` objects, for example, are iterables taht do *not* contain any other objects. Moreover, looping does *not* have to occur in any particular order although this is the case for both `list` and `range` objects.\n", + "On the contrary, the abstract concept of **iterables** is all about looping: Any object that we can loop over is by definition an iterable. So, `range` objects, for example, are iterables that do *not* contain other objects. Moreover, looping does *not* have to occur in a *predictable* order although this is the case for both `list` and `range` objects.\n", "\n", "Typically, containers are iterable and iterables are containers. Yet, only because these two concepts coincide often, we must not think of them as the same. Chapter 10 will finally give an explanation as to how abstract concepts are implemented and play together.\n", "\n", - "`list` objects like `first_names` below are iterable containers. They actually implement even more abstract concepts as we will see in Chapter 7." + "So, `list` objects like `first_names` below are iterable containers. They actually implement even more abstract concepts as we will see in Chapter 7." ] }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 55, "metadata": { "slideshow": { "slide_type": "slide" @@ -5069,12 +5038,12 @@ } }, "source": [ - "The characteristic operator associated with a container type is the `in` operator which checks if a given object evaluates equal to any of the objects in the container. Colloquially, it checks if an object is \"contained\" in the container. This operation is also called **membership testing**." + "The characteristic operator associated with a container type is the `in` operator: It checks if a given object evaluates equal to at least one of the objects in the container. Colloquially, it checks if an object is \"contained\" in the container. Formally, this operation is called **membership testing**." ] }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 56, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5087,7 +5056,7 @@ "True" ] }, - "execution_count": 57, + "execution_count": 56, "metadata": {}, "output_type": "execute_result" } @@ -5098,7 +5067,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 57, "metadata": { "slideshow": { "slide_type": "-" @@ -5111,7 +5080,7 @@ "False" ] }, - "execution_count": 58, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" } @@ -5128,12 +5097,12 @@ } }, "source": [ - "This shows the exact workings of the `in` operator: Although `7.0` is *not* in `numbers`, `7.0` evaluates equal to the `7` that is in it, which is why the following expression evaluates to `True`. So, while we could colloquially say that `numbers` \"contains\" `7.0`, it actually does not." + "The cell below shows the *exact* workings of the `in` operator: Although `3.0` is *not* contained in `numbers`, it evaluates equal to the `3` that is, which is why the following expression evaluates to `True`. So, while we could colloquially say that `numbers` \"contains\" `3.0`, it actually does not." ] }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 58, "metadata": { "slideshow": { "slide_type": "skip" @@ -5146,13 +5115,13 @@ "True" ] }, - "execution_count": 59, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "7.0 in numbers" + "3.0 in numbers" ] }, { @@ -5163,12 +5132,12 @@ } }, "source": [ - "Similarly, the characteristic operation of an iterable type is that it supports iteration, for example, with a `for`-loop." + "Similarly, the characteristic operation of an iterable type is that it supports being looped over, for example, with the `for` statement." ] }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 59, "metadata": { "slideshow": { "slide_type": "slide" @@ -5196,12 +5165,12 @@ } }, "source": [ - "If we need to have an index variable in the loop's body, we use the [enumerate()](https://docs.python.org/3/library/functions.html#enumerate) built-in that takes an iterable as its argument and then generates a stream of \"pairs\" consisting of an index variable and an object provided by the iterable. There is *no* need to ever revert back to the `while` statement to loop over an iterable object." + "If we must have an index variable in the loop's body, we use the [enumerate()](https://docs.python.org/3/library/functions.html#enumerate) built-in that takes an *iterable* as its argument and then generates a \"stream\" of \"pairs\" consisting of an index variable and an object provided by the iterable. There is *no* need to ever revert back to the `while` statement to loop over an iterable object." ] }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 60, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5234,7 +5203,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 61, "metadata": { "slideshow": { "slide_type": "skip" @@ -5267,7 +5236,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 62, "metadata": { "slideshow": { "slide_type": "skip" @@ -5280,7 +5249,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 63, "metadata": { "slideshow": { "slide_type": "skip" @@ -5291,17 +5260,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "Achim MĂĽller\n", - "Berthold Meyer\n", - "Carl Mayer\n", - "Diedrich Schmitt\n", - "Eckardt Schmidt\n" + "Achim MĂĽller Berthold Meyer Carl Mayer Diedrich Schmitt Eckardt Schmidt " ] } ], "source": [ "for first_name, last_name in zip(first_names, last_names):\n", - " print(first_name, last_name)" + " print(first_name, last_name, end=\" \")" ] }, { @@ -5327,12 +5292,12 @@ "\n", "However, one advantage of calculating Fibonacci numbers in a **forwards** fashion with a `for` statement is that we could list the entire sequence in ascending order as we calculate the desired number. To show this, we added `print()` statements in `fibonacci()` below.\n", "\n", - "We do *not* need to store the index variable in the `for`-loop's header line: That is what the underscore \"\\_\" indicates; we \"throw it away\". Also, we do not need to explicitly check if `i` is of type `int` as the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in raises a `TypeError` if used with anything other than an `int`." + "We do *not* need to store the index variable in the `for`-loop's header line: That is what the underscore variable `_` indicates; we \"throw it away\"." ] }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 64, "metadata": { "code_folding": [], "slideshow": { @@ -5351,11 +5316,12 @@ " ith_fibonacci (int)\n", "\n", " Raises:\n", - " TypeError: if i is not of an integer type\n", + " TypeError: if i is not an integer\n", " ValueError: if i is not positive\n", " \"\"\"\n", - " # no need to check if i is an integer as range() does that\n", - " if i < 0:\n", + " if not isinstance(i, int):\n", + " raise TypeError(\"i must be an integer\")\n", + " elif i < 0:\n", " raise ValueError(\"i must be non-negative\")\n", "\n", " a = 0\n", @@ -5372,7 +5338,7 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 65, "metadata": { "slideshow": { "slide_type": "slide" @@ -5392,7 +5358,7 @@ "144" ] }, - "execution_count": 66, + "execution_count": 65, "metadata": {}, "output_type": "execute_result" } @@ -5420,12 +5386,12 @@ } }, "source": [ - "Another more important advantage is that now we can calculate even big Fibonacci numbers *efficiently*." + "Another more important advantage is that now we may calculate even big Fibonacci numbers *efficiently*." ] }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 66, "metadata": { "slideshow": { "slide_type": "slide" @@ -5445,7 +5411,7 @@ "218922995834555169026" ] }, - "execution_count": 67, + "execution_count": 66, "metadata": {}, "output_type": "execute_result" } @@ -5473,12 +5439,12 @@ } }, "source": [ - "The iterative `factorial()` function is comparable to its recursive counterpart when it comes to readability. One advantage of calculating the factorial in a forwards fashion is that we could track the intermediate `product` as it grows." + "The iterative `factorial()` implementation is comparable to its recursive counterpart when it comes to readability. One advantage of calculating the factorial in a forwards fashion is that we could track the intermediate `product` as it grows." ] }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 67, "metadata": { "slideshow": { "slide_type": "slide" @@ -5514,7 +5480,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 68, "metadata": { "slideshow": { "slide_type": "slide" @@ -5534,7 +5500,7 @@ "3628800" ] }, - "execution_count": 69, + "execution_count": 68, "metadata": {}, "output_type": "execute_result" } @@ -5584,12 +5550,12 @@ } }, "source": [ - "Let's say we have a list of numbers and we want to check if the square of at least one of its numbers is above a `threshold` of `100`." + "Let's say we have a `numbers` list and want to check if the square of at least one of its elements is above a `threshold` of `100`." ] }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 69, "metadata": { "slideshow": { "slide_type": "slide" @@ -5602,7 +5568,7 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 70, "metadata": { "slideshow": { "slide_type": "-" @@ -5621,12 +5587,12 @@ } }, "source": [ - "A first naive implementation could look like this: We loop over *every* element in `numbers` and set an **indicator variable** `is_above` to `True` once we encounter an element satisfying our search condition." + "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 search condition." ] }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 71, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5663,16 +5629,16 @@ } }, "source": [ - "This implementation is *inefficient* as even if the *first* number in `numbers` has a square greater than `100`, we loop until the last element, which could take a long time for a big list.\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 seperate from it to check for the final result. The actual business logic is not clear right away.\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* clear right away.\n", "\n", "Luckily, Python provides a `break` [statement](https://docs.python.org/3/reference/simple_stmts.html#the-break-statement) that let's us stop the `for`-loop in any iteration of the loop. Conceptually, it is yet another means of controlling the flow of execution." ] }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 72, "metadata": { "slideshow": { "slide_type": "slide" @@ -5710,7 +5676,7 @@ } }, "source": [ - "This is a computational improvement. However, the code can still be split into *three* groups: initialization, the `for`-loop, and some finalizing logic. We would prefer to convey the program's idea in *one* compound statement." + "This is a computational improvement. However, the code still consists *three* logical sections: some initialization *before* the `for`-loop, the loop itself, and some finalizing logic. We would prefer to convey the program's idea in *one* compound statement instead." ] }, { @@ -5732,9 +5698,9 @@ } }, "source": [ - "To express the logic in a prettier way, we add an `else`-clause at the end of the `for`-loop. The `else`-branch is only executed if the body in the `for`-branch is *not* stopped with a `break` statement before reaching the last iteration in the loop. The word \"else\" implies a rather unintuitive meaning and it had better been named a `then`-clause. In most scenarios, however, the `else`-clause logically goes together with some `if` statement within the `for`-loop's body.\n", + "To express the logic in a prettier way, we add an `else`-clause at the end of the `for`-loop. The `else`-branch 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 rather unintuitive meaning and had better been named a `then`-clause. In most scenarios, however, the `else`-clause logically goes together with some `if` statement in the `for`-loop's body.\n", "\n", - "Overall, the expressive power of our code increases. Not many programming languages support a `for`-`else`-branching, which turns out to be very useful in practice." + "Overall, the code's expressive power increases. Not many programming languages support a `for`-`else`-branching, which turns out to be very useful in practice." ] }, { @@ -5750,7 +5716,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 73, "metadata": { "slideshow": { "slide_type": "slide" @@ -5788,12 +5754,12 @@ } }, "source": [ - "Lastly, we incorporate the `if`-`else` logic at the end into the `for`-loop and avoid the `is_above` variable alltogether." + "Lastly, we incorporate the finalizing `if`-`else` logic into the `for`-loop avoiding the `is_above` variable alltogether." ] }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 74, "metadata": { "slideshow": { "slide_type": "slide" @@ -5826,12 +5792,12 @@ } }, "source": [ - "Of course, if we set the `threshold` a number's square has to pass higher, for example to `200`, we have to loop through the entire `numbers` list. There is no way to optimize this **[linear search](https://en.wikipedia.org/wiki/Linear_search)**, at least as long as we model the list of numbers with a `list` object. More advanced data types, however, exist that mitigate that downside." + "Of course, if we set the `threshold` an element's square has to pass higher, for example to `200`, we have to loop through the entire `numbers` list. There is *no way* to further optimize this **[linear search](https://en.wikipedia.org/wiki/Linear_search)**, at least as long as we model `numbers` as a `list` object. More advanced data types, however, exist that mitigate that downside." ] }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 75, "metadata": { "slideshow": { "slide_type": "skip" @@ -5879,15 +5845,15 @@ "source": [ "Often times, we process some iterable with numeric data, for example, a list of numbers as in the introductory example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) or, more realistically, data from a CSV file with many rows and columns.\n", "\n", - "Processing numeric data usually comes down to operations that can be grouped into one of the following three categories:\n", + "Processing numeric data usually comes down to operations that may be grouped into one of the following three categories:\n", "\n", - "- **mapping**: transform an observation according to some functional relationship $y = f(x)$\n", - "- **filtering**: throw away individual observations (e.g., statistical outliers)\n", - "- **reducing**: collect individual observations into summary statistics\n", + "- **mapping**: transform a sample according to some functional relationship $y = f(x)$\n", + "- **filtering**: throw away individual samples (e.g., statistical outliers)\n", + "- **reducing**: collect individual samples into summary statistics\n", "\n", "We will study this **map-filter-reduce** paradigm extensively in Chapter 7 after introducing more advanced data types that are needed to work with \"big\" data.\n", "\n", - "In this section, we focus on *filtering out* some samples within a `for`-loop." + "In the remainder of this section, we focus on *filtering out* some samples within a `for`-loop." ] }, { @@ -5919,7 +5885,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 76, "metadata": { "slideshow": { "slide_type": "slide" @@ -5932,7 +5898,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 77, "metadata": { "slideshow": { "slide_type": "-" @@ -5952,7 +5918,7 @@ "370" ] }, - "execution_count": 78, + "execution_count": 77, "metadata": {}, "output_type": "execute_result" } @@ -5977,9 +5943,9 @@ } }, "source": [ - "The above code is still rather easy to read as it only involves two levels of indentation.\n", + "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 complient with [PEP 8](https://www.python.org/dev/peps/pep-0008/#maximum-line-length) requires us to use *at most* 79 characters in a line!\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](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\"." ] @@ -6014,7 +5980,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 78, "metadata": { "slideshow": { "slide_type": "slide" @@ -6027,7 +5993,7 @@ "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" ] }, - "execution_count": 79, + "execution_count": 78, "metadata": {}, "output_type": "execute_result" } @@ -6038,7 +6004,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 79, "metadata": { "slideshow": { "slide_type": "-" @@ -6058,7 +6024,7 @@ "182" ] }, - "execution_count": 80, + "execution_count": 79, "metadata": {}, "output_type": "execute_result" } @@ -6084,11 +6050,11 @@ } }, "source": [ - "With already three levels of indentation, less horizontal space is available for the actual code. Of course, one could combine the two `if` statements with the logical `and` operator as shown in [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb). However, then we trade off horizontal space against a more \"complex\" `if` logic and this is not a real improvement.\n", + "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](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb#The-if-Statement). Then, however, we trade off horizontal space against a more \"complex\" `if` logic and this is *not* a real improvement.\n", "\n", - "A Pythonista would instead make use of the `continue` [statement](https://docs.python.org/3/reference/simple_stmts.html#the-continue-statement) that causes a loop to jump right into the next iteration skipping the rest of the code block.\n", + "A Pythonista would instead make use of the `continue` [statement](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 fragement below occupies more vertical space and less horizontal space. A good trade-off." + "The revised code fragement below occupies more vertical space and less horizontal space: A *good* trade-off." ] }, { @@ -6104,7 +6070,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 80, "metadata": { "slideshow": { "slide_type": "slide" @@ -6124,7 +6090,7 @@ "182" ] }, - "execution_count": 81, + "execution_count": 80, "metadata": {}, "output_type": "execute_result" } @@ -6153,13 +6119,11 @@ } }, "source": [ - "This is yet another illustration of why programming is an art. The two preceding code fragments do *exactly* the *same* with *identical* time complexity. However, arguably the latter it a lot easier to read for a human, at least when the business logic grows beyond more than just two nested filters.\n", + "This is yet another illustration of why programming is an art. The two preceding code cells do *exactly* 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 nested filters.\n", "\n", - "The idea behind the `continue` statement is conceptually similar to the early exit pattern we saw in the context of function definitions in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb).\n", + "The idea behind the `continue` statement is conceptually similar to the early exit pattern we saw above.\n", "\n", - "The two examples can be modeled in an even better way as we will see in Chapter 7.\n", - "\n", - "Both the `break` and `continue` statements as well as the optional `else`-clause are not only supported within `for`-loops but also `while`-loops." + "Both the `break` and `continue` statements as well as the optional `else`-clause are not only supported with `for`-loops but also `while`-loops." ] }, { @@ -6181,7 +6145,7 @@ } }, "source": [ - "Sometimes we find ourselves in situations where we can *not* know ahead of time how often or until which point in time a code block is to be executed." + "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." ] }, { @@ -6205,7 +6169,7 @@ "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()](https://docs.python.org/3/library/functions.html#input) function that prints a message to the user, called the **prompt**, and reads in what the user typed in response as a `str` object. We use it to process the user's \"unpredictable\" input to our program (i.e., a user might type in some invalid response). Further, we use the [random()](https://docs.python.org/3/library/random.html#random.random) function in the [random](https://docs.python.org/3/library/random.html) module to model the coin toss.\n", + "Python provides the built-in [input()](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()](https://docs.python.org/3/library/random.html#random.random) function in the [random](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 certain event occurs, we `break` out of the loop.\n", "\n", @@ -6214,7 +6178,7 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 81, "metadata": { "slideshow": { "slide_type": "slide" @@ -6227,7 +6191,7 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 82, "metadata": { "slideshow": { "slide_type": "-" @@ -6240,7 +6204,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 83, "metadata": { "code_folding": [], "slideshow": { @@ -6285,10 +6249,10 @@ } }, "source": [ - "This version has two *severe* aspects where we should improve on:\n", + "This version exhibits two *severe* issues where we should improve on:\n", "\n", - "1. If a user enters something other than \"heads\" or \"tails\", for example, \"Heads\" or \"Tails\", the program keeps running without the user knowing about the mistake!\n", - "2. It intermingles the coin tossing with the comparison against the user's input: Mixing unrelated business logic in the same code fragment makes a program harder to read and maintain in the long run." + "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." ] }, { @@ -6310,16 +6274,16 @@ } }, "source": [ - "Let's refactor the code and make it *modular* and *comprehendable*.\n", + "Let's refactor the code and make it *modular*.\n", "\n", - "First, we divide the logic into two functions `get_guess()` and `toss_coin()` that are controlled from within a `while`-loop.\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()](https://docs.python.org/3/library/stdtypes.html?highlight=__contains__#str.strip) and [lower()](https://docs.python.org/3/library/stdtypes.html?highlight=__contains__#str.lower) methods remove preceeding and trailing whitespace and lower case the input ensuring that the user can 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`." + "`get_guess()` not only reads in the user's input but also implements a simple input validation pattern in that the [strip()](https://docs.python.org/3/library/stdtypes.html?highlight=__contains__#str.strip) and [lower()](https://docs.python.org/3/library/stdtypes.html?highlight=__contains__#str.lower) methods remove preceeding 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": 85, + "execution_count": 84, "metadata": { "slideshow": { "slide_type": "slide" @@ -6351,12 +6315,12 @@ } }, "source": [ - "`toss_coin()` is a simple function that models a fair coin toss when called with default arguments." + "`toss_coin()` models a fair coin toss when called with default arguments." ] }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 85, "metadata": { "slideshow": { "slide_type": "slide" @@ -6387,14 +6351,14 @@ } }, "source": [ - "Second, we rewrite the `if`-`else`-logic to explictly handle the case where `get_guess()` returns `None`: Whenever the user enters something invalid, a warning is shown, and another try is granted. Observe how we use the `is` operator and not the `==` operator as `None` is a singleton object.\n", + "Second, we rewrite the `if`-`else`-logic to explictly handle the case where `get_guess()` returns `None`: 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` statement itself takes on the role of **glue code** that only manages how other parts of the program interact with each other." + "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": 87, + "execution_count": 86, "metadata": { "slideshow": { "slide_type": "skip" @@ -6407,7 +6371,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 87, "metadata": { "slideshow": { "slide_type": "slide" @@ -6447,7 +6411,7 @@ } }, "source": [ - "Now, our little program's business logic is a lot easier to comprehend. More importantly, we can now easily make changes to the program. 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()](https://docs.python.org/3/library/random.html#random.random) function with another one). In general, a modular architecture leads to improved software maintenance." + "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()](https://docs.python.org/3/library/random.html#random.random) function with another one). In general, a modular architecture leads to improved software maintenance." ] }, { @@ -6473,11 +6437,11 @@ "\n", "There are two redundant approaches of achieving that.\n", "\n", - "First, we can 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 **backwards** reasoning might not be intuitive but it turns out to be a very useful tool to have in one's toolbox.\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 **backwards** reasoning might not be intuitive but it turns out to be a very useful 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 **forwards** reasoning. The `for` statement is **syntactic sugar** that allows to rewrite common occurences of the `while` statement in a concise way. 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\" pointers to other objects in memory. On the contrary, the `range` type does not point to any other objects but rather creates new `int` objects \"on the fly\"." + "**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\" pointers to other objects in memory. On the contrary, the `range` type does not point to any other objects but rather creates *new* `int` objects \"on the fly\" (i.e., when being looped over)." ] } ], diff --git a/04_iteration_review_and_exercises.ipynb b/04_iteration_review_and_exercises.ipynb index 0e937b7..8dd9025 100644 --- a/04_iteration_review_and_exercises.ipynb +++ b/04_iteration_review_and_exercises.ipynb @@ -420,7 +420,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In words, this means that in order to move a tower consisting of $n$ disks from an `origin` $o$ to a `destination` $d$, we three steps must be executed:\n", + "In words, this means that in order to move a tower consisting of $n$ disks from an `origin` $o$ to a `destination` $d$, three steps must be executed:\n", "\n", "1. Move the top most $n - 1$ disks of the tower temporarily from $o$ to $i$ (= sub-problem 1)\n", "2. Move the remaining and largest disk from $o$ to $d$\n", @@ -577,7 +577,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q15.9**: Copy the base case from `sol()`." + "**Q15.9**: Copy the base case from `sol()`!" ] }, { From 4d6241cc0cdba4552c3f6938828cebeced8be365 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 7 Oct 2019 22:31:45 +0200 Subject: [PATCH 029/242] Add initial version of notebook 05 --- 05_numbers.ipynb | 6291 ++++++++++++++++++++++++++++++++++++ static/complex_numbers.png | Bin 0 -> 22878 bytes static/floating_point.png | Bin 0 -> 15371 bytes 3 files changed, 6291 insertions(+) create mode 100644 05_numbers.ipynb create mode 100644 static/complex_numbers.png create mode 100644 static/floating_point.png diff --git a/05_numbers.ipynb b/05_numbers.ipynb new file mode 100644 index 0000000..2c06dc3 --- /dev/null +++ b/05_numbers.ipynb @@ -0,0 +1,6291 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 5: Numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "After learning about the basic building blocks of expressing and structuring the business logic in programs, we focus our attention on the **data types** Python offers us, both built-in and available via the [standard library](https://docs.python.org/3/library/index.html) or third-party packages.\n", + "\n", + "We start with the \"simple\" ones: Numeric types in this chapter and textual data in Chapter 6. An important fact that holds true for all objects of these types is that they are **immutable**. To re-use the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Objects-vs.-Types-vs.-Values), this means that the $0$s and $1$s making up an object's *value* cannot be changed once the bag is created in memory implying that any operation with or method on the object creates a *new* object in a *different* memory location.\n", + "\n", + "Chapters 7, 8, and 9 then cover the more \"complex\" data types including, for example, the `list` type. Finally, Chapter 10 completes the picture by introducing language constructs to create custom types.\n", + "\n", + "We have already seen many hints indicating that numbers are not as trivial to work with as it seems at first sight:\n", + "\n", + "- [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#%28Data%29-Type-%2F-%22Behavior%22) revealed that numbers may come in *different* data types (i.e., `int` vs. `float` so far),\n", + "- [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb#Boolean-Expressions) raised questions regarding the **limited precision** of `float` numbers (e.g., `42 == 42.000000000000001` evaluates to `True`), and\n", + "- [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Infinite-Recursion) showed that sometimes a `float` \"walks\" and \"quacks\" like an `int` whereas the reverse is true in other cases.\n", + "\n", + "This chapter introduces all the [built-in numeric types](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex): `int`, `float`, and `complex`. To mitigate the limited precision of floating-point numbers, we also look at two replacements for the `float` type in the [standard library](https://docs.python.org/3/library/index.html), namely the `Decimal` type in the [decimals](https://docs.python.org/3/library/decimal.html#decimal.Decimal) and the `Fraction` type in the [fractions](https://docs.python.org/3/library/fractions.html#fractions.Fraction) module." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The `int` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The simplest numeric type is the `int` type. Python's integers behave like [integers in ordinary math](https://en.wikipedia.org/wiki/Integer) (i.e., the set $\\mathbb{Z}$) and support operators in the way we saw in the section on arithmetic operators in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#%28Arithmetic%29-Operators)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "a = 789" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Just like any other object, `789` has an identity, a type, and a value." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140526570988784" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(a)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "int" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(a)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "789" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A nice feature is using underscores `_` as (thousands) seperators in numeric literals. For example, `1_000_000` evaluates to just `1000000` in memory." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1000000" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1_000_000" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Whereas mathematicians argue what the term $0^0$ means (cf., this [article](https://en.wikipedia.org/wiki/Zero_to_the_power_of_zero)), programmers are pragmatic about this and just define $0^0 = 1$." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0 ** 0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Binary Representations" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As computers can only store $0$s and $1$s, integers are nothing but that in memory as well. Consequently, computer scientists and engineers developed conventions as to how $0$s and $1$s are \"translated\" into integers and one such convention is the **[binary representation](https://en.wikipedia.org/wiki/Binary_number)** of **non-negative integers**. Consider the integers from $0$ through $255$ that are encoded into $0$s and $1$s with the help of this table:" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "|Bit $i$| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |\n", + "|-------|-----|-----|-----|-----|-----|-----|-----|-----|\n", + "| Digit |$2^7$|$2^6$|$2^5$|$2^4$|$2^3$|$2^2$|$2^1$|$2^0$|\n", + "| $=$ |$128$| $64$| $32$| $16$| $8$ | $4$ | $2$ | $1$ |" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A number consists of exactly eight $0$s and $1$s that are read from right to left and referred to as the **bits** of the number. Each bit represents a distinct number, the **digit**, as indicated in the table. For sure, we start counting at $0$ again.\n", + "\n", + "To encode the integer $3$, for example, we need to find a combination of $0$s and $1$s such that the sum of digits marked with a $1$ is equal to the number we want to encode. In the example, we set all bits to $0$ except for the first ($i=0$) and second ($i=1$) because $1 + 2 = 3$. So the binary representation of $3$ is $00~00~00~11$. To be precise, the $3$ is a linear combination of the digits where the coefficients are either $0$ or $1$: $3 = 0*128 + 0*64 + 0*32 + 0*16 + 0*8 + 0*4 + 1*2 + 1*1$. It is guaranteed that there is exactly one such combination for each number between $0$ and $255$.\n", + "\n", + "As each digit in the binary representation is one of two values, we say that this representation has a base of $2$. Often times, the base is indicated with a subscript to avoid confusion. For example, we write $3_{10} = 00000011_2$ or $3_{10} = 11_2$ for short omitting leading $0$s. A subscript of $10$ implies a decimal number as we know it from grade school.\n", + "\n", + "In Python, we use the built-in [bin()](https://docs.python.org/3/library/functions.html#bin) function to obtain an integer's binary representation. It returns a `str` object starting with `\"0b\"` indicating the binary format and as many $0$s and $1$s as are necessary to encode the integer omitting leading $0$s." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b11'" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We may pass such a `str` object as the argument to the [int()](https://docs.python.org/3/library/functions.html#int) built-in, together with `base=2`, to (re-)create an `int` object, for example, with the value of `3`." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(\"0b11\", base=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Moreover, we may also use the contents of the returned `str` object as a **literal** instead: Just like we type, for example, `3` without quotes (i.e., \"literally\") into a code cell to create the `int` object `3`, we may type `0b11` and obtain an `int` object with the same value." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0b11" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Another example is the integer `177` that is the sum of $128 + 32 + 16 + 1$: Its binary representation is the sequence of bits $10~11~00~01$. To use our new notation, $177_{10} = 10110001_2$." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b10110001'" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(177)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Analogous to typing `177` into a code cell, we may type `0b10110001`, or `0b_1011_0001` to make it easier to read, to create the `int` object `177` in memory." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "177" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0b_1011_0001" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`0` and `255` are the edge cases where we set all the bits to either $0$ or $1$." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b0'" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1'" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b10'" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(2)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b11111111'" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(255)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Groups of eight bits are also called a **byte**. As a byte can only represent non-negative integers up to $255$, the table above is extended conceptually with greater digits to the left to express integers beyond $255$. The memory management needed to implement this is built into Python and we do not need to worry about anything.\n", + "\n", + "For example, the `789` from above is encoded with $10$ bits and $789_{10} = 1100010101_2$." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1100010101'" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(a) # = bin(789)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To contrast this bits encoding with the familiar decimal system, we show an equivalent table with powers of $10$ as the digits:\n", + "\n", + "|Decimal| 3 | 2 | 1 | 0 |\n", + "|-------|------|------|------|------|\n", + "| Digit |$10^3$|$10^2$|$10^1$|$10^0$|\n", + "| $=$ |$1000$| $100$| $10$ | $1$ |\n", + "\n", + "Now, an integer is a linear combination of the digits where the coefficents are one of *ten* values. For example, the number $123$ can be expressed as $0*1000 + 1*100 + 2*10 + 3*1$. So, the binary representation follows the same logic as the decimal system taught in grade school. The decimal system is intuitive to us humans mostly as we learn to count with our *ten* fingers." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Hexadecimal Representations" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While in the binary and decimal systems there are two and ten distinct coefficients per digit, another convenient representation, the **hexadecimal representation**, uses a base of $16$. It is convenient as one digit stores the same amount of information as *four* bits and the binary representation quickly becomes unreadable for larger numbers. The letters \"a\" through \"f\" are used as digits \"10\" through \"15\".\n", + "\n", + "The following table summarizes the relationship between the three systems:" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "|Decimal|Hexadecimal|Binary|$~~~~~~$|Decimal|Hexadecimal|Binary|$~~~~~~$|Decimal|Hexadecimal|Binary|$~~~~~~$|...|\n", + "|-------|-----------|------|--------|-------|-----------|------|--------|-------|-----------|------|--------|---|\n", + "| 0 | 0 | 0000 |$~~~~~~$| 16 | 10 | 10000|$~~~~~~$| 32 | 20 |100000|$~~~~~~$|...|\n", + "| 1 | 1 | 0001 |$~~~~~~$| 17 | 11 | 10001|$~~~~~~$| 33 | 21 |100001|$~~~~~~$|...|\n", + "| 2 | 2 | 0010 |$~~~~~~$| 18 | 12 | 10010|$~~~~~~$| 34 | 22 |100010|$~~~~~~$|...|\n", + "| 3 | 3 | 0011 |$~~~~~~$| 19 | 13 | 10011|$~~~~~~$| 35 | 23 |100011|$~~~~~~$|...|\n", + "| 4 | 4 | 0100 |$~~~~~~$| 20 | 14 | 10100|$~~~~~~$| 36 | 24 |100100|$~~~~~~$|...|\n", + "| 5 | 5 | 0101 |$~~~~~~$| 21 | 15 | 10101|$~~~~~~$| 37 | 25 |100101|$~~~~~~$|...|\n", + "| 6 | 6 | 0110 |$~~~~~~$| 22 | 16 | 10110|$~~~~~~$| 38 | 26 |100110|$~~~~~~$|...|\n", + "| 7 | 7 | 0111 |$~~~~~~$| 23 | 17 | 10111|$~~~~~~$| 39 | 27 |100111|$~~~~~~$|...|\n", + "| 8 | 8 | 1000 |$~~~~~~$| 24 | 18 | 11000|$~~~~~~$| 40 | 28 |101000|$~~~~~~$|...|\n", + "| 9 | 9 | 1001 |$~~~~~~$| 25 | 19 | 11001|$~~~~~~$| 41 | 29 |101001|$~~~~~~$|...|\n", + "| 10 | a | 1010 |$~~~~~~$| 26 | 1a | 11010|$~~~~~~$| 42 | 2a |101010|$~~~~~~$|...|\n", + "| 11 | b | 1011 |$~~~~~~$| 27 | 1b | 11011|$~~~~~~$| 43 | 2b |101011|$~~~~~~$|...|\n", + "| 12 | c | 1100 |$~~~~~~$| 28 | 1c | 11100|$~~~~~~$| 44 | 2c |101100|$~~~~~~$|...|\n", + "| 13 | d | 1101 |$~~~~~~$| 29 | 1d | 11101|$~~~~~~$| 45 | 2d |101101|$~~~~~~$|...|\n", + "| 14 | e | 1110 |$~~~~~~$| 30 | 1e | 11110|$~~~~~~$| 46 | 2e |101110|$~~~~~~$|...|\n", + "| 15 | f | 1111 |$~~~~~~$| 31 | 1f | 11111|$~~~~~~$| 47 | 2f |101111|$~~~~~~$|...|" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To show more examples of the above subscript convention, we pick three random entries from the table:\n", + "\n", + "$11_{10} = \\text{b}_{16} = 1011_2$\n", + "\n", + "$25_{10} = 19_{16} = 11001_2$\n", + "\n", + "$46_{10} = 2\\text{e}_{16} = 101110_2$\n", + "\n", + "The built-in [hex()](https://docs.python.org/3/library/functions.html#hex) function creates a `str` object starting with `\"0x\"` representing an integer's hexadecimal representation. The length depends on how many groups of four bits are implied by the corresponding binary representation.\n", + "\n", + "For `0` and `1` the hexadecimal representation is similar to the binary one." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0x0'" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0x1'" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Whereas `bin(3)` already requires two digits, one is enough for `hex(3)`." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0x3'" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(3) # bin(3) => \"0b11\"; two digits needed" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For `10` and `15` we see the letter digits for the first time." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0xa'" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0xf'" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(15)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For `177` the binary representation, `\"0b10110001\"`, can be read as *two* groups of four bits $1011$ and $0001$ that are encoded as $\\text{b}$ and $1$ in accordance with the first three columns of the table above." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b10110001'" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(177)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0xb1'" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(177)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To obtain the original integer back, we call the [int()](https://docs.python.org/3/library/functions.html#int) built-in with a `base=16` argument." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "177" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(\"0xb1\", base=16)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Alternatively, we could use a literal notation instead." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "177" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0xb1" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Hexadecimal values between $00_{16}$ and $\\text{ff}_{16}$ (i.e., $0_{10}$ and $255_{10}$) are commonly used to describe colors, for example, in web development but also in graphics editors. See this [online tool](https://www.w3schools.com/colors/colors_hexadecimal.asp) for some more background." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0xff'" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(255)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Just like binary representations, the hexadecimals extend to the left for larger values like `789`." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0x315'" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(a) # = hex(789)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For completeness sake, we mention that there is also the built-in [oct()](https://docs.python.org/3/library/functions.html#oct) function to obtain an integer's **octal representation**. The logic is the same as for the hexadecimal representation where we just use *eight* instead of *sixteen* digits. That is the equivalent of viewing the binary representations in groups of three bits." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### Negative Values" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While there are conventions that model negative integers with the $0$s and $1$s in memory (cf., [Two's Complement](https://en.wikipedia.org/wiki/Two%27s_complement)), Python manages that for us and we do not look into this here for brevity.\n", + "\n", + "The binary and hexadecimal representations of negative integers are identical to their positive counterparts except that they start with a minus sign `-`." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'-0b11'" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(-3)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'-0x3'" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(-3)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'-0b11111111'" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(-255)" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'-0xff'" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(-255)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### The `bool` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Whereas the boolean objects `True` and `False` are commonly *not* regarded as numeric types, they behave like `1` and `0` in an arithmetic context." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "b = True" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "94554490840032" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(b)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "bool" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(b)" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "c = False" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "94554490840000" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(c)" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "bool" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(c)" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "By including booleans as operands in arithmetic expressions, Python implicitly casts them as `int` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b + c # = True + False" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "790" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a + b # = 789 + True" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a * c # = 789 * False" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We may explicitly cast `bool` objects as integers ourselves with the [int()](https://docs.python.org/3/library/functions.html#int) built-in." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(True)" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(False)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Of course, their binary representations only need *one* bit of information." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1'" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(True)" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b0'" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(False)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Their hexadecimal representations occupy *four* bits in memory while only *one* bit is actually needed. This is because \"1\" and \"0\" are just two of the sixteen possible digits." + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0x1'" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(True)" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0x0'" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(False)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As a reminder, `None` is a type on its own, namely the `NoneType`, and different from `False`. It *cannot* be casted as an integer as the `TypeError` indicates." + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "int() argument must be a string, a bytes-like object or a number, not 'NoneType'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: int() argument must be a string, a bytes-like object or a number, not 'NoneType'" + ] + } + ], + "source": [ + "int(None)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Bitwise Operators" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Now that we know how integers are represented with $0$s and $1$s in memory, we look at ways of working with these individual bits, in particular with the so-called **[bitwise operators](https://wiki.python.org/moin/BitwiseOperators)**: As the name suggests, the operators perform some operation on a bit by bit basis. They only work with `int` objects.\n", + "\n", + "We keep this overview rather short as such \"low-level\" operations are not needed by the data science practicioner on a regular basis. Yet, it is worthwhile to have heard about them as they form the basis of all of arithmetic in computers.\n", + "\n", + "The first operator is the **bitwise AND** `&` operator: This operator looks at the bits of its two operands, `11` and `13` in the example, in a pairwise fashion and *only if* both operands have a $1$ in the same position will the resulting integer have a $1$ in this position. The binary representations of `11` and `13` have $1$s in their respective first and fourth bits, which is why `bin(11 & 13)` evaluates to `\"Ob1001\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1011 & 0b1101'" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(11) + \" & \" + bin(13) # to show the operands' binary representations" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1001'" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(11 & 13)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Of course, `0b1001` is the binary representation of `9`." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "9" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0b1001" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "9" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "11 & 13" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The **bitwise OR** `|` operator evaluates to an `int` object whose bits are set to $1$ if the corresponding bits of either *one* or *both* operands are $1$. So in the example `9 | 13` only the second bit is $0$ for both operands, which is why the expression evaluates to `\"0b1101\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1001 | 0b1101'" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(9) + \" | \" + bin(13) # to show the operands' binary representations" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1101'" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(9 | 13)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`0b1101` evaluates to the `int` object `13`." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "13" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0b1101" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "13" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "9 | 13" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The **bitwise XOR** `^` operator is a special case of the `|` operator in that it evaluates to an `int` object whose bits are set to $1$ if the corresponding bit of exactly *one* of the two operands is $1$. Colloquially, the \"X\" stands for \"exclusive\". The `^` operator must *not* be confused with the `**` operator that does exponentiation! In the example `9 ^ 13` only the third bit differs between the two operands, which is why it evaluates to `\"0b100\"` omitting the fourth bit." + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1001 ^ 0b1101'" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(9) + \" ^ \" + bin(13) # to show the operands' binary representations" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b100'" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(9 ^ 13)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`0b100` evaluates to the `int` object `4`." + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0b100" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "9 ^ 13" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The **bitwise NOT** `~` operator, sometimes also called **inversion** operator, is said to simply \"flips\" the $0$s into $1$s and the $1$s into $0$s. However, it is based on the aforementioned [Two's Complement](https://en.wikipedia.org/wiki/Two%27s_complement) convention and `~x` is defined to be `~x = -(x + 1)` (cf., the [reference](https://docs.python.org/3/reference/expressions.html#unary-arithmetic-and-bitwise-operations)). The full logic behind this is considered out of scope in this book.\n", + "\n", + "We can at least verify the definition by comparing the binary representations of `8` and `-9`: They are indeed the same." + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'-0b1001'" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(~8)" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'-0b1001'" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(-(8 + 1))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`~x = -(x + 1)` can be reformulated as `~x + x = -1`, which is slightly easier to check." + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "~8 + 8" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Lastly, the **bitwise left and right shift** operators, `<<` and `>>`, simply shift all the bits either to the left or to the right. This corresponds to multiplying or dividing the integer by powers of $2$.\n", + "\n", + "When shifting left, $0$s are filled in." + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1001'" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(9)" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b100100'" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(9 << 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "36" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "9 << 2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When shifting right, some bits are always lost." + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b10'" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(9 >> 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "9 >> 2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The `float` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As we have seen above, some assumptions need to be made as to how the $0$s and $1$s in a computer's memory should be translated into numbers. This process becomes a lot more involved when we go beyond integers and model [real numbers](https://en.wikipedia.org/wiki/Real_number) (i.e., the set $\\mathbb{R}$) with possibly infinitely many digits to the right of the period like $1.23$.\n", + "\n", + "The **[Institute of Electrical and Electronics Engineers](https://en.wikipedia.org/wiki/Institute_of_Electrical_and_Electronics_Engineers)** (IEEE, pronounced \"eye-tripple-E\") is one of the most important professional associations when it comes to standardizing all kinds of aspects regarding the implementation of soft- and hardware.\n", + "\n", + "The **[IEEE 754](https://en.wikipedia.org/wiki/IEEE_754)** standard defines the so-called **floating-point arithmetic** that is commonly used today by all major programming languages. The standard not only defines how the $0$s and $1$s are organized in memory but also, for example, how values are to be rounded, what happens in exceptional cases like divisions by zero, or what is a zero value to begin with.\n", + "\n", + "In Python, the simplest way to create a `float` object is to just use a literal notation with a dot `.` in it." + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "d = 1.23" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140526571166576" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(d)" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "float" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(d)" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1.23" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "d" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As with integer literals above, we may use underscores `_` to make longer `float` objects easier to read." + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.123456" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0.123_456" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In cases where the dot `.` is unnecessary from a mathematical point of view, we either need to still end the number with it or use the [float()](https://docs.python.org/3/library/functions.html#float) built-in to cast the number as a float explicitly. [float()](https://docs.python.org/3/library/functions.html#float) can also `str` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1.0" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1. # on the contrary, 1 creates an integer object" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1.0" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1.0" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"1.000\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Floats are implicitly created as a result of dividing an integer by another with the division operator `/`." + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.3333333333333333" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 / 3" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In general, if we combine `float` and `int` objects in arithmetic operations, we always end up with a `float` type." + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3.0" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1.0 + 2" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42.0" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "21 * 2.0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Scientific Notation" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`float` objects may also be created with the **scientifc literal notation**: We use the symbol `e` to indicate powers of $10$, so $1.23 * 10^0$ translates into `1.23e0`." + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1.23" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1.23e0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Syntactically, `e` needs a `float` object in its literal notation to its left and an `int` object to its right, both without a space. Otherwise, we get a `SyntaxError`." + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23 e0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + ] + } + ], + "source": [ + "1.23 e0" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23e 0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + ] + } + ], + "source": [ + "1.23e 0" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23e0.0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + ] + } + ], + "source": [ + "1.23e0.0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we leave out the number to the left, Python raises a `NameError` as it actually tries to look up a variable named `e1`." + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'e1' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0me1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mNameError\u001b[0m: name 'e1' is not defined" + ] + } + ], + "source": [ + "e1" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "So, in order to write $10^1$ in Python, we need to think of it as $1*10^1$ and write `1e1`." + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10.0" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1e1" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Special Values" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "There are also three special values representing **\"not a number\"**, called `nan`, and positive or negative **infinity**, called `inf`, that are created by passing in the corresponding abbreviation as a `str` object to the [float()](https://docs.python.org/3/library/functions.html#float) built-in. These values could be used, for example, as the result of a mathematically undefined operation like division by zero or to model the value of a mathematical function as it goes to infinity." + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "nan" + ] + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"nan\") # also works as float(\"NaN\")" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "inf" + ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"+inf\") # also works as float(\"+infinity\")" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "inf" + ] + }, + "execution_count": 89, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"inf\") # by omitting the plus sign, we mean positive infinity" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-inf" + ] + }, + "execution_count": 90, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"-inf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`nan` objects *never* compare equal to *anything*, not even to themselves. This happens in accordance with the IEEE 754 standard." + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 91, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"nan\") == float(\"nan\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "On the contrary, as two values go to infinity, there is no such concept as difference any more and *everything* compares equal." + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 92, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"inf\") == float(\"inf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Adding `42` to `inf` makes no difference." + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "inf" + ] + }, + "execution_count": 93, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"inf\") + 42" + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 94, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"inf\") + 42 == float(\"inf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We observe the same for multiplication ..." + ] + }, + { + "cell_type": "code", + "execution_count": 95, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "inf" + ] + }, + "execution_count": 95, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 * float(\"inf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 96, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 * float(\"inf\") == float(\"inf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... and even exponentiation!" + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "inf" + ] + }, + "execution_count": 97, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"inf\") ** 42" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 98, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"inf\") ** 42 == float(\"inf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Although differences become unmeaningful as we approach infinity, signs are still respected." + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "inf" + ] + }, + "execution_count": 99, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "-42 * float(\"-inf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 100, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 100, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "-42 * float(\"-inf\") == float(\"inf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As a caveat, adding infinities of different signs is an *undefined operation* in math and results in a `nan` object." + ] + }, + { + "cell_type": "code", + "execution_count": 101, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "nan" + ] + }, + "execution_count": 101, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"inf\") + float(\"-inf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "nan" + ] + }, + "execution_count": 102, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"inf\") - float(\"inf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Imprecision" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`float` objects are *inherently* imprecise and there is *nothing* we can do about it! In particular, arithmetic operations with two `float` objects may result in \"weird\" rounding \"errors\" if their exponents are \"too far apart\". These \"errors\" are actually stictly deterministic and occur as a consequence of the IEEE 754 standard.\n", + "\n", + "For example, let's add `1e0` to `1e15` and `1e16`, respectively. We see that in the second case, the `1e0` somehow gets \"lost\"." + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1000000000000001.0" + ] + }, + "execution_count": 103, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1e15 + 1e0" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1e+16" + ] + }, + "execution_count": 104, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1e16 + 1e0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Of course, we may also add the integer `1` to the `float` objects in the literal `e` notation with the same outcome." + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1000000000000001.0" + ] + }, + "execution_count": 105, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1e15 + 1" + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1e+16" + ] + }, + "execution_count": 106, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1e16 + 1" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Interactions between very large and very small `float` objects are not the only source of imprecision." + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "from math import sqrt" + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2.0000000000000004" + ] + }, + "execution_count": 108, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sqrt(2) ** 2" + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.30000000000000004" + ] + }, + "execution_count": 109, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0.1 + 0.2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This may become a problem if we rely on equality checks in our programs." + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 110, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sqrt(2) ** 2 == 2" + ] + }, + { + "cell_type": "code", + "execution_count": 111, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 111, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0.1 + 0.2 == 0.3" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A popular workaround is to check if the difference between the two numbers is smaller than a pre-defined `threshold` *sufficiently* close to `0`, for example, `1e-15`." + ] + }, + { + "cell_type": "code", + "execution_count": 112, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "threshold = 1e-15" + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 113, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(sqrt(2) ** 2) - 2 < threshold" + ] + }, + { + "cell_type": "code", + "execution_count": 114, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 114, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(0.1 + 0.2) - 0.3 < threshold" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The built-in [format()](https://docs.python.org/3/library/functions.html#format) function allows us to show the **significant digits** of a `float` number, as they exist in memory, to an arbitrary precision. To exemplify it, let's view a couple of `float` objects with `50` digits. This analysis reveals that almost no `float` number is precise. After $14$ or $15$ digits \"weird\" things happen. As we will see below, the \"random\" digits do *not* actually exist in memory, which is why Python shows them to us in this \"weird\" way.\n", + "\n", + "Note that the [format()](https://docs.python.org/3/library/functions.html#format) function is different from the [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method on `str` objects introduced in the next chapter and that both work with the so-called [format specification mini-language](https://docs.python.org/3/library/string.html#format-specification-mini-language): `\".50f\"` is the instruction to show `50` digits of a `float` number. But let's not worry too much about these details for now." + ] + }, + { + "cell_type": "code", + "execution_count": 115, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.10000000000000000555111512312578270211815834045410'" + ] + }, + "execution_count": 115, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "format(0.1, \".50f\")" + ] + }, + { + "cell_type": "code", + "execution_count": 116, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.20000000000000001110223024625156540423631668090820'" + ] + }, + "execution_count": 116, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "format(0.2, \".50f\")" + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.29999999999999998889776975374843459576368331909180'" + ] + }, + "execution_count": 117, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "format(0.3, \".50f\")" + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.33333333333333331482961625624739099293947219848633'" + ] + }, + "execution_count": 118, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "format(1 / 3, \".50f\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [format()](https://docs.python.org/3/library/functions.html#format) function does *not* round a number in the mathematical sense! It just allows us to show an arbitrary number of the digits as stored in memory and it also does *not* change these.\n", + "\n", + "On the contrary, the built-in [round()](https://docs.python.org/3/library/functions.html#round) function creates a *new* numeric object that is a rounded version according to the common rules from math.\n", + "\n", + "For example, let's \"round\" `1 / 3` to five digits. We see that the obtained value for `roughly_a_third` is also *imprecise* but different from the \"exact\" represenation of `1 / 3` above." + ] + }, + { + "cell_type": "code", + "execution_count": 119, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "roughly_a_third = round(1 / 3, 5)" + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.33333" + ] + }, + "execution_count": 120, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "roughly_a_third" + ] + }, + { + "cell_type": "code", + "execution_count": 121, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.33333000000000001517008740847813896834850311279297'" + ] + }, + "execution_count": 121, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "format(roughly_a_third, \".50f\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Surprisingly, `0.125` and `0.25` appear to be *precise* and equality comparison works without the `threshold` workaround." + ] + }, + { + "cell_type": "code", + "execution_count": 122, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.12500000000000000000000000000000000000000000000000'" + ] + }, + "execution_count": 122, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "format(0.125, \".50f\")" + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.25000000000000000000000000000000000000000000000000'" + ] + }, + "execution_count": 123, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "format(0.25, \".50f\")" + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 124, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0.125 + 0.125 == 0.25" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Binary Representations" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In order to understand these subtleties, we need to look at the **[binary representation of floats](https://en.wikipedia.org/wiki/Double-precision_floating-point_format)** and review the basics of the **[IEEE 754](https://en.wikipedia.org/wiki/IEEE_754)** standard. On modern machines, floats are modelled in so-called double precision with $64$ bits that are grouped as in the picture below. The first bit determines the sign ($0$ for plus, $1$ for minus), the next $11$ bits represent an $exponent$ term, and the last $52$ bits resemble the actual significant digits, the so-called $fraction$ part. The three groups are put together like so:\n", + "\n", + "$$float = (-1)^{sign} * 1.fraction * 2^{exponent-1023}$$\n", + "\n", + "Observe that a $1.$ is implicitly prepended as the first digit and that both $fraction$ and $exponent$ are stored in base $2$ representation (i.e., they both are interpreted just as the integers above). As $exponent$ is consequently non-negative, between $0_{10}$ and $2047_{10}$ to be precise, the $-1023$ centers the entire $2^{exponent-1023}$ term around $1$ and allows for the period within the $1.fraction$ part to be shifted into either direction by the same amount. Floating-point numbers received their name as the period, formally called the **[radix point](https://en.wikipedia.org/wiki/Radix_point)**,\"floats\" along the significant digits. As an aside, an $exponent$ of all $0$s or all $1$s is used to model the special values `nan` or `inf`.\n", + "\n", + "As the standard defines the exponent part to come as a power of $2$, we now see why `0.125` is a *precise* float. It simply can be represented as a power of $2$, i.e., $0.125 = (-1)^0 * 1.0 * 2^{1020-1023} = 2^{-3} = \\frac{1}{8}$. In other words, the floating-point representation of $0.125_{10}$ is $0_2$, $1111111100_2 = 1020_{10}$, and $0_2$ for the three groups, respectively." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The important fact for data science practitioners to understand is that mapping the *infinite* set of the real numbers $\\mathbb{R}$ to a *finite* set of bits leads to the imprecisions shown above!\n", + "\n", + "So, floats are usually good approximations of real numbers only with their first $14$ or $15$ significant digits. If more precision is required, we need to revert to other data types such as a `Decimal` or a `Fraction` as shown in the next two sections.\n", + "\n", + "This [blog post](http://fabiensanglard.net/floating_point_visually_explained/) gives another neat and *visual* way as to how to think of floats. It also explains why floats become worse approximations of the reals as their absolute values increase.\n", + "\n", + "The Python [documentation](https://docs.python.org/3/tutorial/floatingpoint.html) provides another good discussion of floats and the goodness of their approximations.\n", + "\n", + "If we are interested in the exact bits behind a `float` object, we use the [hex()](https://docs.python.org/3/library/stdtypes.html#float.hex) method that returns a `str` object beginning with `\"0x1.\"` followed by the $fraction$ in hexadecimal notation and the $exponent$ as an integer after subtraction of the $1023$ seperated by a `\"p\"`.\n", + "\n", + "Also, the [as_integer_ratio()](https://docs.python.org/3/library/stdtypes.html#float.as_integer_ratio) method returns the two smallest integers whose ratio best approximates a `float` object." + ] + }, + { + "cell_type": "code", + "execution_count": 125, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "one_eighth = 1 / 8" + ] + }, + { + "cell_type": "code", + "execution_count": 126, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0x1.0000000000000p-3'" + ] + }, + "execution_count": 126, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "one_eighth.hex() # the result basically says 2 ** (-3)" + ] + }, + { + "cell_type": "code", + "execution_count": 127, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 8)" + ] + }, + "execution_count": 127, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "one_eighth.as_integer_ratio()" + ] + }, + { + "cell_type": "code", + "execution_count": 128, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0x1.555475a31a4bep-2'" + ] + }, + "execution_count": 128, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "roughly_a_third.hex()" + ] + }, + { + "cell_type": "code", + "execution_count": 129, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(3002369727582815, 9007199254740992)" + ] + }, + "execution_count": 129, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "roughly_a_third.as_integer_ratio()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`0.0` is also a *precise* `float` number." + ] + }, + { + "cell_type": "code", + "execution_count": 130, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "zero = 0.0" + ] + }, + { + "cell_type": "code", + "execution_count": 131, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0x0.0p+0'" + ] + }, + "execution_count": 131, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "zero.hex()" + ] + }, + { + "cell_type": "code", + "execution_count": 132, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(0, 1)" + ] + }, + "execution_count": 132, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "zero.as_integer_ratio()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As seen in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#%28Data%29-Type-%2F-%22Behavior%22), the [is_integer()](https://docs.python.org/3/library/stdtypes.html#float.is_integer) method tells us if a `float` can be converted into an `int` object without any loss in precision." + ] + }, + { + "cell_type": "code", + "execution_count": 133, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 133, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "roughly_a_third.is_integer()" + ] + }, + { + "cell_type": "code", + "execution_count": 134, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 134, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "one = roughly_a_third / roughly_a_third\n", + "\n", + "one.is_integer()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As the exact implementation of floats may vary and be dependend on a particular Python installation, we can always use the [sys.float_info](https://docs.python.org/3/library/sys.html#sys.float_info) attribute in the [sys](https://docs.python.org/3/library/sys.html) module in the [standard library](https://docs.python.org/3/library/) to check the details. Usually, this is not necessary." + ] + }, + { + "cell_type": "code", + "execution_count": 135, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import sys" + ] + }, + { + "cell_type": "code", + "execution_count": 136, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)" + ] + }, + "execution_count": 136, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sys.float_info" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The `Decimal` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [decimal](https://docs.python.org/3/library/decimal.html) module in the standard library provides a [Decimal](https://docs.python.org/3/library/decimal.html#decimal.Decimal) type that is used to represent any real number to a user-defined level of precision: \"User-defined\" does *not* mean an infinite or \"exact\" precision! The `Decimal` type merely allows us to work with a number of bits *different* from the $64$ as specified for the `float` type and also to customize the rounding rules and some other settings.\n", + "\n", + "Let's first import the `Decimal` type and also the [getcontext()](https://docs.python.org/3/library/decimal.html#decimal.getcontext) function from the [decimal](https://docs.python.org/3/library/decimal.html) module." + ] + }, + { + "cell_type": "code", + "execution_count": 137, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "from decimal import Decimal, getcontext" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[getcontext()](https://docs.python.org/3/library/decimal.html#decimal.getcontext) shows us how the [decimal](https://docs.python.org/3/library/decimal.html) module is set up. By default, the precision is set to $28$ significant digits, which is roughly twice as many as with `float` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 138, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])" + ] + }, + "execution_count": 138, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "getcontext()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The two simplest ways to create a `Decimal` object is to either **instantiate** it with an `int` object or a `str` object consisting of all the significant digits. In the latter case, a scientific notation is also possible." + ] + }, + { + "cell_type": "code", + "execution_count": 139, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('42')" + ] + }, + "execution_count": 139, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 140, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('0.1')" + ] + }, + "execution_count": 140, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"0.1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 141, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('1E+5')" + ] + }, + "execution_count": 141, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"1e5\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Is is *not* a good idea to create a `Decimal` object from a `float` object. If we did so, we would create a `Decimal` object that internally used extra bits to store the \"random\" digits that are not stored for a `float` but \"seen\" by Python nevertheless." + ] + }, + { + "cell_type": "code", + "execution_count": 142, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('0.1000000000000000055511151231257827021181583404541015625')" + ] + }, + "execution_count": 142, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(0.1) # do not create a Decimal from a float" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With the `Decimal` type, the imprecisions in the arithmetic and equality comparisons from above are gone." + ] + }, + { + "cell_type": "code", + "execution_count": 143, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('0.3')" + ] + }, + "execution_count": 143, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"0.1\") + Decimal(\"0.2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 144, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 144, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"0.1\") + Decimal(\"0.2\") == Decimal(\"0.3\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Arithmetic operations between `Decimal` and `int` objects work as the latter are inherently precise: The results are *new* `Decimal` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 145, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('42')" + ] + }, + "execution_count": 145, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 + Decimal(42) - 42" + ] + }, + { + "cell_type": "code", + "execution_count": 146, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('420')" + ] + }, + "execution_count": 146, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "10 * Decimal(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 147, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('1.05')" + ] + }, + "execution_count": 147, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(42) / 40" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To verify the precision, we apply the built-in [format()](https://docs.python.org/3/library/functions.html#format) function to the previous code cell and compare it with the same division resulting in a `float` object." + ] + }, + { + "cell_type": "code", + "execution_count": 148, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'1.05000000000000000000000000000000000000000000000000'" + ] + }, + "execution_count": 148, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "format(Decimal(42) / 40, \".50f\")" + ] + }, + { + "cell_type": "code", + "execution_count": 149, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'1.05000000000000004440892098500626161694526672363281'" + ] + }, + "execution_count": 149, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "format(42 / 40, \".50f\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, mixing `Decimal` and `float` objects raises a `TypeError`: So, Python prevents us from potentially introducing imprecisions via innocent looking arithmetic." + ] + }, + { + "cell_type": "code", + "execution_count": 150, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "unsupported operand type(s) for *: 'float' and 'decimal.Decimal'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m1.0\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m42\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for *: 'float' and 'decimal.Decimal'" + ] + } + ], + "source": [ + "1.0 * Decimal(42)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In order to preserve the precision even for more advanced mathematical functions, `Decimal` objects come with many methods bound on them. For example, [ln()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.ln) and [log10()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.log10) take the logarithm while [sqrt()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.sqrt) calculates the square root. In general, the functions in the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/) should only be used with `float` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 151, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('2')" + ] + }, + "execution_count": 151, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(100).log10()" + ] + }, + { + "cell_type": "code", + "execution_count": 152, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('1.414213562373095048801688724')" + ] + }, + "execution_count": 152, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(2).sqrt()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [sqrt()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.sqrt) method is still limited with respect to the precision of the returned value as, for example, $\\sqrt{2}$ is an **[irrational number](https://en.wikipedia.org/wiki/Irrational_number)** that *cannot* be expressed with absolute precision using *any* number of bits even in theory.\n", + "\n", + "We see this as raising $\\sqrt{2}$ to the power of $2$ results in an imprecise value as before!" + ] + }, + { + "cell_type": "code", + "execution_count": 153, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('1.999999999999999999999999999')" + ] + }, + "execution_count": 153, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "two = Decimal(2).sqrt() ** 2\n", + "\n", + "two" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [quantize()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize) method allows us to cut off a `Decimal` number at any digit that is smaller than the set precision while adhering to the rounding rules from math.\n", + "\n", + "For example, as the overall imprecise value of `two` still has a precision of $28$, we can correctly round it with respect to the first $20$ digits." + ] + }, + { + "cell_type": "code", + "execution_count": 154, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('2')" + ] + }, + "execution_count": 154, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "two.quantize(20)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Consequently, with this little workaround $\\sqrt{2}^2 = 2$ works even in Python." + ] + }, + { + "cell_type": "code", + "execution_count": 155, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 155, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "two.quantize(20) == 2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The downside is that the entire expression is not as pretty as `sqrt(2) ** 2 == 2` from above." + ] + }, + { + "cell_type": "code", + "execution_count": 156, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 156, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(Decimal(2).sqrt() ** 2).quantize(20) == 2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`nan` and positive and negative `inf` exist as well and the same remarks from above apply." + ] + }, + { + "cell_type": "code", + "execution_count": 157, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('NaN')" + ] + }, + "execution_count": 157, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"nan\")" + ] + }, + { + "cell_type": "code", + "execution_count": 158, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 158, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"nan\") == Decimal(\"nan\") # nan's never compare equal to anything, not even to themselves" + ] + }, + { + "cell_type": "code", + "execution_count": 159, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('Infinity')" + ] + }, + "execution_count": 159, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"inf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 160, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('-Infinity')" + ] + }, + "execution_count": 160, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"-inf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 161, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('Infinity')" + ] + }, + "execution_count": 161, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"inf\") + 42 # Infinity is infinity, concrete numbers loose its meaning" + ] + }, + { + "cell_type": "code", + "execution_count": 162, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 162, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"inf\") + 42 == Decimal(\"inf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As with `float` objects, we cannot add infinities of different signs: Now get a module-specific `InvalidOperation` exception instead of a `nan` value." + ] + }, + { + "cell_type": "code", + "execution_count": 163, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "InvalidOperation", + "evalue": "[]", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mInvalidOperation\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"-inf\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mInvalidOperation\u001b[0m: []" + ] + } + ], + "source": [ + "Decimal(\"inf\") + Decimal(\"-inf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 164, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "InvalidOperation", + "evalue": "[]", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mInvalidOperation\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mInvalidOperation\u001b[0m: []" + ] + } + ], + "source": [ + "Decimal(\"inf\") - Decimal(\"inf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For more information on decimals, see the tutorial at [PYMOTW](https://pymotw.com/3/decimal/index.html) or the official [documentation](https://docs.python.org/3/library/decimal.html)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The `Fraction` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If the numbers in an application can be expressed as [rational numbers](https://en.wikipedia.org/wiki/Rational_number) (i.e., the set $\\mathbb{Q}$), we may model them as a [Fraction](https://docs.python.org/3/library/fractions.html#fractions.Fraction) type from the [fractions](https://docs.python.org/3/library/fractions.html) module in the [standard library](https://docs.python.org/3/library/). As any fraction can always be formulated as the division of one integer by another, `Fraction` objects are inherently precise, just as `int` objects on their own. Further, we maintain the precision as long as we do not use them in a mathematical operation that could result in an irrational number (e.g., taking the square root).\n", + "\n", + "Let's first import the `Fraction` type from the [fractions](https://docs.python.org/3/library/fractions.html) module." + ] + }, + { + "cell_type": "code", + "execution_count": 165, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "from fractions import Fraction" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Among others, there are two simple ways to create a `Fraction` object: We either instantiate one with two `int` objects representing the numerator and denominator or with a `str` object. In the latter case, we have again two options and use either the format \"numerator/denominator\" (i.e., *without* any spaces) or the same format as for `float` and `Decimal` objects above." + ] + }, + { + "cell_type": "code", + "execution_count": 166, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(1, 3)" + ] + }, + "execution_count": 166, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Fraction(1, 3) # this is now 1/3 with \"full\" precision" + ] + }, + { + "cell_type": "code", + "execution_count": 167, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(1, 3)" + ] + }, + "execution_count": 167, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Fraction(\"1/3\") # this is 1/3 with \"full\" precision again" + ] + }, + { + "cell_type": "code", + "execution_count": 168, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(3333333333, 10000000000)" + ] + }, + "execution_count": 168, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Fraction(\"0.3333333333\") # this is 1/3 with a precision of 10 significant digits" + ] + }, + { + "cell_type": "code", + "execution_count": 169, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(3333333333, 10000000000)" + ] + }, + "execution_count": 169, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Fraction(\"3333333333e-10\") # the same in scientific notation" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Only the lowest common denominator version is maintained after creation: For example, $\\frac{3}{2}$ and $\\frac{6}{4}$ are the same and both become `Fraction(3, 2)`." + ] + }, + { + "cell_type": "code", + "execution_count": 170, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(3, 2)" + ] + }, + "execution_count": 170, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Fraction(3, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 171, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(3, 2)" + ] + }, + "execution_count": 171, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Fraction(6, 4)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We could also cast a `Decimal` object as a `Fraction` object: This only makes sense as `Decimal` objects come with a pre-defined precision." + ] + }, + { + "cell_type": "code", + "execution_count": 172, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(1, 10)" + ] + }, + "execution_count": 172, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Fraction(Decimal(\"0.1\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`float` objects may *syntactically* be casted as `Fraction` objects as well. However, then we create a `Fraction` object that precisely remembers the `float` object's imprecision: A *bad* idea!" + ] + }, + { + "cell_type": "code", + "execution_count": 173, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(3602879701896397, 36028797018963968)" + ] + }, + "execution_count": 173, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Fraction(0.1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`Fraction` objects follow the arithmetic rules from middle school and may be mixed with `int` objects *without* any loss of precision." + ] + }, + { + "cell_type": "code", + "execution_count": 174, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(7, 4)" + ] + }, + "execution_count": 174, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Fraction(3, 2) + Fraction(1, 4)" + ] + }, + { + "cell_type": "code", + "execution_count": 175, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(1, 2)" + ] + }, + "execution_count": 175, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Fraction(5, 2) - 2" + ] + }, + { + "cell_type": "code", + "execution_count": 176, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(1, 1)" + ] + }, + "execution_count": 176, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "3 * Fraction(1, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": 177, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(1, 1)" + ] + }, + "execution_count": 177, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Fraction(3, 2) * Fraction(2, 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`Fraction` and `float` objects may also be mixed syntactically. However, then the results may exhibit imprecision again, even if we do not see them at first sight!" + ] + }, + { + "cell_type": "code", + "execution_count": 178, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.1" + ] + }, + "execution_count": 178, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "10.0 * Fraction(1, 100)" + ] + }, + { + "cell_type": "code", + "execution_count": 179, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.10000000000000000555111512312578270211815834045410'" + ] + }, + "execution_count": 179, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "format(10.0 * Fraction(1, 100), \".50f\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For more examples and discussions, see the tutorial at [PYMOTW](https://pymotw.com/3/fractions/index.html) or the official [documentation](https://docs.python.org/3/library/fractions.html)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The `complex` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Some mathematical equations cannot be solved if the solution has to be in the set of the real numbers $\\mathbb{R}$: For example: $x^2 = -1 \\implies x = \\sqrt{-1}$ and the square root is not defined for negative numbers. To mitigate this, mathematicians introduced the concept of an [imaginary number](https://en.wikipedia.org/wiki/Imaginary_number) $\\textbf{i}$ that is *defined* as $\\textbf{i} = \\sqrt{-1}$ or often times as the solution to the equation $\\textbf{i}^2 = -1$. So, the solution to $x = \\sqrt{-1}$ then becomes $x = \\textbf{i}$.\n", + "\n", + "If we generalize the example equation into $(mx-n)^2 = -1 \\implies x = \\frac{1}{m}(\\sqrt{-1} + n)$ where $m$ and $n$ are constants chosen from the reals $\\mathbb{R}$, then the solution to the equation comes in the form $x = a + b\\textbf{i}$, the sum of a real number and an imaginary number, with $a=\\frac{n}{m}$ and $b = \\frac{1}{m}$ also from the reals $\\mathbb{R}$.\n", + "\n", + "Such compound numbers are called a **[complex numbers](https://en.wikipedia.org/wiki/Complex_number)** and the set of all such numbers is commonly denoted by $\\mathbb{C}$. The reals $\\mathbb{R}$ are a strict subset of $\\mathbb{C}$ with $b=0$. $a$ is referred to as the **real part** and $b$ as the **imaginary part** of the complex number.\n", + "\n", + "Complex numbers are often visualized in a plane like below where the real part is depicted on the x-axis and the imaginary part is depicted on the y-axis." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Complex numbers are part of core Python. The simplest way to create one is to write an arithmetic expression with the literal `j` notation for an imaginary number. A `j` notation is commonly used in many engineering disciplines instead of the symbol $\\textbf{i}$ from math as $I$ in engineering more often than not means [electric current](https://en.wikipedia.org/wiki/Electric_current).\n", + "\n", + "For example, the answer to $x^2 = -1$ can be written in Python as `1j` like below. This creates a `complex` object with value `1j`. The same syntactic rules apply as with the above `e` notation: No spaces are allowed between the number and the `j` and the number may be any `float` literal." + ] + }, + { + "cell_type": "code", + "execution_count": 180, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "sqrt_of_neg_one = 1j" + ] + }, + { + "cell_type": "code", + "execution_count": 181, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140526569943600" + ] + }, + "execution_count": 181, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(sqrt_of_neg_one)" + ] + }, + { + "cell_type": "code", + "execution_count": 182, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "complex" + ] + }, + "execution_count": 182, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(sqrt_of_neg_one)" + ] + }, + { + "cell_type": "code", + "execution_count": 183, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1j" + ] + }, + "execution_count": 183, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sqrt_of_neg_one" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To verify that it solves the equation, let's just raise it to the power of $2$: the answer is $-1 + 0\\textbf{i} = -1$." + ] + }, + { + "cell_type": "code", + "execution_count": 184, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 184, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sqrt_of_neg_one ** 2 == -1" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Often, we will write an expression of the form $a + b\\textbf{i}$." + ] + }, + { + "cell_type": "code", + "execution_count": 185, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(2+0.5j)" + ] + }, + "execution_count": 185, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "2 + 0.5j" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Alternatively, we may use the [complex()](https://docs.python.org/3/library/functions.html#complex) built-in: This takes two parameters where the second is optional and defaults to `0`. We may either call it with one or two arguments of any numeric type or a `str` object in the format of the previous code cell without any spaces." + ] + }, + { + "cell_type": "code", + "execution_count": 186, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(2+0.5j)" + ] + }, + "execution_count": 186, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "complex(2, 0.5) # an integer and a float work just fine as arguments" + ] + }, + { + "cell_type": "code", + "execution_count": 187, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(2+0j)" + ] + }, + "execution_count": 187, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "complex(2) # omitting the second argument sets the imaginary part to 0" + ] + }, + { + "cell_type": "code", + "execution_count": 188, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(2+0.5j)" + ] + }, + "execution_count": 188, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "complex(Decimal(\"2.0\"), Fraction(1, 2)) # the arguments may be any numeric type" + ] + }, + { + "cell_type": "code", + "execution_count": 189, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(2+0.5j)" + ] + }, + "execution_count": 189, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "complex(\"2+0.5j\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Spaces in the text string version lead to a `ValueError`." + ] + }, + { + "cell_type": "code", + "execution_count": 190, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "complex() arg is a malformed string", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mcomplex\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"2 + 0.5j\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: complex() arg is a malformed string" + ] + } + ], + "source": [ + "complex(\"2 + 0.5j\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The basic arithmetic rules work for `complex` numbers. They may be mixed with the other numeric types and the result is always a `complex` number again." + ] + }, + { + "cell_type": "code", + "execution_count": 191, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "e = 1 + 2j\n", + "f = 3 + 4j" + ] + }, + { + "cell_type": "code", + "execution_count": 192, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(4+6j)" + ] + }, + "execution_count": 192, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "e + f" + ] + }, + { + "cell_type": "code", + "execution_count": 193, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(2+2j)" + ] + }, + "execution_count": 193, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "f - e" + ] + }, + { + "cell_type": "code", + "execution_count": 194, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(40+2j)" + ] + }, + "execution_count": 194, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "39 + e" + ] + }, + { + "cell_type": "code", + "execution_count": 195, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-4j" + ] + }, + "execution_count": 195, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "3.0 - f" + ] + }, + { + "cell_type": "code", + "execution_count": 196, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(5+10j)" + ] + }, + "execution_count": 196, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "5 * e" + ] + }, + { + "cell_type": "code", + "execution_count": 197, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(0.5+0.6666666666666666j)" + ] + }, + "execution_count": 197, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "f / 6" + ] + }, + { + "cell_type": "code", + "execution_count": 198, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(-5+10j)" + ] + }, + "execution_count": 198, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "e * f" + ] + }, + { + "cell_type": "code", + "execution_count": 199, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(2.2-0.4j)" + ] + }, + "execution_count": 199, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "f / e" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The absolute value of a `complex` number is commonly defined as the Pythagoram Theorem where $\\lVert x \\rVert = \\sqrt{a^2 + b^2}$ and the [abs()](https://docs.python.org/3/library/functions.html#abs) built-in function works implements that in Python." + ] + }, + { + "cell_type": "code", + "execution_count": 200, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "5.0" + ] + }, + "execution_count": 200, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "abs(3 + 4j)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A `complex` number comes with two **attributes** `real` and `imag` that return the two parts as `float` objects on their own." + ] + }, + { + "cell_type": "code", + "execution_count": 201, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1.0" + ] + }, + "execution_count": 201, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "e.real" + ] + }, + { + "cell_type": "code", + "execution_count": 202, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2.0" + ] + }, + "execution_count": 202, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "e.imag" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Also, a conjugate() method is bound to every `complex` object. The [complex conjugate](https://en.wikipedia.org/wiki/Complex_conjugate) is defined to be the complex number with identical real part but an imaginary part reversed in sign." + ] + }, + { + "cell_type": "code", + "execution_count": 203, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1-2j)" + ] + }, + "execution_count": 203, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "e.conjugate()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [cmath](https://docs.python.org/3/library/cmath.html) module in the [standard library](https://docs.python.org/3/library/) implements many of the functions from the [math](https://docs.python.org/3/library/math.html) module such that they work with complex numbers." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## TL;DR" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "There are three numeric types that are part of core Python:\n", + "- `int`: perfect model for whole numbers (i.e., the set $\\mathbb{Z}$); inherently precise\n", + "- `float`: standard to approximate real numbers (i.e., the set $\\mathbb{R}$); inherently imprecise\n", + "- `complex`: layer on top of the `float` type\n", + "\n", + "Furthermore, the [standard library](https://docs.python.org/3/library/) adds two more types that can be used as substitutes for `float` objects:\n", + "- `Decimal`: similar to `float` but allows customizing the precision; still inherently imprecise\n", + "- `Fraction`: perfect model for rational numbers (i.e., the set $\\mathbb{Q}$)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "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": 2 +} diff --git a/static/complex_numbers.png b/static/complex_numbers.png new file mode 100644 index 0000000000000000000000000000000000000000..807eb4647d1136f67c673fd2b81c74d3f813e08c GIT binary patch literal 22878 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 Ok(mi|?``_8;r|0G1$p%V literal 0 HcmV?d00001 diff --git a/static/floating_point.png b/static/floating_point.png new file mode 100644 index 0000000000000000000000000000000000000000..345aabaf1b3f150cfed2bef7739003913c8fd334 GIT binary patch literal 15371 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 Pc}W)(ROQpp82kPY Date: Mon, 7 Oct 2019 22:34:16 +0200 Subject: [PATCH 030/242] Adjust content overview in README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4561afb..57c513a 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ As such they can be viewed in a plain web browser: - [02 - Functions & Modularization](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) - [03 - Conditionals & Exceptions](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb) - [04 - Recursion & Looping](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb) +- [05 - Numbers](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb) However, it is recommended that students **install Python and Jupyter locally** and run the code in the notebooks on their own. From dbc3a67af484b30db9f29d9cdb0a10a92d8edb25 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 14 Oct 2019 14:09:28 +0200 Subject: [PATCH 031/242] Streamline previous content - run grammarly on all notebooks - add section on short-circuiting in chapter 03 --- 00_start_up.ipynb | 134 +- 00_start_up_review_and_exercises.ipynb | 16 +- 01_elements.ipynb | 176 +- 01_elements_review_and_exercises.ipynb | 2 +- 02_functions.ipynb | 134 +- 02_functions_review_and_exercises.ipynb | 6 +- 03_conditionals.ipynb | 516 +++++- 03_conditionals_review_and_exercises.ipynb | 48 +- 04_iteration.ipynb | 182 +-- 04_iteration_review_and_exercises.ipynb | 44 +- 05_numbers.ipynb | 1685 +++++++++----------- 11 files changed, 1566 insertions(+), 1377 deletions(-) diff --git a/00_start_up.ipynb b/00_start_up.ipynb index 1cfaf91..9ace23c 100644 --- a/00_start_up.ipynb +++ b/00_start_up.ipynb @@ -19,11 +19,9 @@ } }, "source": [ - "This book is set up to be a *thorough* introduction to programming in [Python](https://www.python.org/).\n", + "This book is a *thorough* introduction to programming in [Python](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](https://www.python.org/psf/) in the official [language reference](https://docs.python.org/3/reference/index.html) and introduces additions to the language as distributed with the [standard library](https://docs.python.org/3/library/index.html) that come with every installation.\n", - "\n", - "Furthermore, some very popular third-party libraries like [numpy](https://www.numpy.org/), [pandas](https://pandas.pydata.org/), [matplotlib](https://matplotlib.org/), and others are portrayed." + "It teaches the concepts behind and the syntax of the core Python language as defined by the [Python Software Foundation](https://www.python.org/psf/) in the official [language reference](https://docs.python.org/3/reference/index.html). Furthermore, it introduces commonly used functionalities from the [standard library](https://docs.python.org/3/library/index.html) and popular third-party libraries like [numpy](https://www.numpy.org/), [pandas](https://pandas.pydata.org/), [matplotlib](https://matplotlib.org/), and others." ] }, { @@ -56,7 +54,7 @@ } }, "source": [ - "To be suitable for *total beginners*, there are *no* formal prerequisites. It is only expected that the student has:\n", + "This book is suitable for *total beginners*, and there are *no* formal prerequisites. The student only needs to have:\n", "\n", "- a *solid* understanding of the **English language**,\n", "- knowledge of **basic mathematics** from high school,\n", @@ -94,7 +92,7 @@ } }, "source": [ - "This includes but is not limited to topics such as:\n", + "These include but are not limited to topics such as:\n", "- linear algebra\n", "- statistics & econometrics\n", "- data cleaning & wrangling\n", @@ -108,7 +106,7 @@ "- quantitative marketing (e.g., customer segmentation)\n", "- quantitative supply chain management (e.g., forecasting)\n", "- management science & decision models\n", - "- backend / API / web development (to serve data products to clients)" + "- backend/API/web development (to serve data products to clients)" ] }, { @@ -130,9 +128,9 @@ } }, "source": [ - "The term **[data science](https://en.wikipedia.org/wiki/Data_science)** is rather vague and does actually *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](https://en.wikipedia.org/wiki/Domain_knowledge)**.\n", + "The term **[data science](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](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 [artifical intelligence](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 (like the ones mentioned above) 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 every students' studies 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. This could make teaching easier for top-notch researchers who use a lot of programming in their day-to-day lifes answering big questions: The student and the teacher are then more likely to \"speak\" the same language." + "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](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 (like the ones mentioned above) 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 every 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. Top-notch researchers who use programming in their day-to-day lives could then teach students more efficiently in their \"language.\"" ] }, { @@ -154,11 +152,11 @@ } }, "source": [ - "To follow this book, a working installation of **Python 3.6** or higher is needed.\n", + "To \"read\" this book in the most meaningful way, a working installation of **Python 3.6** or higher is needed.\n", "\n", - "A popular and beginner friendly way is to install the [Anaconda Distribution](https://www.anaconda.com/distribution/) that not only ships Python and the standard library but comes pre-packaged with a lot of third-party libraries from the so-called \"scientific stack\". Just go to the [download](https://www.anaconda.com/download/) page and install the latest version (i.e., *2019-07* with Python 3.7 at the time of this writing) for your operating system.\n", + "A popular and beginner-friendly way is to install the [Anaconda Distribution](https://www.anaconda.com/distribution/) that not only ships Python and the standard library but comes pre-packaged with a lot of third-party libraries from the so-called \"scientific stack.\" Just go to the [download](https://www.anaconda.com/download/) page and install the latest version (i.e., *2019-07* with Python 3.7 at the time of this writing) for your operating system.\n", "\n", - "Then, among others, you will find an entry \"Jupyter Notebook\" in your start menu like below. Click on it and a new tab in your web browser will open where you can switch between folders as you could in your computer's default file browser." + "Then, among others, you find an entry \"Jupyter Notebook\" in your start menu like below. Click on it to open a new tab in your web browser where you can switch between folders as you could in your computer's default file browser." ] }, { @@ -180,7 +178,7 @@ } }, "source": [ - "To download the materials accompanying this book as a ZIP file, open this [GitHub repository](https://github.com/webartifex/intro-to-python) in a web browser and click on the green \"Clone or download\" button on the right. Then, unpack the ZIP file into a folder of your choosing (ideally somewhere within your personal user folder so that the files show up right away)." + "To download the materials accompanying this book as a ZIP file, open this [GitHub repository](https://github.com/webartifex/intro-to-python) in a web browser and click on the green \"Clone or download\" button on the right. Then, unpack the ZIP file into a folder of your choosing (ideally somewhere within your user folder so that the files show up right away)." ] }, { @@ -202,7 +200,7 @@ } }, "source": [ - "Python can also be installed in a \"pure\" way as obtained from its core development team. However, this is somewhat too \"advanced\" for a beginner who would then also be responsible for setting up all the third-party libraries needed to actually view this document. Plus, many of the involveld steps must be done in a [terminal](https://en.wikipedia.org/wiki/Terminal_emulator) window, which tends to be a bit intimidating for most beginners." + "Python can also be installed in a \"pure\" way as obtained from its core development team. However, this is somewhat too \"advanced\" for a beginner who would then also be responsible for setting up all the third-party libraries needed to view this document. Plus, many of the involved steps are typed in a [terminal](https://en.wikipedia.org/wiki/Terminal_emulator) window, which tends to be a bit intimidating for most beginners." ] }, { @@ -224,7 +222,7 @@ } }, "source": [ - "For more \"couragous\" beginners wanting to learn how to accomplish this, here is a rough sketch of the aspects to know. First, all Python realeases are available for free on the official [download](https://www.python.org/downloads/) page for any supported operating system. Choose the one you want, then download and install it (cf., the [instruction notes](https://wiki.python.org/moin/BeginnersGuide/Download)). As this only includes core Python and the standard library, the beginner then needs to learn about the [pip](https://pip.pypa.io/en/stable/) module, which is to be used inside a terminal. With the command `python -m pip install jupyter`, all necessary third-party libraries can be installed (cf., more background [here](https://jupyter.readthedocs.io/en/latest/install.html)). However, this would be done in a *system-wide* fashion and is not recommended. Instead, the best practice is to create a so-called **virtual environment** with the [venv](https://docs.python.org/3/library/venv.html) module with which the installed third-party packages are *isolated* on a per-project basis (the command `python -m venv env-name` creates a virtual enviroment called \"env-name\"). This tactic is employed to avoid a situation known as **[dependency hell](https://en.wikipedia.org/wiki/Dependency_hell)** Once created, the virtual environment must then be activated each time before resuming work in each terminal (with the command `source env-name/bin/activate`). While there exist convenience tools that automate parts of this (e.g., [poetry](https://poetry.eustace.io/docs/) or [virtualenvwrapper](https://virtualenvwrapper.readthedocs.io/en/latest/)), it only distracts a beginner from actually studying the Python language. Yet, it is still worthwhile to have heard about these terms and concepts as many online resources often implicitly assume the user to know about them." + "For more \"courageous\" beginners wanting to learn how to accomplish this, here is a rough sketch of the aspects to know. First, all Python releases are available for free on the official [download](https://www.python.org/downloads/) page for any supported operating system. Choose the latest one applicable, then download and install it (cf., the [instruction notes](https://wiki.python.org/moin/BeginnersGuide/Download)). As this only includes core Python and the standard library, the beginner then needs to learn about the [pip](https://pip.pypa.io/en/stable/) module. With the command `python -m pip install jupyter`, all necessary third-party libraries are installed (cf., more background [here](https://jupyter.readthedocs.io/en/latest/install.html)). However, this would be done in a *system-wide* fashion and is not recommended. Instead, the best practice is to create a so-called **virtual environment** with the [venv](https://docs.python.org/3/library/venv.html) module with which the installed third-party packages are *isolated* on a per-project basis (the command `python -m venv env-name` creates a virtual environment called \"env-name\"). This tactic is employed to avoid a situation known as **[dependency hell](https://en.wikipedia.org/wiki/Dependency_hell)**. Once created, the virtual environment must then be activated each time before resuming work in each terminal (with the command `source env-name/bin/activate`). While there exist convenience tools that automate parts of this (e.g., [poetry](https://poetry.eustace.io/docs/) or [virtualenvwrapper](https://virtualenvwrapper.readthedocs.io/en/latest/)), it only distracts a beginner from studying the Python language. Yet, it is still worthwhile to have heard about these terms and concepts as many online resources often implicitly assume the user to know about them." ] }, { @@ -250,9 +248,9 @@ "\n", "\"Jupyter\" is an [acronym](https://en.wikipedia.org/wiki/Acronym) derived from the names of the three major programming languages **[Julia](https://julialang.org/)**, **[Python](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 easily.\n", "\n", - "Furthermore, Jupyter notebooks have become a de-facto standard for communicating and exchanging results in the data science community (both in academia and business) and often provide a more intuitive alternative to terminal based ways of running Python (e.g., the default [Python interpreter](https://docs.python.org/3/tutorial/interpreter.html) as shown above or a more advanced interactive version like [IPython](https://ipython.org/)) or even a full-fledged [Integrated Development Environment](https://en.wikipedia.org/wiki/Integrated_development_environment) (e.g., the commercial [PyCharm](https://www.jetbrains.com/pycharm/) or the free [Spyder](https://github.com/spyder-ide/spyder)).\n", + "Furthermore, Jupyter notebooks have become a de-facto standard for communicating and exchanging results in the data science community (both in academia and business) and often provide a more intuitive alternative to terminal-based ways of running Python (e.g., the default [Python interpreter](https://docs.python.org/3/tutorial/interpreter.html) as shown above or a more advanced interactive version like [IPython](https://ipython.org/)) or even a full-fledged [Integrated Development Environment](https://en.wikipedia.org/wiki/Integrated_development_environment) (e.g., the commercial [PyCharm](https://www.jetbrains.com/pycharm/) or the free [Spyder](https://github.com/spyder-ide/spyder)).\n", "\n", - "In particular, they allow to mix plain English text with Python code cells. The plain text can be formatted using the [Markdown](https://guides.github.com/features/mastering-markdown/) language and mathematical expressions can be typeset with [LaTeX](https://www.overleaf.com/learn/latex/Free_online_introduction_to_LaTeX_%28part_1%29). Lastly, we can include pictures, plots, and even videos. Because of these features, the notebooks developed for this book come in a self-contained \"tutorial\" style that enables students to learn and review the material on their own." + "In particular, they allow mixing plain English text with Python code cells. The plain text can be formatted using the [Markdown](https://guides.github.com/features/mastering-markdown/) language, and mathematical expressions can be typeset with [LaTeX](https://www.overleaf.com/learn/latex/Free_online_introduction_to_LaTeX_%28part_1%29). Lastly, we can include pictures, plots, and even videos. Because of these features, the notebooks developed for this book come in a self-contained \"tutorial\" style that enables students to learn and review the material on their own." ] }, { @@ -274,15 +272,15 @@ } }, "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", + "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 next cell below is an example of a \"Code\" cell containing a line of actual Python code: it simply outputs the text \"Hello world\" when executed. To edit an existing code cell, just enter into it with a mouse click. You know that you are \"in\" a code cell when you see the frame of the code cell turn green.\n", + "The next cell below is an example of a \"Code\" cell containing a line of actual Python code: it merely outputs the text \"Hello world\" when executed. To edit an existing code cell, enter into it with a mouse click. You know that you are \"in\" a code cell when you see the frame of the code cell turn green.\n", "\n", - "Besides this **edit mode** there is also a so-called **command mode** that you can reach by hitting the \"Escape\" key after entering a code cell, which turns the frame's color into blue. Using the Enter\" and \"Escape\" keys you can now switch between the two modes.\n", + "Besides this **edit mode**, there is also a so-called **command mode** that you can reach by hitting the \"Escape\" key after entering a code cell, which turns the frame's color blue. Using the \"Enter\" and \"Escape\" keys, you can now switch between the two modes.\n", "\n", - "To *execute* (or *run*) a code cell, hold the \"Control\" key and press \"Enter\". Note how you do not go to the subsequent cell. Alternatively, you can hold the \"Shift\" key and press \"Enter\", which executes the cell and places your focus on the next cell right after.\n", + "To *execute*, or \"*run*,\" a code cell, hold the \"Control\" key and press \"Enter.\" Note how you do not go to the subsequent cell. Alternatively, you can hold the \"Shift\" key and press \"Enter,\" which executes the cell and places your focus on the next cell right after.\n", "\n", - "Similarly, a Markdown cell is also in either the edit or command mode. For example, simply double-click on the text you are just reading, which takes you into edit mode. Now you could change the formatting (e.g., make a word printed in *italics* or **bold** with single or double asterisks) and then \"execute\" the cell to actually render the text as specified.\n", + "Similarly, a Markdown cell is also in either edit or command mode. For example, double-click on the text you are just reading, which takes you into edit mode. Now you could change the formatting (e.g., make a word printed in *italics* or **bold** with single or double asterisks) and then \"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." ] @@ -316,7 +314,7 @@ } }, "source": [ - "Sometimes a code cell starts with an exclamation mark `!`. Then, the Jupyter notebook behaves as if you just typed the following command right into a terminal. The cell below asks `python` to show its version number. This is actually *not* Python code but a command in the [Shell](https://en.wikipedia.org/wiki/Shell_script) language. The `!` is useful to execute short commands without leaving a Jupyter notebook." + "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 `python` to show its version number and is *not* Python code but a command in the [Shell](https://en.wikipedia.org/wiki/Shell_script) language. The `!` is useful to execute short commands without leaving a Jupyter notebook." ] }, { @@ -359,15 +357,15 @@ } }, "source": [ - "In this book *programming* is \"defined\" as:\n", + "In this book, *programming* is \"defined\" as:\n", "\n", - "- a **structured** way of **problem solving**\n", - "- by **expressing** the steps of a **computation / process**\n", + "- a **structured** way of **problem-solving**\n", + "- by **expressing** the steps of a **computation/process**\n", "- and thereby **documenting** the process in a formal way\n", "\n", "Programming is always **concrete** and based on a **particular case**.\n", "\n", - "Programming definitely exhibits elements of an **art** or a **craft** as we will hear programmers call code \"beautiful\" or \"ugly\" or talk about the \"expressive\" power of an application." + "It exhibits elements of a form of **art** or a **craft** as we hear programmers call code \"beautiful\" or \"ugly\" or talk about the \"expressive\" power of an application." ] }, { @@ -385,7 +383,7 @@ "- 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. In fact, many computer scientists only know how to produce \"ugly\" code in the eyes of professional programmers." + "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." ] }, { @@ -396,7 +394,7 @@ } }, "source": [ - "*IT* or *information technology* is a term that has many meanings to many people. Often, it has something to do with hardware and devices both of which are out of scope for programmers and computer scientists. In fact, many people from the two aforementioned fields are more than happy if their printer and internet just work as they do not know a lot more about that than non-technical people." + "*IT* or *information technology* is a term that has many meanings to many people. Often, it has something to do with hardware or physical devices, both of which are out of scope for programmers and computer scientists. 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." ] }, { @@ -434,8 +432,8 @@ "- [Guido van Rossum](https://en.wikipedia.org/wiki/Guido_van_Rossum) (Python’s **[Benevolent Dictator for Life](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; still in heavy use as of today) was **open-sourced** from the get-go which attracted a **large and global community of programmers** that **contributed** their individual knowledge and best practices in their free time to make Python even better\n", - "- **Python 3** resulted from a major 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", + "- to become more independent from its creator the next major version **Python 2** (released in 2000; still in heavy use as of today) was **open-sourced** 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](https://en.wikipedia.org/wiki/Monty_Python)" ] }, @@ -458,7 +456,7 @@ } }, "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**." + "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**." ] }, { @@ -480,9 +478,9 @@ } }, "source": [ - "Couldn't a company like Google, Facebook, or Microsoft come up with a better programming language? The following argument provides hints why this cannot really be the case.\n", + "Couldn't a company like Google, Facebook, or Microsoft come up with a better programming language? The following argument provides hints on why this cannot 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 certain 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 just *use* the language over time and in new and unpredictable contexts making up their own new conventions." + "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." ] }, { @@ -493,7 +491,7 @@ } }, "source": [ - "Loose *communities* are the main building block around which open-source software is built. Someone starts a project (like Guido) and makes it free to use for anybody (e.g., on a code-sharing platform like [GitHub](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 (via the popularized concept of a \"[pull request](https://help.github.com/articles/about-pull-requests/)\"). The project grows both in lines of code and number of people using it. After a while, people start local user groups to share their same interests and meet on a regular basis (e.g., this is a big market for companies like [Meetup](https://www.meetup.com/) or non-profits like [PyData](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](https://us.pycon.org/) in the US, [EuroPython](https://europython.eu/), or [PyCon.DE](https://de.pycon.org/)). The content presented at these conferences is made publicly available via GitHub and YouTube (e.g., [PyCon 2019](https://www.youtube.com/channel/UCxs2IIVXaEHHA4BtTiWZ2mQ) or [EuroPython](http://europython.tv/)) and serves as references on what people are working on and introductions to the endless number of specialized fields." + "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](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 (via the popularized concept of a \"[pull request](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](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](https://us.pycon.org/) in the US, [EuroPython](https://europython.eu/), or [PyCon.DE](https://de.pycon.org/)). The content presented at these conferences is made publicly available via GitHub and YouTube (e.g., [PyCon 2019](https://www.youtube.com/channel/UCxs2IIVXaEHHA4BtTiWZ2mQ) or [EuroPython](http://europython.tv/)) and serves as references on what people are working on and introductions to the endless number of specialized fields." ] }, { @@ -504,11 +502,11 @@ } }, "source": [ - "While these communities are rather loose and constantly changing, smaller in-groups, often democratically organized and elected (e.g., the Python Software Foundation), take care of, for example, the development of the \"core\" Python language itself.\n", + "While these communities are somewhat loose and continuously changing, smaller in-groups, often democratically organized and elected (e.g., the [Python Software Foundation](https://www.python.org/psf/)), take care of, for example, the development of the \"core\" Python language itself.\n", "\n", - "Interestingly, Python is just a specification (i.e., a set of rules) as to what is allowed and what not. The current version of Python can always be lookep up in the [Python Language Reference](https://docs.python.org/3/reference/index.html). In order to make changes to that, anyone can make a so-called **[Python Enhancement Proposal](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](https://devguide.python.org/coredev/) and anyone interested and are then either accepted, modified, or rejected if, for example, the change introduces internal inconsistencies. This is very similar to the **double blind peer review** established in academia. In fact, many of the contributors 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](https://www.python.org/dev/peps/pep-0001/) that describes the entire process.\n", + "Interestingly, Python is just a specification (i.e., a set of rules) as to what is allowed and what not. The current version of Python can always be looked up in the [Python Language Reference](https://docs.python.org/3/reference/index.html). To make changes to that, anyone can make a so-called **[Python Enhancement Proposal](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](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. Many of the contributors 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](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." + "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." ] }, { @@ -519,7 +517,7 @@ } }, "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). This is a huge benefit compared to commercial languages (e.g., MATLAB) since a programmer can always continue to **study best practices** or how things are done. 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](https://en.wikipedia.org/wiki/Free_software) is consequently free of charge but brings many other freedoms with it, most notable the freedom to change the code." + "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 different as compared to commercial languages (e.g., MATLAB) since 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](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." ] }, { @@ -541,13 +539,13 @@ } }, "source": [ - "The \"weird\" thing is that the default Python implementation is actually written in the C language.\n", + "The \"weird\" thing is that the default Python implementation is written in the C language.\n", "\n", - "[C](https://en.wikipedia.org/wiki/C_%28programming_language%29) and [C++](https://en.wikipedia.org/wiki/C%2B%2B) (check this [introduction](https://www.learncpp.com/)) are wide-spread and long established (i.e., since the 1970s) programming languages that are employed in many mission critical softwares (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 \"manually\" (and the knowledge necessary to do that is not easy to learn).\n", + "[C](https://en.wikipedia.org/wiki/C_%28programming_language%29) and [C++](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 \"manually\" (and the knowledge necessary to do that is not easy to learn).\n", "\n", - "In contrast, Python automatically manages the memory for the programmer. This speeds up the development process a lot. So speed here is really a trade-off of application run time vs. engineering / development time and in many applications the program's run time is not that important (e.g., what if C needs 0.001 seconds in a case where Python needs 0.1 seconds to train a linear regression model?). 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.\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 train a linear regression model? 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.\n", "\n", - "**In a nutshell**: While it is of course true that C is a lot faster than Python when it comes to **pure computation time**, in many cases this does not really matter as the **significantly shorter development cycles** are the more important cost factor in a rapidly changing business world." + "**In a nutshell**: While it is, of course, true that C is a lot faster than Python when it comes to **pure computation time**, often, this does not matter as the **significantly shorter development cycles** are the more significant cost factor in a rapidly changing business world." ] }, { @@ -580,25 +578,23 @@ } }, "source": [ - "While it is usually not the best argument to quote authorative figures like the pope, we will do this in this section anyways:\n", + "While it is usually not the best argument to quote authoritative figures like the pope, we briefly look at who uses Python here 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 ([source](https://news.ycombinator.com/item?id=602307))\n", + " - replaced the infamous course on the [Scheme](https://groups.csail.mit.edu/mac/projects/scheme/) language (cf., [source](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 ([source](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 **re-written in Python** right away ([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", + " - 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](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 **re-written 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", " - Python and C++, Java, and Go are the only four server-side languages to be deployed to production\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 a lot of its projects, many of which are written in Python and regard working with really big data ([source](https://code.nasa.gov/language/python/))\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/)** ([source](https://instagram-engineering.com/web-service-efficiency-at-instagram-with-python-4976d078e366))\n", - "- **[Spotify](https://spotify.com/)** bases its data science on Python ([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 ([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 ([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 ([source](https://www.ft.com/content/4c17d6ce-c8b2-11e8-ba8f-ee390057b8c9?segmentId=a7371401-027d-d8bf-8a7f-2a746e767d56))\n", - "- ...\n", - "- ... this list is intentionally shortened as Python is simply very popular ..." + "- **[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))" ] }, { @@ -631,7 +627,7 @@ } }, "source": [ - "As the graph below shows, neither Google's very own language **[Go](https://golang.org/)** nor **[R](https://www.r-project.org/)**, a very popular language in the niche of statistics and data science, can compete with Python's year-to-year growth." + "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." ] }, { @@ -686,13 +682,13 @@ } }, "source": [ - "Simply put, **always be coding**.\n", + "**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 documentation, blogs with best practices, and tutorials, or researching some problem on Stack Overflow while trying to implement some feature 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 software simultaneously using software like **[git](https://git-scm.com/)**. In short, programming involves a lot of \"muscle memory\". This can only be built and kept up through near-daily usage.\n", + "Programming is more than just writing code into a text file. It means reading through parts of the [documentation](https://docs.python.org/), blogs with best practices, and tutorials, or researching problems on Stack Overflow 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 requirement for the very first time. Coding also means \"breaking\" things to find out what makes them actually work to begin with.\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", - "Coding is learned best by just doing it for some time on a daily or at least regular basis and not right before some task is due, just like learning a \"real\" language." + "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." ] }, { @@ -716,12 +712,12 @@ "source": [ "[Y Combinator](https://www.ycombinator.com/)'s co-founder [Paul Graham](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 (like a \"boss\"). Their schedule is usually organized by the hour or even by 30 minute intervals.\n", + "- **Managers**: People that need to organize things and command others (like a \"boss\"). Their schedule is usually organized by the hour or even 30-minute intervals.\n", "- **Makers**: People that create things (like programmers, artists, or writers). Such people think in half days or full days.\n", "\n", - "Have you ever wondered why so many tech nerds work all the nights and sleep during \"weird\" hours? This is mainly because many programming related tasks require a certain \"flow\" state of one's mind. This is hard to achieve when one can get interupted, 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", + "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 any more to hear a \"I looked at it over the weekend\" from a programmer." + "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." ] }, { @@ -743,17 +739,17 @@ } }, "source": [ - "When being asked the above question, most programmers will answer something that can be classified into one of two broader groups.\n", + "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 / Prototype**: Pick some problem that you want to write a programatic solution for, break it down into smaller packages, and solve them \"backwards\".\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 / Courses**: Research the best book / blog post / video tutorial for something and work it through from start to end.\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 will always reveal issues no book or tutorial can think of before. Data is never clean as it comes. Some algorithm from a text book must be adapted to a very specific but important aspect of a case study. It is important to learn to \"ship a product\", i.e., to finish some project to the very end because only then you will have looked at all the aspects.\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 you likely learn bad \"patterns\" as whatever you learn is overfitted to the case and you do not get the big picture or mental concepts behind a solution. This is a gap well written books can fill in (e.g., check the Python / Programming books by [Packt](https://www.packtpub.com/packt/offers/free-learning/) or [O’Reilly](https://www.oreilly.com/))." + "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 [O’Reilly](https://www.oreilly.com/)." ] }, { @@ -801,7 +797,7 @@ " 7. Sequences\n", " 8. Mappings & Sets\n", " 9. Arrays\n", - "- How can we create our own data types?\n", + "- How can we create custom data types?\n", " 10. Object-Orientation" ] }, diff --git a/00_start_up_review_and_exercises.ipynb b/00_start_up_review_and_exercises.ipynb index 2f4c334..109a2fc 100644 --- a/00_start_up_review_and_exercises.ipynb +++ b/00_start_up_review_and_exercises.ipynb @@ -53,7 +53,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q2**: Explain what is a *pull request* and elaborate how this concept fits to a *distributed* organization of work!" + "**Q2**: Explain what is a *pull request* and elaborate on how this concept fits a *distributed* organization of work!" ] }, { @@ -81,7 +81,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q4**: What is a major advantage of a \"slow\" programming language like Python over a faster one like C?" + "**Q4**: What is a significant advantage of a \"slow\" programming language like Python over a faster one like C?" ] }, { @@ -109,7 +109,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q5**: Python has been the fastest growing *major* programming language in recent years." + "**Q5**: Python has been the fastest-growing *major* programming language in recent years." ] }, { @@ -137,7 +137,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q7**: Python was originally designed for highly intensive numerical computing, in particular for use cases from physics and astronomy." + "**Q7**: Python was initially designed for highly intensive numerical computing, in particular for use cases from physics and astronomy." ] }, { @@ -151,7 +151,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q8**: JavaScript is a special subset of the Java language." + "**Q8**: JavaScript is a subset of the Java language." ] }, { @@ -165,7 +165,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q9**: Python is *free software*. That means it will never cost anything." + "**Q9**: Python is *free software*. That means it does not cost anything." ] }, { @@ -179,7 +179,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q10**: The main purpose of PEPs is to regulate how code should be documented and/or styled." + "**Q10**: The primary purpose of PEPs is to regulate how code should be documented and styled." ] }, { @@ -228,7 +228,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q12**: The quote \"Education is what remains after one has forgotten what one has learned in school\" is attributed to Albert Einstein. Use a special Markdown syntax to display the author and his quote appropriately!" + "**Q12**: 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!" ] }, { diff --git a/01_elements.ipynb b/01_elements.ipynb index 658e696..c941e43 100644 --- a/01_elements.ipynb +++ b/01_elements.ipynb @@ -20,11 +20,11 @@ } }, "source": [ - "Do you remember how you first learned to speak in your mother tounge? Probably not. No one's memory goes back that far. Your earliest memory as a child should probably be around the age of three or four years old when you could already say simple things and interact with your environment. Although you did not know any grammar rules yet, other people just understood what you said. Well, most of the time.\n", + "Do you remember how you first learned to speak in your mother tongue? Probably not. No one's memory goes back that far. Your earliest memory as a child should probably be around the age of three or four years old when you could already say simple things and interact with your environment. Although you did not know any grammar rules yet, other people just understood what you said. Well, most of the time.\n", "\n", - "It is intuitively best to take the very mindset of a small child when learing a foreign language and we do so for learning the Python language as well. This first chapter introduces simplistic examples and we better just accept them as they are without knowing any of the \"grammar\" rules yet. Then, we analyze them in parts and slowly build up our understanding.\n", + "It is intuitively best to take the very mindset of a small child when learning a foreign language. This first chapter introduces simplistic examples, and we accept them as they are *without* knowing any of the \"grammar\" rules yet. Then, we analyze them in parts and slowly build up our understanding.\n", "\n", - "Consequently, if parts of this chapter do not make sense right away, let's not worry too much. Besides introducing some basics that we need to understand, it also serves as an outlook for what is to come. So, many terms and concepts used here will be covered in great detail in following chapters." + "Consequently, if parts of this chapter do not make sense right away, let's not worry too much. Besides introducing the basic elements, it also serves as an outlook for what is to come. So, many terms and concepts used here are deconstructed in great detail in the following chapters." ] }, { @@ -75,7 +75,7 @@ } }, "source": [ - "To verify that something happened in our computer's memory, we simply **reference** `numbers`." + "To verify that something happened in our computer's memory, we **reference** `numbers`." ] }, { @@ -116,11 +116,11 @@ "\n", "The `if number % 2 == 0` may look disturbing at first sight. Both the `%` and `==` must have an unintuitive meaning here. Luckily, the **comment** in the same line after the `#` symbol has the answer: The program only does something if the current `number` is even.\n", "\n", - "In particular, it increases `count` by $1$ and adds the current `number` onto the [running](https://en.wikipedia.org/wiki/Running_total) `total`. Both `count` and `number` were initially set to $0$ and the single `=` symbol reads as \"... is *set* equal to ...\". It could not indicate a mathematical equation as, for example, `count` is generally not equal to `count + 1`.\n", + "In particular, it increases `count` by $1$ and adds the current `number` onto the [running](https://en.wikipedia.org/wiki/Running_total) `total`. Both `count` and `number` are initially set to $0$ and the single `=` symbol reads as \"... is *set* equal to ...\". It could not indicate a mathematical equation as, for example, `count` is generally not equal to `count + 1`.\n", "\n", - "Lastly, the `average` is calculated as the ratio of the final **values** of `total` and `count`. Overall, we divide the sum of all even numbers by the count of all even numbers, which is exactly what we are looking for.\n", + "Lastly, the `average` is calculated as the ratio of the final **values** of `total` and `count`. Overall, we divide the sum of all even numbers by their count: This is nothing but the definition of an average.\n", "\n", - "The lines of code \"within\" the `for` and `if` **statements** are **indented** and *aligned* with multiples of **four spaces**: This shows immediately how the lines relate to each other." + "The lines of code \"within\" the `for` and `if` **statements** are **indented** and **aligned** with multiples of **four spaces**: This shows immediately how the lines relate to each other." ] }, { @@ -133,8 +133,8 @@ }, "outputs": [], "source": [ - "count = 0 # initialize variables to keep track of the sum\n", - "total = 0 # so far and the count of the even numbers\n", + "count = 0 # initialize variables to keep track of the\n", + "total = 0 # running total and the count of even numbers\n", "\n", "for number in numbers:\n", " if number % 2 == 0: # only look at even numbers\n", @@ -152,7 +152,7 @@ } }, "source": [ - "We do not see any **output** yet but obtain the value of `average` by simply referencing it again." + "We do not see any **output** yet but obtain the value of `average` by referencing it again." ] }, { @@ -249,7 +249,7 @@ } }, "source": [ - "To visualize something before the end of the cell, we use the [print()](https://docs.python.org/3/library/functions.html#print) built-in **function**." + "To visualize something before the end of the cell, we use the built-in [print()](https://docs.python.org/3/library/functions.html#print) **function**. Here, the parentheses `()` indicate that we execute code defined somewhere else." ] }, { @@ -283,7 +283,7 @@ } }, "source": [ - "Outside Jupyter notebooks, the semicolon `;` is actually used as a **seperator** between several statements that would otherwise have to be on a line on their own. However, it as *not* considered good practice to use it as it makes code less readable." + "Outside Jupyter notebooks, the semicolon `;` is used as a **separator** between statements that must otherwise be on a line on their own. However, it is *not* considered good practice to use it as it makes code less readable." ] }, { @@ -327,11 +327,11 @@ } }, "source": [ - "Python comes with many **[operators](https://docs.python.org/3/reference/lexical_analysis.html#operators)** built in: They are **tokens** (i.e., \"symbols\") that have a special meaning to the Python interpreter.\n", + "Python comes with many built-in **[operators](https://docs.python.org/3/reference/lexical_analysis.html#operators)**: They are **tokens** (i.e., \"symbols\") that have a special meaning to the Python interpreter.\n", "\n", - "The arithmetic operators either \"operate\" with the number immediately following them (= **unary** operators; e.g., negation) or \"process\" the two numbers \"around\" them (= **binary** operators; e.g., addition). But we will see many exceptions from that as well.\n", + "The arithmetic operators either \"operate\" with the number immediately following them (= **unary** operators; e.g., negation) or \"process\" the two numbers \"around\" them (= **binary** operators; e.g., addition).\n", "\n", - "By definition, operators have **no** permanent **side effects** in the computer's memory. Although the code cells in this section do indeed create *new* numbers in memory (e.g., `77 + 13` creates `90`), they are immediately \"forgotten\" as they are not stored in a **variable** like `numbers` or `average` above. We will continue this thought further below when we compare **expressions** with **statements**.\n", + "By definition, operators have **no** permanent **side effects** in the computer's memory. Although the code cells in this section do indeed create *new* numbers in memory (e.g., `77 + 13` creates `90`), they are immediately \"forgotten\" as they are not stored in a **variable** like `numbers` or `average` above. We continue this thought further below when we compare **expressions** with **statements**.\n", "\n", "Let's see some examples of operators. We start with the binary `+` and the `-` operators for addition and subtraction. Binary operators are designed to resemble what mathematicians call [infix notation](https://en.wikipedia.org/wiki/Infix_notation) and have the expected meaning." ] @@ -427,7 +427,7 @@ } }, "source": [ - "When we compare the output of the `*` and `/` operators for multiplication and division, we note the subtle *difference* between the $42$ and the $42.0$. This is a first illustration of the concept of a **data type**." + "When we compare the output of the `*` and `/` operators for multiplication and division, we note the subtle *difference* between the $42$ and the $42.0$: They are the *same* number represented as a *different* **data type**." ] }, { @@ -486,7 +486,7 @@ } }, "source": [ - "The so-called **floor division operator** `//` always \"rounds\" to the next integer and is thus also called **integer division operator**. This is a first example of an operator we commonly do not know from high school mathematics." + "The so-called **floor division operator** `//` always \"rounds\" to an integer and is thus also called **integer division operator**. It is an example of an arithmetic operator we commonly do not know from high school mathematics." ] }, { @@ -615,7 +615,9 @@ } }, "source": [ - "Note that the remainder is $0$ *only if* a number is *divisable* by another." + "The remainder is $0$ *only if* a number is *divisible* by another.\n", + "\n", + "A popular convention in both, computer science and mathematics, is to abbreviate \"only if\" as **iff**, which is short for \"**[if and only if](https://en.wikipedia.org/wiki/If_and_only_if)**.\" The iff means that a remainder of $0$ implies that a number is divisible by another but also that a number divisible by another implies a remainder of $0$. The implication goes in *both* directions!" ] }, { @@ -744,7 +746,7 @@ } }, "source": [ - "Raising a number to a power is performed with the **exponentiation operator** `**`. This is different from the `^` operator many other programming languages use and that also exists in Python with a *different* meaning." + "Raising a number to a power is performed with the **exponentiation operator** `**`. It is different from the `^` operator other programming languages may use and that also exists in Python with a *different* meaning." ] }, { @@ -779,7 +781,7 @@ } }, "source": [ - "The normal [order of precedence](https://docs.python.org/3/reference/expressions.html#operator-precedence) from mathematics applies (i.e., \"PEMDAS\" rule)." + "The standard [order of precedence](https://docs.python.org/3/reference/expressions.html#operator-precedence) from mathematics applies (i.e., \"PEMDAS\" rule)." ] }, { @@ -908,7 +910,7 @@ } }, "source": [ - "There are many more non-mathematical operators that are introduced throughout this book together with the concepts they implement. Some of these are already shown in the next section." + "There exist many non-mathematical operators that are introduced throughout this book, together with the concepts they implement. They often come in a form different from the unary and binary mentioned above." ] }, { @@ -932,9 +934,9 @@ "source": [ "Python is a so-called **object-oriented** language, which is a paradigm of organizing a program's memory.\n", "\n", - "An **object** can be viewed as a \"bag\" of $0$s and $1$s in a distinct memory location that not only portrayes the idea of a certain **value** but also has some associated rules as to how this value is treated and may be worked with.\n", + "An **object** may be viewed as a \"bag\" of $0$s and $1$s in a distinct memory location. The concrete $0$s and $1$s in a bag portray the idea of the object's **value**, and there exist different **types** of bags that each come with associated rules as to how the $0$s and $1$s are interpreted and may be worked with.\n", "\n", - "An object *always* has **three** main characteristics. Let's look at the following examples and work them out." + "So, an object *always* has **three** main characteristics. Let's look at the following examples and work them out." ] }, { @@ -986,7 +988,7 @@ { "data": { "text/plain": [ - "140098012863472" + "139829040614096" ] }, "execution_count": 28, @@ -1010,7 +1012,7 @@ { "data": { "text/plain": [ - "140098013042536" + "139829040789352" ] }, "execution_count": 29, @@ -1034,7 +1036,7 @@ { "data": { "text/plain": [ - "140098012714672" + "139829040474736" ] }, "execution_count": 30, @@ -1054,7 +1056,7 @@ } }, "source": [ - "These addresses are really not that meaningful for anything other than checking if two variables actually **point** at the same object. This may be helpful as, for example, different objects in memory may of course have the same value." + "These addresses are *not* meaningful for anything other than checking if two variables **point** at the same object. Let's create a second variable `d` and also set it to `789`." ] }, { @@ -1078,7 +1080,7 @@ } }, "source": [ - "`a` and `d` indeed have the same value as is checked with the **equality operator** `==`. The resulting `True` (and the `False` below) is yet another data type, a so-called **boolean**. We will look into that closely in [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb)." + "`a` and `d` indeed have the same value as is checked with the **equality operator** `==`. The resulting `True` (and the `False` further below) is yet another data type, a so-called **boolean**. We look into them closely in [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb)." ] }, { @@ -1113,7 +1115,7 @@ } }, "source": [ - "On the contrary, `a` and `d` are different objects as the **identity operator** `is` shows: they are stored at seperate addresses in the memory." + "On the contrary, `a` and `d` are different objects as the **identity operator** `is` shows: they are stored at separate addresses in the memory." ] }, { @@ -1220,9 +1222,7 @@ "source": [ "Different types imply different behaviors for the objects. The `b` object, for example, can be \"asked\" if it could also be interpreted as an `int` with the [is_integer()](https://docs.python.org/3/library/stdtypes.html#float.is_integer) \"functionality\" that comes with every `float` object.\n", "\n", - "Formally, we call such type-specific functionalities **methods** (to differentiate them from functions) and we will eventually fully introduce them when we talk about object-orientation in Chapter 10. For now, it suffices to know that we access them using the **dot operator** `.`. Of course `b` could be converted into an `int`, which the boolean value `True` tells us.\n", - "\n", - "Also note how the `.` operator is neiter a unary nor a binary operator as specified above." + "Formally, we call such type-specific functionalities **methods** (to differentiate them from functions) and we formally introduce them in Chapter 10. For now, it suffices to know that we access them using the **dot operator** `.`. Of course, `b` could be converted into an `int`, which the boolean value `True` tells us." ] }, { @@ -1257,7 +1257,7 @@ } }, "source": [ - "For an `int` object this [is_integer()](https://docs.python.org/3/library/stdtypes.html#float.is_integer) check does not make sense as we know it is an `int` to begin with. This is why we see the `AttributeError` below as `a` does not even know what `is_integer()` means." + "For an `int` object, this [is_integer()](https://docs.python.org/3/library/stdtypes.html#float.is_integer) check does *not* make sense as we already know it is an `int`: We see the `AttributeError` below as `a` does not even know what `is_integer()` means." ] }, { @@ -1293,7 +1293,7 @@ } }, "source": [ - "The `c` object is a so-called **string** type (i.e., `str`), which is Python's way of representing \"text\". Strings also come with their own behaviors, for example, to convert a text to lower or upper case." + "The `c` object is a so-called **string** type (i.e., `str`), which is Python's way of representing \"text.\" Strings also come with peculiar behaviors, for example, to convert a text to lower or upper case." ] }, { @@ -1411,9 +1411,9 @@ } }, "source": [ - "Almost trivially, every object also has a value to which it \"evaluates\" when referenced. We think of the value as the **conceptual idea** of what the $0$s and $1$s in memory mean to us humans. A machine does not really see beyond the $0$s and $1$s. At least, not yet.\n", + "Almost trivially, every object also has a value to which it \"evaluates\" when referenced. We think of the value as the **conceptual idea** of what the $0$s and $1$s in memory mean to humans as machines cannot see beyond $0$s and $1$s.\n", "\n", - "For built-in data types, Python prints out the object's value in a so-called **[literal](https://docs.python.org/3/reference/lexical_analysis.html#literals)** notation. This basically means that we can just copy & paste the output back into a code cell to create a *new* object with the *same* value." + "For built-in data types, Python prints out the object's value as a so-called **[literal](https://docs.python.org/3/reference/lexical_analysis.html#literals)**: This means that we can copy and paste the output back into a code cell to create a *new* object with the *same* value." ] }, { @@ -1472,7 +1472,7 @@ } }, "source": [ - "In this book, we follow the convention of creating strings with **double quotes** `\"` instead of the **single quotes** `'` to which Python defaults in its literal output for `str` objects. Both types of quotes may be used interchangebly." + "In this book, we follow the convention of creating strings with **double quotes** `\"` instead of the **single quotes** `'` to which Python defaults in its literal output for `str` objects. Both types of quotes may be used interchangeably." ] }, { @@ -1544,7 +1544,7 @@ "source": [ "If we do not follow the rules, the code cannot be **parsed** correctly, i.e., the program does not even start to run but **raises** a **syntax error** indicated as `SyntaxError` in the output. Computers are very dumb in the sense that the slightest syntax error leads to the machine not understanding our code.\n", "\n", - "For example, if we wanted to write an accounting program that adds up currencies, we would have to model dollar prices as `float` objects as the dollar symbol cannot be read by Python." + "For example, if we wanted to write an accounting program that adds up currencies, we would have to model dollar prices as `float` objects as the dollar symbol cannot be understood by Python." ] }, { @@ -1577,7 +1577,7 @@ } }, "source": [ - "Python requires certain symbols at certain places (e.g., a `:` is missing here) ..." + "Python requires certain symbols at certain places (e.g., a `:` is missing here)." ] }, { @@ -1611,7 +1611,7 @@ } }, "source": [ - "... and relies on whitespace (i.e., indentation) unlike many other programming languages. An `IndentationError` is just a special type of a `SyntaxError`." + "Furthermore, it relies on whitespace (i.e., indentation), unlike many other programming languages. The `IndentationError` below is just a particular type of a `SyntaxError`." ] }, { @@ -1656,7 +1656,7 @@ } }, "source": [ - "Syntax errors as above are easy to find as the code will not even run to begin with.\n", + "Syntax errors are easy to find as the code does not even run in the first place.\n", "\n", "However, there are also so-called **runtime errors**, often called **exceptions**, that occur whenever otherwise (i.e., syntactically) correct code does not run because of invalid input.\n", "\n", @@ -1709,7 +1709,7 @@ "source": [ "So-called **semantic errors**, on the contrary, are hard to spot as they do *not* crash the program. The only way to find such errors is to run a program with test input for which we can predict the output. However, testing software is a whole discipline on its own and often very hard to do in practice.\n", "\n", - "The cell below copies our introductory example from above with a \"tiny\" error. How fast could you have spotted it without the comment?" + "The cell below copies our first example from above with a \"tiny\" error. How fast could you have spotted it without the comment?" ] }, { @@ -1766,7 +1766,7 @@ } }, "source": [ - "Finding errors in a systematic way is called **debugging**. For the history of the term, see this [article](https://en.wikipedia.org/wiki/Debugging)." + "Systematically finding errors is called **debugging**. For the history of the term, see this [article](https://en.wikipedia.org/wiki/Debugging)." ] }, { @@ -1788,7 +1788,7 @@ } }, "source": [ - "Adhering to just syntax rules is therefore *never* enough. Over time, **best practices** and common **style guides** were created to make it less likely for a developer to mess up a program and also to allow \"onboarding\" him as a contributor to an established code base, often called **legacy code**, faster. These rules are not enforced by Python itself: Badly styled and un-readable code will still run. At the very least, Python programs should be styled according to [PEP 8](https://www.python.org/dev/peps/pep-0008/) and documented \"inline\" (i.e., in the code itself) according to [PEP 257](https://www.python.org/dev/peps/pep-0257/).\n", + "Thus, adhering to just syntax rules is *never* enough. Over time, **best practices** and **style guides** were created to make it less likely for a developer to mess up a program and also to allow \"onboarding\" him as a contributor to an established code base, often called **legacy code**, faster. These rules are not enforced by Python itself: Badly styled code still runs. At the very least, Python programs should be styled according to [PEP 8](https://www.python.org/dev/peps/pep-0008/) and documented \"inline\" (i.e., in the code itself) according to [PEP 257](https://www.python.org/dev/peps/pep-0257/).\n", "\n", "An easier to read version of PEP 8 is [here](https://pep8.org/). The video below features a well known \"[Pythonista](https://en.wiktionary.org/wiki/Pythonista)\" talking about the importance of code style." ] @@ -1817,7 +1817,7 @@ " " ], "text/plain": [ - "" + "" ] }, "execution_count": 51, @@ -1838,7 +1838,7 @@ } }, "source": [ - "For example, while the above code to calculate the average of the even numbers from 1 through 10 is correct, a Pythonista would re-write it in a more \"Pythonic\" way and use the [sum()](https://docs.python.org/3/library/functions.html#sum) and [len()](https://docs.python.org/3/library/functions.html#len) (= \"length\") built-in functions (cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb)) as well as a so-called **list comprehension** (cf., Chapter 7). Pythonic code runs faster in many cases and is less error prone." + "For example, while the above code to calculate the average of the even numbers from 1 through 10 is correct, a Pythonista would re-write it in a more \"Pythonic\" way and use the [sum()](https://docs.python.org/3/library/functions.html#sum) and [len()](https://docs.python.org/3/library/functions.html#len) (= \"length\") built-in functions (cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb)) as well as a so-called **list comprehension** (cf., Chapter 7). Pythonic code runs faster in many cases and is less error-prone." ] }, { @@ -1936,7 +1936,7 @@ } }, "source": [ - "To get a rough overview of the mindsets of a typical Python programmer, check these rules by an early Python core developer that are deemed so important that they are actually included in every Python installation." + "To get a rough overview of the mindsets of a typical Python programmer, see these rules by an early Python core developer deemed so important that they are included in every Python installation." ] }, { @@ -2012,7 +2012,7 @@ "source": [ "Observe that you can run the code cells in a Jupyter notebook in any arbitrary order.\n", "\n", - "That means, for example, that a variable defined towards the bottom could accidently be referenced at the top of the notebook. This happens easily if we iteratively built a program and go back and forth between cells.\n", + "That means, for example, that a variable defined towards the bottom could accidentally be referenced at the top of the notebook. This happens quickly when we iteratively built a program and go back and forth between cells.\n", "\n", "As a good practice, it is recommended to click on \"Kernel\" > \"Restart & Run All\" in the navigation bar once a notebook is finished. That restarts the Python process forgetting any **state** (i.e., all variables) and ensures that the notebook runs top to bottom without any errors the next time it is opened." ] @@ -2036,11 +2036,11 @@ } }, "source": [ - "While this book is built with Jupyter notebooks, it is important to understand that \"real\" programs are almost always just \"linear\" (= top to bottom) sequences of instructions but instead may take many different **flows of execution**.\n", + "While this book is built with Jupyter notebooks, it is crucial to understand that \"real\" programs are almost always just \"linear\" (= top to bottom) sequences of instructions but instead may take many different **flows of execution**.\n", "\n", - "At the same time, for a beginner's course it is often easier to just code in a linear fashion.\n", + "At the same time, for a beginner's course, it is often easier to code linearly.\n", "\n", - "In real data science projects one would probably employ a mixed approach and put re-usable code into so-called Python modules (i.e., *.py* files; cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb)) and then use Jupyter notebooks to built up a linear report or story line for a business argument to be made." + "In real data science projects, one would probably employ a mixed approach and put re-usable code into so-called Python modules (i.e., *.py* files; cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb)) and then use Jupyter notebooks to build up a linear report or storyline for a business argument to be made." ] }, { @@ -2064,7 +2064,7 @@ "source": [ "**Variables** are created with the **[assignment statement](https://docs.python.org/3/reference/simple_stmts.html#assignment-statements)** `=`, which is *not* an operator, mainly because of its side effect of making a **[name](https://docs.python.org/3/reference/lexical_analysis.html#identifiers)** point to an object in memory.\n", "\n", - "We will read the terms **variable**, **name**, and **identifier** used interchangebly in many Python related texts. In this book, we adopt the following convention: First, we treat *name* and *identifier* as perfect synonyms but only use the term *name* in the text for clarity. Second, whereas *name* only refers to a string of letters, numbers, and some other symbols, a *variable* refers to the combination of a *name* and a *pointer* to some object in memory." + "We read the terms **variable**, **name**, and **identifier** used interchangebly in many Python-related texts. In this book, we adopt the following convention: First, we treat *name* and *identifier* as perfect synonyms but only use the term *name* in the text for clarity. Second, whereas *name* only refers to a string of letters, numbers, and some other symbols, a *variable* refers to the combination of a *name* and a *pointer* to some object in memory." ] }, { @@ -2089,7 +2089,7 @@ } }, "source": [ - "When referenced, a variable evaluates to the value of the object it points to. Colloquially, we could say that `a` evaluates to `20.0` here but this would not be a full description of what is really going on in memory. We will see some more colloquial jargons in this section but should always relate this to what Python actually does in memory." + "When referenced, a variable evaluates to the value of the object it points to. Colloquially, we could say that `a` evaluates to `20.0`, but this would not be an accurate description of what is going on in memory. We see some more colloquialisms in this section but should always relate this to what Python actually does in memory." ] }, { @@ -2172,7 +2172,7 @@ } }, "source": [ - "If we want to re-assign a variable while referencing its \"old\" (i.e., current) object, we may also **update** it using a so-called **[augmented assignment statement](https://docs.python.org/3/reference/simple_stmts.html#augmented-assignment-statements)** (*not* operator), originally introduced with [PEP 203](https://www.python.org/dev/peps/pep-0203/). This implicitly inserts the currently mapped object as the first operand on the right-hand side." + "If we want to re-assign a variable while referencing its \"old\" (i.e., current) object, we may also **update** it using a so-called **[augmented assignment statement](https://docs.python.org/3/reference/simple_stmts.html#augmented-assignment-statements)** (*not* operator), introduced with [PEP 203](https://www.python.org/dev/peps/pep-0203/): The currently mapped object is implicitly inserted as the first operand on the right-hand side." ] }, { @@ -2246,7 +2246,7 @@ } }, "source": [ - "Variables are **[de-referenced](https://docs.python.org/3/reference/simple_stmts.html#the-del-statement)** (i.e., \"deleted\") with the `del` statement. This does *not* delete the object to which a variable points to. It merely removes the variable's name from the \"global list of all names\"." + "Variables are **[de-referenced](https://docs.python.org/3/reference/simple_stmts.html#the-del-statement)** (i.e., \"deleted\") with the `del` statement. It does *not* delete the object to which a variable points to but merely removes the variable's name from the \"global list of all names.\"" ] }, { @@ -2294,7 +2294,7 @@ } }, "source": [ - "If we refer to an unknown name, a *runtime* error occurs, namely a `NameError`. The `Name` in `NameError` gives a hint as to why we prefer the term *name* over *identifier*: Python just uses it more often in its error messages." + "If we refer to an unknown name, a *runtime* error occurs, namely a `NameError`. The `Name` in `NameError` gives a hint as to why we prefer the term *name* over *identifier*: Python uses it more often in its error messages." ] }, { @@ -2557,9 +2557,9 @@ } }, "source": [ - "It is important to understand that *several* variables may point to the *same* object in memory. This may be counter-intuitive in the beginning and lead to many hard to track down bugs.\n", + "It is *crucial* to understand that *several* variables may point to the *same* object in memory. Not having this in mind may lead to many hard to track down bugs.\n", "\n", - "This makes `b` point to whatever object `a` is pointing to." + "Make `b` point to whatever object `a` is pointing to." ] }, { @@ -2631,9 +2631,9 @@ } }, "source": [ - "For \"simple\" types like `int` or `float` this will never cause troubles.\n", + "For \"simple\" types like `int` or `float` this never causes troubles.\n", "\n", - "Let's \"change the value\" of `a`. Really, let's create a *new* `123` object and make `a` point to it." + "Let's \"change the value\" of `a`. To be precise, let's create a *new* `123` object and make `a` point to it." ] }, { @@ -2681,7 +2681,7 @@ } }, "source": [ - "`b` \"is still the same\" as before. Really, `b` still points to the same object as before." + "`b` \"is still the same\" as before. To be precise, `b` still points to the *same object* as before." ] }, { @@ -2779,7 +2779,7 @@ "source": [ "Let's change the first element of `x`.\n", "\n", - "Chapter 7 discusses lists in more depth. For now, let's just view a `list` object as some sort of **container** that holds an arbitrary number of pointers to other objects and treat the brackets `[]` attached to it as just another operator, called the **indexing operator**. `x[0]` instructs Python to first follow the pointer from the global list of all names to the `x` object. Then, it follows the first pointer it finds there to the `1` object. The indexing operator must be an operator as we merely read the first element and do not change anything in memory.\n", + "Chapter 7 discusses lists in more depth. For now, let's view a `list` object as some sort of **container** that holds an arbitrary number of pointers to other objects and treat the brackets `[]` attached to it as just another operator, called the **indexing operator**. `x[0]` instructs Python to first follow the pointer from the global list of all names to the `x` object. Then, it follows the first pointer it finds there to the `1` object. The indexing operator must be an operator as we merely read the first element and do not change anything in memory.\n", "\n", "Note how Python **begins counting at 0**. This is not the case for many other languages, for example, [MATLAB](https://en.wikipedia.org/wiki/MATLAB), [R](https://en.wikipedia.org/wiki/R_%28programming_language%29), or [Stata](https://en.wikipedia.org/wiki/Stata). To understand why this makes sense, see this short [note](https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html) by one of the all-time greats in computer science, the late [Edsger Dijkstra](https://en.wikipedia.org/wiki/Edsger_W._Dijkstra)." ] @@ -2816,7 +2816,7 @@ } }, "source": [ - "To change the first entry in the list, we use the assignment statement `=` again. Here, this does actually *not* create a *new* variable (or overwrite an existing one) but only changes the object to which the first pointer in the `x` list points to. As we only change parts of the `x` object, we say that we **mutate** (i.e., \"change\") its **state**. To use the bag analogy from above, we keep the same bag but \"flip\" some of the $0$s into $1$s and some of the $1$s into $0$s." + "To change the first entry in the list, we use the assignment statement `=` again. Here, this does *not* create a *new* variable (or overwrite an existing one) but only changes the object to which the first pointer in the `x` list points to. As we only change parts of the `x` object, we say that we **mutate** (i.e., \"change\") its **state**. To use the bag analogy from above, we keep the same bag but \"flip\" some of the $0$s into $1$s and some of the $1$s into $0$s." ] }, { @@ -2901,11 +2901,11 @@ "source": [ "The illustrated difference in behavior has to do with the fact that integers and floats are **immutable** types while lists are **mutable**.\n", "\n", - "In the first case, an object cannot be changed \"in place\" once it is created in memory. When we assigned `123` to the already existing `a`, we actually did not change the $0$s and $1$s in the object `a` pointed to before the assignment but created a new integer object and made `a` point to it while the `b` variable is *not* affected.\n", + "In the first case, an object cannot be changed \"in place\" once it is created in memory. When we assigned `123` to the already existing `a`, we did not change the $0$s and $1$s in the object `a` pointed to before the assignment but created a new integer object and made `a` point to it while the `b` variable is *not* affected.\n", "\n", "In the second case, `x[0] = 99` creates a *new* integer object `99` and merely changes the first pointer in the `x` list.\n", "\n", - "In general, the assignment statement creates (or overwrites) a variable and makes it point to whatever object is on the right-hand side *only if* the left-hand side is a *pure* name (i.e., it contains no operators like the indexing operator in the example). Otherwise, it mutates some already existing object. And we always have to expect that the latter might have more than one variable pointing at it.\n", + "In general, the assignment statement creates a new name and makes it point to whatever object is on the right-hand side *iff* the left-hand side is a *pure* name (i.e., it contains no operators like the indexing operator in the example). Otherwise, it *mutates* an already existing object. And, we always must expect that the latter might have more than one variable pointing to it.\n", "\n", "Visualizing what is going on in the memory with a tool like [PythonTutor](http://pythontutor.com/visualize.html#code=x%20%3D%20%5B1,%202,%203%5D%0Ay%20%3D%20x%0Ax%5B0%5D%20%3D%2099%0Aprint%28y%5B0%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) might be helpful for a beginner." ] @@ -2944,7 +2944,7 @@ "source": [ "Variable names may contain upper and lower case letters, numbers, and underscores (i.e., `_`) and be as long as we want them to be. However, they must not begin with a number. Also, they must not be any of Python's built-in **[keywords](https://docs.python.org/3/reference/lexical_analysis.html#keywords)**.\n", "\n", - "Variable names are usually chosen such that they do not need any more documentation and are self-explanatory. A very common convention is to use so-called **[snake\\_case](https://en.wikipedia.org/wiki/Snake_case)**: Keep everything lowercase and use underscores to seperate words.\n", + "Variable names should be chosen such that they do not need any more documentation and are self-explanatory. A widespread convention is to use so-called **[snake\\_case](https://en.wikipedia.org/wiki/Snake_case)**: Keep everything lowercase and use underscores to separate words.\n", "\n", "See this [link](https://en.wikipedia.org/wiki/Naming_convention_%28programming%29#Python_and_Ruby) for a comparison of different naming conventions." ] @@ -3092,7 +3092,7 @@ } }, "source": [ - "If a variable name collides with a built-in name, just add a trailing underscore." + "If a variable name collides with a built-in name, we add a trailing underscore." ] }, { @@ -3116,7 +3116,7 @@ } }, "source": [ - "Variables with leading and trailing double underscores, referred to as **dunder** in Python jargon, are used for important built-in functionalities. Do *not* use this style for custom variables unless you know exactly what you are doing!" + "Variables with leading and trailing double underscores, referred to as **dunder** in Python jargon, are used for built-in functionalities. Do *not* use this style for custom variables unless you exactly know what you are doing!" ] }, { @@ -3162,7 +3162,7 @@ } }, "source": [ - "This PyCon talk by [Ned Batchelder](https://nedbatchelder.com/), a well-known Pythonista and the organizer of the [Python User Group](https://www.meetup.com/bostonpython/) in Boston, summarizes all situations where some sort of variable assignment is done in Python. The content is intermediate and therefore it is ok if you do not understand everything at this point. However, the contents should be known by everyone claiming to be proficient in Python." + "This PyCon talk by [Ned Batchelder](https://nedbatchelder.com/), a well-known Pythonista and the organizer of the [Python User Group](https://www.meetup.com/bostonpython/) in Boston, summarizes all situations where some sort of assignment is done in Python. The content is intermediate, and, thus, it might be worthwhile to come back to this talk at a later point in time. However, the contents should be known by everyone claiming to be proficient in Python." ] }, { @@ -3189,7 +3189,7 @@ " " ], "text/plain": [ - "" + "" ] }, "execution_count": 94, @@ -3225,7 +3225,7 @@ "\n", "In simple words, anything that may be used on the right-hand side of an assignment statement without creating a `SyntaxError` is an expression.\n", "\n", - "What we said about individual operators above, namely that they have *no* side effects, should have been put here to begin with. The examples in the section on operators above were actually all expressions!\n", + "What we said about individual operators above, namely that they have *no* side effects, should have been put here, to begin with. The code cells in the section on operators above are all expressions!\n", "\n", "The simplest possible expressions contain only one variable or literal." ] @@ -3313,7 +3313,7 @@ } }, "source": [ - "The definition of an expression is **recursive**. So, here the sub-expression `a + b` is combined with the literal `3` by the operator `**` to form the full expression." + "The definition of an expression is **recursive**. So, the sub-expression `a + b` is combined with the literal `3` by the operator `**` to form the full expression." ] }, { @@ -3383,7 +3383,7 @@ } }, "source": [ - "When not used as a delimiter, parentheses also constitute an operator, namely the **call operator** `()`. We have seen this syntax above when we \"called\" (i.e., executed) built-in functions and methods." + "When not used as a delimiter, parentheses also constitute an operator, namely the **call operator** `()`. We saw this syntax above when we \"called\" (i.e., executed) built-in functions and methods." ] }, { @@ -3429,7 +3429,7 @@ } }, "source": [ - "Python **overloads** certain operators. For example, you may not only \"add\" numbers but also strings. This is called **string concatenation**." + "Python **overloads** certain operators. For example, you may not only \"add\" numbers but also strings: This is called **string concatenation**." ] }, { @@ -3524,9 +3524,9 @@ } }, "source": [ - "A **[statement](https://docs.python.org/3/reference/simple_stmts.html)** is anything that *changes* the *state of a program* or has some other *side effect*. Statements do not just evaluate to a value like expressions; instead, they create or change values.\n", + "A **[statement](https://docs.python.org/3/reference/simple_stmts.html)** is anything that *changes* the *state of a program* or has another *side effect*. Statements, unlike expressions, do not just evaluate to a value; instead, they create or change values.\n", "\n", - "Most notably of course are the `=` and `del` statements." + "Most notably, of course, are the `=` and `del` statements." ] }, { @@ -3563,7 +3563,7 @@ } }, "source": [ - "The built-in [print()](https://docs.python.org/3/library/functions.html#print) function is regarded a \"statement\" as well. In fact, it used to be a real statement in Python 2 and has all the necessary properties. It is a bit of a corner case as expressions are also \"printed\" in a Jupyter notebook when evaluated last in a code cell." + "The built-in [print()](https://docs.python.org/3/library/functions.html#print) function is regarded as a \"statement\" as well. It used to be an actual statement in Python 2 and has all the necessary properties. It is a bit of a corner case as expressions are also \"printed\" in a Jupyter notebook when evaluated last in a code cell." ] }, { @@ -3608,9 +3608,9 @@ "source": [ "We use the `#` symbol to write comments in plain English right into the code.\n", "\n", - "As a good practice, comments should not describe what happens (this should be evident by reading the code, otherwise it is most likely badly written code) but why something happens.\n", + "As a good practice, comments should not describe *what* happens (this should be evident by reading the code; otherwise, it is most likely badly written code) but *why* something happens.\n", "\n", - "Comments may be added either at the end of a line of code, by convention seperated with two spaces, or on a line on their own." + "Comments may be added either at the end of a line of code, by convention separated with two spaces, or on a line on their own." ] }, { @@ -3637,8 +3637,8 @@ } }, "source": [ - "But let's think wisely if we really need to use a comment.\n", - "The second cell is a lot more \"Pythonic\"." + "But let's think wisely if we need to use a comment.\n", + "The second cell is a lot more Pythonic." ] }, { @@ -3686,7 +3686,7 @@ } }, "source": [ - "We end each chapter with a summary of the main points. The essence in this first chapter is that just like a sentence in a real language like English may be decomposed into its parts (subject, predicate, objects, ...) the same may be done with programming languages." + "We end each chapter with a summary of the main points. The essence in this first chapter is that just like 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." ] }, { @@ -3707,18 +3707,18 @@ " - (numeric) data from a CSV file\n", " - text entered on a command line\n", " - (relational) data obtained from a database\n", - " - ...\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", - " - ...\n", + " - etc.\n", "\n", "\n", "- objects\n", - " - distinct and well-contained areas / parts of the memory that hold the actual data\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 8)\n", @@ -3733,13 +3733,13 @@ "\n", "- operators\n", " - special built-in symbols that perform operations with objects in memory\n", - " - usually operate with one or two objects\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", + " - 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", @@ -3747,7 +3747,7 @@ "- statements\n", " - instructions that **\"do\" something** and **have side effects** in memory\n", " - re-map names to different objects and *change* the state of the program\n", - " - usually work with expressions\n", + " - usually, work with expressions\n", " - e.g., the assignment statement `=` makes a name point to an object\n", "\n", "\n", diff --git a/01_elements_review_and_exercises.ipynb b/01_elements_review_and_exercises.ipynb index 8940f24..965b8be 100644 --- a/01_elements_review_and_exercises.ipynb +++ b/01_elements_review_and_exercises.ipynb @@ -40,7 +40,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q1**: Elaborate on how **modulo division** might be a very useful operation to know!" + "**Q1**: Elaborate on how **modulo division** might be a handy operation to know!" ] }, { diff --git a/02_functions.ipynb b/02_functions.ipynb index 03452c7..6ab7c32 100644 --- a/02_functions.ipynb +++ b/02_functions.ipynb @@ -19,11 +19,11 @@ } }, "source": [ - "In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) we typed the **business logic** of our little program to calculate the mean of a subset of a list of numbers right into the code cells. Then, we executed them one after another. We had no way of **re-using** the code except for either re-executing the cells or copying and pasting their contents into other cells. And whenever we find ourselves doing repetitive manual work, we can be sure that there must be a way of automating what we are doing.\n", + "In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb), we typed the **business logic** of our little program to calculate the mean of a subset of a list of numbers right into the code cells. Then, we executed them one after another. We had no way of **re-using** the code except for either re-executing the cells or copying and pasting their contents into other cells. And whenever we find ourselves doing repetitive manual work, we can be sure that there must be a way of automating what we are doing.\n", "\n", "At the same time, we executed built-in functions (e.g., [print()](https://docs.python.org/3/library/functions.html#print), [sum()](https://docs.python.org/3/library/functions.html#sum), [len()](https://docs.python.org/3/library/functions.html#len), [id()](https://docs.python.org/3/library/functions.html#id), or [type()](https://docs.python.org/3/library/functions.html#type)) that obviously must be re-using the same parts inside core Python every time we use them.\n", "\n", - "This chapter shows how Python offers language constructs that let us **define** our own functions that we may then **call** just like the built-in ones." + "This chapter shows how Python offers language constructs that let us **define** functions ourselves that we may then **call** just like the built-in ones." ] }, { @@ -47,17 +47,17 @@ "source": [ "So-called **[user-defined functions](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)** may be created with the `def` statement. To extend an already familiar example, we re-use the introductory example from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) in its final Pythonic version and transform it into the function `average_evens()` below. \n", "\n", - "A function's **name** must be chosen according to the same naming rules as for ordinary variables. In fact, Python manages function names just like variables. In this book, we further adopt the convention of ending function names with parentheses \"`()`\" in text cells for faster comprehension when reading (i.e., `average_evens()` vs. `average_evens`). These are not actually part of the name but must always be written out in the `def` statement for syntactic reasons.\n", + "A function's **name** must be chosen according to the same naming rules as ordinary variables as Python manages function names like variables. In this book, we further adopt the convention of ending function names with parentheses `()` in text cells for faster comprehension when reading (i.e., `average_evens()` vs. `average_evens`). These are *not* part of the name but must always be written out in the `def` statement for syntactic reasons.\n", "\n", - "Functions may define an arbitrary number of **parameters** as inputs that are then referenced within the indented **code block**: They are simply listed within the parentheses in the `def` statement (i.e., `numbers` below). \n", + "Functions may define an arbitrary number of **parameters** as inputs that can then be referenced within the indented **code block**: They are listed within the parentheses in the `def` statement (i.e., `numbers` below). \n", "\n", - "The code block is often also called a function's **body** while the first line with the `def` in it is the **header** and must end with a colon.\n", + "The code block is also called a function's **body**, while the first line starting with `def` and ending with a colon is the **header**.\n", "\n", "Together, the name and the list of parameters are also referred to as the function's **[signature](https://en.wikipedia.org/wiki/Type_signature)** (i.e., `average_evens(numbers)` below).\n", "\n", "A function may come with an *explicit* **[return value](https://docs.python.org/3/reference/simple_stmts.html#the-return-statement)** (i.e., \"result\" or \"output\") specified with the `return` statement: Functions that have one are considered **fruitful**; otherwise, they are **void**. Functions of the latter kind are still useful because of their **side effects** (e.g., the [print()](https://docs.python.org/3/library/functions.html#print) built-in). Strictly speaking, they also have an *implicit* return value of `None` that is different from the `False` we saw in Chapter 1.\n", "\n", - "To maintain good coding practices, a function should define a **docstring** that describes what it does in a short subject line, what parameters it expects (i.e., their types), and what it returns (if anything). A docstring is a syntactically valid multi-line string (i.e., type `str`) defined with **triple-double quotes** (strings are covered in depth in Chapter 6). Good standards as to how to format a docstring are [PEP 257](https://www.python.org/dev/peps/pep-0257/) and section 3.8 in [Google's Python Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md)." + "A function should define a **docstring** that describes what it does in a short subject line, what parameters it expects (i.e., their types), and what it returns (if anything). A docstring is a syntactically valid multi-line string (i.e., type `str`) defined within **triple-double quotes** `\"\"\"`. Strings are covered in depth in Chapter 6. Widely adopted standards as to how to format a docstring are [PEP 257](https://www.python.org/dev/peps/pep-0257/) and section 3.8 in [Google's Python Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md)." ] }, { @@ -142,7 +142,7 @@ { "data": { "text/plain": [ - "139925407773152" + "140519730762208" ] }, "execution_count": 3, @@ -254,7 +254,7 @@ } }, "source": [ - "Two questions marks even show a function's source code." + "Two question marks even show a function's source code." ] }, { @@ -337,7 +337,7 @@ } }, "source": [ - "The return value is usually assigned to a new variable for later reference. Otherwise we would loose access to it in memory right away." + "The return value is commonly assigned to a new variable for later reference. Otherwise, we would lose access to it in memory right away." ] }, { @@ -493,7 +493,7 @@ } }, "source": [ - "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010%5D%0A%0Adef%20average_evens%28numbers%29%3A%0A%20%20%20%20evens%20%3D%20%5Bn%20for%20n%20in%20numbers%20if%20n%20%25%202%20%3D%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28evens%29%20/%20len%28evens%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_evens%28nums%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) visualizes what happens in memory: To be precise, in the exact moment when the function call is initiated and `nums` is passed in as the `numbers` argument, there are *two* pointers to the *same* `list` object (cf., steps 4-5 in the visualization). We also see how Python creates a *new* **frame** that holds the function's local scope (i.e., \"internal names\") in addition to the **global** frame. Frames are nothing but [namespaces](https://en.wikipedia.org/wiki/Namespace) to *isolate* the names of different **scopes** from each other. The list comprehension `[n for n in numbers if n % 2 == 0]` constitutes yet another frame that is in scope as the `list` object assigned to `evens` is *being* created (cf., steps 6-18). When the function returns, only the global frame is left (cf., steps 21-22)." + "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010%5D%0A%0Adef%20average_evens%28numbers%29%3A%0A%20%20%20%20evens%20%3D%20%5Bn%20for%20n%20in%20numbers%20if%20n%20%25%202%20%3D%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28evens%29%20/%20len%28evens%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_evens%28nums%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) visualizes what happens in memory: To be precise, in the exact moment when the function call is initiated and `nums` passed in as the `numbers` argument, there are *two* pointers to the *same* `list` object (cf., steps 4-5 in the visualization). We also see how Python creates a *new* **frame** that holds the function's local scope (i.e., \"internal names\") in addition to the **global** frame. Frames are nothing but [namespaces](https://en.wikipedia.org/wiki/Namespace) to *isolate* the names of different **scopes** from each other. The list comprehension `[n for n in numbers if n % 2 == 0]` constitutes yet another frame that is in scope as the `list` object assigned to `evens` is *being* created (cf., steps 6-18). When the function returns, only the global frame is left (cf., steps 21-22)." ] }, { @@ -515,7 +515,7 @@ } }, "source": [ - "On the contrary, while a function is being executed, it can reference the variables of **enclosing scopes** (i.e., \"outside\" of it). This is a common source of *semantic* errors. Consider the following stylized (and incorrect) example `average_wrong()`. The error is hard to spot with eyes: The function never references the `numbers` parameter but the `nums` variable in the **global scope** instead." + "On the contrary, while a function is *being* executed, it can reference the variables of **enclosing scopes** (i.e., \"outside\" of it). This is a common source of *semantic* errors. Consider the following stylized (and incorrect) example `average_wrong()`. The error is hard to spot with eyes: The function never references the `numbers` parameter but the `nums` variable in the **global scope** instead." ] }, { @@ -550,7 +550,7 @@ } }, "source": [ - "`nums` in the global scope is of course unchanged." + "`nums` in the global scope is, of course, unchanged." ] }, { @@ -657,7 +657,7 @@ "source": [ "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010%5D%0A%0Adef%20average_wrong%28numbers%29%3A%0A%20%20%20%20evens%20%3D%20%5Bn%20for%20n%20in%20nums%20if%20n%20%25%202%20%3D%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28evens%29%20/%20len%28evens%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_wrong%28%5B123,%20456,%20789%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) is again helpful at visualizing the error interactively: Creating the `list` object `evens` eventually points to takes *12* computational steps, namely one for setting up an empty `list` object, *ten* for filling it with elements derived from `nums` in the global scope, and one to make `evens` point at it (cf., steps 6-18).\n", "\n", - "The frames logic shown by PythonTutor is the mechanism by which Python not only manages the names inside one function call but for potentially many calls occuring simultaneously as we will see in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb). It is the reason why we may re-use the same names for the parameters and variables inside both `average_evens()` and `average_wrong()` without Python mixing them up. So, as we already read in the [Zen of Python](https://www.python.org/dev/peps/pep-0020/), \"namespaces are one honking great idea\" (cf., `import this`) and a frame is just a special kind of namespace." + "The frames logic shown by PythonTutor is the mechanism by which Python not only manages the names inside *one* function call but also for *many* potentially simultaneous* calls, as revealed in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb). It is the reason why we may re-use the same names for the parameters and variables inside both `average_evens()` and `average_wrong()` without Python mixing them up. So, as we already read in the [Zen of Python](https://www.python.org/dev/peps/pep-0020/), \"namespaces are one honking great idea\" (cf., `import this`), and a frame is just a special kind of namespace." ] }, { @@ -752,7 +752,7 @@ } }, "source": [ - "As good practice, let's first use inputs for which we can calculate the answer in our heads to \"verify\" that `average_odds()` is \"correct\"." + "As good practice, let's first use inputs for which we can calculate the answer in our heads to \"verify\" that `average_odds()` is \"correct.\"" ] }, { @@ -787,7 +787,7 @@ } }, "source": [ - "To make the confusion even bigger, let's also pass the global `nums` as an argument to `average_odds()`." + "To add to the confusion, let's also pass the global `nums` as an argument to `average_odds()`." ] }, { @@ -857,13 +857,13 @@ } }, "source": [ - "The reason why everything just works is that *every time* we (re-)assign an object to a variable *inside* a function with the `=` statement, this is done in the *local* scope by default. There are ways to change variables existing in an outer scope from within a function but this is a rather advanced topic on its own.\n", + "The reason why everything works is that *every time* we (re-)assign an object to a variable *inside* a function with the `=` statement, this is done in the *local* scope by default. There are ways to change variables existing in an outer scope from within a function, but this is a rather advanced topic on its own.\n", "\n", "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010%5D%0A%0Adef%20average_odds%28numbers%29%3A%0A%20%20%20%20nums%20%3D%20%5Bint%28n%29%20for%20n%20in%20numbers%5D%0A%20%20%20%20odds%20%3D%20%5Bn%20for%20n%20in%20nums%20if%20n%20%25%202%20!%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28odds%29%20/%20len%28odds%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_odds%28%5B1.0,%2010.0,%203.0,%2010.0,%205.0%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how *two* `nums` variables exist in *different* scopes pointing to *different* objects (cf., steps 14-25) when we execute `average_odds([1.0, 10.0, 3.0, 10.0, 5.0])`.\n", "\n", "Variables whose names collide with the ones of variables in enclosing scopes - and the global scope is just the most enclosing scope - are said to **shadow** them.\n", "\n", - "While this is not a problem for Python, it may lead to less readable code for us humans and should be avoided if possible. But, as we have also heard, \"[naming things](https://skeptics.stackexchange.com/questions/19836/has-phil-karlton-ever-said-there-are-only-two-hard-things-in-computer-science)\" is often considered hard as well and we have to be prepared to encounter shadowing variables." + "While this is not a problem for Python, it may lead to less readable code for us humans and should be avoided if possible. But, as we have also heard, \"[naming things](https://skeptics.stackexchange.com/questions/19836/has-phil-karlton-ever-said-there-are-only-two-hard-things-in-computer-science)\" is often considered hard as well, and we have to be prepared to encounter shadowing variables." ] }, { @@ -946,7 +946,7 @@ } }, "source": [ - "We may cast certain objects as a different type. For example, to \"convert\" a float or a text into an integer, we use the [int()](https://docs.python.org/3/library/functions.html#int) built-in. This actually creates a *new* object of type `int` from the provided `avg` or `\"6\"` objects who continue to exist in memory unchanged." + "We may cast objects as a different type. For example, to \"convert\" a float or a text into an integer, we use the [int()](https://docs.python.org/3/library/functions.html#int) built-in. It creates a *new* object of type `int` from the provided `avg` or `\"6\"` objects that continue to exist in memory unchanged." ] }, { @@ -1170,7 +1170,7 @@ } }, "source": [ - "So far we have only specified one parameter in each of our user-defined functions. In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb), however, we saw the built-in function [divmod()](https://docs.python.org/3/library/functions.html#divmod) taking two arguments. Obviously, the order of the numbers passed in mattered. Whenever we call a function and list its arguments in a comma seperated manner, we say that we pass in the arguments by position or refer to them as **positional arguments**." + "So far, we have only specified one parameter in each of our user-defined functions. In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb), however, we saw the built-in function [divmod()](https://docs.python.org/3/library/functions.html#divmod) take two arguments. And, the order of the numbers passed in mattered! Whenever we call a function and list its arguments in a comma separated manner, we say that we pass in the arguments by position or refer to them as **positional arguments**." ] }, { @@ -1229,7 +1229,7 @@ } }, "source": [ - "For many functions there is a natural order to the arguments. But what if this is not the case? For example, let's create a close relative of the above `average_evens()` function that also scales the resulting average by a factor. What is more natural? Passing in `numbers` first? Or `scalar`? There is no obvious way and we continue with the first alternative for no real reason." + "For many functions, there is a natural order to the arguments. But what if this is not the case? For example, let's create a close relative of the above `average_evens()` function that also scales the resulting average by a factor. What is more natural? Passing in `numbers` first? Or `scalar`? There is no obvious way and we continue with the first alternative for no concrete reason." ] }, { @@ -1301,9 +1301,9 @@ } }, "source": [ - "However, now the function call is a bit harder to comprehend as we need to always remember what the `2` means. This becomes even harder the more parameters we specify.\n", + "However, now the function call is a bit harder to comprehend as we always need to remember what the `2` means. This becomes harder the more parameters we specify.\n", "\n", - "Luckily, we may also reference the formal parameter names as **keyword arguments**. We can even combine positional and keyword arguments in the same function call. Each of the following does the exact same thing." + "Luckily, we may also reference the formal parameter names as **keyword arguments**. We can even combine positional and keyword arguments in the same function call. Each of the following does the *same*." ] }, { @@ -1430,11 +1430,11 @@ } }, "source": [ - "Defining both `average_evens()` and `scaled_average_evens()` is also kind of repetitive as most of their code is the same. Such a redundancy will make a code base harder to maintain in the long run as whenever we change the logic in one function we must *not* forget to do so for the other function as well.\n", + "Defining both `average_evens()` and `scaled_average_evens()` is also kind of repetitive as most of their code is the same. Such a redundancy makes a codebase harder to maintain in the long run as whenever we change the logic in one function, we must *not* forget to do so for the other function as well.\n", "\n", "A better way is to design related functions in a **modular** fashion such that they re-use each other's logic.\n", "\n", - "For example, as not scaling an average is just a special case of scaling it with `1`, we could re-define the two functions like below: In this setting, the function resembling the *special* case (i.e., `average_evens()`) simply **forwards** the call to the more *general* function (i.e., `scaled_average_evens()`) using a `scalar=1` argument." + "For example, as not scaling an average is just a special case of scaling it with `1`, we could re-define the two functions like below: In this setting, the function resembling the *special* case (i.e., `average_evens()`) **forwards** the call to the more *general* function (i.e., `scaled_average_evens()`) using a `scalar=1` argument." ] }, { @@ -1483,7 +1483,7 @@ } }, "source": [ - "The outcome of `average_evens(nums)` is of course still `6.0`." + "The outcome of `average_evens(nums)` is, of course, still `6.0`." ] }, { @@ -1557,7 +1557,7 @@ "source": [ "Now we call the function either with or without the `scalar` argument.\n", "\n", - "If `scalar` is passed in, this may be done as either a positional or a keyword argument. Which of the two versions where `scalar` is `2` is easier to comprehend in a large program?" + "If `scalar` is to be passed in, this may be done as either a positional or a keyword argument. Which of the two versions where `scalar` is `2` is more \"natural\" and faster to comprehend in a larger program?" ] }, { @@ -1651,9 +1651,9 @@ } }, "source": [ - "Since we *assumed* that scaling will occur *rarely*, we'd prefer that our new version of `average_evens()` be called with a *keyword argument* whenever `scalar` is passed in explicitly. Then, the second argument is never ambiguous as we could always read its name.\n", + "Since we *assumed* that scaling occurs *rarely*, we'd prefer that our new version of `average_evens()` be called with a *keyword argument* whenever `scalar` is to be passed in explicitly. Then, the second argument is never ambiguous as we could always read its name.\n", "\n", - "Luckily, Python offers a **keyword-only** syntax, where all we need to do is to place the arguments for which we require *explicit* keyword use after an asterix `*`." + "Luckily, Python offers a **keyword-only** syntax, where all we need to do is to place the arguments for which we require *explicit* keyword use after an asterisk `*`." ] }, { @@ -1798,13 +1798,13 @@ "source": [ "The `def` statement is a statement because of its side effect of creating a *new* name that points to a *new* `function` object in memory.\n", "\n", - "We can thus think of it as doing *two* things at once (i.e., either both of them happen or none). First, a `function` object is created that contains the concrete $0$s and $1$s that resemble the instructions we put into the function's body. In the context of a function, these $0$s and $1$s are also called **[byte code](https://en.wikipedia.org/wiki/Bytecode)**. Then, a name is created pointing at the new `function` object.\n", + "We can thus think of it as doing *two* things at once (i.e., either both of them happen or none). First, a `function` object is created that contains the concrete $0$s and $1$s that resemble the instructions we put into the function's body. In the context of a function, these $0$s and $1$s are also called **[byte code](https://en.wikipedia.org/wiki/Bytecode)**. Then, a name pointing at the new `function` object is created.\n", "\n", - "Only this second aspect makes `def` a statement: Merely creating a new object in memory without making it accessible for later reference does *not* constitute a side effect. This is because the state the program is *not* changed. After all, if we cannot reference an object, how do we know that it is actually existing?\n", + "Only this second aspect makes `def` a statement: Merely creating a new object in memory without making it accessible for later reference does *not* constitute a side effect because the state the program is *not* changed. After all, if we cannot reference an object, how do we know it exists in the first place?\n", "\n", "Python provides a so-called **[lambda expression](https://docs.python.org/3/reference/expressions.html#lambda)** syntax that allows us to *only* create a `function` object in memory *without* making a name point to it.\n", "\n", - "It starts with the keyword `lambda` followed by an optional comma seperated enumeration of parameters, a mandatory colon, and *one* expression that also is the resulting `function` object's return value.\n", + "It starts with the keyword `lambda` followed by an optional comma separated enumeration of parameters, a mandatory colon, and *one* expression that also is the resulting `function` object's return value.\n", "\n", "Because it does not create a name pointing to the object, we effectively create \"anonymous\" functions with it. In the example, we create a `function` object that adds `3` to the only argument passed in as the parameter `x` and returns that sum." ] @@ -1845,7 +1845,7 @@ "\n", "We created a `function` object, dit *not* call it, and Python immediately forgot about it. So what's the point?\n", "\n", - "Just to prove that the `lambda` expression really creates a callable `function` object, we use the simple `=` statement to assign it to the variable `add_three`, which is really `add_three()` as per our convention from above." + "To prove that a `lambda` expression creates a callable `function` object, we use the simple `=` statement to assign it to the variable `add_three`, which is really `add_three()` as per our convention from above." ] }, { @@ -1869,7 +1869,7 @@ } }, "source": [ - "Now we call `add_three()` as if we defined it with the `def` statement to begin with." + "Now we call `add_three()` as if we defined it with the `def` statement in the first place." ] }, { @@ -1904,7 +1904,7 @@ } }, "source": [ - "Alternatively, we could call any anonymous `function` object created with an `lambda` expression right away (i.e. without assigning it to a variable), which looks really weird for now as we need *two* pairs of parentheses: The first is just a delimiter whereas the second the call operator." + "Alternatively, we could call an anonymous `function` object created with a `lambda` expression right away (i.e., without assigning it to a variable), which looks quite weird for now as we need *two* pairs of parentheses: The first is just a delimiter whereas the second the call operator." ] }, { @@ -1939,9 +1939,9 @@ } }, "source": [ - "The main point of having functions without a name is to use them in a situation where we know ahead of time that we will use the function only once.\n", + "The main point of having functions without a name is to use them in a situation where we know ahead of time that we use the function *once* only.\n", "\n", - "Very popular contexts where we will apply lambda expressions are with the **map-filter-reduce** paradigm in Chapter 7 or when we do \"number crunching\" with **arrays** and **data frames** in Chapter 9." + "Popular applications of lambda expressions are with the **map-filter-reduce** paradigm in Chapter 7 or when we do \"number crunching\" with **arrays** and **data frames** in Chapter 9." ] }, { @@ -1963,13 +1963,13 @@ } }, "source": [ - "So far, we have only used what we refer to as **core** Python in this book. By this, we mean all the syntactical rules as specified in the [language reference](https://docs.python.org/3/reference/) and a minimal set of about 50 built-in [functions](https://docs.python.org/3/library/functions.html). With this we could already implement any algorithm or business logic we can think of!\n", + "So far, we have only used what we refer to as **core** Python in this book. By this, we mean all the syntactical rules as specified in the [language reference](https://docs.python.org/3/reference/) and a minimal set of about 50 built-in [functions](https://docs.python.org/3/library/functions.html). With this, we could already implement any algorithm or business logic we can think of!\n", "\n", "However, after our first couple of programs, we would already start seeing recurring patterns in the code we write. In other words, we would constantly be \"re-inventing the wheel\" in each new project.\n", "\n", "Would it not be smarter to pull out the re-usable components from our programs and put them into some project independent **library** of generically useful functionalities? Then we would only need a way of including these **utilities** in our projects.\n", "\n", - "As all programmers across all languages face this very same issue, most programming languages come with a so-called **[standard library](https://en.wikipedia.org/wiki/Standard_library)** that provides utilities to accomplish common tasks without a lot of code. Examples are making an HTTP request to some website, open and read popular file types (e.g., CSV or Excel files), do something on a computer's file system, and many more." + "As all programmers across all languages face this very same issue, most programming languages come with a so-called **[standard library](https://en.wikipedia.org/wiki/Standard_library)** that provides utilities to accomplish everyday tasks without much code. Examples are making an HTTP request to some website, open and read popular file types (e.g., CSV or Excel files), do something on a computer's file system, and many more." ] }, { @@ -1991,13 +1991,13 @@ } }, "source": [ - "Python also comes with its own [standard library](https://docs.python.org/3/library/index.html) that is structured into coherent modules and packages for given topics: A **module** is just a plain text file with the file extension *.py* that contains Python code while a **package** is a folder that groups several related modules.\n", + "Python also comes with a [standard library](https://docs.python.org/3/library/index.html) that is structured into coherent modules and packages for given topics: A **module** is just a plain text file with the file extension *.py* that contains Python code while a **package** is a folder that groups several related modules.\n", "\n", - "The code in the [standard library](https://docs.python.org/3/library/index.html) is contributed and maintained by many volunteers around the world. In contrast to so-called \"third-party\" packages (cf., the next section below), the Python core development team closely monitors and tests the code in the [standard library](https://docs.python.org/3/library/index.html). Consequently, we can be reasonably sure that anything provided by it works correctly independent of our computer's operating system and will most likely also be there in the next Python versions. Parts in the [standard library](https://docs.python.org/3/library/index.html) that are computationally expensive are often re-written in C and therefore much faster than anything we could code in Python ourselves. So, whenever we can solve a problem with the help of the [standard library](https://docs.python.org/3/library/index.html), it is almost always the best way to do so as well.\n", + "The code in the [standard library](https://docs.python.org/3/library/index.html) is contributed and maintained by many volunteers around the world. In contrast to so-called \"third-party\" packages (cf., the next section below), the Python core development team closely monitors and tests the code in the [standard library](https://docs.python.org/3/library/index.html). Consequently, we can be reasonably sure that anything provided by it works correctly independent of our computer's operating system and will most likely also be there in the next Python versions. Parts in the [standard library](https://docs.python.org/3/library/index.html) that are computationally expensive are often re-written in C and, therefore, much faster than anything we could write in Python ourselves. So, whenever we can solve a problem with the help of the [standard library](https://docs.python.org/3/library/index.html), it is almost always the best way to do so as well.\n", "\n", - "The [standard library](https://docs.python.org/3/library/index.html) has grown very big over the years and we refer to the website [PYMOTW](https://pymotw.com/3/index.html) (i.e., \"Python Module of the Week\") that features well written introductory tutorials and how-to guides to most parts of the library. The same author also published a [book](https://www.amazon.com/Python-Standard-Library-Example-Developers/dp/0134291050/ref=as_li_ss_tl?ie=UTF8&qid=1493563121&sr=8-1&keywords=python+3+standard+library+by+example) that many Pythonistas keep on their shelf for reference. Knowing what is in the [standard library](https://docs.python.org/3/library/index.html) is quite valuable for solving real world tasks quickly.\n", + "The [standard library](https://docs.python.org/3/library/index.html) has grown very big over the years, and we refer to the website [PYMOTW](https://pymotw.com/3/index.html) (i.e., \"Python Module of the Week\") that features well written introductory tutorials and how-to guides to most parts of the library. The same author also published a [book](https://www.amazon.com/Python-Standard-Library-Example-Developers/dp/0134291050/ref=as_li_ss_tl?ie=UTF8&qid=1493563121&sr=8-1&keywords=python+3+standard+library+by+example) that many Pythonistas keep on their shelf for reference. Knowing what is in the [standard library](https://docs.python.org/3/library/index.html) is quite valuable for solving real-world tasks quickly.\n", "\n", - "Throughout this book we will look at many modules and packages from the [standard library](https://docs.python.org/3/library/index.html) in more depth, starting with the [math](https://docs.python.org/3/library/math.html) and [random](https://docs.python.org/3/library/random.html) modules in this chapter." + "Throughout this book, we look at many modules and packages from the [standard library](https://docs.python.org/3/library/index.html) in more depth, starting with the [math](https://docs.python.org/3/library/math.html) and [random](https://docs.python.org/3/library/random.html) modules in this chapter." ] }, { @@ -2084,7 +2084,7 @@ { "data": { "text/plain": [ - "139925472068136" + "140519828111832" ] }, "execution_count": 58, @@ -2132,7 +2132,7 @@ "\n", "Let's see what we can do with the `math` module.\n", "\n", - "The [dir()](https://docs.python.org/3/library/functions.html#dir) built-in function may also be used with an argument passed in. Ignoring the dunder-style names, `math` offers quite a lot of ... names. As we cannot know at this point in time if a listed name refers to a function or an ordinary variable, we use the more generic term **attribute** to mean either one of them." + "The [dir()](https://docs.python.org/3/library/functions.html#dir) built-in function may also be used with an argument passed in. Ignoring the dunder-style names, `math` offers quite a lot of names. As we cannot know at this point if a listed name refers to a function or an ordinary variable, we use the more generic term **attribute** to mean either one of them." ] }, { @@ -2356,7 +2356,7 @@ "source": [ "Observe how the arguments passed to functions do not need to be just variables or simple literals. Instead, we may pass in any *expression* that evaluates to a *new* object of the type the function expects.\n", "\n", - "So just as a reminder from the expression vs. statement discussion in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb): An expression is *any* syntactically correct combination of variables and literals with operators. And the call operator `()` is just ... well another operator. So both of the next two code cells are just expressions! They have no permanent side effect in memory. We may execute them as often as we want *without* changing the state of the program (i.e., this Jupyter notebook).\n", + "So just as a reminder from the expression vs. statement discussion in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb): An expression is *any* syntactically correct combination of variables and literals with operators. And the call operator `()` is yet another operator. So both of the next two code cells are just expressions! They have no permanent side effects in memory. We may execute them as often as we want *without* changing the state of the program (i.e., this Jupyter notebook).\n", "\n", "So, regarding the very next cell in particular: Although the `2 ** 2` creates a *new* object `4` in memory that is then immediately passed into the [math.sqrt()](https://docs.python.org/3/library/math.html#math.sqrt) function, once that function call returns, \"all is lost\" and the newly created `4` object is forgotten again, as well as the return value of [math.sqrt()](https://docs.python.org/3/library/math.html#math.sqrt)." ] @@ -2489,7 +2489,7 @@ } }, "source": [ - "Often times, we need a random variable, for example, when we want to build a simulation program. The [random](https://docs.python.org/3/library/random.html) module in the [standard library](https://docs.python.org/3/library/index.html) often suffices for that." + "Often, we need a random variable, for example, when we want to build a simulation program. The [random](https://docs.python.org/3/library/random.html) module in the [standard library](https://docs.python.org/3/library/index.html) often suffices for that." ] }, { @@ -2537,7 +2537,7 @@ } }, "source": [ - "Besides the usual dunder-style attributes, the [dir()](https://docs.python.org/3/library/functions.html#dir) built-in function lists some attributes in an upper case naming convention and many others starting with a single underscore `_`. To understand the former, we have to wait until Chapter 10 while the latter are explained further below." + "Besides the usual dunder-style attributes, the built-in [dir()](https://docs.python.org/3/library/functions.html#dir) function lists some attributes in an upper case naming convention and many others starting with a *single* underscore `_`. To understand the former, we must wait until Chapter 10, while the latter is explained further below." ] }, { @@ -2697,7 +2697,7 @@ { "data": { "text/plain": [ - "0.270353921677863" + "0.5291407120147841" ] }, "execution_count": 75, @@ -2717,7 +2717,7 @@ } }, "source": [ - "While we could build some conditional logic with an `if` statement to map the number generated by [random.random()](https://docs.python.org/3/library/random.html#random.random) to a finite set of elements manually, the [random.choice()](https://docs.python.org/3/library/random.html#random.choice) function provides a lot more **convenience** for us. We simply call it with, for example, the `nums` list and it draws one element out of it with equal chance." + "While we could build some conditional logic with an `if` statement to map the number generated by [random.random()](https://docs.python.org/3/library/random.html#random.random) to a finite set of elements manually, the [random.choice()](https://docs.python.org/3/library/random.html#random.choice) function provides a lot more **convenience** for us. We call it with, for example, the `nums` list and it draws one element out of it with equal chance." ] }, { @@ -2732,7 +2732,7 @@ { "data": { "text/plain": [ - ">" + ">" ] }, "execution_count": 76, @@ -2781,7 +2781,7 @@ { "data": { "text/plain": [ - "7" + "4" ] }, "execution_count": 78, @@ -2801,7 +2801,7 @@ } }, "source": [ - "In order to re-produce the same random numbers in a simulation each time we run it, we set the **[random seed](https://en.wikipedia.org/wiki/Random_seed)**. It is good practice to do this at the beginning of a program or notebook. Then every time we re-start the program, we will get the exact same random numbers again. This becomes very important, for example, when we employ certain machine learning algorithms that rely on randomization, like the infamous [Random Forest](https://en.wikipedia.org/wiki/Random_forest), and want to obtain **re-producable** results.\n", + "To reproduce the same random numbers in a simulation each time we run it, we set the **[random seed](https://en.wikipedia.org/wiki/Random_seed)**. It is good practice to do this at the beginning of a program or notebook. Then every time we re-start the program, we get the *same* random numbers again. This becomes very important, for example, when we employ machine learning algorithms that rely on randomization, like the infamous [Random Forest](https://en.wikipedia.org/wiki/Random_forest), and want to obtain **reproducable** results.\n", "\n", "The [random](https://docs.python.org/3/library/random.html) module provides the [random.seed()](https://docs.python.org/3/library/random.html#random.seed) function to do that." ] @@ -2899,11 +2899,11 @@ } }, "source": [ - "As the Python community is based around open source, many developers publish their code, for example, on the Python Package Index [PyPI](https://pypi.org) from where anyone may download and install it for free using command line based tools like [pip](https://pip.pypa.io/en/stable/) or [conda](https://conda.io/en/latest/). This way, we can always customize our Python installation even more. Managing many such packages is actually quite a deep topic on its own, sometimes fearfully called **[dependency hell](https://en.wikipedia.org/wiki/Dependency_hell)**.\n", + "As the Python community is based around open source, many developers publish their code, for example, on the Python Package Index [PyPI](https://pypi.org) from where anyone may download and install it for free using command-line based tools like [pip](https://pip.pypa.io/en/stable/) or [conda](https://conda.io/en/latest/). This way, we can always customize our Python installation even more. Managing many such packages is quite a deep topic on its own, sometimes fearfully called **[dependency hell](https://en.wikipedia.org/wiki/Dependency_hell)**.\n", "\n", - "The difference between the [standard library](https://docs.python.org/3/library/index.html) and such **third-party** packages is that in the first case the code goes through a much more formalized review process and is officially endorsed by the Python core developers. Yet, many third-party projects also offer the highest quality standards and a lot of such software is actually also relied on by many businesses and researchers.\n", + "The difference between the [standard library](https://docs.python.org/3/library/index.html) and such **third-party** packages is that in the first case, the code goes through a much more formalized review process and is officially endorsed by the Python core developers. Yet, many third-party projects also offer the highest quality standards and are also relied on by many businesses and researchers.\n", "\n", - "Throughout this book, we will look at many third-party libraries, mostly from Python's [scientific stack](https://scipy.org/about.html), a tightly coupled set of third-party libraries for storing **big data** efficiently ([numpy](http://www.numpy.org/)), \"wrangling\" ([pandas](https://pandas.pydata.org/)) and visualizing them ([matplotlib](https://matplotlib.org/) and [seaborn](https://seaborn.pydata.org/)), fitting classical statistical models ([statsmodels](http://www.statsmodels.org/)), training machine learning models ([sklearn](http://scikit-learn.org/)), and much more.\n", + "Throughout this book, we will look at many third-party libraries, mostly from Python's [scientific stack](https://scipy.org/about.html), a tightly coupled set of third-party libraries for storing **big data** efficiently (e.g., [numpy](http://www.numpy.org/)), \"wrangling\" (e.g., [pandas](https://pandas.pydata.org/)) and visualizing them (e.g., [matplotlib](https://matplotlib.org/) or [seaborn](https://seaborn.pydata.org/)), fitting classical statistical models (e.g., [statsmodels](http://www.statsmodels.org/)), training machine learning models (e.g., [sklearn](http://scikit-learn.org/)), and much more.\n", "\n", "Below, we briefly show how to install third-party libraries." ] @@ -2927,11 +2927,11 @@ } }, "source": [ - "[numpy](http://www.numpy.org/) is the de-facto standard in the Python world for handling **array-like** data. That is a fancy word for data that can be put into a matrix or vector format. We will look at it in depth in Chapter 9.\n", + "[numpy](http://www.numpy.org/) is the de-facto standard in the Python world for handling **array-like** data. That is a fancy word for data that can be put into a matrix or vector format. We look at it in depth in Chapter 9.\n", "\n", - "As [numpy](http://www.numpy.org/) is *not* in the [standard library](https://docs.python.org/3/library/index.html), it must be *manually* installed, for example, with the [pip](https://pip.pypa.io/en/stable/) tool. As mentioned in [Chapter 0](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb), to execute terminal commands from within a Jupyter notebook, we just need to start a code cell with an exclamation mark.\n", + "As [numpy](http://www.numpy.org/) is *not* in the [standard library](https://docs.python.org/3/library/index.html), it must be *manually* installed, for example, with the [pip](https://pip.pypa.io/en/stable/) tool. As mentioned in [Chapter 0](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb), to execute terminal commands from within a Jupyter notebook, we start a code cell with an exclamation mark.\n", "\n", - "If you are running this notebook with an installation of the [Anaconda Distribution](https://www.anaconda.com/distribution/), then [numpy](http://www.numpy.org/) is probably already installed. Running the cell below, will just confirm that." + "If you are running this notebook with an installation of the [Anaconda Distribution](https://www.anaconda.com/distribution/), then [numpy](http://www.numpy.org/) is probably already installed. Running the cell below confirms that." ] }, { @@ -2963,7 +2963,7 @@ } }, "source": [ - "[numpy](http://www.numpy.org/) is conventionally imported with the shorter **idiomatic** name `np`. The `as` in the import statement just changes the resulting variable name. It is a shortcut for the three lines `import numpy`, `np = numpy`, and `del numpy`." + "[numpy](http://www.numpy.org/) is conventionally imported with the shorter **idiomatic** name `np`. The `as` in the import statement changes the resulting variable name. It is a shortcut for the three lines `import numpy`, `np = numpy`, and `del numpy`." ] }, { @@ -3096,7 +3096,7 @@ "source": [ "[numpy](http://www.numpy.org/) somehow magically adds new behavior to Python's built-in arithmetic operators. For example, we may now [scalar-multiply](https://en.wikipedia.org/wiki/Scalar_multiplication) `vec`.\n", "\n", - "[numpy](http://www.numpy.org/)'s functions are implemented in highly optimized C code and therefore fast, especially when it comes to big data." + "[numpy](http://www.numpy.org/)'s functions are implemented in highly optimized C code and, therefore, are fast, especially when dealing with bigger amounts of data." ] }, { @@ -3236,13 +3236,13 @@ } }, "source": [ - "For sure, we can create our own modules and packages. In the repository's main directory, there is a [*sample_module.py*](https://github.com/webartifex/intro-to-python/blob/master/sample_module.py) file that contains, among others, a function equivalent to the final version of `average_evens()`. To be realistic, this sample module is structured in a modular manner with several functions building on each other. It is best to skim over it *now* before reading on.\n", + "For sure, we can create local modules and packages. In the repository's main directory, there is a [*sample_module.py*](https://github.com/webartifex/intro-to-python/blob/master/sample_module.py) file that contains, among others, a function equivalent to the final version of `average_evens()`. To be realistic, this sample module is structured in a modular manner with several functions building on each other. It is best to skim over it *now* before reading on.\n", "\n", "To make code we put into a *.py* file available in our program, we import it as a module just as we did above with modules in the [standard library](https://docs.python.org/3/library/index.html) or third-party packages.\n", "\n", - "The *name* to be imported is the file's name except for the *.py* part. In order for this to work, the file's name *must* adhere to the *same* rules as hold for [variable names](https://docs.python.org/3/reference/lexical_analysis.html#identifiers) in general.\n", + "The *name* to be imported is the file's name except for the *.py* part. For this to work, the file's name *must* adhere to the *same* rules as hold for [variable names](https://docs.python.org/3/reference/lexical_analysis.html#identifiers) in general.\n", "\n", - "What happens during an import is conceptually as follows. When Python sees the `import sample_module` part, it first creates a *new* object of type `module` in memory. This is effectively an *empty* namespace. Then, it executes the imported file's code from top to bottom. Whatever variables are still defined at the end of this, are put into the module's namespace. Only if the file's code does *not* raise an error, will Python make a variable in our current location (i.e., `mod` here) point to the created `module` object. Otherwise, it is discarded. In essence, it is as if we copied and pasted the file's code in place of the import statement. If we import an already imported module again, Python is smart enough to avoid doing all this work all over and does nothing." + "What happens during an import is as follows. When Python sees the `import sample_module` part, it first creates a *new* object of type `module` in memory. This is effectively an *empty* namespace. Then, it executes the imported file's code from top to bottom. Whatever variables are still defined at the end of this, are put into the module's namespace. Only if the file's code does *not* raise an error, will Python make a variable in our current location (i.e., `mod` here) point to the created `module` object. Otherwise, it is discarded. In essence, it is as if we copied and pasted the file's code in place of the import statement. If we import an already imported module again, Python is smart enough to avoid doing all this work all over and does nothing." ] }, { @@ -3292,7 +3292,7 @@ "source": [ "Disregarding the dunder-style attributes, `mod` defines the five attributes `_default_scalar`, `_scaled_average`, `average`, `average_evens`, and `average_odds`, which are exactly the ones we would expect from reading the [*sample_module.py*](https://github.com/webartifex/intro-to-python/blob/master/sample_module.py) file.\n", "\n", - "An important convention when working with imported code is to *disregard* any attributes starting with an underscore `_`. These are considered **private** and constitute **implementation details** the author of the imported code might change in a future version of his software. We *must* not rely on them in any way.\n", + "A convention when working with imported code is to *disregard* any attributes starting with an underscore `_`. These are considered **private** and constitute **implementation details** the author of the imported code might change in a future version of his software. We *must* not rely on them in any way.\n", "\n", "In contrast, the three remaining **public** attributes are the functions `average()`, `average_evens()`, and `average_odds()` that we may use after the import." ] @@ -3433,9 +3433,9 @@ } }, "source": [ - "Packages are a generalization of modules and we will look at one in Chapter 10 in detail. You may, however, already look at a [sample package](https://github.com/webartifex/intro-to-python/tree/master/sample_package) in the repository, which is nothing but a folder with *.py* files in it.\n", + "Packages are a generalization of modules, and we look at one in detail in Chapter 10. You may, however, already look at a [sample package](https://github.com/webartifex/intro-to-python/tree/master/sample_package) in the repository, which is nothing but a folder with *.py* files in it.\n", "\n", - "As a further references on modules, we refer to the [official tutorial](https://docs.python.org/3/tutorial/modules.html)." + "As a further reading on modules and packages, we refer to the [official tutorial](https://docs.python.org/3/tutorial/modules.html)." ] }, { @@ -3461,7 +3461,7 @@ "\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", + "- 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 codebase), and\n", "- eliminate redundancies by allowing **re-use of code**.\n", "\n", "Functions are **defined** once with the `def` statement. Then, they may be **called** many times with the call operator `()`.\n", diff --git a/02_functions_review_and_exercises.ipynb b/02_functions_review_and_exercises.ipynb index dcd0a9c..d0935c6 100644 --- a/02_functions_review_and_exercises.ipynb +++ b/02_functions_review_and_exercises.ipynb @@ -324,9 +324,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q11.5**: Using the [range()](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 seperate line.\n", + "**Q11.5**: Using the [range()](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 actually need to use the [print()](https://docs.python.org/3/library/functions.html#print) built-in function." + "Note: This is the first task where you need to use the built-in [print()](https://docs.python.org/3/library/functions.html#print) function." ] }, { @@ -349,7 +349,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q11.6**: What important lesson did you learn about the `float` type?" + "**Q11.6**: What lesson did you learn about the `float` type?" ] }, { diff --git a/03_conditionals.ipynb b/03_conditionals.ipynb index f350397..d0ed61d 100644 --- a/03_conditionals.ipynb +++ b/03_conditionals.ipynb @@ -19,7 +19,7 @@ } }, "source": [ - "We analyzed every aspect of the `average_evens()` function in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) except for the `if` related parts. While it seems to intuitively do what we expect it to, there is a whole lot more to be learned from taking it apart. In particular, the `if` may occur within both a **statement** as in our introductory example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) but also an **expression** as in `average_evens()`. This is analogous as to how a noun in a natural language is *either* the subject of *or* an object in a sentence. What is common to both versions of the `if` is that it leads to code being executed for *parts* of the input only. It is our first way of **controlling** the **flow of execution** in a program.\n", + "We analyzed every aspect of the `average_evens()` function in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) except for the `if` related parts. While it seems to do what we expect it to, there is a whole lot more we learn from taking it apart. In particular, the `if` may occur within both a **statement** or an **expression**, analogous as to how a noun in a natural language is *either* the subject of *or* an object in a sentence. What is common to both usages is that it leads to code being executed for *parts* of the input only. It is our first way of **controlling** the **flow of execution** in a program.\n", "\n", "After deconstructing `if` in the first part of this chapter, we take a close look at a similar concept, namely handling **exceptions**." ] @@ -43,7 +43,7 @@ } }, "source": [ - "Any expression that is either true or not is called a **boolean expression**. If you think such expressions are boring or just not so useful, read a bit on [propositional logic](https://en.wikipedia.org/wiki/Propositional_calculus) and you will quickly realize how mathematicians and originally philosophers base their rules of how to prove or disprove a conclusion on simple true-or-false \"statements\" about the world. It is the underlying principle of all of reasoning.\n", + "Any expression that is either true or not is called a **boolean expression**. It is such simple true-or-false \"statements\" about the world on which mathematicians, and originally philosophers, base their rules of reasoning: They are studied formally in the field of [propositional logic](https://en.wikipedia.org/wiki/Propositional_calculus).\n", "\n", "A trivial example involves the equality operator `==` that evaluates to either `True` or `False` depending on its operands \"comparing equal\" or not." ] @@ -104,7 +104,7 @@ } }, "source": [ - "The `==` operator handles objects of *different* type. This shows how it implements a notion of equality in line with how we humans think of things being equal or not. After all, `42` and `42.0` are totally different $0$s and $1$s for a computer and many programming languages would actually say `False` here! Technically, this is yet another example of operator overloading." + "The `==` operator handles objects of *different* types: It implements a notion of equality in line with how humans think of things being equal or not. After all, `42` and `42.0` are different $0$s and $1$s for a computer and other programming languages might say `False` here! Technically, this is yet another example of operator overloading." ] }, { @@ -139,7 +139,7 @@ } }, "source": [ - "There are, however, cases where even well-behaved Python does not make us happy. [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb) will provide more insights on this \"bug\"." + "There are, however, cases where even well-behaved Python does not make us happy. [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb) provides more insights into this \"bug.\"" ] }, { @@ -174,7 +174,7 @@ } }, "source": [ - "`True` and `False` are special built-in *objects* of type `bool`." + "`True` and `False` are built-in *objects* of type `bool`." ] }, { @@ -189,7 +189,7 @@ { "data": { "text/plain": [ - "94697002906592" + "94906834637792" ] }, "execution_count": 5, @@ -213,7 +213,7 @@ { "data": { "text/plain": [ - "94697002906560" + "94906834637760" ] }, "execution_count": 6, @@ -281,11 +281,11 @@ } }, "source": [ - "Let's not confuse the boolean `False` with `None`, another special built-in object! We saw the latter before in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) as the *implicit* return value of a function without a `return` statement.\n", + "Let's not confuse the boolean `False` with `None`, another built-in object! We saw the latter before in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) as the *implicit* return value of a function without a `return` statement.\n", "\n", - "We might think of `None` in a boolean context indicating a \"maybe\" or even an \"unknown\" answer; however, for Python, there are no \"maybe\" or \"unknown\" objects as we will see further below!\n", + "We might think of `None` in a boolean context indicating a \"maybe\" or even an \"unknown\" answer; however, for Python, there are no \"maybe\" or \"unknown\" objects, as we see further below!\n", "\n", - "Whereas `False` is of type `bool`, `None` is of type `NoneType`. So, they are totally unrelated. On the contrary, as both `True` and `False` are of the same type, we could call them \"siblings\"." + "Whereas `False` is of type `bool`, `None` is of type `NoneType`. So, they are unrelated. On the contrary, as both `True` and `False` are of the same type, we could call them \"siblings.\"" ] }, { @@ -313,7 +313,7 @@ { "data": { "text/plain": [ - "94697002893552" + "94906834624752" ] }, "execution_count": 10, @@ -357,7 +357,7 @@ } }, "source": [ - "`True`, `False`, and `None` have the property that they each exist in memory only *once*. Objects designed this way are so-called **singletons**. This **[design pattern](https://en.wikipedia.org/wiki/Design_Patterns)** was originally developed to keep a program's memory usage at a minimum. It may only be employed in situations where we know that an object will *not* mutate its value (i.e., to re-use the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb), no flipping of $0$s and $1$s in the bag is allowed). In languages \"closer\" to the memory like C we would have to code this singleton logic ourselves but Python has this already built in for *some* types.\n", + "`True`, `False`, and `None` have the property that they each exist in memory only *once*. Objects designed this way are so-called **singletons**. This **[design pattern](https://en.wikipedia.org/wiki/Design_Patterns)** was originally developed to keep a program's memory usage at a minimum. It may only be employed in situations where we know that an object does *not* mutate its value (i.e., to re-use the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb), no flipping of $0$s and $1$s in the bag is allowed). In languages \"closer\" to the memory like C, we would have to code this singleton logic ourselves, but Python has this built in for *some* types.\n", "\n", "We verify this with either the `is` operator or by comparing memory addresses." ] @@ -523,7 +523,7 @@ } }, "source": [ - "The \"less than\" `<` or \"greater than\" `>` operators on their own mean \"strictly less than\" or \"strictly greater than\" but may be combined with the equality operator into just `<=` and `>=`. This is a shortcut for using the logical `or` operator as described in the next section." + "The \"less than\" `<` or \"greater than\" `>` operators mean \"strictly less than\" or \"strictly greater than\" but may be combined with the equality operator into just `<=` and `>=`. This is a shortcut for using the logical `or` operator as described in the next section." ] }, { @@ -643,7 +643,7 @@ "source": [ "Boolean expressions may be combined or negated with the **logical operators** `and`, `or`, and `not` to form new boolean expressions. Of course, this may be done *recursively* as well to obtain boolean expressions of arbitrary complexity.\n", "\n", - "Their usage is similar to how the equivalent words are used in plain English:\n", + "Their usage is similar to how the equivalent words are used in everyday English:\n", "\n", "- `and` evaluates to `True` if *both* sub-expressions evaluate to `True` and `False` otherwise,\n", "- `or` evaluates to `True` if either one *or* both sub-expressions evaluate to `True` and `False` otherwise, and\n", @@ -672,7 +672,7 @@ } }, "source": [ - "Relational operators have a **[higher precedence](https://docs.python.org/3/reference/expressions.html#operator-precedence)** over logical operators. So the following expression means what we intuitively think it does." + "Relational operators have **[higher precedence](https://docs.python.org/3/reference/expressions.html#operator-precedence)** over logical operators. So the following expression means what we intuitively think it does." ] }, { @@ -707,7 +707,7 @@ } }, "source": [ - "However, sometimes it is good to use *parentheses* around each sub-expression for clarity." + "However, sometimes, it is good to use *parentheses* around each sub-expression for clarity." ] }, { @@ -742,7 +742,7 @@ } }, "source": [ - "This is especially useful when several logical operators are combined." + "This is especially the case when several logical operators are combined." ] }, { @@ -825,9 +825,9 @@ } }, "source": [ - "For even better readability, [some practitioners](https://llewellynfalco.blogspot.com/2016/02/dont-use-greater-than-sign-in.html) suggest to *never* use the `>` and `>=` operators (note that the included example is written in [Java](https://en.wikipedia.org/wiki/Java_%28programming_language%29) and `&&` means `and` and `||` means `or`).\n", + "For even better readability, some practitioners suggest to *never* use the `>` and `>=` operators (cf., [source](https://llewellynfalco.blogspot.com/2016/02/dont-use-greater-than-sign-in.html); note that the included example is written in [Java](https://en.wikipedia.org/wiki/Java_%28programming_language%29) where `&&` means `and` and `||` means `or`).\n", "\n", - "Python allows **chaining** relational operators that are combined with the `and` operator. For example, the following two cells implement the same logic where the second is a lot easier to read." + "Python allows **chaining** relational operators that are combined with the `and` operator. For example, the following two cells implement the same logic, where the second is a lot easier to read." ] }, { @@ -897,9 +897,9 @@ } }, "source": [ - "The operands of the logical operators do not actually have to be *boolean* expressions as defined above but may be *any* kind of expression. If a sub-expression does *not* evaluate to an object of type `bool`, Python automatically casts the resulting object as such.\n", + "The operands of the logical operators do not need to be *boolean* expressions as defined above but may be *any* expression. If a sub-expression does *not* evaluate to an object of type `bool`, Python automatically casts it as such.\n", "\n", - "For example, any non-zero numeric object becomes `True`. While this behavior allows writing conciser and thus more \"beautiful\" code, it is also a common source of confusion." + "For example, any non-zero numeric object becomes `True`. While this behavior allows writing more concise and thus more \"beautiful\" code, it is also a common source of confusion. `(x - 9)` is cast as `True` and then the overall expression evaluates to `True` as well." ] }, { @@ -923,7 +923,7 @@ } ], "source": [ - "(x - 9) and (y < 100) # = 33 and (y < 100)" + "(x - 9) and (y < 100) # = 33 and (y < 100) = 33 and True" ] }, { @@ -934,7 +934,7 @@ } }, "source": [ - "Whenever we are unsure as to how Python will evaluate a non-boolean expression in a boolean context, the [bool()](https://docs.python.org/3/library/functions.html#bool) built-in allows us to check it ourselves." + "Whenever we are unsure as to how Python evaluates a non-boolean expression in a boolean context, the [bool()](https://docs.python.org/3/library/functions.html#bool) built-in allows us to check it ourselves." ] }, { @@ -1028,7 +1028,7 @@ } }, "source": [ - "In a boolean context, `None` is casted as `False`! So, `None` is really *not* a \"maybe\" answer but a \"no\"." + "In a boolean context, `None` is cast as `False`! So, `None` is *not* a \"maybe\" answer but a \"no.\"" ] }, { @@ -1063,7 +1063,7 @@ } }, "source": [ - "Another good rule to know is that container types (e.g., `list`) evaluate to `True` whenever they are not empty and `False` otherwise." + "Another good rule to know is that container types (e.g., `list`) evaluate to `True` whenever they are *not* empty and `False` if they are." ] }, { @@ -1125,6 +1125,388 @@ "Pythonistas often use the terms **truthy** or **falsy** to describe a non-boolean expression's behavior when used in place of a boolean one." ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Short-Circuiting" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When evaluating boolean expressions with logical operators in it, Python follows the **[short-circuiting](https://en.wikipedia.org/wiki/Short-circuit_evaluation)** strategy: First, the inner-most sub-expressions are evaluated. Second, with identical **[operator precedence](https://docs.python.org/3/reference/expressions.html#operator-precedence)**, evaluation goes from left to right. Once it is clear what the overall truth value is, no more sub-expressions are evaluated, and the result is *immediately* returned.\n", + "\n", + "In summary, data science practitioners must know *how* the following two generic expressions are evaluated:\n", + "\n", + "- `x or y`: The `y` expression is evaluated *only if* `x` evaluates to `False`, in which case `y` is returned; otherwise, `x` is returned *without* even looking at `y`.\n", + "- `x and y`: The `y` expression is evaluated *only if* `x` evaluates to `True`. Then, if `y` also evaluates to `True`, it is returned; otherwise, `x` is returned.\n", + "\n", + "Let's look at a couple of examples." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "x = 0\n", + "y = 1\n", + "z = 2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We define a helper function `expr()` that prints out the only argument it is passed before returning it. With `expr()`, we can see if a sub-expression is evaluated or not." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def expr(arg):\n", + " \"\"\"Print and return the only argument.\"\"\"\n", + " print(\"Arg:\", arg)\n", + " return arg" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With the `or` operator, the first sub-expression that evaluates to `True` is returned." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Arg: 0\n", + "Arg: 1\n" + ] + }, + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr(x) or expr(y)" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Arg: 1\n" + ] + }, + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr(y) or expr(z)" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Arg: 0\n", + "Arg: 1\n" + ] + }, + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr(x) or expr(y) or expr(z)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If all sub-expressions evaluate to `False`, the last one is the result." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Arg: False\n", + "Arg: []\n", + "Arg: 0\n" + ] + }, + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr(False) or expr([]) or expr(x)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With the `and` operator, the first sub-expression that evaluates to `False` is returned." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Arg: 0\n" + ] + }, + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr(x) and expr(y)" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Arg: 1\n", + "Arg: 0\n" + ] + }, + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr(y) and expr(x)" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Arg: 2\n", + "Arg: 1\n", + "Arg: 0\n" + ] + }, + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr(z) and expr(y) and expr(x)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If all sub-expressions evaluate to `True`, the last one is returned." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Arg: 1\n", + "Arg: 2\n" + ] + }, + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr(y) and expr(z)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The crucial takeaway is that Python does *not* necessarily evaluate *all* sub-expressions and, therefore, our code should never rely on that assumption." + ] + }, { "cell_type": "markdown", "metadata": { @@ -1144,9 +1526,9 @@ } }, "source": [ - "In order to write useful programs, we need to control the flow of execution, for example, to react to user input. The logic by which a program does that is referred to as **business logic**.\n", + "To write useful programs, we need to control the flow of execution, for example, to react to user input. The logic by which a program does that is referred to as **business logic**.\n", "\n", - "One major language construct to do so is the **[conditional statement](https://docs.python.org/3/reference/compound_stmts.html#the-if-statement)**, or `if` statement for short. It consists of:\n", + "One language construct to do so is the **[conditional statement](https://docs.python.org/3/reference/compound_stmts.html#the-if-statement)**, or `if` statement for short. It consists of:\n", "\n", "- *one* mandatory `if`-clause,\n", "- an *arbitrary* number of `elif`-clauses (i.e. \"else if\"), and\n", @@ -1154,14 +1536,14 @@ "\n", "The `if`- and `elif`-clauses each specify one *boolean* expression, also called **condition**, while the `else`-clause serves as the \"catch everything else\" case.\n", "\n", - "In terms of syntax, the header lines end with a colon and the code blocks are indented.\n", + "In terms of syntax, the header lines end with a colon, and the code blocks are indented.\n", "\n", "In contrast to our intuitive interpretation in natural languages, only the code in *one* of the alternatives, also called **branches**, is executed. To be precise, it is always the code in the first branch whose condition evaluates to `True`." ] }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 46, "metadata": { "code_folding": [], "slideshow": { @@ -1175,7 +1557,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 47, "metadata": { "code_folding": [], "slideshow": { @@ -1212,12 +1594,12 @@ "source": [ "In many situations, we only need a reduced form of the `if` statement.\n", "\n", - "We could **inject** code only at random to, for example, implement some sort of [A/B testing](https://en.wikipedia.org/wiki/A/B_testing)." + "We could **inject** code only at random, for example, to implement an [A/B testing](https://en.wikipedia.org/wiki/A/B_testing) strategy." ] }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 48, "metadata": { "slideshow": { "slide_type": "slide" @@ -1230,7 +1612,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 49, "metadata": { "slideshow": { "slide_type": "-" @@ -1239,7 +1621,7 @@ "outputs": [], "source": [ "if random.random() > 0.5:\n", - " print(\"You will read this just as often as you see heads when tossing a coin\")" + " print(\"You read this just as often as you see heads when tossing a coin\")" ] }, { @@ -1255,7 +1637,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 50, "metadata": { "slideshow": { "slide_type": "skip" @@ -1292,7 +1674,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 51, "metadata": { "slideshow": { "slide_type": "slide" @@ -1303,7 +1685,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "z is odd\n" + "z is positive\n" ] } ], @@ -1328,14 +1710,14 @@ } }, "source": [ - "A good way to make this code more readable is to introduce **temporary variables** *in combination* with the `and` operator to **flatten** the branching logic. The `if` statement then reads almost like plain English. In contrast to many other languages, creating variables is a computationally *cheap* operation in Python and also helps to document the code *inline* with meaningful variable names.\n", + "A way to make this code more readable is to introduce **temporary variables** *in combination* with the `and` operator to **flatten** the branching logic. The `if` statement then reads almost like plain English. In contrast to many other languages, creating variables is a computationally *cheap* operation in Python and also helps to document the code *inline* with meaningful variable names.\n", "\n", - "Flattening the logic *without* temporary variables could actually lead to more sub-expressions in the conditions be evaluated than necessary. Do you see why?" + "Flattening the logic *without* temporary variables could lead to *more* sub-expressions in the conditions be evaluated than necessary. Do you see why?" ] }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 52, "metadata": { "slideshow": { "slide_type": "slide" @@ -1384,7 +1766,7 @@ } }, "source": [ - "When all we do with an `if` statement is to assign an object to a variable with respect to a single true-or-false condition (i.e., a binary choice), there is a shortcut for that: We could simply assign the result of a so-called **conditional expression**, or `if` expression for short, to the variable.\n", + "When all we do with an `if` statement is to assign an object to a variable according to a single true-or-false condition (i.e., a binary choice), there is a shortcut: We assign the variable the result of a so-called **conditional expression**, or `if` expression for short, instead.\n", "\n", "Think of a situation where we evaluate a piece-wise functional relationship $y = f(x)$ at a given $x$, for example:" ] @@ -1408,7 +1790,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 53, "metadata": { "slideshow": { "slide_type": "slide" @@ -1432,7 +1814,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 54, "metadata": { "slideshow": { "slide_type": "-" @@ -1448,7 +1830,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 55, "metadata": { "slideshow": { "slide_type": "-" @@ -1461,7 +1843,7 @@ "9" ] }, - "execution_count": 45, + "execution_count": 55, "metadata": {}, "output_type": "execute_result" } @@ -1483,7 +1865,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 56, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1496,7 +1878,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 57, "metadata": { "slideshow": { "slide_type": "skip" @@ -1509,7 +1891,7 @@ "9" ] }, - "execution_count": 47, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" } @@ -1531,7 +1913,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 58, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1544,7 +1926,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 59, "metadata": { "slideshow": { "slide_type": "skip" @@ -1557,7 +1939,7 @@ "9" ] }, - "execution_count": 49, + "execution_count": 59, "metadata": {}, "output_type": "execute_result" } @@ -1574,7 +1956,7 @@ } }, "source": [ - "Conditional expressions may not only be used in the way described in this section. We already saw them as part of a list comprehension in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) and [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb)." + "Conditional expressions may not only be used in the way described in this section. We already saw them as part of a *list comprehension* in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) and [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) and revisit this in Chapter 7 in greater detail." ] }, { @@ -1596,16 +1978,16 @@ } }, "source": [ - "In the previous two chapters we already encountered a couple of *runtime* errors. A natural urge we might have after reading about conditional statements is to write code that somehow reacts to the occurence of such exceptions. All we need is a way to formulate a condition for that.\n", + "In the previous two chapters, we encountered a couple of *runtime* errors. A natural urge we might have after reading about conditional statements is to write code that somehow reacts to the occurrence of such exceptions. All we need is a way to formulate a condition for that.\n", "\n", - "For sure, this is such a common thing to do that Python provides its own language construct for it, namely the `try` [statement](https://docs.python.org/3/reference/compound_stmts.html#the-try-statement).\n", + "For sure, this is such a common thing to do that Python provides a language construct for it, namely the `try` [statement](https://docs.python.org/3/reference/compound_stmts.html#the-try-statement).\n", "\n", - "In its simplest form, it comes with just two branches: `try` and `except`. The following basically tells Python to execute the code in the `try`-branch and if *anything* goes wrong, continue in the `except`-branch instead of **raising** an error to us. Of course, if nothing goes wrong, the `except`-branch is *not* executed." + "In its simplest form, it comes with just two branches: `try` and `except`. The following tells Python to execute the code in the `try`-branch, and if *anything* goes wrong, continue in the `except`-branch instead of **raising** an error to us. Of course, if nothing goes wrong, the `except`-branch is *not* executed." ] }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 60, "metadata": { "slideshow": { "slide_type": "slide" @@ -1618,7 +2000,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 61, "metadata": { "slideshow": { "slide_type": "-" @@ -1648,16 +2030,16 @@ } }, "source": [ - "However, it is good practice to *not* **handle** *any* possible exception but only the ones we may expect from the code in the `try`-branch. The reasoning why this is done is a bit involved. We only remark here that the code base becomes easier to understand as we clearly communicate to any human reader what could go wrong during execution. Python comes with a lot of [built-in exceptions](https://docs.python.org/3/library/exceptions.html#concrete-exceptions) that we should familiarize ourselves with.\n", + "However, it is good practice *not* to **handle** *any* possible exception but only the ones we may *expect* from the code in the `try`-branch. The reasoning why this is done is a bit involved. We only remark that the codebase becomes easier to understand as we communicate to any human reader what could go wrong during execution in an *explicit* way. Python comes with a lot of [built-in exceptions](https://docs.python.org/3/library/exceptions.html#concrete-exceptions) that we should familiarize ourselves with.\n", "\n", - "Another good practice is to always keep the code in the `try`-branch short so as to not accidently handle an exception we do not want to handle.\n", + "Another good practice is to always keep the code in the `try`-branch short to not *accidentally* handle an exception we do *not* want to handle.\n", "\n", - "In the example, we are dividing numbers and may therefore expect a `ZeroDivisionError`." + "In the example, we are dividing numbers and may expect a `ZeroDivisionError`." ] }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 62, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1687,16 +2069,16 @@ } }, "source": [ - "Often, we may have to run some code *independent* of an exception occuring, for example, to close a connection to a database. To achieve that, we add a `finally`-branch to the `try` statement.\n", + "Often, we may have to run some code *independent* of an exception occurring, for example, to close a connection to a database. To achieve that, we add a `finally`-branch to the `try` statement.\n", "\n", - "Similarly, we may have to run some code *only if* no exception occurs but we do not want to put it in the `try`-branch as per the good practice mentioned above. To achieve that, we add an `else`-branch to the `try` statement.\n", + "Similarly, we may have to run some code *only if* no exception occurs, but we do not want to put it in the `try`-branch as per the good practice mentioned above. To achieve that, we add an `else`-branch to the `try` statement.\n", "\n", - "To showcase everything together, we look at one last example. To spice it up a bit, we randomize the input. So run the cell several times and see for yourself. It's actually quite easy." + "To showcase everything together, we look at one last example. To spice it up a bit, we randomize the input. So run the cell several times and see for yourself." ] }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 63, "metadata": { "slideshow": { "slide_type": "slide" @@ -1747,9 +2129,9 @@ "- **boolean expressions** evaluate to either `True` or `False`\n", "- **relational operators** compare operands according to \"human\" interpretations\n", "- **logical operators** combine boolean sub-expressions to more \"complex\" expressions\n", - "- the **conditional statement** is a *major* concept to **control** the **flow of execution** depending on some **conditions**\n", + "- the **conditional 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" + "- **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" ] } ], diff --git a/03_conditionals_review_and_exercises.ipynb b/03_conditionals_review_and_exercises.ipynb index d39c551..89d7009 100644 --- a/03_conditionals_review_and_exercises.ipynb +++ b/03_conditionals_review_and_exercises.ipynb @@ -19,7 +19,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Read [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb) of the book. Then work through the seven review questions." + "Read [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb) of the book. Then work through the eight review questions." ] }, { @@ -68,7 +68,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q3**: Explain how the conceptual difference between a **statement** and an **expression** relates to the difference between a **conditional statement** and a **conditional expression**." + "**Q3**: Describe in your own words the concept of **short-circuiting**! What does it imply for an individual sub-expression in a boolean expression?" ] }, { @@ -82,7 +82,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q4**: Why is the use of **temporary variables** encouraged in Python?" + "**Q4**: Explain how the conceptual difference between a **statement** and an **expression** relates to the difference between a **conditional statement** and a **conditional expression**." ] }, { @@ -96,7 +96,21 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q5**: What does the `finally`-branch enforce in this code snippet? How can a `try` statement be useful *without* an `except`-branch?" + "**Q5**: Why is the use of **temporary variables** encouraged in Python?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: What does the `finally`-branch enforce in this code snippet? How can a `try` statement be useful *without* an `except`-branch?" ] }, { @@ -136,7 +150,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q6**: The objects `True`, `False`, and `None` represent the idea of \"yes\", \"no\", and \"maybe\" answers in a natural language.\n", + "**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." ] @@ -152,7 +166,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q7**: The `try` statement is useful for handling **syntax** errors." + "**Q8**: The `try` statement is useful for handling **syntax** errors." ] }, { @@ -180,7 +194,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q8.1**: 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", + "**Q9.1**: 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", @@ -218,7 +232,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q8.2**: Calculate the final price for the following line items of an order:\n", + "**Q9.2**: 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", @@ -265,7 +279,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q8.3**: Re-calculate the last two line items with order quantities of $20$ and $15$. What do you observe?" + "**Q9.3**: Re-calculate the last two line items with order quantities of $20$ and $15$. What do you observe?" ] }, { @@ -297,7 +311,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q8.4**: Looking at the `if`-`else`-logic in the function, why do you think the four example line items in **Q8.2** were chosen as they were?" + "**Q9.4**: Looking at the `if`-`else`-logic in the function, why do you think the four example line items in **Q9.2** were chosen as they were?" ] }, { @@ -318,16 +332,16 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The kids game [Fizz Buzz](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 actually is ([source](https://news.ycombinator.com/item?id=16446774)).\n", + "The kids game [Fizz Buzz](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](https://news.ycombinator.com/item?id=16446774)).\n", "\n", - "In its simplest form, a group of people start 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." + "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": [ - "**Q9.1**: 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()](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()](https://docs.python.org/3/library/functions.html#func-range) a `list` object, you have to wrap it with the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in (i.e., `list(range(...))`)." + "**Q10.1**: 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()](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()](https://docs.python.org/3/library/functions.html#func-range) a `list` object, you have to wrap it with the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in (i.e., `list(range(...))`)." ] }, { @@ -343,11 +357,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q9.2**: 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", + "**Q10.2**: 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 we saw that Python starts indexing with `0` as the first element. Keep that in mind.\n", + "In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb), 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 checking the actual `number` for its divisors.\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!" ] @@ -372,7 +386,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q9.3**: Create a loop that prints out either the number or any of the Fizz Buzz substitutes. Do it in such a way that we do not end up with 100 lines of output here." + "**Q10.3**: Create a loop that prints out either the number or any of the Fizz Buzz substitutes. Do it in such a way that we do not end up with 100 lines of output here." ] }, { diff --git a/04_iteration.ipynb b/04_iteration.ipynb index 7ab75c3..b760a25 100644 --- a/04_iteration.ipynb +++ b/04_iteration.ipynb @@ -19,11 +19,11 @@ } }, "source": [ - "While controlling the flow of execution with an `if` statement is definitely a must-have building block in any programming language, it alone does not allow us to run a block of code repetitively and we need to be able to do exactly that in order to write useful software.\n", + "While controlling the flow of execution with an `if` statement is a must-have building block in any programming language, it alone does not allow us to run a block of code repetitively, and we need to be able to do precisely that to write useful software.\n", "\n", - "You might think that the `for` statement shown in some examples before is the missing piece in the puzzle. However, we can actually live without it and postpone its official introduction until the second half of this chapter.\n", + "The `for` statement shown in some examples before might be the missing piece in the puzzle. However, we can live without it and postpone its official introduction until the second half of this chapter.\n", "\n", - "Instead, we dive into the big idea of **iteration** by studying the concept of **recursion** first. This is quite the opposite of many introductory books on programming that only treat the latter as a nice-to-have artifact, if at all. Yet, understanding recursion sharpens one's mind and contributes to seeing problems from a different angle." + "Instead, we dive into the big idea of **iteration** by studying the concept of **recursion** first. This order is opposite to many other introductory books that only treat the latter as a nice-to-have artifact, if at all. Yet, understanding recursion sharpens one's mind and contributes to seeing problems from a different angle." ] }, { @@ -45,11 +45,11 @@ } }, "source": [ - "A function that calls itself is **recursive** and the process of executing such a function it is called **recursion**.\n", + "A function that calls itself is **recursive**, and the process of executing such a function is called **recursion**.\n", "\n", "Recursive functions contain some form of a conditional check (e.g., `if` statement) to identify a **base case** that ends the recursion *when* reached. Otherwise, the function would keep calling itself forever.\n", "\n", - "The meaning of the word *recursive* is similar to *circular*. However, a truly circular definition is not very helpful and we think of a recursive function as kind of a \"circular function with a way out at the end\"." + "The meaning of the word *recursive* is similar to *circular*. However, a truly circular definition is not very helpful, and we think of a recursive function as kind of a \"circular function with a way out at the end.\"" ] }, { @@ -71,7 +71,7 @@ } }, "source": [ - "A rather trivial toy example illustrates the important aspects concretely: If called with any positive integer as its `n` argument, `countdown()` just prints that number and calls itself with the *new* `n` being the *old* `n` minus `1`. This continues until `countdown()` is called with `n=0`. Then, the flow of execution hits the base case and the function calls stop." + "A rather trivial toy example illustrates the important aspects concretely: If called with any positive integer as its `n` argument, `countdown()` just prints that number and calls itself with the *new* `n` being the *old* `n` minus `1`. This continues until `countdown()` is called with `n=0`. Then, the flow of execution hits the base case, and the function calls stop." ] }, { @@ -92,7 +92,7 @@ " n (int): seconds until the party begins\n", " \"\"\"\n", " if n == 0: # base case\n", - " print(\"Happy new Year!\")\n", + " print(\"Happy New Year!\")\n", " else:\n", " print(n)\n", " countdown(n - 1)" @@ -114,7 +114,7 @@ "3\n", "2\n", "1\n", - "Happy new Year!\n" + "Happy New Year!\n" ] } ], @@ -130,7 +130,7 @@ } }, "source": [ - "As trivial as this seems at first sight, a lot of complexity is hidden in this implementation. In particular, the order in which objects are created and de-referenced in memory might not be obvious right away as [PythonTutor](http://pythontutor.com/visualize.html#code=def%20countdown%28n%29%3A%0A%20%20%20%20if%20n%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20print%28%22Happy%20new%20Year!%22%29%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20print%28n%29%0A%20%20%20%20%20%20%20%20countdown%28n%20-%201%29%0A%0Acountdown%283%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows: Each time `countdown()` is called, Python creates a *new* frame in the part of the memory where it manages all the names. This way Python *isolates* all the different `n` variables from each other. As new frames are created until we reach the base case after which the frames are destroyed in the *reversed* order, this is called a **[stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type))** of frames in computer science terminology. In simple words, a stack is a last-in-first-out (LIFO) task queue. Each frame has a single parent frame, namely the one whose recursive function call created it." + "As trivial as this seems, a lot of complexity is hidden in this implementation. In particular, the order in which objects are created and de-referenced in memory might not be apparent right away as [PythonTutor](http://pythontutor.com/visualize.html#code=def%20countdown%28n%29%3A%0A%20%20%20%20if%20n%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20print%28%22Happy%20new%20Year!%22%29%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20print%28n%29%0A%20%20%20%20%20%20%20%20countdown%28n%20-%201%29%0A%0Acountdown%283%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows: Each time `countdown()` is called, Python creates a *new* frame in the part of the memory where it manages all the names. This way, Python *isolates* all the different `n` variables from each other. As new frames are created until we reach the base case, after which the frames are destroyed in the *reversed* order, this is called a **[stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type))** of frames in computer science terminology. In simple words, a stack is a last-in-first-out (LIFO) task queue. Each frame has a single parent frame, namely the one whose recursive function call created it." ] }, { @@ -152,7 +152,7 @@ } }, "source": [ - "Recursion plays an important role in mathematics as well and we likely know about it from some introductory course, for example, in [combinatorics](https://en.wikipedia.org/wiki/Combinatorics)." + "Recursion plays a vital role in mathematics as well, and we likely know about it from some introductory course, for example, in [combinatorics](https://en.wikipedia.org/wiki/Combinatorics)." ] }, { @@ -174,7 +174,7 @@ } }, "source": [ - "The factorial function, denoted with the symbol $!$, is defined as follows for all non-negative integers:" + "The factorial function, denoted with the $!$ symbol, is defined as follows for all non-negative integers:" ] }, { @@ -199,7 +199,7 @@ "source": [ "Whenever we find a recursive way of formulating an idea, we can immediately translate it into Python in a *naive* way (i.e., we create a *correct* program that may *not* be an *efficient* implementation yet).\n", "\n", - "Below is a first version of `factorial()`: The `return` statement does not have to be a function's last code line and we may certainly have several `return` statements as well." + "Below is a first version of `factorial()`: The `return` statement does not have to be a function's last code line, and we may indeed have several `return` statements as well." ] }, { @@ -238,9 +238,9 @@ } }, "source": [ - "When we read such code, it is often easier not to follow every function call (i.e., `factorial(n - 1)` here) in one's mind but assume we receive a return value as specified in the documentation. Some call this approach **[leap of faith](http://greenteapress.com/thinkpython2/html/thinkpython2007.html#sec75)**. In fact, we practice this anyways whenever we call built-in functions (e.g., [print()](https://docs.python.org/3/library/functions.html#print) or [len()](https://docs.python.org/3/library/functions.html#len)) where we would have to read C code in many cases.\n", + "When we read such code, it is often easier not to follow every function call (i.e., `factorial(n - 1)` here) in one's mind but assume we receive a return value as specified in the documentation. Some call this approach a **[leap of faith](http://greenteapress.com/thinkpython2/html/thinkpython2007.html#sec75)**. We practice this already whenever we call built-in functions (e.g., [print()](https://docs.python.org/3/library/functions.html#print) or [len()](https://docs.python.org/3/library/functions.html#len)) where we would have to read C code in many cases.\n", "\n", - "To visualize *all* the computational steps of the examplary `factorial(3)`, we use [PythonTutor](http://pythontutor.com/visualize.html#code=def%20factorial%28n%29%3A%0A%20%20%20%20if%20n%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20recurse%20%3D%20factorial%28n%20-%201%29%0A%20%20%20%20%20%20%20%20result%20%3D%20n%20*%20recurse%0A%20%20%20%20%20%20%20%20return%20result%0A%0Asolution%20%3D%20factorial%283%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false): The recursion again creates a stack of frames in memory. In contrast to the previous trivial example, each frame leaves a return value in memory after it is destroyed. This return value is then assigned to the `recurse` variable within the parent frame and used to compute `result`." + "To visualize *all* the computational steps of the exemplary `factorial(3)`, we use [PythonTutor](http://pythontutor.com/visualize.html#code=def%20factorial%28n%29%3A%0A%20%20%20%20if%20n%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20recurse%20%3D%20factorial%28n%20-%201%29%0A%20%20%20%20%20%20%20%20result%20%3D%20n%20*%20recurse%0A%20%20%20%20%20%20%20%20return%20result%0A%0Asolution%20%3D%20factorial%283%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false): The recursion again creates a stack of frames in memory. In contrast to the previous trivial example, each frame leaves a return value in memory after it is destroyed. This return value is then assigned to the `recurse` variable within the parent frame and used to compute `result`." ] }, { @@ -299,7 +299,7 @@ } }, "source": [ - "A Pythonista would formulate `factorial()` in a conciser way using the so-called **early exit** pattern: No `else`-clause is needed as reaching a `return` statement immediately ends a function call. Furthermore, we do not really need the temporary variables `recurse` and `result`.\n", + "A Pythonista would formulate `factorial()` in a more concise way using the so-called **early exit** pattern: No `else`-clause is needed as reaching a `return` statement ends a function call *immediately*. Furthermore, we do not need the temporary variables `recurse` and `result`.\n", "\n", "As [PythonTutor](http://pythontutor.com/visualize.html#code=def%20factorial%28n%29%3A%0A%20%20%20%20if%20n%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20return%20n%20*%20factorial%28n%20-%201%29%0A%0Asolution%20%3D%20factorial%283%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows, this implementation is more efficient as it only requires 18 computational steps instead of 24 to calculate `factorial(3)`, an improvement of 25 percent! " ] @@ -384,7 +384,7 @@ } }, "source": [ - "Note that the [math](https://docs.python.org/3/library/math.html) module in the standard library provides a [factorial()](https://docs.python.org/3/library/math.html#math.factorial) function as well and we should therefore *never* implement it ourselves in a real code base." + "Note that the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html) provides a [factorial()](https://docs.python.org/3/library/math.html#math.factorial) function as well, and we should, therefore, *never* implement it ourselves in a real codebase." ] }, { @@ -554,7 +554,7 @@ } }, "source": [ - "Euclid's algorithm is stunningly fast, even for large numbers. Its speed comes from the use of the modulo operator `%`. However, this is *not* true for recusion in general, which may result in very slow programs if not applied correctly." + "Euclid's algorithm is stunningly fast, even for large numbers. Its speed comes from the use of the modulo operator `%`. However, this is *not* true for recursion in general, which may result in slow programs if not applied correctly." ] }, { @@ -624,7 +624,7 @@ } }, "source": [ - "The [math](https://docs.python.org/3/library/math.html) module in the standard library provides a [gcd()](https://docs.python.org/3/library/math.html#math.gcd) function as well and therefore we should again *never* implement it on our own." + "The [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html) provides a [gcd()](https://docs.python.org/3/library/math.html#math.gcd) function as well, and, therefore, we should again *never* implement it on our own." ] }, { @@ -765,7 +765,7 @@ } }, "source": [ - "Let's write a function `fibonacci()` that calculates the $i$th Fibonacci number where $0$ will be the $0$th number. Looking at the numbers in a **backwards** fashion (i.e., from right to left), we realize that the return value for `fibonacci(i)` can be reduced to the sum of the return values for `fibonacci(i - 1)` and `fibonacci(i - 2)` disregarding the *two* base cases." + "Let's write a function `fibonacci()` that calculates the $i$th Fibonacci number where $0$ is the $0$th number. Looking at the numbers in a **backward** fashion (i.e., from right to left), we realize that the return value for `fibonacci(i)` can be reduced to the sum of the return values for `fibonacci(i - 1)` and `fibonacci(i - 2)` disregarding the *two* base cases." ] }, { @@ -841,7 +841,7 @@ "\n", "To understand this in detail, we would have to study algorithms and data structures (e.g., with [this book](https://www.amazon.de/Introduction-Algorithms-Press-Thomas-Cormen/dp/0262033844/ref=sr_1_1?__mk_de_DE=%C3%85M%C3%85%C5%BD%C3%95%C3%91&crid=1JNE8U0VZGU0O&qid=1569837169&s=gateway&sprefix=algorithms+an%2Caps%2C180&sr=8-1)), a discipline within computer science, and dive into the analysis of **[time complexity of algorithms](https://en.wikipedia.org/wiki/Time_complexity)**.\n", "\n", - "Luckily, in the Fibonacci case the inefficiency can be resolved with a **caching** (i.e., \"re-use\") strategy from the field of **[dynamic programming](https://en.wikipedia.org/wiki/Dynamic_programming)**, namely **[memoization](https://en.wikipedia.org/wiki/Memoization)**. We will do so in Chapter 8 after introducing the [dictionaries](https://docs.python.org/3/library/stdtypes.html#dict) data type.\n", + "Luckily, in the Fibonacci case, the inefficiency can be resolved with a **caching** (i.e., \"re-use\") strategy from the field of **[dynamic programming](https://en.wikipedia.org/wiki/Dynamic_programming)**, namely **[memoization](https://en.wikipedia.org/wiki/Memoization)**. We do so in Chapter 8, after introducing the [dictionaries](https://docs.python.org/3/library/stdtypes.html#dict) data type.\n", "\n", "Let's measure the average run times for `fibonacci()` and varying `i` arguments with the `%%timeit` [cell magic](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-timeit) that comes with Jupyter." ] @@ -859,7 +859,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "47.3 µs ± 12.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + "39.9 µs ± 5.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], @@ -881,7 +881,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "1.63 ms ± 21.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + "1.61 ms ± 8.84 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], @@ -903,7 +903,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "211 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "199 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -925,7 +925,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "2.22 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "2.21 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -947,7 +947,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "5.81 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "5.8 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -975,7 +975,7 @@ } }, "source": [ - "If a recursion does not reach its base case, it will theoretically run forever. Luckily, Python detects that and saves the computer from crashing by raising a `RecursionError`.\n", + "If a recursion does not reach its base case, it theoretically runs forever. Luckily, Python detects that and saves the computer from crashing by raising a `RecursionError`.\n", "\n", "The simplest possible infinite recursion is generated like so." ] @@ -1031,7 +1031,7 @@ } }, "source": [ - "However, even the trivial `countdown()` function from above is not immune to an infinite recursion. Let's call it with `3.1` instead of `3`. What goes wrong here?" + "However, even the trivial `countdown()` function from above is not immune to infinite recursion. Let's call it with `3.1` instead of `3`. What goes wrong here?" ] }, { @@ -4011,9 +4011,9 @@ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mRecursionError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mcountdown\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m\u001b[0m in \u001b[0;36mcountdown\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 11\u001b[0;31m \u001b[0mcountdown\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mcountdown\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 11\u001b[0;31m \u001b[0mcountdown\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "... last 1 frames repeated, from the frame below ...\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36mcountdown\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 11\u001b[0;31m \u001b[0mcountdown\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mcountdown\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 11\u001b[0;31m \u001b[0mcountdown\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mRecursionError\u001b[0m: maximum recursion depth exceeded while calling a Python object" ] } @@ -4069,21 +4069,21 @@ } }, "source": [ - "The infinite recursions could easily be avoided by replacing `n == 0` with `n <= 0` in both functions and thereby **generalizing** them. But even then, calling either `countdown()` or `factorial()` with a non-integer number is *semantically* wrong and therefore we better leave the base cases unchanged.\n", + "The infinite recursions could easily be avoided by replacing `n == 0` with `n <= 0` in both functions and thereby **generalizing** them. But even then, calling either `countdown()` or `factorial()` with a non-integer number is *semantically* wrong, and, therefore, we better leave the base cases unchanged.\n", "\n", - "Errors as above are a symptom of missing **type checking**: By design, Python allows us to pass in not only integers but objects of any type as arguments to the `countdown()` and `factorial()` functions. As long as the arguments \"behave\" like integers, we will not encounter any *runtime* errors. This is the case here as the two example functions only use the `-` and `*` operators internally and, in the context of arithmetic, a `float` object behaves exactly like an `int` object. So, the functions keep calling themselves until Python decides with a built-in heuristic that the recursion is likely not going to end and aborts the computations with a `RecursionError`. Stricly speaking, a `RecursionError` is of course a *runtime* error as well.\n", + "Errors as above are a symptom of missing **type checking**: By design, Python allows us to pass in not only integers but objects of any type as arguments to the `countdown()` and `factorial()` functions. As long as the arguments \"behave\" like integers, we do not encounter any *runtime* errors. This is the case here as the two example functions only use the `-` and `*` operators internally, and, in the context of arithmetic, a `float` object behaves like an `int` object. So, the functions keep calling themselves until Python decides with a built-in heuristic that the recursion is likely not going to end and aborts the computations with a `RecursionError`. Strictly speaking, a `RecursionError` is, of course, a *runtime* error as well.\n", "\n", "The missing type checking is *100% intentional* and considered a **[feature of rather than a bug](https://www.urbandictionary.com/define.php?term=It%27s%20not%20a%20bug%2C%20it%27s%20a%20feature)** in Python!\n", "\n", - "Pythonistas often use the colloquial term **[duck typing](https://en.wikipedia.org/wiki/Duck_typing)** when refering to the same idea and the saying goes \"If it walks like a duck and it quacks like a duck, it must be a duck\". For example, we could call `factorial()` with the `float` object `3.0` and the recursion works out fine. So, as long as the `3.0` \"walks\" like a `3` and \"quacks\" like a `3`, it \"must be\" a `3`.\n", + "Pythonistas often use the colloquial term **[duck typing](https://en.wikipedia.org/wiki/Duck_typing)** when referring to the same idea, and the saying goes, \"If it walks like a duck and it quacks like a duck, it must be a duck.\" For example, we could call `factorial()` with the `float` object `3.0`, and the recursion works out fine. So, as long as the `3.0` \"walks\" like a `3` and \"quacks\" like a `3`, it \"must be\" a `3`.\n", "\n", - "We see a similar behavior when we mix objects of types `int` and `float` with arithmetic operators. For example, `1 + 2.0` works because Python implicitly views the `1` as a `1.0` at runtime and then knows how to do floating-point arithmetic: Here, the `int` \"walks\" and \"quacks\" like a `float`. Strictly speaking, this is yet another example of operator overloading whereas duck typing refers to the same behavior when passing arguments to function calls.\n", + "We see similar behavior when we mix objects of types `int` and `float` with arithmetic operators. For example, `1 + 2.0` works because Python implicitly views the `1` as a `1.0` at runtime and then knows how to do floating-point arithmetic: Here, the `int` \"walks\" and \"quacks\" like a `float`. Strictly speaking, this is yet another example of operator overloading, whereas duck typing refers to the same behavior when passing arguments to function calls.\n", "\n", - "The important lesson is that we must expect our functions be called with objects of *any* type at runtime, as opposed to the one type we had in mind when we defined the function.\n", + "The important lesson is that we must expect our functions to be called with objects of *any* type at runtime, as opposed to the one type we had in mind when we defined the function.\n", "\n", - "Duck typing is possible because Python is a dynamically typed language. On the contrary, in statically typed languages like C we *must* declare (i.e., \"specify\") the data type of every parameter in a function definition. Then, a `RecursionError` as for `countdown(3.1)` or `factorial(3.1)` above could not occur. For example, if we declared the `countdown()` and `factorial()` functions to only accept `int` objects, calling the functions with a `float` argument would immediately fail *syntactically*. As a downside, we would then lose the ability to call `factorial()` with `3.0`, which is *semantically* correct nevertheless.\n", + "Duck typing is possible because Python is a dynamically typed language. On the contrary, in statically typed languages like C, we *must* declare (i.e., \"specify\") the data type of every parameter in a function definition. Then, a `RecursionError` as for `countdown(3.1)` or `factorial(3.1)` above could not occur. For example, if we declared the `countdown()` and `factorial()` functions to only accept `int` objects, calling the functions with a `float` argument would immediately fail *syntactically*. As a downside, we would then lose the ability to call `factorial()` with `3.0`, which is *semantically* correct nevertheless.\n", "\n", - "So, there is no black or white answer as to which of the two language designs is better. Yet, most professional programmers have very strong opinions with respect to duck typing reaching from \"love\" to \"hate\". This is another example as to how programming is a subjective art rather than an \"objective\" science. Python's design is probably more appealing to beginners who intuitively regard `3` and `3.0` as interchangeable." + "So, there is no black or white answer as to which of the two language designs is better. Yet, most professional programmers have strong opinions concerning duck typing, reaching from \"love\" to \"hate.\" This is another example of how programming is a subjective art rather than \"objective\" science. Python's design is probably more appealing to beginners who intuitively regard `3` and `3.0` as interchangeable." ] }, { @@ -4109,11 +4109,11 @@ "\n", "Meanwhile, we also see how we manually raise exceptions with the `raise` [statement](https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement), another way of controlling the flow of execution.\n", "\n", - "The first two branches in the revised `factorial()` function act as **guardians** ensuring that the code does not produce *unexpected* runtime errors: Errors may certainly be expected when mentioned in the docstring.\n", + "The first two branches in the revised `factorial()` function act as **guardians** ensuring that the code does not produce *unexpected* runtime errors: Errors may be expected when mentioned in the docstring.\n", "\n", - "Forcing `n` to be an `int` is a very puritan way of handling the issues discussed above. A more relaxed approach could be to also accept a `float` and use its [is_integer()](https://docs.python.org/3/library/stdtypes.html#float.is_integer) method to check if `n` could be casted as an `int`. After all, being too puritan we cannot take advantage of duck typing.\n", + "Forcing `n` to be an `int` is a very puritan way of handling the issues discussed above. A more relaxed approach could be to also accept a `float` and use its [is_integer()](https://docs.python.org/3/library/stdtypes.html#float.is_integer) method to check if `n` could be cast as an `int`. After all, being too puritan, we cannot take advantage of duck typing.\n", "\n", - "So in essence, we are doing *two* things here: Besides checking the type, we also enforce **domain-specific** (i.e., mathematical here) rules with respect to the non-negativity of `n`." + "So, in essence, we are doing *two* things here: Besides checking the type, we also enforce **domain-specific** (i.e., mathematical here) rules concerning the non-negativity of `n`." ] }, { @@ -4215,7 +4215,7 @@ } }, "source": [ - "Instead of running into an infinite recursion, we now receive very specifc error messages." + "Instead of running into a situation of infinite recursion, we now receive specific error messages." ] }, { @@ -4289,7 +4289,7 @@ } }, "source": [ - "With everything *officially* introduced so far (i.e., without the introductary example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) that only served as an overview), Python would be what is called **[Turing complete](https://en.wikipedia.org/wiki/Turing_completeness)**. That means that anything that could be formulated as an algorithm could be expressed with all the language features we have seen. Note that, in particular, we have *not* yet formally *introduced* the `for` and `while` statements!" + "With everything *officially* introduced so far (i.e., without the introductory example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) that only served as an overview), Python would be what is called **[Turing complete](https://en.wikipedia.org/wiki/Turing_completeness)**. That means that anything that could be formulated as an algorithm could be expressed with all the language features we have seen. Note that, in particular, we have *not* yet formally *introduced* the `for` and `while` statements!" ] }, { @@ -4322,11 +4322,11 @@ } }, "source": [ - "Whereas functions combined with `if` statements suffice to model any type of iteration with a recursion, Python comes with a `while` [statement](https://docs.python.org/3/reference/compound_stmts.html#the-while-statement) that often makes it easier to implement iterative ideas.\n", + "Whereas functions combined with `if` statements suffice to model any repetitive logic, Python comes with a `while` [statement](https://docs.python.org/3/reference/compound_stmts.html#the-while-statement) that often makes it easier to implement iterative ideas.\n", "\n", - "It consists of a header line with a boolean expression followed by an indented code block. Before the first and after every execution of the code block, the boolean expression is evaluated and if it is (still) equal to `True`, the code block runs (again). Eventually, some variable referenced in the boolean expression is changed in the code block such that the condition becomes `False`.\n", + "It consists of a header line with a boolean expression followed by an indented code block. Before the first and after every execution of the code block, the boolean expression is evaluated, and if it is (still) equal to `True`, the code block runs (again). Eventually, some variable referenced in the boolean expression is changed in the code block such that the condition becomes `False`.\n", "\n", - "If the condition is `False` before the first iteration, the entire code block is *never* executed. As the flow of control keeps **looping** (or **iterating**) back to the beginning of the code block, this concept is also called a `while`-loop and each pass through the loop an **iteration**." + "If the condition is `False` before the first iteration, the entire code block is *never* executed. As the flow of control keeps \"**looping**\" (i.e., more formally, **iterating**) back to the beginning of the code block, this concept is also called a `while`-loop and each pass through the loop an **iteration**." ] }, { @@ -4348,7 +4348,7 @@ } }, "source": [ - "Let's rewrite the `countdown()` example in an iterative style. We also build in **input validation** by allowing the function to only be called with strictly positive integers. As any positive integer will hit $0$ at some point in time when iteratively decremented by $1$, `countdown()` is guaranteed to **terminate**. Note also that the base case is now handled at the end of the function, which commonly happens with iterative solutions to problems." + "Let's rewrite the `countdown()` example in an iterative style. We also build in **input validation** by allowing the function only to be called with strictly positive integers. As any positive integer hits $0$ at some point when iteratively decremented by $1$, `countdown()` is guaranteed to **terminate**. Also, the base case is now handled at the end of the function, which commonly happens with iterative solutions to problems." ] }, { @@ -4416,7 +4416,7 @@ } }, "source": [ - "As [PythonTutor](http://pythontutor.com/visualize.html#code=def%20countdown%28n%29%3A%0A%20%20%20%20if%20not%20isinstance%28n,%20int%29%3A%0A%20%20%20%20%20%20%20%20raise%20TypeError%28%22...%22%29%0A%20%20%20%20elif%20n%20%3C%3D%200%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError%28%22...%22%29%0A%0A%20%20%20%20while%20n%20!%3D%200%3A%0A%20%20%20%20%20%20%20%20print%28n%29%0A%20%20%20%20%20%20%20%20n%20-%3D%201%0A%0A%20%20%20%20print%28%22Happy%20new%20Year!%22%29%0A%0Acountdown%283%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows, there is a subtle but important difference in the way a `while` statement is treated in memory: In short, `while` statements can *not* run into a `RecursionError` as only *one* frame is needed to manage the names. After all, there is only *one* function call to be made. For common day-to-day applications this difference is, however, not important." + "As [PythonTutor](http://pythontutor.com/visualize.html#code=def%20countdown%28n%29%3A%0A%20%20%20%20if%20not%20isinstance%28n,%20int%29%3A%0A%20%20%20%20%20%20%20%20raise%20TypeError%28%22...%22%29%0A%20%20%20%20elif%20n%20%3C%3D%200%3A%0A%20%20%20%20%20%20%20%20raise%20ValueError%28%22...%22%29%0A%0A%20%20%20%20while%20n%20!%3D%200%3A%0A%20%20%20%20%20%20%20%20print%28n%29%0A%20%20%20%20%20%20%20%20n%20-%3D%201%0A%0A%20%20%20%20print%28%22Happy%20new%20Year!%22%29%0A%0Acountdown%283%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows, there is a subtle but essential difference in the way a `while` statement is treated in memory: In short, `while` statements can *not* run into a `RecursionError` as only *one* frame is needed to manage the names. After all, there is only *one* function call to be made. For typical day-to-day applications, this difference is, however, not so important *unless* a problem instance becomes so big that a large (i.e., $> 3.000$) number of recursive calls must be made." ] }, { @@ -4438,9 +4438,9 @@ } }, "source": [ - "Finding the greatest common divisor of two numbers is still not so obvious when using a `while`-loop instead of a recursion.\n", + "Finding the greatest common divisor of two numbers is still not so obvious when using a `while`-loop instead of a recursive formulation.\n", "\n", - "The iterative implementation of `gcd()` below accepts any two strictly positive integers. As in any iteration through the loop the smaller number is subtracted from the larger one, the two decremented values of `a` and `b` will eventually be equal. Thus, this algorithm is also guaranteed to terminate. If one of the two numbers were negative or $0$ to begin with, `gcd()` would run forever and not even Python could detect this. Try this out by removing the input validation and running the function with negative arguments!" + "The iterative implementation of `gcd()` below accepts any two strictly positive integers. As in any iteration through the loop, the smaller number is subtracted from the larger one, the two decremented values of `a` and `b` eventually become equal. Thus, this algorithm is also guaranteed to terminate. If one of the two numbers were negative or $0$ in the first place, `gcd()` would run forever, and not even Python could detect this. Try this out by removing the input validation and running the function with negative arguments!" ] }, { @@ -4565,7 +4565,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "4.76 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "4.69 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -4593,7 +4593,7 @@ } }, "source": [ - "As with recursion, we must ensure that the iteration ends. For the above `countdown()` and `gcd()` examples we could \"prove\" (i.e., at least argue in favor) that some pre-defined **termination criterion** will be reached eventually. However, this cannot be done in all cases as the following example shows." + "As with recursion, we must ensure that the iteration ends. For the above `countdown()` and `gcd()` examples, we could \"prove\" (i.e., at least argue in favor) that some pre-defined **termination criterion** is reached eventually. However, this cannot be done in all cases, as the following example shows." ] }, { @@ -4616,10 +4616,10 @@ }, "source": [ "Let's play the following game:\n", - "- think of any positive integer $n$\n", - "- if $n$ is even, the next $n$ is half the old $n$\n", - "- if $n$ is odd, multiply the old $n$ by $3$ and add $1$ to obtain the next $n$\n", - "- repeat these steps until you reach $1$\n", + "- Think of any positive integer $n$.\n", + "- If $n$ is even, the next $n$ is half the old $n$.\n", + "- If $n$ is odd, multiply the old $n$ by $3$ and add $1$ to obtain the next $n$.\n", + "- Repeat these steps until you reach $1$.\n", "\n", "**Do we always reach the final $1$?**" ] @@ -4632,7 +4632,7 @@ } }, "source": [ - "The function below implements this game. Does it always reach $1$? No one has proven it so far! We include some input validation as before because `collatz()` would definitely not terminate if we called it with a negative number. Further, the Collatz sequence also works for real numbers but then we would have to study fractals (cf., [this](https://en.wikipedia.org/wiki/Collatz_conjecture#Iterating_on_real_or_complex_numbers)). So we restrict our example to integers only." + "The function below implements this game. Does it always reach $1$? No one has proven it so far! We include some input validation as before because `collatz()` would for sure not terminate if we called it with a negative number. Further, the Collatz sequence also works for real numbers, but then we would have to study fractals (cf., [this](https://en.wikipedia.org/wiki/Collatz_conjecture#Iterating_on_real_or_complex_numbers)). So we restrict our example to integers only." ] }, { @@ -4790,9 +4790,9 @@ } }, "source": [ - "Recursion and the `while` statement are two sides of the same coin. Disregarding that in the case of recursion Python internally faces some additional burden for managing the stack of frames in memory, both approaches lead to the *same* computational steps in memory. More importantly, we can re-formulate a recursive implementation in an iterative way and vice verca despite one of the two ways often \"feeling\" a lot more natural given a particular problem.\n", + "Recursion and the `while` statement are two sides of the same coin. Disregarding that in the case of recursion Python internally faces some additional burden for managing the stack of frames in memory, both approaches lead to the *same* computational steps in memory. More importantly, we can re-formulate a recursive implementation in an iterative way and vice versa despite one of the two ways often \"feeling\" a lot more natural given a particular problem.\n", "\n", - "So how does the `for` [statement](https://docs.python.org/3/reference/compound_stmts.html#the-for-statement) we saw in the very first example in this book fit into this picture? It is really a *redundant* language construct to provide a *shorter* and more *convenient* syntax for common applications of the `while` statement. In programming, such additions to a language are called **syntactic sugar**. Sugar makes a cup of tea taste better but we may drink tea without sugar too.\n", + "So how does the `for` [statement](https://docs.python.org/3/reference/compound_stmts.html#the-for-statement) we saw in the very first example in this book fit into this picture? It is a *redundant* language construct to provide a *shorter* and more *convenient* syntax for common applications of the `while` statement. In programming, such additions to a language are called **syntactic sugar**. A cup of tea tastes better with sugar, but we may drink tea without sugar too.\n", "\n", "Consider the following `numbers` list. Without the `for` statement, we have to manage a temporary **index variable** `i` to loop over all the elements and also obtain the individual elements with the `[]` operator in each iteration of the loop." ] @@ -4844,7 +4844,7 @@ } }, "source": [ - "The `for` statement, on the contrary, makes the actual business logic more apparent by stripping all the **boilerplate code** away." + "The `for` statement, on the contrary, makes the actual business logic more apparent by stripping all the **[boilerplate code](https://en.wikipedia.org/wiki/Boilerplate_code)** away." ] }, { @@ -4877,7 +4877,7 @@ } }, "source": [ - "For sequences of integers the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in makes the `for` statement even more convenient: It creates a list-like object of type `range` that generates integers \"on the fly\" and we will look closely at the underlying effects in memory in Chapter 7." + "For sequences of integers, the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in makes the `for` statement even more convenient: It creates a list-like object of type `range` that generates integers \"on the fly,\" and we look closely at the underlying effects in memory in Chapter 7." ] }, { @@ -5000,7 +5000,7 @@ } }, "source": [ - "The important difference between the above `list` objects, `[0, 1, 2, 3, 4]` and `[1, 3, 5, 7, 9]`, and the `range` objects, `range(5)` and `range(1, 10, 2)`, is that in the former case *six* objects are created in memory *before* the `for` statement starts running, *one* `list` holding pointers to *five* `int` objects, whereas in the latter case only *one* `range` object is created that **generates** `int` objects one at a time *while* the `for`-loop runs.\n", + "The essential difference between the above `list` objects, `[0, 1, 2, 3, 4]` and `[1, 3, 5, 7, 9]`, and the `range` objects, `range(5)` and `range(1, 10, 2)`, is that in the former case *six* objects are created in memory *before* the `for` statement starts running, *one* `list` holding pointers to *five* `int` objects, whereas in the latter case only *one* `range` object is created that **generates** `int` objects one at a time *while* the `for`-loop runs.\n", "\n", "However, we can loop over both of them. So a natural question to ask is why Python treats objects of *different* types in the *same* way when used with a `for` statement.\n", "\n", @@ -5008,13 +5008,13 @@ "\n", "Now, just as we classify objects by their types, we also classify these **concrete data types** (e.g., `int`, `float`, `str`, or `list`) into **abstract concepts**.\n", "\n", - "We actually did this already in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) when we described a `list` object as \"some sort of container that holds [...] pointers to other objects\". So, abstractly speaking, **containers** are any objects that are \"composed\" of other objects and also \"manage\" how these objects are organized. `list` objects, for example, have the property that they model an internal order associated with its elements. There exist, however, other container types, many of which do *not* come with an order. So, containers primarily \"contain\" other objects and have *nothing* to do with looping.\n", + "We did this already in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) when we described a `list` object as \"some sort of container that holds [...] pointers to other objects\". So, abstractly speaking, **containers** are any objects that are \"composed\" of other objects and also \"manage\" how these objects are organized. `list` objects, for example, have the property that they model an internal order associated with its elements. There exist, however, other container types, many of which do *not* come with an order. So, containers primarily \"contain\" other objects and have *nothing* to do with looping.\n", "\n", - "On the contrary, the abstract concept of **iterables** is all about looping: Any object that we can loop over is by definition an iterable. So, `range` objects, for example, are iterables that do *not* contain other objects. Moreover, looping does *not* have to occur in a *predictable* order although this is the case for both `list` and `range` objects.\n", + "On the contrary, the abstract concept of **iterables** is all about looping: Any object that we can loop over is, by definition, an iterable. So, `range` objects, for example, are iterables that do *not* contain other objects. Moreover, looping does *not* have to occur in a *predictable* order, although this is the case for both `list` and `range` objects.\n", "\n", - "Typically, containers are iterable and iterables are containers. Yet, only because these two concepts coincide often, we must not think of them as the same. Chapter 10 will finally give an explanation as to how abstract concepts are implemented and play together.\n", + "Typically, containers are iterables, and iterables are containers. Yet, only because these two concepts coincide often, we must not think of them as the same. Chapter 10 finally gives an explanation as to how abstract concepts are implemented and play together.\n", "\n", - "So, `list` objects like `first_names` below are iterable containers. They actually implement even more abstract concepts as we will see in Chapter 7." + "So, `list` objects like `first_names` below are iterable containers. They implement even more abstract concepts, as Chapter 7 reveals." ] }, { @@ -5165,7 +5165,7 @@ } }, "source": [ - "If we must have an index variable in the loop's body, we use the [enumerate()](https://docs.python.org/3/library/functions.html#enumerate) built-in that takes an *iterable* as its argument and then generates a \"stream\" of \"pairs\" consisting of an index variable and an object provided by the iterable. There is *no* need to ever revert back to the `while` statement to loop over an iterable object." + "If we must have an index variable in the loop's body, we use the [enumerate()](https://docs.python.org/3/library/functions.html#enumerate) built-in that takes an *iterable* as its argument and then generates a \"stream\" of \"pairs\" consisting of an index variable and an object provided by the iterable. There is *no* need to ever revert to the `while` statement to loop over an iterable object." ] }, { @@ -5290,9 +5290,9 @@ "source": [ "In contrast to its recursive counterpart, the iterative `fibonacci()` function below is somewhat harder to read. For example, it is not so obvious as to how many iterations through the `for`-loop we need to make when implementing it. There is an increased risk of making an *off-by-one* error. Moreover, we need to track a `temp` variable along, at least until we have worked through Chapter 7. Do you understand what `temp` does?\n", "\n", - "However, one advantage of calculating Fibonacci numbers in a **forwards** fashion with a `for` statement is that we could list the entire sequence in ascending order as we calculate the desired number. To show this, we added `print()` statements in `fibonacci()` below.\n", + "However, one advantage of calculating Fibonacci numbers in a **forward** fashion with a `for` statement is that we could list the entire sequence in ascending order as we calculate the desired number. To show this, we added `print()` statements in `fibonacci()` below.\n", "\n", - "We do *not* need to store the index variable in the `for`-loop's header line: That is what the underscore variable `_` indicates; we \"throw it away\"." + "We do *not* need to store the index variable in the `for`-loop's header line: That is what the underscore variable `_` indicates; we \"throw it away.\"" ] }, { @@ -5439,7 +5439,7 @@ } }, "source": [ - "The iterative `factorial()` implementation is comparable to its recursive counterpart when it comes to readability. One advantage of calculating the factorial in a forwards fashion is that we could track the intermediate `product` as it grows." + "The iterative `factorial()` implementation is comparable to its recursive counterpart when it comes to readability. One advantage of calculating the factorial in a forward fashion is that we could track the intermediate `product` as it grows." ] }, { @@ -5528,7 +5528,7 @@ } }, "source": [ - "The remainder of this chapter introduces more syntactic sugar. None of the language constructs introduced below are actually needed but contribute to making Python the expressive and easy to read language that it is." + "The remainder of this chapter introduces more syntactic sugar: None of the language constructs introduced below are needed but contribute to making Python the expressive and easy to read language that it is." ] }, { @@ -5629,11 +5629,11 @@ } }, "source": [ - "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", + "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* clear right away.\n", "\n", - "Luckily, Python provides a `break` [statement](https://docs.python.org/3/reference/simple_stmts.html#the-break-statement) that let's us stop the `for`-loop in any iteration of the loop. Conceptually, it is yet another means of controlling the flow of execution." + "Luckily, Python provides a `break` [statement](https://docs.python.org/3/reference/simple_stmts.html#the-break-statement) that lets us stop the `for`-loop in any iteration of the loop. Conceptually, it is yet another means of controlling the flow of execution." ] }, { @@ -5676,7 +5676,7 @@ } }, "source": [ - "This is a computational improvement. However, the code still consists *three* logical sections: some initialization *before* the `for`-loop, the loop itself, and some finalizing logic. We would prefer to convey the program's idea in *one* compound statement instead." + "This is a computational improvement. However, the code still consists of *three* logical 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." ] }, { @@ -5698,7 +5698,7 @@ } }, "source": [ - "To express the logic in a prettier way, we add an `else`-clause at the end of the `for`-loop. The `else`-branch 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 rather unintuitive meaning and had better been named a `then`-clause. In most scenarios, however, the `else`-clause logically goes together with some `if` statement in the `for`-loop's body.\n", + "To express the logic in a prettier way, we add an `else`-clause at the end of the `for`-loop. The `else`-branch is executed *iff* 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 had better been named a `then`-clause. In most scenarios, however, the `else`-clause logically goes together with some `if` statement in the `for`-loop's body.\n", "\n", "Overall, the code's expressive power increases. Not many programming languages support a `for`-`else`-branching, which turns out to be very useful in practice." ] @@ -5754,7 +5754,7 @@ } }, "source": [ - "Lastly, we incorporate the finalizing `if`-`else` logic into the `for`-loop avoiding the `is_above` variable alltogether." + "Lastly, we incorporate the finalizing `if`-`else` logic into the `for`-loop, avoiding the `is_above` variable altogether." ] }, { @@ -5792,7 +5792,7 @@ } }, "source": [ - "Of course, if we set the `threshold` an element's square has to pass higher, for example to `200`, we have to loop through the entire `numbers` list. There is *no way* to further optimize this **[linear search](https://en.wikipedia.org/wiki/Linear_search)**, at least as long as we model `numbers` as a `list` object. More advanced data types, however, exist that mitigate that downside." + "Of course, if we set the `threshold` an element's square has to pass higher, for example, to `200`, we have to loop through the entire `numbers` list. There is *no way* to optimize this **[linear search](https://en.wikipedia.org/wiki/Linear_search)** further, at least as long as we model `numbers` as a `list` object. More advanced data types, however, exist that mitigate that downside." ] }, { @@ -5843,7 +5843,7 @@ } }, "source": [ - "Often times, we process some iterable with numeric data, for example, a list of numbers as in the introductory example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) or, more realistically, data from a CSV file with many rows and columns.\n", + "Often, we process some iterable with numeric data, for example, a list of numbers as in the introductory example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) 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", @@ -5851,7 +5851,7 @@ "- **filtering**: throw away individual samples (e.g., statistical outliers)\n", "- **reducing**: collect individual samples into summary statistics\n", "\n", - "We will study this **map-filter-reduce** paradigm extensively in Chapter 7 after introducing more advanced data types that are needed to work with \"big\" data.\n", + "We study this **map-filter-reduce** paradigm extensively in Chapter 7 after introducing more advanced data types that are needed to work with \"big\" data.\n", "\n", "In the remainder of this section, we focus on *filtering out* some samples within a `for`-loop." ] @@ -5947,7 +5947,7 @@ "\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](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\"." + "Consider the next example, whose implementation in code already starts to look unbalanced." ] }, { @@ -6050,11 +6050,11 @@ } }, "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](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb#The-if-Statement). Then, however, we trade off horizontal space against a more \"complex\" `if` logic and this is *not* a real improvement.\n", + "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](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb#The-if-Statement). Then, however, we trade off horizontal space against a more \"complex\" `if` logic, and this is *not* a real improvement.\n", "\n", "A Pythonista would instead make use of the `continue` [statement](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 fragement below occupies more vertical space and less horizontal space: A *good* trade-off." + "The revised code fragment below occupies more vertical space and less horizontal space: A *good* trade-off." ] }, { @@ -6119,11 +6119,11 @@ } }, "source": [ - "This is yet another illustration of why programming is an art. The two preceding code cells do *exactly* 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 nested filters.\n", + "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 nested filters.\n", "\n", "The idea behind the `continue` statement is conceptually similar to the early exit pattern we saw above.\n", "\n", - "Both the `break` and `continue` statements as well as the optional `else`-clause are not only supported with `for`-loops but also `while`-loops." + "Both the `break` and `continue` statements, as well as the optional `else`-clause, are not only supported with `for`-loops but also `while`-loops." ] }, { @@ -6171,9 +6171,9 @@ "\n", "Python provides the built-in [input()](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()](https://docs.python.org/3/library/random.html#random.random) function in the [random](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 certain event occurs, we `break` out of the loop.\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 naive implementation." + "Let's look at a first and naive implementation." ] }, { @@ -6278,7 +6278,7 @@ "\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()](https://docs.python.org/3/library/stdtypes.html?highlight=__contains__#str.strip) and [lower()](https://docs.python.org/3/library/stdtypes.html?highlight=__contains__#str.lower) methods remove preceeding 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`." + "`get_guess()` not only reads in the user's input but also implements a simple input validation pattern in that the [strip()](https://docs.python.org/3/library/stdtypes.html?highlight=__contains__#str.strip) and [lower()](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`." ] }, { @@ -6351,7 +6351,7 @@ } }, "source": [ - "Second, we rewrite the `if`-`else`-logic to explictly handle the case where `get_guess()` returns `None`: 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", + "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." ] @@ -6411,7 +6411,7 @@ } }, "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()](https://docs.python.org/3/library/random.html#random.random) function with another one). In general, a modular architecture leads to improved software maintenance." + "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()](https://docs.python.org/3/library/random.html#random.random) function with another one). In general, modular architecture leads to improved software maintenance." ] }, { @@ -6435,13 +6435,13 @@ "source": [ "**Iteration** is about **running blocks of code repeatedly**.\n", "\n", - "There are two redundant approaches of achieving that.\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 **backwards** reasoning might not be intuitive but it turns out to be a very useful tool to have in one's toolbox.\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 **forwards** reasoning. The `for` statement is **syntactic sugar** that allows to rewrite common occurences of the `while` statement in a concise way. 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", + "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\" pointers to other objects in memory. On the contrary, the `range` type does not point to any other objects but rather creates *new* `int` objects \"on the fly\" (i.e., when being looped over)." + "**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\" pointers to other objects in memory. On the contrary, the `range` type does not point to any other objects but instead creates *new* `int` objects \"on the fly\" (i.e., when being looped over)." ] } ], diff --git a/04_iteration_review_and_exercises.ipynb b/04_iteration_review_and_exercises.ipynb index 8dd9025..a953796 100644 --- a/04_iteration_review_and_exercises.ipynb +++ b/04_iteration_review_and_exercises.ipynb @@ -54,7 +54,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q2**: Solving a problem with a **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 **backwards** fashion!" + "**Q2**: 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!" ] }, { @@ -68,7 +68,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q3**: Explain what **duck typing** means! Why can it cause problems? Why it is [not a bug but a feature](https://www.urbandictionary.com/define.php?term=It%27s%20not%20a%20bug%2C%20it%27s%20a%20feature)?" + "**Q3**: 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)?" ] }, { @@ -208,7 +208,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q12**: Whereas a **recursion** may accidently result in a **never ending** program, `while`-loops and `for`-loops are guaranteed to **terminate**." + "**Q12**: Whereas a **recursion** may accidentally result in a **never-ending** program, `while`-loops and `for`-loops are guaranteed to **terminate**." ] }, { @@ -275,7 +275,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "A popular example for a problem that is solved with a recursion art the **[Towers of Hanoi](https://en.wikipedia.org/wiki/Tower_of_Hanoi)**.\n", + "A popular example of a problem that is solved by recursion art the **[Towers of Hanoi](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", @@ -304,9 +304,9 @@ "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 Initative](https://blossoms.mit.edu/) is primarily aimed at high school students and does not have any prerequisites.\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 in order to solve the tasks below. So, watch the video until 37:55." + "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." ] }, { @@ -393,7 +393,7 @@ "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 progam's output: For example, an online version of the game can be found **[here](https://www.mathsisfun.com/games/towerofhanoi.html)**." + "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)**." ] }, { @@ -402,7 +402,7 @@ "source": [ "Let's first **generalize** the mathematical relationship from above.\n", "\n", - "While the first number of $Sol(\\cdot)$ is the number of `disks` $n$, the second and third \"numbers\" are actually the **labels** for the three spots. Instead of spots `1`, `2`, and `3` we could also call them `\"left\"`, `\"center\"`, and `\"right\"` in our Python implementation. When \"passed\" to the $Sol(\\cdot)$ \"function\" they take on the role of an `origin` (= $o$) and `destination` (= $d$) pair.\n", + "While the first number of $Sol(\\cdot)$ is the number of `disks` $n$, the second and third \"numbers\" are the **labels** for the three spots. Instead of spots `1`, `2`, and `3`, we could also call them `\"left\"`, `\"center\"`, and `\"right\"` in our Python implementation. When \"passed\" to the $Sol(\\cdot)$ \"function\" they take on the role of an `origin` (= $o$) and `destination` (= $d$) pair.\n", "\n", "So, the expression $Sol(4, 1, 3)$ is the same as $Sol(4, \\text{\"left\"}, \\text{\"right\"})$ and describes the problem of moving a tower consisting of $n = 4$ disks from `origin` `1` / `\"left\"` to `destination` `3` / `right`. As we have seen in the video, we need some `intermediate` (= $i$) spot." ] @@ -420,9 +420,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In words, this means that in order to move a tower consisting of $n$ disks from an `origin` $o$ to a `destination` $d$, three steps must be executed:\n", + "In words, this means that to move a tower consisting of $n$ disks from an `origin` $o$ to a `destination` $d$, three steps must be executed:\n", "\n", - "1. Move the top most $n - 1$ disks of the tower temporarily from $o$ to $i$ (= sub-problem 1)\n", + "1. Move the topmost $n - 1$ disks of the tower temporarily from $o$ to $i$ (= sub-problem 1)\n", "2. Move the remaining and largest disk from $o$ to $d$\n", "3. Move the the $n - 1$ disks from the temporary spot $i$ to $d$ (= sub-problem 2)\n", "\n", @@ -435,9 +435,9 @@ "source": [ "$Sol(\\cdot)$ can be written in Python as a function `sol()` that takes three arguments `disks`, `origin`, and `destination` that mirror $n$, $o$, and $d$.\n", "\n", - "Assume that all arguments to `sol()` will be `int` objects!\n", + "Assume that all arguments to `sol()` are `int` objects!\n", "\n", - "Once completed, `sol()` should print out all the moves in the correct order. With **printing a move**, we simply mean a line like \"1 -> 3\", short for \"Move the top-most disk from spot 1 to spot 3\".\n", + "Once completed, `sol()` should print out all the moves in the correct order. With **printing a move**, we mean a line like \"1 -> 3\", short for \"Move the top-most disk from spot 1 to spot 3\".\n", "\n", "Write your answers to **Q15.5** to **Q15.7** into the subsequent code cell and finalize `sol()`! No need to write a docstring or validate the input here." ] @@ -550,9 +550,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The previous `sol()` implementation does the job but the conditional statement needed in unnecessarily tedious. \n", + "The previous `sol()` implementation does the job, but the conditional statement needed in unnecessarily tedious. \n", "\n", - "Let's create a more concise `hanoi()` function that in addition to a positional `disks` argument takes three keyword-only arguments `origin`, `intermediate`, and `destination` with default values `\"left\"`, `\"center\"`, and `\"right\"`.\n", + "Let's create a more concise `hanoi()` function that, in addition to a positional `disks` argument, takes three keyword-only arguments `origin`, `intermediate`, and `destination` with default values `\"left\"`, `\"center\"`, and `\"right\"`.\n", "\n", "Write your answers to **Q15.9** and **Q15.10** into the subsequent code cell and finalize `hanoi()`! No need to write a docstring or validate the input here." ] @@ -588,7 +588,7 @@ "\n", "Figure out how the arguments are passed on in the two recursive `hanoi()` calls!\n", "\n", - "Also, write a `print()` statement analogous to the one in `sol()` in between the two recursive function calls. Is it ok to just copy and paste it?" + "Also, write a `print()` statement analogous to the one in `sol()` in between the two recursive function calls. Is it ok to copy and paste it?" ] }, { @@ -638,7 +638,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We could of course also use **numeric labels** for the three steps like so." + "We could, of course, also use **numeric labels** for the three steps like so." ] }, { @@ -661,13 +661,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Let's say, we did not know about the **analytical formula** for the number of **minimal moves** given $n$.\n", + "Let's say we did not know about the **analytical formula** for the number of **minimal moves** given $n$.\n", "\n", "In such cases, we could modify a recursive function to return a count value to be passed up the recursion tree.\n", "\n", - "In fact, this is similar to what we do in the recursive versions of `factorial()` and `fibonacci()` in [Chapter 4](https://github.com/webartifex/intro-to-python/blob/master/04_iteration.ipynb) where we pass up an intermediate result.\n", + "This is similar to what we do in the recursive versions of `factorial()` and `fibonacci()` in [Chapter 4](https://github.com/webartifex/intro-to-python/blob/master/04_iteration.ipynb), where we pass up an intermediate result.\n", "\n", - "Let's create a `hanoi_moves()` function that follows the same internal logic as `hanoi()` but instead of printing out the moves returns the number of steps done so far in the recursion.\n", + "Let's create a `hanoi_moves()` function that follows the same internal logic as `hanoi()`, but, instead of printing out the moves, returns the number of steps done so far in the recursion.\n", "\n", "Write your answers to **Q15.12** to **Q15.14** into the subsequent code cell and finalize `hanoi_moves()`! No need to write a docstring or validate the input here." ] @@ -742,7 +742,7 @@ "source": [ "Observe how quickly the `hanoi_moves()` function slows down for increasing `disks` arguments.\n", "\n", - "With `disks` in the range from `24` through `26` the computation time roughly doubles for each increase of `disks` by 1.\n", + "With `disks` in the range from `24` through `26`, the computation time roughly doubles for each increase of `disks` by 1.\n", "\n", "**Q15.16**: Execute the code cells below and see for yourself!" ] @@ -788,7 +788,7 @@ "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 can be build in by passing on one more argument `offset` down the recursion tree. As the logic gets a bit \"involved\", `hanoi_ordered()` below is almost finished.\n", + "The above `hanoi()` prints the optimal solution's moves in the correct order but fails to label each move with an order number. This can be built in by passing on one more argument `offset` down the recursion tree. As the logic gets a bit \"involved,\" `hanoi_ordered()` below is almost finished.\n", "\n", "Write your answers to **Q15.17** and **Q15.18** into the subsequent code cell and finalize `hanoi_ordered()`! No need to write a docstring or validate the input here." ] @@ -879,7 +879,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Lastly, it is to be mentioned that for problem instances with a small `disks` argument it is easier to collect all the moves first in a list and then add the order number with the [enumerate()](https://docs.python.org/3/library/functions.html#enumerate) built-in." + "Lastly, it is to be mentioned that for problem instances with a small `disks` argument, it is easier to collect all the moves first in a list and then add the order number with the [enumerate()](https://docs.python.org/3/library/functions.html#enumerate) built-in." ] }, { diff --git a/05_numbers.ipynb b/05_numbers.ipynb index 2c06dc3..ccb1412 100644 --- a/05_numbers.ipynb +++ b/05_numbers.ipynb @@ -21,15 +21,15 @@ "source": [ "After learning about the basic building blocks of expressing and structuring the business logic in programs, we focus our attention on the **data types** Python offers us, both built-in and available via the [standard library](https://docs.python.org/3/library/index.html) or third-party packages.\n", "\n", - "We start with the \"simple\" ones: Numeric types in this chapter and textual data in Chapter 6. An important fact that holds true for all objects of these types is that they are **immutable**. To re-use the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Objects-vs.-Types-vs.-Values), this means that the $0$s and $1$s making up an object's *value* cannot be changed once the bag is created in memory implying that any operation with or method on the object creates a *new* object in a *different* memory location.\n", + "We start with the \"simple\" ones: Numeric types in this chapter and textual data in Chapter 6. An important fact that holds for all objects of these types is that they are **immutable**. To re-use the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Objects-vs.-Types-vs.-Values), this means that the $0$s and $1$s making up an object's *value* cannot be changed once the bag is created in memory, implying that any operation with or method on the object creates a *new* object in a *different* memory location.\n", "\n", - "Chapters 7, 8, and 9 then cover the more \"complex\" data types including, for example, the `list` type. Finally, Chapter 10 completes the picture by introducing language constructs to create custom types.\n", + "Chapters 7, 8, and 9 then cover the more \"complex\" data types, including, for example, the `list` type. Finally, Chapter 10 completes the picture by introducing language constructs to create custom types.\n", "\n", "We have already seen many hints indicating that numbers are not as trivial to work with as it seems at first sight:\n", "\n", - "- [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#%28Data%29-Type-%2F-%22Behavior%22) revealed that numbers may come in *different* data types (i.e., `int` vs. `float` so far),\n", - "- [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb#Boolean-Expressions) raised questions regarding the **limited precision** of `float` numbers (e.g., `42 == 42.000000000000001` evaluates to `True`), and\n", - "- [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Infinite-Recursion) showed that sometimes a `float` \"walks\" and \"quacks\" like an `int` whereas the reverse is true in other cases.\n", + "- [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#%28Data%29-Type-%2F-%22Behavior%22) reveals that numbers may come in *different* data types (i.e., `int` vs. `float` so far),\n", + "- [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb#Boolean-Expressions) raises questions regarding the **limited precision** of `float` numbers (e.g., `42 == 42.000000000000001` evaluates to `True`), and\n", + "- [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Infinite-Recursion) shows that sometimes a `float` \"walks\" and \"quacks\" like an `int`, whereas the reverse is true in other cases.\n", "\n", "This chapter introduces all the [built-in numeric types](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex): `int`, `float`, and `complex`. To mitigate the limited precision of floating-point numbers, we also look at two replacements for the `float` type in the [standard library](https://docs.python.org/3/library/index.html), namely the `Decimal` type in the [decimals](https://docs.python.org/3/library/decimal.html#decimal.Decimal) and the `Fraction` type in the [fractions](https://docs.python.org/3/library/fractions.html#fractions.Fraction) module." ] @@ -53,7 +53,7 @@ } }, "source": [ - "The simplest numeric type is the `int` type. Python's integers behave like [integers in ordinary math](https://en.wikipedia.org/wiki/Integer) (i.e., the set $\\mathbb{Z}$) and support operators in the way we saw in the section on arithmetic operators in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#%28Arithmetic%29-Operators)." + "The simplest numeric type is the `int` type: It behaves like an [integer in ordinary math](https://en.wikipedia.org/wiki/Integer) (i.e., the set $\\mathbb{Z}$) and supports operators in the way we saw in the section on arithmetic operators in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#%28Arithmetic%29-Operators)." ] }, { @@ -66,7 +66,7 @@ }, "outputs": [], "source": [ - "a = 789" + "i = 789" ] }, { @@ -92,7 +92,7 @@ { "data": { "text/plain": [ - "140526570988784" + "139927641322704" ] }, "execution_count": 2, @@ -101,7 +101,7 @@ } ], "source": [ - "id(a)" + "id(i)" ] }, { @@ -125,7 +125,7 @@ } ], "source": [ - "type(a)" + "type(i)" ] }, { @@ -149,7 +149,7 @@ } ], "source": [ - "a" + "i" ] }, { @@ -160,7 +160,7 @@ } }, "source": [ - "A nice feature is using underscores `_` as (thousands) seperators in numeric literals. For example, `1_000_000` evaluates to just `1000000` in memory." + "A nice feature is using underscores `_` as (thousands) separator in numeric literals. For example, `1_000_000` evaluates to `1000000` in memory." ] }, { @@ -195,7 +195,7 @@ } }, "source": [ - "Whereas mathematicians argue what the term $0^0$ means (cf., this [article](https://en.wikipedia.org/wiki/Zero_to_the_power_of_zero)), programmers are pragmatic about this and just define $0^0 = 1$." + "Whereas mathematicians argue what the term $0^0$ means (cf., this [article](https://en.wikipedia.org/wiki/Zero_to_the_power_of_zero)), programmers are pragmatic about this and define $0^0 = 1$." ] }, { @@ -241,7 +241,7 @@ } }, "source": [ - "As computers can only store $0$s and $1$s, integers are nothing but that in memory as well. Consequently, computer scientists and engineers developed conventions as to how $0$s and $1$s are \"translated\" into integers and one such convention is the **[binary representation](https://en.wikipedia.org/wiki/Binary_number)** of **non-negative integers**. Consider the integers from $0$ through $255$ that are encoded into $0$s and $1$s with the help of this table:" + "As computers can only store $0$s and $1$s, `int` objects are nothing but that in memory as well. Consequently, computer scientists and engineers developed conventions as to how $0$s and $1$s are \"translated\" into integers, and one such convention is the **[binary representation](https://en.wikipedia.org/wiki/Binary_number)** of **non-negative integers**. Consider the integers from $0$ through $255$ that are encoded into $0$s and $1$s with the help of this table:" ] }, { @@ -266,13 +266,13 @@ } }, "source": [ - "A number consists of exactly eight $0$s and $1$s that are read from right to left and referred to as the **bits** of the number. Each bit represents a distinct number, the **digit**, as indicated in the table. For sure, we start counting at $0$ again.\n", + "A number consists of exactly eight $0$s and $1$s that are read from right to left and referred to as the **bits** of the number. Each bit represents a distinct multiple of $2$, the **digit**. For sure, we start counting at $0$ again.\n", "\n", - "To encode the integer $3$, for example, we need to find a combination of $0$s and $1$s such that the sum of digits marked with a $1$ is equal to the number we want to encode. In the example, we set all bits to $0$ except for the first ($i=0$) and second ($i=1$) because $1 + 2 = 3$. So the binary representation of $3$ is $00~00~00~11$. To be precise, the $3$ is a linear combination of the digits where the coefficients are either $0$ or $1$: $3 = 0*128 + 0*64 + 0*32 + 0*16 + 0*8 + 0*4 + 1*2 + 1*1$. It is guaranteed that there is exactly one such combination for each number between $0$ and $255$.\n", + "To encode the integer $3$, for example, we need to find a combination of $0$s and $1$s such that the sum of digits marked with a $1$ is equal to the number we want to encode. In the example, we set all bits to $0$ except for the first ($i=0$) and second ($i=1$) as $2^0 + 2^1 = 1 + 2 = 3$. So the binary representation of $3$ is $00~00~00~11$. To borrow some terminology from linear algebra, the $3$ is a linear combination of the digits where the coefficients are either $0$ or $1$: $3 = 0*128 + 0*64 + 0*32 + 0*16 + 0*8 + 0*4 + 1*2 + 1*1$. It is *guaranteed* that there is exactly *one* such combination for each number between $0$ and $255$.\n", "\n", - "As each digit in the binary representation is one of two values, we say that this representation has a base of $2$. Often times, the base is indicated with a subscript to avoid confusion. For example, we write $3_{10} = 00000011_2$ or $3_{10} = 11_2$ for short omitting leading $0$s. A subscript of $10$ implies a decimal number as we know it from grade school.\n", + "As each bit in the binary representation is one of two values, we say that this representation has a base of $2$. Often, the base is indicated with a subscript to avoid confusion. For example, we write $3_{10} = 00000011_2$ or $3_{10} = 11_2$ for short omitting leading $0$s. A subscript of $10$ implies a decimal number as we know it from grade school.\n", "\n", - "In Python, we use the built-in [bin()](https://docs.python.org/3/library/functions.html#bin) function to obtain an integer's binary representation. It returns a `str` object starting with `\"0b\"` indicating the binary format and as many $0$s and $1$s as are necessary to encode the integer omitting leading $0$s." + "We use the built-in [bin()](https://docs.python.org/3/library/functions.html#bin) function to obtain an `int` object's binary representation: It returns a `str` object starting with `\"0b\"` indicating the binary format and as many $0$s and $1$s as are necessary to encode the integer omitting leading $0$s." ] }, { @@ -307,7 +307,7 @@ } }, "source": [ - "We may pass such a `str` object as the argument to the [int()](https://docs.python.org/3/library/functions.html#int) built-in, together with `base=2`, to (re-)create an `int` object, for example, with the value of `3`." + "We may pass a `str` object formatted this way as the argument to the [int()](https://docs.python.org/3/library/functions.html#int) built-in, together with `base=2`, to (re-)create an `int` object, for example, with the value of `3`." ] }, { @@ -342,7 +342,7 @@ } }, "source": [ - "Moreover, we may also use the contents of the returned `str` object as a **literal** instead: Just like we type, for example, `3` without quotes (i.e., \"literally\") into a code cell to create the `int` object `3`, we may type `0b11` and obtain an `int` object with the same value." + "Moreover, we may also use the contents of the returned `str` object as a **literal** instead: Just like we type, for example, `3` without quotes (i.e., \"literally\") into a code cell to create the `int` object `3`, we may type `0b11` to obtain an `int` object with the same value." ] }, { @@ -377,7 +377,7 @@ } }, "source": [ - "Another example is the integer `177` that is the sum of $128 + 32 + 16 + 1$: Its binary representation is the sequence of bits $10~11~00~01$. To use our new notation, $177_{10} = 10110001_2$." + "Another example is the integer `177` that is the sum of $128 + 32 + 16 + 1$: Thus, its binary representation is the sequence of bits $10~11~00~01$, or to use our new notation, $177_{10} = 10110001_2$." ] }, { @@ -412,7 +412,7 @@ } }, "source": [ - "Analogous to typing `177` into a code cell, we may type `0b10110001`, or `0b_1011_0001` to make it easier to read, to create the `int` object `177` in memory." + "Analogous to typing `177` into a code cell, we may write `0b10110001`, or `0b_1011_0001` to make use of the underscores, and create an `int` object with the value `177`." ] }, { @@ -554,9 +554,9 @@ } }, "source": [ - "Groups of eight bits are also called a **byte**. As a byte can only represent non-negative integers up to $255$, the table above is extended conceptually with greater digits to the left to express integers beyond $255$. The memory management needed to implement this is built into Python and we do not need to worry about anything.\n", + "Groups of eight bits are also called a **byte**. As a byte can only represent non-negative integers up to $255$, the table above is extended conceptually with greater digits to the left to model integers beyond $255$. The memory management needed to implement this is built into Python, and we do not need to worry about it.\n", "\n", - "For example, the `789` from above is encoded with $10$ bits and $789_{10} = 1100010101_2$." + "For example, the `789` from above is encoded with ten bits and $789_{10} = 1100010101_2$." ] }, { @@ -580,7 +580,7 @@ } ], "source": [ - "bin(a) # = bin(789)" + "bin(i) # = bin(789)" ] }, { @@ -598,7 +598,7 @@ "| Digit |$10^3$|$10^2$|$10^1$|$10^0$|\n", "| $=$ |$1000$| $100$| $10$ | $1$ |\n", "\n", - "Now, an integer is a linear combination of the digits where the coefficents are one of *ten* values. For example, the number $123$ can be expressed as $0*1000 + 1*100 + 2*10 + 3*1$. So, the binary representation follows the same logic as the decimal system taught in grade school. The decimal system is intuitive to us humans mostly as we learn to count with our *ten* fingers." + "Now, an integer is a linear combination of the digits where the coefficients are one of *ten* values, and the base is now $10$. For example, the number $123$ can be expressed as $0*1000 + 1*100 + 2*10 + 3*1$. So, the binary representation follows the same logic as the decimal system taught in grade school. The decimal system is intuitive to us humans, mostly as we learn to count with our *ten* fingers. The $0$s and $1$s in a computer's memory are therefore no rocket science; they only feel unintuitive for a beginner." ] }, { @@ -669,9 +669,9 @@ "\n", "$46_{10} = 2\\text{e}_{16} = 101110_2$\n", "\n", - "The built-in [hex()](https://docs.python.org/3/library/functions.html#hex) function creates a `str` object starting with `\"0x\"` representing an integer's hexadecimal representation. The length depends on how many groups of four bits are implied by the corresponding binary representation.\n", + "The built-in [hex()](https://docs.python.org/3/library/functions.html#hex) function creates a `str` object starting with `\"0x\"` representing an `int` object's hexadecimal representation. The length depends on how many groups of four bits are implied by the corresponding binary representation.\n", "\n", - "For `0` and `1` the hexadecimal representation is similar to the binary one." + "For `0` and `1`, the hexadecimal representation is similar to the binary one." ] }, { @@ -765,7 +765,7 @@ } }, "source": [ - "For `10` and `15` we see the letter digits for the first time." + "For `10` and `15`, we see the letter digits for the first time." ] }, { @@ -824,7 +824,7 @@ } }, "source": [ - "For `177` the binary representation, `\"0b10110001\"`, can be read as *two* groups of four bits $1011$ and $0001$ that are encoded as $\\text{b}$ and $1$ in accordance with the first three columns of the table above." + "The binary representation of `177`, `\"0b10110001\"`, can be viewed as *two* groups of four bits, $1011$ and $0001$, that are encoded as $\\text{b}$ and $1$ in hexadecimal (cf., table above)." ] }, { @@ -883,7 +883,7 @@ } }, "source": [ - "To obtain the original integer back, we call the [int()](https://docs.python.org/3/library/functions.html#int) built-in with a `base=16` argument." + "To (re-)create an `int` object with the value `177`, we call the [int()](https://docs.python.org/3/library/functions.html#int) built-in with a properly formatted `str` object and `base=16` as arguments." ] }, { @@ -953,7 +953,7 @@ } }, "source": [ - "Hexadecimal values between $00_{16}$ and $\\text{ff}_{16}$ (i.e., $0_{10}$ and $255_{10}$) are commonly used to describe colors, for example, in web development but also in graphics editors. See this [online tool](https://www.w3schools.com/colors/colors_hexadecimal.asp) for some more background." + "Hexadecimals between $00_{16}$ and $\\text{ff}_{16}$ (i.e., $0_{10}$ and $255_{10}$) are commonly used to describe colors, for example, in web development but also graphics editors. See this [online tool](https://www.w3schools.com/colors/colors_hexadecimal.asp) for some more background." ] }, { @@ -988,7 +988,7 @@ } }, "source": [ - "Just like binary representations, the hexadecimals extend to the left for larger values like `789`." + "Just like binary representations, the hexadecimals extend to the left for larger numbers like `789`." ] }, { @@ -1012,7 +1012,7 @@ } ], "source": [ - "hex(a) # = hex(789)" + "hex(i) # = hex(789)" ] }, { @@ -1023,7 +1023,7 @@ } }, "source": [ - "For completeness sake, we mention that there is also the built-in [oct()](https://docs.python.org/3/library/functions.html#oct) function to obtain an integer's **octal representation**. The logic is the same as for the hexadecimal representation where we just use *eight* instead of *sixteen* digits. That is the equivalent of viewing the binary representations in groups of three bits." + "For completeness sake, we mention that there is also the [oct()](https://docs.python.org/3/library/functions.html#oct) built-in to obtain an integer's **octal representation**. The logic is the same as for the hexadecimal representation, and we use *eight* instead of *sixteen* digits. That is the equivalent of viewing the binary representations in groups of three bits. As of today, octal representations have become less important, and the data science practitioner may probably live without them quite well." ] }, { @@ -1045,7 +1045,7 @@ } }, "source": [ - "While there are conventions that model negative integers with the $0$s and $1$s in memory (cf., [Two's Complement](https://en.wikipedia.org/wiki/Two%27s_complement)), Python manages that for us and we do not look into this here for brevity.\n", + "While there are conventions that model negative integers with $0$s and $1$s in memory (cf., [Two's Complement](https://en.wikipedia.org/wiki/Two%27s_complement)), Python manages that for us, and we do not look into the theory here for brevity. We have learned all we need to know about how integers are modeled in a computer.\n", "\n", "The binary and hexadecimal representations of negative integers are identical to their positive counterparts except that they start with a minus sign `-`." ] @@ -1165,7 +1165,7 @@ } }, "source": [ - "Whereas the boolean objects `True` and `False` are commonly *not* regarded as numeric types, they behave like `1` and `0` in an arithmetic context." + "Whereas the boolean literals `True` and `False` are commonly *not* regarded as numeric types, they behave like `1` and `0` in an arithmetic context." ] }, { @@ -1176,9 +1176,20 @@ "slide_type": "slide" } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "b = True" + "True + False" ] }, { @@ -1186,14 +1197,14 @@ "execution_count": 33, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ - "94554490840032" + "42" ] }, "execution_count": 33, @@ -1202,7 +1213,7 @@ } ], "source": [ - "id(b)" + "41 + True" ] }, { @@ -1217,7 +1228,7 @@ { "data": { "text/plain": [ - "bool" + "0.0" ] }, "execution_count": 34, @@ -1226,199 +1237,7 @@ } ], "source": [ - "type(b)" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "b" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [], - "source": [ - "c = False" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "94554490840000" - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "id(c)" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "bool" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(c)" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 39, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "By including booleans as operands in arithmetic expressions, Python implicitly casts them as `int` objects." - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "1" - ] - }, - "execution_count": 40, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "b + c # = True + False" - ] - }, - { - "cell_type": "code", - "execution_count": 41, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "790" - ] - }, - "execution_count": 41, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a + b # = 789 + True" - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 42, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a * c # = 789 * False" + "42.0 * False" ] }, { @@ -1434,7 +1253,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 35, "metadata": { "slideshow": { "slide_type": "slide" @@ -1447,7 +1266,7 @@ "1" ] }, - "execution_count": 43, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } @@ -1458,7 +1277,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 36, "metadata": { "slideshow": { "slide_type": "-" @@ -1471,7 +1290,7 @@ "0" ] }, - "execution_count": 44, + "execution_count": 36, "metadata": {}, "output_type": "execute_result" } @@ -1493,10 +1312,10 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 37, "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "slide" } }, "outputs": [ @@ -1506,7 +1325,7 @@ "'0b1'" ] }, - "execution_count": 45, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } @@ -1517,10 +1336,10 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 38, "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "-" } }, "outputs": [ @@ -1530,7 +1349,7 @@ "'0b0'" ] }, - "execution_count": 46, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } @@ -1547,15 +1366,15 @@ } }, "source": [ - "Their hexadecimal representations occupy *four* bits in memory while only *one* bit is actually needed. This is because \"1\" and \"0\" are just two of the sixteen possible digits." + "Their hexadecimal representations occupy *four* bits in memory while only *one* bit is needed. This is because \"1\" and \"0\" are just two of the sixteen possible digits." ] }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 39, "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "slide" } }, "outputs": [ @@ -1565,7 +1384,7 @@ "'0x1'" ] }, - "execution_count": 47, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } @@ -1576,10 +1395,10 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 40, "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "-" } }, "outputs": [ @@ -1589,7 +1408,7 @@ "'0x0'" ] }, - "execution_count": 48, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } @@ -1606,12 +1425,12 @@ } }, "source": [ - "As a reminder, `None` is a type on its own, namely the `NoneType`, and different from `False`. It *cannot* be casted as an integer as the `TypeError` indicates." + "As a reminder, the `None` literal is a type on its own, namely the `NoneType`, and different from `False`. It *cannot* be cast as an integer as the `TypeError` indicates." ] }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 41, "metadata": { "slideshow": { "slide_type": "slide" @@ -1625,7 +1444,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: int() argument must be a string, a bytes-like object or a number, not 'NoneType'" ] } @@ -1653,16 +1472,16 @@ } }, "source": [ - "Now that we know how integers are represented with $0$s and $1$s in memory, we look at ways of working with these individual bits, in particular with the so-called **[bitwise operators](https://wiki.python.org/moin/BitwiseOperators)**: As the name suggests, the operators perform some operation on a bit by bit basis. They only work with `int` objects.\n", + "Now that we know how integers are represented with $0$s and $1$s, we look at ways of working with the individual bits, in particular with the so-called **[bitwise operators](https://wiki.python.org/moin/BitwiseOperators)**: As the name suggests, the operators perform some operation on a bit by bit basis. They only work with and always return `int` objects.\n", "\n", - "We keep this overview rather short as such \"low-level\" operations are not needed by the data science practicioner on a regular basis. Yet, it is worthwhile to have heard about them as they form the basis of all of arithmetic in computers.\n", + "We keep this overview rather short as such \"low-level\" operations are not needed by the data science practitioner regularly. Yet, it is worthwhile to have heard about them as they form the basis of all of arithmetic in computers.\n", "\n", - "The first operator is the **bitwise AND** `&` operator: This operator looks at the bits of its two operands, `11` and `13` in the example, in a pairwise fashion and *only if* both operands have a $1$ in the same position will the resulting integer have a $1$ in this position. The binary representations of `11` and `13` have $1$s in their respective first and fourth bits, which is why `bin(11 & 13)` evaluates to `\"Ob1001\"`." + "The first operator is the **bitwise AND** operator `&`: It looks at the bits of its two operands, `11` and `13` in the example, in a pairwise fashion and *iff* both operands have a $1$ in the same position, will the resulting integer have a $1$ in this position as well. The binary representations of `11` and `13` have $1$s in their respective first and fourth bits, which is why `bin(11 & 13)` evaluates to `\"Ob1001\"`." ] }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 42, "metadata": { "slideshow": { "slide_type": "slide" @@ -1675,7 +1494,7 @@ "'0b1011 & 0b1101'" ] }, - "execution_count": 50, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } @@ -1686,7 +1505,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 43, "metadata": { "slideshow": { "slide_type": "-" @@ -1699,7 +1518,7 @@ "'0b1001'" ] }, - "execution_count": 51, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } @@ -1716,15 +1535,15 @@ } }, "source": [ - "Of course, `0b1001` is the binary representation of `9`." + "`0b1001` is the binary representation of `9`." ] }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 44, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -1734,7 +1553,7 @@ "9" ] }, - "execution_count": 52, + "execution_count": 44, "metadata": {}, "output_type": "execute_result" } @@ -1745,7 +1564,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 45, "metadata": { "slideshow": { "slide_type": "-" @@ -1758,7 +1577,7 @@ "9" ] }, - "execution_count": 53, + "execution_count": 45, "metadata": {}, "output_type": "execute_result" } @@ -1775,12 +1594,12 @@ } }, "source": [ - "The **bitwise OR** `|` operator evaluates to an `int` object whose bits are set to $1$ if the corresponding bits of either *one* or *both* operands are $1$. So in the example `9 | 13` only the second bit is $0$ for both operands, which is why the expression evaluates to `\"0b1101\"`." + "The **bitwise OR** operator `|` evaluates to an `int` object whose bits are set to $1$ if the corresponding bits of either *one* or *both* operands are $1$. So in the example `9 | 13` only the second bit is $0$ for both operands, which is why the expression evaluates to `\"0b1101\"`." ] }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 46, "metadata": { "slideshow": { "slide_type": "slide" @@ -1793,7 +1612,7 @@ "'0b1001 | 0b1101'" ] }, - "execution_count": 54, + "execution_count": 46, "metadata": {}, "output_type": "execute_result" } @@ -1804,7 +1623,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 47, "metadata": { "slideshow": { "slide_type": "-" @@ -1817,7 +1636,7 @@ "'0b1101'" ] }, - "execution_count": 55, + "execution_count": 47, "metadata": {}, "output_type": "execute_result" } @@ -1839,10 +1658,10 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 48, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -1852,7 +1671,7 @@ "13" ] }, - "execution_count": 56, + "execution_count": 48, "metadata": {}, "output_type": "execute_result" } @@ -1863,7 +1682,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 49, "metadata": { "slideshow": { "slide_type": "-" @@ -1876,7 +1695,7 @@ "13" ] }, - "execution_count": 57, + "execution_count": 49, "metadata": {}, "output_type": "execute_result" } @@ -1893,12 +1712,12 @@ } }, "source": [ - "The **bitwise XOR** `^` operator is a special case of the `|` operator in that it evaluates to an `int` object whose bits are set to $1$ if the corresponding bit of exactly *one* of the two operands is $1$. Colloquially, the \"X\" stands for \"exclusive\". The `^` operator must *not* be confused with the `**` operator that does exponentiation! In the example `9 ^ 13` only the third bit differs between the two operands, which is why it evaluates to `\"0b100\"` omitting the fourth bit." + "The **bitwise XOR** operator `^` is a special case of the `|` operator in that it evaluates to an `int` object whose bits are set to $1$ if the corresponding bit of *exactly one* of the two operands is $1$. Colloquially, the \"X\" stands for \"exclusive.\" The `^` operator must *not* be confused with the exponentiation operator `**`! In the example, `9 ^ 13`, only the third bit differs between the two operands, which is why it evaluates to `\"0b100\"` omitting the fourth bit." ] }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 50, "metadata": { "slideshow": { "slide_type": "slide" @@ -1911,7 +1730,7 @@ "'0b1001 ^ 0b1101'" ] }, - "execution_count": 58, + "execution_count": 50, "metadata": {}, "output_type": "execute_result" } @@ -1922,7 +1741,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 51, "metadata": { "slideshow": { "slide_type": "-" @@ -1935,7 +1754,7 @@ "'0b100'" ] }, - "execution_count": 59, + "execution_count": 51, "metadata": {}, "output_type": "execute_result" } @@ -1957,10 +1776,10 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 52, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -1970,7 +1789,7 @@ "4" ] }, - "execution_count": 60, + "execution_count": 52, "metadata": {}, "output_type": "execute_result" } @@ -1981,7 +1800,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 53, "metadata": { "slideshow": { "slide_type": "-" @@ -1994,7 +1813,7 @@ "4" ] }, - "execution_count": 61, + "execution_count": 53, "metadata": {}, "output_type": "execute_result" } @@ -2011,14 +1830,14 @@ } }, "source": [ - "The **bitwise NOT** `~` operator, sometimes also called **inversion** operator, is said to simply \"flips\" the $0$s into $1$s and the $1$s into $0$s. However, it is based on the aforementioned [Two's Complement](https://en.wikipedia.org/wiki/Two%27s_complement) convention and `~x` is defined to be `~x = -(x + 1)` (cf., the [reference](https://docs.python.org/3/reference/expressions.html#unary-arithmetic-and-bitwise-operations)). The full logic behind this is considered out of scope in this book.\n", + "The **bitwise NOT** operator `~`, sometimes also called **inversion** operator, is said to \"flip\" the $0$s into $1$s and the $1$s into $0$s. However, it is based on the aforementioned [Two's Complement](https://en.wikipedia.org/wiki/Two%27s_complement) convention and `~x = -(x + 1)` by definition (cf., the [reference](https://docs.python.org/3/reference/expressions.html#unary-arithmetic-and-bitwise-operations)). The full logic behind this is considered out of scope in this book.\n", "\n", "We can at least verify the definition by comparing the binary representations of `8` and `-9`: They are indeed the same." ] }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 54, "metadata": { "slideshow": { "slide_type": "slide" @@ -2031,7 +1850,7 @@ "'-0b1001'" ] }, - "execution_count": 62, + "execution_count": 54, "metadata": {}, "output_type": "execute_result" } @@ -2042,7 +1861,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 55, "metadata": { "slideshow": { "slide_type": "-" @@ -2055,7 +1874,7 @@ "'-0b1001'" ] }, - "execution_count": 63, + "execution_count": 55, "metadata": {}, "output_type": "execute_result" } @@ -2077,10 +1896,10 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 56, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -2090,7 +1909,7 @@ "-1" ] }, - "execution_count": 64, + "execution_count": 56, "metadata": {}, "output_type": "execute_result" } @@ -2107,14 +1926,14 @@ } }, "source": [ - "Lastly, the **bitwise left and right shift** operators, `<<` and `>>`, simply shift all the bits either to the left or to the right. This corresponds to multiplying or dividing the integer by powers of $2$.\n", + "Lastly, the **bitwise left and right shift** operators, `<<` and `>>`, shift all the bits either to the left or to the right. This corresponds to multiplying or dividing an integer by powers of $2$.\n", "\n", "When shifting left, $0$s are filled in." ] }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 57, "metadata": { "slideshow": { "slide_type": "slide" @@ -2127,7 +1946,7 @@ "'0b1001'" ] }, - "execution_count": 65, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" } @@ -2138,7 +1957,7 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 58, "metadata": { "slideshow": { "slide_type": "-" @@ -2151,7 +1970,7 @@ "'0b100100'" ] }, - "execution_count": 66, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } @@ -2162,7 +1981,7 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 59, "metadata": { "slideshow": { "slide_type": "-" @@ -2175,7 +1994,7 @@ "36" ] }, - "execution_count": 67, + "execution_count": 59, "metadata": {}, "output_type": "execute_result" } @@ -2197,7 +2016,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 60, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2210,7 +2029,7 @@ "'0b10'" ] }, - "execution_count": 68, + "execution_count": 60, "metadata": {}, "output_type": "execute_result" } @@ -2221,7 +2040,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 61, "metadata": { "slideshow": { "slide_type": "-" @@ -2234,7 +2053,7 @@ "2" ] }, - "execution_count": 69, + "execution_count": 61, "metadata": {}, "output_type": "execute_result" } @@ -2262,18 +2081,18 @@ } }, "source": [ - "As we have seen above, some assumptions need to be made as to how the $0$s and $1$s in a computer's memory should be translated into numbers. This process becomes a lot more involved when we go beyond integers and model [real numbers](https://en.wikipedia.org/wiki/Real_number) (i.e., the set $\\mathbb{R}$) with possibly infinitely many digits to the right of the period like $1.23$.\n", + "As we have seen above, some assumptions need to be made as to how the $0$s and $1$s in a computer's memory are to be translated into numbers. This process becomes a lot more involved when we go beyond integers and model [real numbers](https://en.wikipedia.org/wiki/Real_number) (i.e., the set $\\mathbb{R}$) with possibly infinitely many digits to the right of the period like $1.23$.\n", "\n", - "The **[Institute of Electrical and Electronics Engineers](https://en.wikipedia.org/wiki/Institute_of_Electrical_and_Electronics_Engineers)** (IEEE, pronounced \"eye-tripple-E\") is one of the most important professional associations when it comes to standardizing all kinds of aspects regarding the implementation of soft- and hardware.\n", + "The **[Institute of Electrical and Electronics Engineers](https://en.wikipedia.org/wiki/Institute_of_Electrical_and_Electronics_Engineers)** (IEEE, pronounced \"eye-triple-E\") is one of the important professional associations when it comes to standardizing all kinds of aspects regarding the implementation of soft- and hardware.\n", "\n", - "The **[IEEE 754](https://en.wikipedia.org/wiki/IEEE_754)** standard defines the so-called **floating-point arithmetic** that is commonly used today by all major programming languages. The standard not only defines how the $0$s and $1$s are organized in memory but also, for example, how values are to be rounded, what happens in exceptional cases like divisions by zero, or what is a zero value to begin with.\n", + "The **[IEEE 754](https://en.wikipedia.org/wiki/IEEE_754)** standard defines the so-called **floating-point arithmetic** that is commonly used today by all major programming languages. The standard not only defines how the $0$s and $1$s are organized in memory but also, for example, how values are to be rounded, what happens in exceptional cases like divisions by zero, or what is a zero value in the first place.\n", "\n", - "In Python, the simplest way to create a `float` object is to just use a literal notation with a dot `.` in it." + "In Python, the simplest way to create a `float` object is to use a literal notation with a dot `.` in it." ] }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 62, "metadata": { "slideshow": { "slide_type": "slide" @@ -2281,12 +2100,12 @@ }, "outputs": [], "source": [ - "d = 1.23" + "f = 1.23" ] }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 63, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2296,21 +2115,21 @@ { "data": { "text/plain": [ - "140526571166576" + "139927641500360" ] }, - "execution_count": 71, + "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "id(d)" + "id(f)" ] }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 64, "metadata": { "slideshow": { "slide_type": "-" @@ -2323,18 +2142,18 @@ "float" ] }, - "execution_count": 72, + "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "type(d)" + "type(f)" ] }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 65, "metadata": { "slideshow": { "slide_type": "-" @@ -2347,13 +2166,13 @@ "1.23" ] }, - "execution_count": 73, + "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "d" + "f" ] }, { @@ -2369,7 +2188,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 66, "metadata": { "slideshow": { "slide_type": "skip" @@ -2379,16 +2198,16 @@ { "data": { "text/plain": [ - "0.123456" + "0.123456789" ] }, - "execution_count": 74, + "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "0.123_456" + "0.123_456_789" ] }, { @@ -2399,12 +2218,12 @@ } }, "source": [ - "In cases where the dot `.` is unnecessary from a mathematical point of view, we either need to still end the number with it or use the [float()](https://docs.python.org/3/library/functions.html#float) built-in to cast the number as a float explicitly. [float()](https://docs.python.org/3/library/functions.html#float) can also `str` objects." + "In cases where the dot `.` is unnecessary from a mathematical point of view, we either need to end the number with it nevertheless or use the [float()](https://docs.python.org/3/library/functions.html#float) built-in to cast the number explicitly. [float()](https://docs.python.org/3/library/functions.html#float) can process any numeric object or a properly formatted `str` object." ] }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 67, "metadata": { "slideshow": { "slide_type": "slide" @@ -2417,18 +2236,18 @@ "1.0" ] }, - "execution_count": 75, + "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "1. # on the contrary, 1 creates an integer object" + "1. # on the contrary, 1 creates an int object" ] }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 68, "metadata": { "slideshow": { "slide_type": "-" @@ -2441,7 +2260,7 @@ "1.0" ] }, - "execution_count": 76, + "execution_count": 68, "metadata": {}, "output_type": "execute_result" } @@ -2452,7 +2271,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 69, "metadata": { "slideshow": { "slide_type": "skip" @@ -2465,7 +2284,7 @@ "1.0" ] }, - "execution_count": 77, + "execution_count": 69, "metadata": {}, "output_type": "execute_result" } @@ -2482,15 +2301,15 @@ } }, "source": [ - "Floats are implicitly created as a result of dividing an integer by another with the division operator `/`." + "`float` objects are implicitly created as the result of dividing an `int` object by another with the division operator `/`." ] }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 70, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "slide" } }, "outputs": [ @@ -2500,7 +2319,7 @@ "0.3333333333333333" ] }, - "execution_count": 78, + "execution_count": 70, "metadata": {}, "output_type": "execute_result" } @@ -2517,36 +2336,36 @@ } }, "source": [ - "In general, if we combine `float` and `int` objects in arithmetic operations, we always end up with a `float` type." + "In general, if we combine `float` and `int` objects in arithmetic operations, we always end up with a `float` type: Python uses the \"broader\" representation." ] }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 71, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "3.0" + "42.0" ] }, - "execution_count": 79, + "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "1.0 + 2" + "40.0 + 2" ] }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 72, "metadata": { "slideshow": { "slide_type": "-" @@ -2559,7 +2378,7 @@ "42.0" ] }, - "execution_count": 80, + "execution_count": 72, "metadata": {}, "output_type": "execute_result" } @@ -2587,12 +2406,12 @@ } }, "source": [ - "`float` objects may also be created with the **scientifc literal notation**: We use the symbol `e` to indicate powers of $10$, so $1.23 * 10^0$ translates into `1.23e0`." + "`float` objects may also be created with the **scientific literal notation**: We use the symbol `e` to indicate powers of $10$, so $1.23 * 10^0$ translates into `1.23e0`." ] }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 73, "metadata": { "slideshow": { "slide_type": "slide" @@ -2605,7 +2424,7 @@ "1.23" ] }, - "execution_count": 81, + "execution_count": 73, "metadata": {}, "output_type": "execute_result" } @@ -2622,12 +2441,12 @@ } }, "source": [ - "Syntactically, `e` needs a `float` object in its literal notation to its left and an `int` object to its right, both without a space. Otherwise, we get a `SyntaxError`." + "Syntactically, `e` needs a `float` or `int` object in its literal notation on its left and an `int` object on its right, both without a space. Otherwise, we get a `SyntaxError`." ] }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 74, "metadata": { "slideshow": { "slide_type": "skip" @@ -2636,10 +2455,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "invalid syntax (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23 e0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23 e0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], @@ -2649,7 +2468,7 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 75, "metadata": { "slideshow": { "slide_type": "skip" @@ -2658,10 +2477,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "invalid syntax (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23e 0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23e 0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], @@ -2671,7 +2490,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 76, "metadata": { "slideshow": { "slide_type": "skip" @@ -2680,10 +2499,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "invalid syntax (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23e0.0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23e0.0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], @@ -2699,12 +2518,12 @@ } }, "source": [ - "If we leave out the number to the left, Python raises a `NameError` as it actually tries to look up a variable named `e1`." + "If we leave out the number to the left, Python raises a `NameError` as it unsuccessfully tries to look up a variable named `e1`." ] }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 77, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2718,7 +2537,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0me1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0me1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'e1' is not defined" ] } @@ -2735,12 +2554,12 @@ } }, "source": [ - "So, in order to write $10^1$ in Python, we need to think of it as $1*10^1$ and write `1e1`." + "So, to write $10^1$ in Python, we need to think of it as $1*10^1$ and write `1e1`." ] }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 78, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2753,7 +2572,7 @@ "10.0" ] }, - "execution_count": 86, + "execution_count": 78, "metadata": {}, "output_type": "execute_result" } @@ -2781,12 +2600,12 @@ } }, "source": [ - "There are also three special values representing **\"not a number\"**, called `nan`, and positive or negative **infinity**, called `inf`, that are created by passing in the corresponding abbreviation as a `str` object to the [float()](https://docs.python.org/3/library/functions.html#float) built-in. These values could be used, for example, as the result of a mathematically undefined operation like division by zero or to model the value of a mathematical function as it goes to infinity." + "There are also three special values representing \"**not a number,**\" called `nan`, and positive or negative **infinity**, called `inf` or `-inf`, that are created by passing in the corresponding abbreviation as a `str` object to the [float()](https://docs.python.org/3/library/functions.html#float) built-in. These values could be used, for example, as the result of a mathematically undefined operation like division by zero or to model the value of a mathematical function as it goes to infinity." ] }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 79, "metadata": { "slideshow": { "slide_type": "slide" @@ -2799,7 +2618,7 @@ "nan" ] }, - "execution_count": 87, + "execution_count": 79, "metadata": {}, "output_type": "execute_result" } @@ -2810,7 +2629,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 80, "metadata": { "slideshow": { "slide_type": "skip" @@ -2823,7 +2642,7 @@ "inf" ] }, - "execution_count": 88, + "execution_count": 80, "metadata": {}, "output_type": "execute_result" } @@ -2834,7 +2653,7 @@ }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 81, "metadata": { "slideshow": { "slide_type": "-" @@ -2847,18 +2666,18 @@ "inf" ] }, - "execution_count": 89, + "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "float(\"inf\") # by omitting the plus sign, we mean positive infinity" + "float(\"inf\") # by omitting the plus sign we mean positive infinity" ] }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 82, "metadata": { "slideshow": { "slide_type": "-" @@ -2871,7 +2690,7 @@ "-inf" ] }, - "execution_count": 90, + "execution_count": 82, "metadata": {}, "output_type": "execute_result" } @@ -2888,12 +2707,12 @@ } }, "source": [ - "`nan` objects *never* compare equal to *anything*, not even to themselves. This happens in accordance with the IEEE 754 standard." + "`nan` objects *never* compare equal to *anything*, not even to themselves. This happens in accordance with the [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) standard." ] }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 83, "metadata": { "slideshow": { "slide_type": "slide" @@ -2906,7 +2725,7 @@ "False" ] }, - "execution_count": 91, + "execution_count": 83, "metadata": {}, "output_type": "execute_result" } @@ -2923,7 +2742,243 @@ } }, "source": [ - "On the contrary, as two values go to infinity, there is no such concept as difference any more and *everything* compares equal." + "On the contrary, as two values go to infinity, there is no such concept as difference and *everything* compares equal." + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"inf\") == float(\"inf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Adding `42` to `inf` makes no difference." + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "inf" + ] + }, + "execution_count": 85, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"inf\") + 42" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"inf\") + 42 == float(\"inf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We observe the same for multiplication ..." + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "inf" + ] + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 * float(\"inf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 * float(\"inf\") == float(\"inf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... and even exponentiation!" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "inf" + ] + }, + "execution_count": 89, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"inf\") ** 42" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 90, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"inf\") ** 42 == float(\"inf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Although absolute differences become unmeaningful as we approach infinity, signs are still respected." + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "inf" + ] + }, + "execution_count": 91, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "-42 * float(\"-inf\")" ] }, { @@ -2946,242 +3001,6 @@ "output_type": "execute_result" } ], - "source": [ - "float(\"inf\") == float(\"inf\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "Adding `42` to `inf` makes no difference." - ] - }, - { - "cell_type": "code", - "execution_count": 93, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "inf" - ] - }, - "execution_count": 93, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "float(\"inf\") + 42" - ] - }, - { - "cell_type": "code", - "execution_count": 94, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 94, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "float(\"inf\") + 42 == float(\"inf\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "We observe the same for multiplication ..." - ] - }, - { - "cell_type": "code", - "execution_count": 95, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "inf" - ] - }, - "execution_count": 95, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "42 * float(\"inf\")" - ] - }, - { - "cell_type": "code", - "execution_count": 96, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 96, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "42 * float(\"inf\") == float(\"inf\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "... and even exponentiation!" - ] - }, - { - "cell_type": "code", - "execution_count": 97, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "inf" - ] - }, - "execution_count": 97, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "float(\"inf\") ** 42" - ] - }, - { - "cell_type": "code", - "execution_count": 98, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 98, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "float(\"inf\") ** 42 == float(\"inf\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "Although differences become unmeaningful as we approach infinity, signs are still respected." - ] - }, - { - "cell_type": "code", - "execution_count": 99, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "inf" - ] - }, - "execution_count": 99, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "-42 * float(\"-inf\")" - ] - }, - { - "cell_type": "code", - "execution_count": 100, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 100, - "metadata": {}, - "output_type": "execute_result" - } - ], "source": [ "-42 * float(\"-inf\") == float(\"inf\")" ] @@ -3194,12 +3013,12 @@ } }, "source": [ - "As a caveat, adding infinities of different signs is an *undefined operation* in math and results in a `nan` object." + "As a caveat, adding infinities of different signs is an *undefined operation* in math and results in a `nan` object. So, if we (accidentally or unknowingly) do this on a real dataset, we do *not* see any error messages, and our program may continue to run with non-meaningful results! This is an example of a piece of code **failing silently**." ] }, { "cell_type": "code", - "execution_count": 101, + "execution_count": 93, "metadata": { "slideshow": { "slide_type": "slide" @@ -3212,7 +3031,7 @@ "nan" ] }, - "execution_count": 101, + "execution_count": 93, "metadata": {}, "output_type": "execute_result" } @@ -3223,10 +3042,10 @@ }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 94, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "-" } }, "outputs": [ @@ -3236,7 +3055,7 @@ "nan" ] }, - "execution_count": 102, + "execution_count": 94, "metadata": {}, "output_type": "execute_result" } @@ -3264,14 +3083,14 @@ } }, "source": [ - "`float` objects are *inherently* imprecise and there is *nothing* we can do about it! In particular, arithmetic operations with two `float` objects may result in \"weird\" rounding \"errors\" if their exponents are \"too far apart\". These \"errors\" are actually stictly deterministic and occur as a consequence of the IEEE 754 standard.\n", + "`float` objects are *inherently* imprecise, and there is *nothing* we can do about it! In particular, arithmetic operations with two `float` objects may result in \"weird\" rounding \"errors\" that are strictly deterministic and occur in accordance with the [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) standard.\n", "\n", - "For example, let's add `1e0` to `1e15` and `1e16`, respectively. We see that in the second case, the `1e0` somehow gets \"lost\"." + "For example, let's add `1e0` to `1e15` and `1e16`, respectively. In the latter case, the `1e0` somehow gets \"lost\"." ] }, { "cell_type": "code", - "execution_count": 103, + "execution_count": 95, "metadata": { "slideshow": { "slide_type": "skip" @@ -3284,7 +3103,7 @@ "1000000000000001.0" ] }, - "execution_count": 103, + "execution_count": 95, "metadata": {}, "output_type": "execute_result" } @@ -3295,7 +3114,7 @@ }, { "cell_type": "code", - "execution_count": 104, + "execution_count": 96, "metadata": { "slideshow": { "slide_type": "skip" @@ -3308,7 +3127,7 @@ "1e+16" ] }, - "execution_count": 104, + "execution_count": 96, "metadata": {}, "output_type": "execute_result" } @@ -3325,12 +3144,12 @@ } }, "source": [ - "Of course, we may also add the integer `1` to the `float` objects in the literal `e` notation with the same outcome." + "Of course, we may also add the `int` object `1` to the `float` objects in the literal `e` notation with the same outcome." ] }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 97, "metadata": { "slideshow": { "slide_type": "slide" @@ -3343,7 +3162,7 @@ "1000000000000001.0" ] }, - "execution_count": 105, + "execution_count": 97, "metadata": {}, "output_type": "execute_result" } @@ -3354,7 +3173,7 @@ }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 98, "metadata": { "slideshow": { "slide_type": "-" @@ -3367,7 +3186,7 @@ "1e+16" ] }, - "execution_count": 106, + "execution_count": 98, "metadata": {}, "output_type": "execute_result" } @@ -3384,12 +3203,12 @@ } }, "source": [ - "Interactions between very large and very small `float` objects are not the only source of imprecision." + "Interactions between sufficiently large and small `float` objects are not the only source of imprecision." ] }, { "cell_type": "code", - "execution_count": 107, + "execution_count": 99, "metadata": { "slideshow": { "slide_type": "slide" @@ -3402,7 +3221,7 @@ }, { "cell_type": "code", - "execution_count": 108, + "execution_count": 100, "metadata": { "slideshow": { "slide_type": "-" @@ -3415,7 +3234,7 @@ "2.0000000000000004" ] }, - "execution_count": 108, + "execution_count": 100, "metadata": {}, "output_type": "execute_result" } @@ -3426,7 +3245,7 @@ }, { "cell_type": "code", - "execution_count": 109, + "execution_count": 101, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3439,7 +3258,7 @@ "0.30000000000000004" ] }, - "execution_count": 109, + "execution_count": 101, "metadata": {}, "output_type": "execute_result" } @@ -3461,7 +3280,7 @@ }, { "cell_type": "code", - "execution_count": 110, + "execution_count": 102, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3474,7 +3293,7 @@ "False" ] }, - "execution_count": 110, + "execution_count": 102, "metadata": {}, "output_type": "execute_result" } @@ -3485,7 +3304,7 @@ }, { "cell_type": "code", - "execution_count": 111, + "execution_count": 103, "metadata": { "slideshow": { "slide_type": "-" @@ -3498,7 +3317,7 @@ "False" ] }, - "execution_count": 111, + "execution_count": 103, "metadata": {}, "output_type": "execute_result" } @@ -3515,12 +3334,12 @@ } }, "source": [ - "A popular workaround is to check if the difference between the two numbers is smaller than a pre-defined `threshold` *sufficiently* close to `0`, for example, `1e-15`." + "A popular workaround is to benchmark the difference between the two numbers to be checked for equality against a pre-defined `threshold` *sufficiently* close to `0`, for example, `1e-15`." ] }, { "cell_type": "code", - "execution_count": 112, + "execution_count": 104, "metadata": { "slideshow": { "slide_type": "slide" @@ -3533,7 +3352,7 @@ }, { "cell_type": "code", - "execution_count": 113, + "execution_count": 105, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3546,7 +3365,7 @@ "True" ] }, - "execution_count": 113, + "execution_count": 105, "metadata": {}, "output_type": "execute_result" } @@ -3557,7 +3376,7 @@ }, { "cell_type": "code", - "execution_count": 114, + "execution_count": 106, "metadata": { "slideshow": { "slide_type": "-" @@ -3570,7 +3389,7 @@ "True" ] }, - "execution_count": 114, + "execution_count": 106, "metadata": {}, "output_type": "execute_result" } @@ -3587,14 +3406,14 @@ } }, "source": [ - "The built-in [format()](https://docs.python.org/3/library/functions.html#format) function allows us to show the **significant digits** of a `float` number, as they exist in memory, to an arbitrary precision. To exemplify it, let's view a couple of `float` objects with `50` digits. This analysis reveals that almost no `float` number is precise. After $14$ or $15$ digits \"weird\" things happen. As we will see below, the \"random\" digits do *not* actually exist in memory, which is why Python shows them to us in this \"weird\" way.\n", + "The built-in [format()](https://docs.python.org/3/library/functions.html#format) function allows us to show the **significant digits** of a `float` number as they exist in memory to arbitrary precision. To exemplify it, let's view a couple of `float` objects with `50` digits. This analysis reveals that almost no `float` number is precise! After $14$ or $15$ digits \"weird\" things happen. As we see further below, the \"random\" digits ending the `float` numbers do *not* \"physically\" exist in memory.\n", "\n", - "Note that the [format()](https://docs.python.org/3/library/functions.html#format) function is different from the [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method on `str` objects introduced in the next chapter and that both work with the so-called [format specification mini-language](https://docs.python.org/3/library/string.html#format-specification-mini-language): `\".50f\"` is the instruction to show `50` digits of a `float` number. But let's not worry too much about these details for now." + "The [format()](https://docs.python.org/3/library/functions.html#format) function is different from the [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method on `str` objects introduced in the next chapter and both work with the so-called [format specification mini-language](https://docs.python.org/3/library/string.html#format-specification-mini-language): `\".50f\"` is the instruction to show `50` digits of a `float` number. But let's not worry too much about these details for now." ] }, { "cell_type": "code", - "execution_count": 115, + "execution_count": 107, "metadata": { "slideshow": { "slide_type": "slide" @@ -3607,7 +3426,7 @@ "'0.10000000000000000555111512312578270211815834045410'" ] }, - "execution_count": 115, + "execution_count": 107, "metadata": {}, "output_type": "execute_result" } @@ -3618,7 +3437,7 @@ }, { "cell_type": "code", - "execution_count": 116, + "execution_count": 108, "metadata": { "slideshow": { "slide_type": "-" @@ -3631,7 +3450,7 @@ "'0.20000000000000001110223024625156540423631668090820'" ] }, - "execution_count": 116, + "execution_count": 108, "metadata": {}, "output_type": "execute_result" } @@ -3642,7 +3461,7 @@ }, { "cell_type": "code", - "execution_count": 117, + "execution_count": 109, "metadata": { "slideshow": { "slide_type": "-" @@ -3655,7 +3474,7 @@ "'0.29999999999999998889776975374843459576368331909180'" ] }, - "execution_count": 117, + "execution_count": 109, "metadata": {}, "output_type": "execute_result" } @@ -3666,7 +3485,7 @@ }, { "cell_type": "code", - "execution_count": 118, + "execution_count": 110, "metadata": { "slideshow": { "slide_type": "slide" @@ -3679,7 +3498,7 @@ "'0.33333333333333331482961625624739099293947219848633'" ] }, - "execution_count": 118, + "execution_count": 110, "metadata": {}, "output_type": "execute_result" } @@ -3696,16 +3515,16 @@ } }, "source": [ - "The [format()](https://docs.python.org/3/library/functions.html#format) function does *not* round a number in the mathematical sense! It just allows us to show an arbitrary number of the digits as stored in memory and it also does *not* change these.\n", + "The [format()](https://docs.python.org/3/library/functions.html#format) function does *not* round a `float` object in the mathematical sense! It just allows us to show an arbitrary number of the digits as stored in memory, and it also does *not* change these.\n", "\n", - "On the contrary, the built-in [round()](https://docs.python.org/3/library/functions.html#round) function creates a *new* numeric object that is a rounded version according to the common rules from math.\n", + "On the contrary, the built-in [round()](https://docs.python.org/3/library/functions.html#round) function creates a *new* numeric object that is a rounded version of the one passed in as the argument. It adheres to the common rules of math.\n", "\n", - "For example, let's \"round\" `1 / 3` to five digits. We see that the obtained value for `roughly_a_third` is also *imprecise* but different from the \"exact\" represenation of `1 / 3` above." + "For example, let's round `1 / 3` to five decimals. The obtained value for `roughly_a_third` is also *imprecise* but different from the \"exact\" representation of `1 / 3` above." ] }, { "cell_type": "code", - "execution_count": 119, + "execution_count": 111, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3718,7 +3537,7 @@ }, { "cell_type": "code", - "execution_count": 120, + "execution_count": 112, "metadata": { "slideshow": { "slide_type": "-" @@ -3731,7 +3550,7 @@ "0.33333" ] }, - "execution_count": 120, + "execution_count": 112, "metadata": {}, "output_type": "execute_result" } @@ -3742,7 +3561,7 @@ }, { "cell_type": "code", - "execution_count": 121, + "execution_count": 113, "metadata": { "slideshow": { "slide_type": "-" @@ -3755,7 +3574,7 @@ "'0.33333000000000001517008740847813896834850311279297'" ] }, - "execution_count": 121, + "execution_count": 113, "metadata": {}, "output_type": "execute_result" } @@ -3772,12 +3591,12 @@ } }, "source": [ - "Surprisingly, `0.125` and `0.25` appear to be *precise* and equality comparison works without the `threshold` workaround." + "Surprisingly, `0.125` and `0.25` appear to be *precise*, and equality comparison works without the `threshold` workaround: Both are powers of $2$ in disguise." ] }, { "cell_type": "code", - "execution_count": 122, + "execution_count": 114, "metadata": { "slideshow": { "slide_type": "slide" @@ -3790,7 +3609,7 @@ "'0.12500000000000000000000000000000000000000000000000'" ] }, - "execution_count": 122, + "execution_count": 114, "metadata": {}, "output_type": "execute_result" } @@ -3801,7 +3620,7 @@ }, { "cell_type": "code", - "execution_count": 123, + "execution_count": 115, "metadata": { "slideshow": { "slide_type": "-" @@ -3814,7 +3633,7 @@ "'0.25000000000000000000000000000000000000000000000000'" ] }, - "execution_count": 123, + "execution_count": 115, "metadata": {}, "output_type": "execute_result" } @@ -3825,7 +3644,7 @@ }, { "cell_type": "code", - "execution_count": 124, + "execution_count": 116, "metadata": { "slideshow": { "slide_type": "-" @@ -3838,7 +3657,7 @@ "True" ] }, - "execution_count": 124, + "execution_count": 116, "metadata": {}, "output_type": "execute_result" } @@ -3866,13 +3685,13 @@ } }, "source": [ - "In order to understand these subtleties, we need to look at the **[binary representation of floats](https://en.wikipedia.org/wiki/Double-precision_floating-point_format)** and review the basics of the **[IEEE 754](https://en.wikipedia.org/wiki/IEEE_754)** standard. On modern machines, floats are modelled in so-called double precision with $64$ bits that are grouped as in the picture below. The first bit determines the sign ($0$ for plus, $1$ for minus), the next $11$ bits represent an $exponent$ term, and the last $52$ bits resemble the actual significant digits, the so-called $fraction$ part. The three groups are put together like so:\n", + "To understand these subtleties, we need to look at the **[binary representation of floats](https://en.wikipedia.org/wiki/Double-precision_floating-point_format)** and review the basics of the **[IEEE 754](https://en.wikipedia.org/wiki/IEEE_754)** standard. On modern machines, floats are modeled in so-called double precision with $64$ bits that are grouped as in the figure below. The first bit determines the sign ($0$ for plus, $1$ for minus), the next $11$ bits represent an $exponent$ term, and the last $52$ bits resemble the actual significant digits, the so-called $fraction$ part. The three groups are put together like so:\n", "\n", "$$float = (-1)^{sign} * 1.fraction * 2^{exponent-1023}$$\n", "\n", - "Observe that a $1.$ is implicitly prepended as the first digit and that both $fraction$ and $exponent$ are stored in base $2$ representation (i.e., they both are interpreted just as the integers above). As $exponent$ is consequently non-negative, between $0_{10}$ and $2047_{10}$ to be precise, the $-1023$ centers the entire $2^{exponent-1023}$ term around $1$ and allows for the period within the $1.fraction$ part to be shifted into either direction by the same amount. Floating-point numbers received their name as the period, formally called the **[radix point](https://en.wikipedia.org/wiki/Radix_point)**,\"floats\" along the significant digits. As an aside, an $exponent$ of all $0$s or all $1$s is used to model the special values `nan` or `inf`.\n", + "A $1.$ is implicitly prepended as the first digit, and both, $fraction$ and $exponent$, are stored in base $2$ representation (i.e., they both are interpreted like integers above). As $exponent$ is consequently non-negative, between $0_{10}$ and $2047_{10}$ to be precise, the $-1023$ centers the entire $2^{exponent-1023}$ term around $1$ and allows the period within the $1.fraction$ part be shifted into either direction by the same amount. Floating-point numbers received their name as the period, formally called the **[radix point](https://en.wikipedia.org/wiki/Radix_point)**, \"floats\" along the significant digits. As an aside, an $exponent$ of all $0$s or all $1$s is used to model the special values `nan` or `inf`.\n", "\n", - "As the standard defines the exponent part to come as a power of $2$, we now see why `0.125` is a *precise* float. It simply can be represented as a power of $2$, i.e., $0.125 = (-1)^0 * 1.0 * 2^{1020-1023} = 2^{-3} = \\frac{1}{8}$. In other words, the floating-point representation of $0.125_{10}$ is $0_2$, $1111111100_2 = 1020_{10}$, and $0_2$ for the three groups, respectively." + "As the standard defines the exponent part to come as a power of $2$, we now see why `0.125` is a *precise* float: It can be represented as a power of $2$, i.e., $0.125 = (-1)^0 * 1.0 * 2^{1020-1023} = 2^{-3} = \\frac{1}{8}$. In other words, the floating-point representation of $0.125_{10}$ is $0_2$, $1111111100_2 = 1020_{10}$, and $0_2$ for the three groups, respectively." ] }, { @@ -3894,22 +3713,20 @@ } }, "source": [ - "The important fact for data science practitioners to understand is that mapping the *infinite* set of the real numbers $\\mathbb{R}$ to a *finite* set of bits leads to the imprecisions shown above!\n", + "The crucial fact for the data science practitioner to understand is that mapping the *infinite* set of the real numbers $\\mathbb{R}$ to a *finite* set of bits leads to the imprecisions shown above!\n", "\n", - "So, floats are usually good approximations of real numbers only with their first $14$ or $15$ significant digits. If more precision is required, we need to revert to other data types such as a `Decimal` or a `Fraction` as shown in the next two sections.\n", + "So, floats are usually good approximations of real numbers only with their first $14$ or $15$ digits. If more precision is required, we need to revert to other data types such as a `Decimal` or a `Fraction`, as shown in the next two sections.\n", "\n", "This [blog post](http://fabiensanglard.net/floating_point_visually_explained/) gives another neat and *visual* way as to how to think of floats. It also explains why floats become worse approximations of the reals as their absolute values increase.\n", "\n", "The Python [documentation](https://docs.python.org/3/tutorial/floatingpoint.html) provides another good discussion of floats and the goodness of their approximations.\n", "\n", - "If we are interested in the exact bits behind a `float` object, we use the [hex()](https://docs.python.org/3/library/stdtypes.html#float.hex) method that returns a `str` object beginning with `\"0x1.\"` followed by the $fraction$ in hexadecimal notation and the $exponent$ as an integer after subtraction of the $1023$ seperated by a `\"p\"`.\n", - "\n", - "Also, the [as_integer_ratio()](https://docs.python.org/3/library/stdtypes.html#float.as_integer_ratio) method returns the two smallest integers whose ratio best approximates a `float` object." + "If we are interested in the exact bits behind a `float` object, we use the [hex()](https://docs.python.org/3/library/stdtypes.html#float.hex) method that returns a `str` object beginning with `\"0x1.\"` followed by the $fraction$ in hexadecimal notation and the $exponent$ as an integer after subtraction of $1023$ and separated by a `\"p\"`." ] }, { "cell_type": "code", - "execution_count": 125, + "execution_count": 117, "metadata": { "slideshow": { "slide_type": "slide" @@ -3922,7 +3739,7 @@ }, { "cell_type": "code", - "execution_count": 126, + "execution_count": 118, "metadata": { "slideshow": { "slide_type": "-" @@ -3935,18 +3752,29 @@ "'0x1.0000000000000p-3'" ] }, - "execution_count": 126, + "execution_count": 118, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "one_eighth.hex() # the result basically says 2 ** (-3)" + "one_eighth.hex() # this basically says 2 ** (-3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Also, the [as_integer_ratio()](https://docs.python.org/3/library/stdtypes.html#float.as_integer_ratio) method returns the two smallest integers whose ratio best approximates a `float` object." ] }, { "cell_type": "code", - "execution_count": 127, + "execution_count": 119, "metadata": { "slideshow": { "slide_type": "-" @@ -3959,7 +3787,7 @@ "(1, 8)" ] }, - "execution_count": 127, + "execution_count": 119, "metadata": {}, "output_type": "execute_result" } @@ -3970,7 +3798,7 @@ }, { "cell_type": "code", - "execution_count": 128, + "execution_count": 120, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3983,7 +3811,7 @@ "'0x1.555475a31a4bep-2'" ] }, - "execution_count": 128, + "execution_count": 120, "metadata": {}, "output_type": "execute_result" } @@ -3994,7 +3822,7 @@ }, { "cell_type": "code", - "execution_count": 129, + "execution_count": 121, "metadata": { "slideshow": { "slide_type": "-" @@ -4007,7 +3835,7 @@ "(3002369727582815, 9007199254740992)" ] }, - "execution_count": 129, + "execution_count": 121, "metadata": {}, "output_type": "execute_result" } @@ -4024,12 +3852,12 @@ } }, "source": [ - "`0.0` is also a *precise* `float` number." + "`0.0` is also a power of $2$ and thus a *precise* `float` number." ] }, { "cell_type": "code", - "execution_count": 130, + "execution_count": 122, "metadata": { "slideshow": { "slide_type": "slide" @@ -4042,7 +3870,7 @@ }, { "cell_type": "code", - "execution_count": 131, + "execution_count": 123, "metadata": { "slideshow": { "slide_type": "-" @@ -4055,7 +3883,7 @@ "'0x0.0p+0'" ] }, - "execution_count": 131, + "execution_count": 123, "metadata": {}, "output_type": "execute_result" } @@ -4066,7 +3894,7 @@ }, { "cell_type": "code", - "execution_count": 132, + "execution_count": 124, "metadata": { "slideshow": { "slide_type": "-" @@ -4079,7 +3907,7 @@ "(0, 1)" ] }, - "execution_count": 132, + "execution_count": 124, "metadata": {}, "output_type": "execute_result" } @@ -4096,12 +3924,12 @@ } }, "source": [ - "As seen in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#%28Data%29-Type-%2F-%22Behavior%22), the [is_integer()](https://docs.python.org/3/library/stdtypes.html#float.is_integer) method tells us if a `float` can be converted into an `int` object without any loss in precision." + "As seen in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#%28Data%29-Type-%2F-%22Behavior%22), the [is_integer()](https://docs.python.org/3/library/stdtypes.html#float.is_integer) method tells us if a `float` can be casted as an `int` object without any loss in precision." ] }, { "cell_type": "code", - "execution_count": 133, + "execution_count": 125, "metadata": { "slideshow": { "slide_type": "skip" @@ -4114,7 +3942,7 @@ "False" ] }, - "execution_count": 133, + "execution_count": 125, "metadata": {}, "output_type": "execute_result" } @@ -4125,7 +3953,7 @@ }, { "cell_type": "code", - "execution_count": 134, + "execution_count": 126, "metadata": { "slideshow": { "slide_type": "skip" @@ -4138,7 +3966,7 @@ "True" ] }, - "execution_count": 134, + "execution_count": 126, "metadata": {}, "output_type": "execute_result" } @@ -4157,12 +3985,12 @@ } }, "source": [ - "As the exact implementation of floats may vary and be dependend on a particular Python installation, we can always use the [sys.float_info](https://docs.python.org/3/library/sys.html#sys.float_info) attribute in the [sys](https://docs.python.org/3/library/sys.html) module in the [standard library](https://docs.python.org/3/library/) to check the details. Usually, this is not necessary." + "As the exact implementation of floats may vary and be dependent on a particular Python installation, we look up the [float_info](https://docs.python.org/3/library/sys.html#sys.float_info) attribute in the [sys](https://docs.python.org/3/library/sys.html) module in the [standard library](https://docs.python.org/3/library/) to check the details. Usually, this is not necessary." ] }, { "cell_type": "code", - "execution_count": 135, + "execution_count": 127, "metadata": { "slideshow": { "slide_type": "skip" @@ -4175,7 +4003,7 @@ }, { "cell_type": "code", - "execution_count": 136, + "execution_count": 128, "metadata": { "slideshow": { "slide_type": "skip" @@ -4188,7 +4016,7 @@ "sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)" ] }, - "execution_count": 136, + "execution_count": 128, "metadata": {}, "output_type": "execute_result" } @@ -4216,14 +4044,14 @@ } }, "source": [ - "The [decimal](https://docs.python.org/3/library/decimal.html) module in the standard library provides a [Decimal](https://docs.python.org/3/library/decimal.html#decimal.Decimal) type that is used to represent any real number to a user-defined level of precision: \"User-defined\" does *not* mean an infinite or \"exact\" precision! The `Decimal` type merely allows us to work with a number of bits *different* from the $64$ as specified for the `float` type and also to customize the rounding rules and some other settings.\n", + "The [decimal](https://docs.python.org/3/library/decimal.html) module in the standard library provides a [Decimal](https://docs.python.org/3/library/decimal.html#decimal.Decimal) type that may be used to represent any real number to a user-defined level of precision: \"User-defined\" does *not* mean an infinite or \"exact\" precision! The `Decimal` type merely allows us to work with a number of bits *different* from the $64$ as specified for the `float` type and also to customize the rounding rules and some other settings.\n", "\n", - "Let's first import the `Decimal` type and also the [getcontext()](https://docs.python.org/3/library/decimal.html#decimal.getcontext) function from the [decimal](https://docs.python.org/3/library/decimal.html) module." + "We import the `Decimal` type and also the [getcontext()](https://docs.python.org/3/library/decimal.html#decimal.getcontext) function from the [decimal](https://docs.python.org/3/library/decimal.html) module." ] }, { "cell_type": "code", - "execution_count": 137, + "execution_count": 129, "metadata": { "slideshow": { "slide_type": "slide" @@ -4247,7 +4075,7 @@ }, { "cell_type": "code", - "execution_count": 138, + "execution_count": 130, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4260,7 +4088,7 @@ "Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])" ] }, - "execution_count": 138, + "execution_count": 130, "metadata": {}, "output_type": "execute_result" } @@ -4277,12 +4105,12 @@ } }, "source": [ - "The two simplest ways to create a `Decimal` object is to either **instantiate** it with an `int` object or a `str` object consisting of all the significant digits. In the latter case, a scientific notation is also possible." + "The two simplest ways to create a `Decimal` object is to either **instantiate** it with an `int` or a `str` object consisting of all the significant digits. In the latter case, the scientific notation is also possible." ] }, { "cell_type": "code", - "execution_count": 139, + "execution_count": 131, "metadata": { "slideshow": { "slide_type": "slide" @@ -4295,7 +4123,7 @@ "Decimal('42')" ] }, - "execution_count": 139, + "execution_count": 131, "metadata": {}, "output_type": "execute_result" } @@ -4306,7 +4134,7 @@ }, { "cell_type": "code", - "execution_count": 140, + "execution_count": 132, "metadata": { "slideshow": { "slide_type": "-" @@ -4319,7 +4147,7 @@ "Decimal('0.1')" ] }, - "execution_count": 140, + "execution_count": 132, "metadata": {}, "output_type": "execute_result" } @@ -4330,7 +4158,7 @@ }, { "cell_type": "code", - "execution_count": 141, + "execution_count": 133, "metadata": { "slideshow": { "slide_type": "-" @@ -4343,7 +4171,7 @@ "Decimal('1E+5')" ] }, - "execution_count": 141, + "execution_count": 133, "metadata": {}, "output_type": "execute_result" } @@ -4360,12 +4188,12 @@ } }, "source": [ - "Is is *not* a good idea to create a `Decimal` object from a `float` object. If we did so, we would create a `Decimal` object that internally used extra bits to store the \"random\" digits that are not stored for a `float` but \"seen\" by Python nevertheless." + "It is *not* a good idea to create a `Decimal` from a `float` object. If we did so, we would create a `Decimal` object that internally used extra bits to store the \"random\" digits that are not stored in the `float` object in the first place." ] }, { "cell_type": "code", - "execution_count": 142, + "execution_count": 134, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4378,7 +4206,7 @@ "Decimal('0.1000000000000000055511151231257827021181583404541015625')" ] }, - "execution_count": 142, + "execution_count": 134, "metadata": {}, "output_type": "execute_result" } @@ -4395,12 +4223,12 @@ } }, "source": [ - "With the `Decimal` type, the imprecisions in the arithmetic and equality comparisons from above are gone." + "With the `Decimal` type, the imprecisions in the arithmetic and equality comparisons from above go away." ] }, { "cell_type": "code", - "execution_count": 143, + "execution_count": 135, "metadata": { "slideshow": { "slide_type": "slide" @@ -4413,7 +4241,7 @@ "Decimal('0.3')" ] }, - "execution_count": 143, + "execution_count": 135, "metadata": {}, "output_type": "execute_result" } @@ -4424,7 +4252,7 @@ }, { "cell_type": "code", - "execution_count": 144, + "execution_count": 136, "metadata": { "slideshow": { "slide_type": "-" @@ -4437,7 +4265,7 @@ "True" ] }, - "execution_count": 144, + "execution_count": 136, "metadata": {}, "output_type": "execute_result" } @@ -4459,7 +4287,7 @@ }, { "cell_type": "code", - "execution_count": 145, + "execution_count": 137, "metadata": { "slideshow": { "slide_type": "slide" @@ -4472,7 +4300,7 @@ "Decimal('42')" ] }, - "execution_count": 145, + "execution_count": 137, "metadata": {}, "output_type": "execute_result" } @@ -4483,7 +4311,7 @@ }, { "cell_type": "code", - "execution_count": 146, + "execution_count": 138, "metadata": { "slideshow": { "slide_type": "-" @@ -4493,40 +4321,40 @@ { "data": { "text/plain": [ - "Decimal('420')" + "Decimal('42')" ] }, - "execution_count": 146, + "execution_count": 138, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "10 * Decimal(42)" + "10 * Decimal(42) / 10" ] }, { "cell_type": "code", - "execution_count": 147, + "execution_count": 139, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ - "Decimal('1.05')" + "Decimal('0.1')" ] }, - "execution_count": 147, + "execution_count": 139, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "Decimal(42) / 40" + "Decimal(1) / 10" ] }, { @@ -4542,7 +4370,7 @@ }, { "cell_type": "code", - "execution_count": 148, + "execution_count": 140, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4552,21 +4380,21 @@ { "data": { "text/plain": [ - "'1.05000000000000000000000000000000000000000000000000'" + "'0.10000000000000000000000000000000000000000000000000'" ] }, - "execution_count": 148, + "execution_count": 140, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "format(Decimal(42) / 40, \".50f\")" + "format(Decimal(1) / 10, \".50f\")" ] }, { "cell_type": "code", - "execution_count": 149, + "execution_count": 141, "metadata": { "slideshow": { "slide_type": "-" @@ -4576,16 +4404,16 @@ { "data": { "text/plain": [ - "'1.05000000000000004440892098500626161694526672363281'" + "'0.10000000000000000555111512312578270211815834045410'" ] }, - "execution_count": 149, + "execution_count": 141, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "format(42 / 40, \".50f\")" + "format(1 / 10, \".50f\")" ] }, { @@ -4596,12 +4424,12 @@ } }, "source": [ - "However, mixing `Decimal` and `float` objects raises a `TypeError`: So, Python prevents us from potentially introducing imprecisions via innocent looking arithmetic." + "However, mixing `Decimal` and `float` objects raises a `TypeError`: So, Python prevents us from potentially introducing imprecisions via innocent-looking arithmetic by **failing loudly**." ] }, { "cell_type": "code", - "execution_count": 150, + "execution_count": 142, "metadata": { "slideshow": { "slide_type": "slide" @@ -4615,7 +4443,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m1.0\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m42\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m1.0\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m42\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for *: 'float' and 'decimal.Decimal'" ] } @@ -4632,12 +4460,12 @@ } }, "source": [ - "In order to preserve the precision even for more advanced mathematical functions, `Decimal` objects come with many methods bound on them. For example, [ln()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.ln) and [log10()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.log10) take the logarithm while [sqrt()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.sqrt) calculates the square root. In general, the functions in the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/) should only be used with `float` objects." + "To preserve the precision for more advanced mathematical functions, `Decimal` objects come with many methods bound on them. For example, [ln()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.ln) and [log10()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.log10) take the logarithm while [sqrt()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.sqrt) calculates the square root. In general, the functions in the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/) should only be used with `float` objects as they do *not* preserve precision." ] }, { "cell_type": "code", - "execution_count": 151, + "execution_count": 143, "metadata": { "slideshow": { "slide_type": "slide" @@ -4650,7 +4478,7 @@ "Decimal('2')" ] }, - "execution_count": 151, + "execution_count": 143, "metadata": {}, "output_type": "execute_result" } @@ -4661,7 +4489,7 @@ }, { "cell_type": "code", - "execution_count": 152, + "execution_count": 144, "metadata": { "slideshow": { "slide_type": "-" @@ -4674,7 +4502,7 @@ "Decimal('1.414213562373095048801688724')" ] }, - "execution_count": 152, + "execution_count": 144, "metadata": {}, "output_type": "execute_result" } @@ -4691,14 +4519,14 @@ } }, "source": [ - "The [sqrt()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.sqrt) method is still limited with respect to the precision of the returned value as, for example, $\\sqrt{2}$ is an **[irrational number](https://en.wikipedia.org/wiki/Irrational_number)** that *cannot* be expressed with absolute precision using *any* number of bits even in theory.\n", + "The object returned by the [sqrt()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.sqrt) method is still limited in precision: This must be so as, for example, $\\sqrt{2}$ is an **[irrational number](https://en.wikipedia.org/wiki/Irrational_number)** that *cannot* be expressed with absolute precision using *any* number of bits, even in theory.\n", "\n", "We see this as raising $\\sqrt{2}$ to the power of $2$ results in an imprecise value as before!" ] }, { "cell_type": "code", - "execution_count": 153, + "execution_count": 145, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4711,7 +4539,7 @@ "Decimal('1.999999999999999999999999999')" ] }, - "execution_count": 153, + "execution_count": 145, "metadata": {}, "output_type": "execute_result" } @@ -4732,12 +4560,12 @@ "source": [ "The [quantize()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize) method allows us to cut off a `Decimal` number at any digit that is smaller than the set precision while adhering to the rounding rules from math.\n", "\n", - "For example, as the overall imprecise value of `two` still has a precision of $28$, we can correctly round it with respect to the first $20$ digits." + "For example, as the overall imprecise value of `two` still has a precision of $28$ digits, we can correctly round it to the first $20$ digits." ] }, { "cell_type": "code", - "execution_count": 154, + "execution_count": 146, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4750,7 +4578,7 @@ "Decimal('2')" ] }, - "execution_count": 154, + "execution_count": 146, "metadata": {}, "output_type": "execute_result" } @@ -4772,10 +4600,10 @@ }, { "cell_type": "code", - "execution_count": 155, + "execution_count": 147, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ @@ -4785,7 +4613,7 @@ "True" ] }, - "execution_count": 155, + "execution_count": 147, "metadata": {}, "output_type": "execute_result" } @@ -4807,10 +4635,10 @@ }, { "cell_type": "code", - "execution_count": 156, + "execution_count": 148, "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "fragment" } }, "outputs": [ @@ -4820,7 +4648,7 @@ "True" ] }, - "execution_count": 156, + "execution_count": 148, "metadata": {}, "output_type": "execute_result" } @@ -4837,12 +4665,12 @@ } }, "source": [ - "`nan` and positive and negative `inf` exist as well and the same remarks from above apply." + "`nan` and positive and negative `inf` exist as well, and the same remarks from above apply." ] }, { "cell_type": "code", - "execution_count": 157, + "execution_count": 149, "metadata": { "slideshow": { "slide_type": "skip" @@ -4855,7 +4683,7 @@ "Decimal('NaN')" ] }, - "execution_count": 157, + "execution_count": 149, "metadata": {}, "output_type": "execute_result" } @@ -4866,7 +4694,7 @@ }, { "cell_type": "code", - "execution_count": 158, + "execution_count": 150, "metadata": { "slideshow": { "slide_type": "skip" @@ -4879,7 +4707,7 @@ "False" ] }, - "execution_count": 158, + "execution_count": 150, "metadata": {}, "output_type": "execute_result" } @@ -4890,7 +4718,7 @@ }, { "cell_type": "code", - "execution_count": 159, + "execution_count": 151, "metadata": { "slideshow": { "slide_type": "skip" @@ -4903,7 +4731,7 @@ "Decimal('Infinity')" ] }, - "execution_count": 159, + "execution_count": 151, "metadata": {}, "output_type": "execute_result" } @@ -4914,7 +4742,7 @@ }, { "cell_type": "code", - "execution_count": 160, + "execution_count": 152, "metadata": { "slideshow": { "slide_type": "skip" @@ -4927,7 +4755,7 @@ "Decimal('-Infinity')" ] }, - "execution_count": 160, + "execution_count": 152, "metadata": {}, "output_type": "execute_result" } @@ -4938,7 +4766,7 @@ }, { "cell_type": "code", - "execution_count": 161, + "execution_count": 153, "metadata": { "slideshow": { "slide_type": "skip" @@ -4951,7 +4779,7 @@ "Decimal('Infinity')" ] }, - "execution_count": 161, + "execution_count": 153, "metadata": {}, "output_type": "execute_result" } @@ -4962,7 +4790,7 @@ }, { "cell_type": "code", - "execution_count": 162, + "execution_count": 154, "metadata": { "slideshow": { "slide_type": "skip" @@ -4975,7 +4803,7 @@ "True" ] }, - "execution_count": 162, + "execution_count": 154, "metadata": {}, "output_type": "execute_result" } @@ -4992,12 +4820,12 @@ } }, "source": [ - "As with `float` objects, we cannot add infinities of different signs: Now get a module-specific `InvalidOperation` exception instead of a `nan` value." + "As with `float` objects, we cannot add infinities of different signs: Now get a module-specific `InvalidOperation` exception instead of a `nan` value. Here, **failing loudly** is a good thing as it prevents us from working with invalid results." ] }, { "cell_type": "code", - "execution_count": 163, + "execution_count": 155, "metadata": { "slideshow": { "slide_type": "skip" @@ -5011,7 +4839,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"-inf\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"-inf\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m: []" ] } @@ -5022,7 +4850,7 @@ }, { "cell_type": "code", - "execution_count": 164, + "execution_count": 156, "metadata": { "slideshow": { "slide_type": "skip" @@ -5036,7 +4864,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m: []" ] } @@ -5053,7 +4881,7 @@ } }, "source": [ - "For more information on decimals, see the tutorial at [PYMOTW](https://pymotw.com/3/decimal/index.html) or the official [documentation](https://docs.python.org/3/library/decimal.html)." + "For more information on the `Decimal` type, see the tutorial at [PYMOTW](https://pymotw.com/3/decimal/index.html) or the official [documentation](https://docs.python.org/3/library/decimal.html)." ] }, { @@ -5077,12 +4905,12 @@ "source": [ "If the numbers in an application can be expressed as [rational numbers](https://en.wikipedia.org/wiki/Rational_number) (i.e., the set $\\mathbb{Q}$), we may model them as a [Fraction](https://docs.python.org/3/library/fractions.html#fractions.Fraction) type from the [fractions](https://docs.python.org/3/library/fractions.html) module in the [standard library](https://docs.python.org/3/library/). As any fraction can always be formulated as the division of one integer by another, `Fraction` objects are inherently precise, just as `int` objects on their own. Further, we maintain the precision as long as we do not use them in a mathematical operation that could result in an irrational number (e.g., taking the square root).\n", "\n", - "Let's first import the `Fraction` type from the [fractions](https://docs.python.org/3/library/fractions.html) module." + "We import the `Fraction` type from the [fractions](https://docs.python.org/3/library/fractions.html) module." ] }, { "cell_type": "code", - "execution_count": 165, + "execution_count": 157, "metadata": { "slideshow": { "slide_type": "slide" @@ -5101,12 +4929,12 @@ } }, "source": [ - "Among others, there are two simple ways to create a `Fraction` object: We either instantiate one with two `int` objects representing the numerator and denominator or with a `str` object. In the latter case, we have again two options and use either the format \"numerator/denominator\" (i.e., *without* any spaces) or the same format as for `float` and `Decimal` objects above." + "Among others, there are two simple ways to create a `Fraction` object: We either instantiate one with two `int` objects representing the numerator and denominator or with a `str` object. In the latter case, we have two options again and use either the format \"numerator/denominator\" (i.e., *without* any spaces) or the same format as for `float` and `Decimal` objects above." ] }, { "cell_type": "code", - "execution_count": 166, + "execution_count": 158, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5119,7 +4947,7 @@ "Fraction(1, 3)" ] }, - "execution_count": 166, + "execution_count": 158, "metadata": {}, "output_type": "execute_result" } @@ -5130,7 +4958,7 @@ }, { "cell_type": "code", - "execution_count": 167, + "execution_count": 159, "metadata": { "slideshow": { "slide_type": "-" @@ -5143,7 +4971,7 @@ "Fraction(1, 3)" ] }, - "execution_count": 167, + "execution_count": 159, "metadata": {}, "output_type": "execute_result" } @@ -5154,7 +4982,7 @@ }, { "cell_type": "code", - "execution_count": 168, + "execution_count": 160, "metadata": { "slideshow": { "slide_type": "-" @@ -5167,7 +4995,7 @@ "Fraction(3333333333, 10000000000)" ] }, - "execution_count": 168, + "execution_count": 160, "metadata": {}, "output_type": "execute_result" } @@ -5178,7 +5006,7 @@ }, { "cell_type": "code", - "execution_count": 169, + "execution_count": 161, "metadata": { "slideshow": { "slide_type": "-" @@ -5191,7 +5019,7 @@ "Fraction(3333333333, 10000000000)" ] }, - "execution_count": 169, + "execution_count": 161, "metadata": {}, "output_type": "execute_result" } @@ -5208,12 +5036,12 @@ } }, "source": [ - "Only the lowest common denominator version is maintained after creation: For example, $\\frac{3}{2}$ and $\\frac{6}{4}$ are the same and both become `Fraction(3, 2)`." + "Only the lowest common denominator version is maintained after creation: For example, $\\frac{3}{2}$ and $\\frac{6}{4}$ are the same, and both become `Fraction(3, 2)`." ] }, { "cell_type": "code", - "execution_count": 170, + "execution_count": 162, "metadata": { "slideshow": { "slide_type": "slide" @@ -5226,7 +5054,7 @@ "Fraction(3, 2)" ] }, - "execution_count": 170, + "execution_count": 162, "metadata": {}, "output_type": "execute_result" } @@ -5237,7 +5065,7 @@ }, { "cell_type": "code", - "execution_count": 171, + "execution_count": 163, "metadata": { "slideshow": { "slide_type": "-" @@ -5250,7 +5078,7 @@ "Fraction(3, 2)" ] }, - "execution_count": 171, + "execution_count": 163, "metadata": {}, "output_type": "execute_result" } @@ -5272,7 +5100,7 @@ }, { "cell_type": "code", - "execution_count": 172, + "execution_count": 164, "metadata": { "slideshow": { "slide_type": "slide" @@ -5285,7 +5113,7 @@ "Fraction(1, 10)" ] }, - "execution_count": 172, + "execution_count": 164, "metadata": {}, "output_type": "execute_result" } @@ -5302,12 +5130,12 @@ } }, "source": [ - "`float` objects may *syntactically* be casted as `Fraction` objects as well. However, then we create a `Fraction` object that precisely remembers the `float` object's imprecision: A *bad* idea!" + "`float` objects may *syntactically* be cast as `Fraction` objects as well. However, then we create a `Fraction` object that precisely remembers the `float` object's imprecision: A *bad* idea!" ] }, { "cell_type": "code", - "execution_count": 173, + "execution_count": 165, "metadata": { "slideshow": { "slide_type": "-" @@ -5320,7 +5148,7 @@ "Fraction(3602879701896397, 36028797018963968)" ] }, - "execution_count": 173, + "execution_count": 165, "metadata": {}, "output_type": "execute_result" } @@ -5337,12 +5165,12 @@ } }, "source": [ - "`Fraction` objects follow the arithmetic rules from middle school and may be mixed with `int` objects *without* any loss of precision." + "`Fraction` objects follow the arithmetic rules from middle school and may be mixed with `int` objects *without* any loss of precision. The result is always a *new* `Fraction` object." ] }, { "cell_type": "code", - "execution_count": 174, + "execution_count": 166, "metadata": { "slideshow": { "slide_type": "slide" @@ -5355,7 +5183,7 @@ "Fraction(7, 4)" ] }, - "execution_count": 174, + "execution_count": 166, "metadata": {}, "output_type": "execute_result" } @@ -5366,7 +5194,7 @@ }, { "cell_type": "code", - "execution_count": 175, + "execution_count": 167, "metadata": { "slideshow": { "slide_type": "-" @@ -5379,7 +5207,7 @@ "Fraction(1, 2)" ] }, - "execution_count": 175, + "execution_count": 167, "metadata": {}, "output_type": "execute_result" } @@ -5390,7 +5218,7 @@ }, { "cell_type": "code", - "execution_count": 176, + "execution_count": 168, "metadata": { "slideshow": { "slide_type": "-" @@ -5403,7 +5231,7 @@ "Fraction(1, 1)" ] }, - "execution_count": 176, + "execution_count": 168, "metadata": {}, "output_type": "execute_result" } @@ -5414,7 +5242,7 @@ }, { "cell_type": "code", - "execution_count": 177, + "execution_count": 169, "metadata": { "slideshow": { "slide_type": "-" @@ -5427,7 +5255,7 @@ "Fraction(1, 1)" ] }, - "execution_count": 177, + "execution_count": 169, "metadata": {}, "output_type": "execute_result" } @@ -5444,12 +5272,12 @@ } }, "source": [ - "`Fraction` and `float` objects may also be mixed syntactically. However, then the results may exhibit imprecision again, even if we do not see them at first sight!" + "`Fraction` and `float` objects may also be mixed *syntactically*. However, then the results may exhibit imprecision again, even if we do not see them at first sight! This is another example of code **failing silently**." ] }, { "cell_type": "code", - "execution_count": 178, + "execution_count": 170, "metadata": { "slideshow": { "slide_type": "slide" @@ -5462,7 +5290,7 @@ "0.1" ] }, - "execution_count": 178, + "execution_count": 170, "metadata": {}, "output_type": "execute_result" } @@ -5473,10 +5301,10 @@ }, { "cell_type": "code", - "execution_count": 179, + "execution_count": 171, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -5486,7 +5314,7 @@ "'0.10000000000000000555111512312578270211815834045410'" ] }, - "execution_count": 179, + "execution_count": 171, "metadata": {}, "output_type": "execute_result" } @@ -5525,13 +5353,13 @@ } }, "source": [ - "Some mathematical equations cannot be solved if the solution has to be in the set of the real numbers $\\mathbb{R}$: For example: $x^2 = -1 \\implies x = \\sqrt{-1}$ and the square root is not defined for negative numbers. To mitigate this, mathematicians introduced the concept of an [imaginary number](https://en.wikipedia.org/wiki/Imaginary_number) $\\textbf{i}$ that is *defined* as $\\textbf{i} = \\sqrt{-1}$ or often times as the solution to the equation $\\textbf{i}^2 = -1$. So, the solution to $x = \\sqrt{-1}$ then becomes $x = \\textbf{i}$.\n", + "Some mathematical equations cannot be solved if the solution has to be in the set of the real numbers $\\mathbb{R}$. For example, $x^2 = -1$ can be rearranged into $x = \\sqrt{-1}$, but the square root is not defined for negative numbers. To mitigate this, mathematicians introduced the concept of an [imaginary number](https://en.wikipedia.org/wiki/Imaginary_number) $\\textbf{i}$ that is *defined* as $\\textbf{i} = \\sqrt{-1}$ or often as the solution to the equation $\\textbf{i}^2 = -1$. So, the solution to $x = \\sqrt{-1}$ then becomes $x = \\textbf{i}$.\n", "\n", - "If we generalize the example equation into $(mx-n)^2 = -1 \\implies x = \\frac{1}{m}(\\sqrt{-1} + n)$ where $m$ and $n$ are constants chosen from the reals $\\mathbb{R}$, then the solution to the equation comes in the form $x = a + b\\textbf{i}$, the sum of a real number and an imaginary number, with $a=\\frac{n}{m}$ and $b = \\frac{1}{m}$ also from the reals $\\mathbb{R}$.\n", + "If we generalize the example equation into $(mx-n)^2 = -1 \\implies x = \\frac{1}{m}(\\sqrt{-1} + n)$ where $m$ and $n$ are constants chosen from the reals $\\mathbb{R}$, then the solution to the equation comes in the form $x = a + b\\textbf{i}$, the sum of a real number and an imaginary number, with $a=\\frac{n}{m}$ and $b = \\frac{1}{m}$.\n", "\n", - "Such compound numbers are called a **[complex numbers](https://en.wikipedia.org/wiki/Complex_number)** and the set of all such numbers is commonly denoted by $\\mathbb{C}$. The reals $\\mathbb{R}$ are a strict subset of $\\mathbb{C}$ with $b=0$. $a$ is referred to as the **real part** and $b$ as the **imaginary part** of the complex number.\n", + "Such \"compound\" numbers are called **[complex numbers](https://en.wikipedia.org/wiki/Complex_number)**, and the set of all such numbers is commonly denoted by $\\mathbb{C}$. The reals $\\mathbb{R}$ are a strict subset of $\\mathbb{C}$ with $b=0$. Further, $a$ is referred to as the **real part** and $b$ as the **imaginary part** of the complex number.\n", "\n", - "Complex numbers are often visualized in a plane like below where the real part is depicted on the x-axis and the imaginary part is depicted on the y-axis." + "Complex numbers are often visualized in a plane like below, where the real part is depicted on the x-axis and the imaginary part on the y-axis." ] }, { @@ -5553,14 +5381,14 @@ } }, "source": [ - "Complex numbers are part of core Python. The simplest way to create one is to write an arithmetic expression with the literal `j` notation for an imaginary number. A `j` notation is commonly used in many engineering disciplines instead of the symbol $\\textbf{i}$ from math as $I$ in engineering more often than not means [electric current](https://en.wikipedia.org/wiki/Electric_current).\n", + "`complex` numbers are part of core Python. The simplest way to create one is to write an arithmetic expression with the literal `j` notation for $\\textbf{i}$. The `j` is commonly used in many engineering disciplines instead of the symbol $\\textbf{i}$ from math as $I$ in engineering more often than not means [electric current](https://en.wikipedia.org/wiki/Electric_current).\n", "\n", - "For example, the answer to $x^2 = -1$ can be written in Python as `1j` like below. This creates a `complex` object with value `1j`. The same syntactic rules apply as with the above `e` notation: No spaces are allowed between the number and the `j` and the number may be any `float` literal." + "For example, the answer to $x^2 = -1$ can be written in Python as `1j` like below. This creates a `complex` object with value `1j`. The same syntactic rules apply as with the above `e` notation: No spaces are allowed between the number and the `j`, and the number may be any `int` or `float` literal." ] }, { "cell_type": "code", - "execution_count": 180, + "execution_count": 172, "metadata": { "slideshow": { "slide_type": "slide" @@ -5568,12 +5396,12 @@ }, "outputs": [], "source": [ - "sqrt_of_neg_one = 1j" + "x = 1j" ] }, { "cell_type": "code", - "execution_count": 181, + "execution_count": 173, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5583,21 +5411,21 @@ { "data": { "text/plain": [ - "140526569943600" + "139927632267248" ] }, - "execution_count": 181, + "execution_count": 173, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "id(sqrt_of_neg_one)" + "id(x)" ] }, { "cell_type": "code", - "execution_count": 182, + "execution_count": 174, "metadata": { "slideshow": { "slide_type": "-" @@ -5610,18 +5438,18 @@ "complex" ] }, - "execution_count": 182, + "execution_count": 174, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "type(sqrt_of_neg_one)" + "type(x)" ] }, { "cell_type": "code", - "execution_count": 183, + "execution_count": 175, "metadata": { "slideshow": { "slide_type": "-" @@ -5634,13 +5462,13 @@ "1j" ] }, - "execution_count": 183, + "execution_count": 175, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "sqrt_of_neg_one" + "x" ] }, { @@ -5651,12 +5479,12 @@ } }, "source": [ - "To verify that it solves the equation, let's just raise it to the power of $2$: the answer is $-1 + 0\\textbf{i} = -1$." + "To verify that it solves the equation, let's raise it to the power of $2$." ] }, { "cell_type": "code", - "execution_count": 184, + "execution_count": 176, "metadata": { "slideshow": { "slide_type": "slide" @@ -5669,13 +5497,13 @@ "True" ] }, - "execution_count": 184, + "execution_count": 176, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "sqrt_of_neg_one ** 2 == -1" + "x ** 2 == -1" ] }, { @@ -5686,12 +5514,12 @@ } }, "source": [ - "Often, we will write an expression of the form $a + b\\textbf{i}$." + "Often, we write an expression of the form $a + b\\textbf{i}$." ] }, { "cell_type": "code", - "execution_count": 185, + "execution_count": 177, "metadata": { "slideshow": { "slide_type": "slide" @@ -5704,7 +5532,7 @@ "(2+0.5j)" ] }, - "execution_count": 185, + "execution_count": 177, "metadata": {}, "output_type": "execute_result" } @@ -5726,7 +5554,7 @@ }, { "cell_type": "code", - "execution_count": 186, + "execution_count": 178, "metadata": { "slideshow": { "slide_type": "-" @@ -5739,7 +5567,7 @@ "(2+0.5j)" ] }, - "execution_count": 186, + "execution_count": 178, "metadata": {}, "output_type": "execute_result" } @@ -5750,7 +5578,7 @@ }, { "cell_type": "code", - "execution_count": 187, + "execution_count": 179, "metadata": { "slideshow": { "slide_type": "-" @@ -5763,7 +5591,7 @@ "(2+0j)" ] }, - "execution_count": 187, + "execution_count": 179, "metadata": {}, "output_type": "execute_result" } @@ -5774,7 +5602,7 @@ }, { "cell_type": "code", - "execution_count": 188, + "execution_count": 180, "metadata": { "slideshow": { "slide_type": "skip" @@ -5787,7 +5615,7 @@ "(2+0.5j)" ] }, - "execution_count": 188, + "execution_count": 180, "metadata": {}, "output_type": "execute_result" } @@ -5798,7 +5626,7 @@ }, { "cell_type": "code", - "execution_count": 189, + "execution_count": 181, "metadata": { "slideshow": { "slide_type": "skip" @@ -5811,7 +5639,7 @@ "(2+0.5j)" ] }, - "execution_count": 189, + "execution_count": 181, "metadata": {}, "output_type": "execute_result" } @@ -5828,48 +5656,12 @@ } }, "source": [ - "Spaces in the text string version lead to a `ValueError`." + "Arithmetic expressions work with `complex` numbers. They may be mixed with the other numeric types, and the result is always a `complex` number." ] }, { "cell_type": "code", - "execution_count": 190, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "ename": "ValueError", - "evalue": "complex() arg is a malformed string", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mcomplex\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"2 + 0.5j\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mValueError\u001b[0m: complex() arg is a malformed string" - ] - } - ], - "source": [ - "complex(\"2 + 0.5j\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "The basic arithmetic rules work for `complex` numbers. They may be mixed with the other numeric types and the result is always a `complex` number again." - ] - }, - { - "cell_type": "code", - "execution_count": 191, + "execution_count": 182, "metadata": { "slideshow": { "slide_type": "slide" @@ -5877,13 +5669,13 @@ }, "outputs": [], "source": [ - "e = 1 + 2j\n", - "f = 3 + 4j" + "c1 = 1 + 2j\n", + "c2 = 3 + 4j" ] }, { "cell_type": "code", - "execution_count": 192, + "execution_count": 183, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5896,18 +5688,18 @@ "(4+6j)" ] }, - "execution_count": 192, + "execution_count": 183, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "e + f" + "c1 + c2" ] }, { "cell_type": "code", - "execution_count": 193, + "execution_count": 184, "metadata": { "slideshow": { "slide_type": "-" @@ -5917,24 +5709,24 @@ { "data": { "text/plain": [ - "(2+2j)" + "(-2-2j)" ] }, - "execution_count": 193, + "execution_count": 184, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "f - e" + "c1 - c2" ] }, { "cell_type": "code", - "execution_count": 194, + "execution_count": 185, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ @@ -5944,21 +5736,21 @@ "(40+2j)" ] }, - "execution_count": 194, + "execution_count": 185, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "39 + e" + "c1 + 39" ] }, { "cell_type": "code", - "execution_count": 195, + "execution_count": 186, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ @@ -5968,21 +5760,21 @@ "-4j" ] }, - "execution_count": 195, + "execution_count": 186, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "3.0 - f" + "3.0 - c2" ] }, { "cell_type": "code", - "execution_count": 196, + "execution_count": 187, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "skip" } }, "outputs": [ @@ -5992,21 +5784,21 @@ "(5+10j)" ] }, - "execution_count": 196, + "execution_count": 187, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "5 * e" + "5 * c1" ] }, { "cell_type": "code", - "execution_count": 197, + "execution_count": 188, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ @@ -6016,21 +5808,21 @@ "(0.5+0.6666666666666666j)" ] }, - "execution_count": 197, + "execution_count": 188, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "f / 6" + "c2 / 6" ] }, { "cell_type": "code", - "execution_count": 198, + "execution_count": 189, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -6040,18 +5832,18 @@ "(-5+10j)" ] }, - "execution_count": 198, + "execution_count": 189, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "e * f" + "c1 * c2" ] }, { "cell_type": "code", - "execution_count": 199, + "execution_count": 190, "metadata": { "slideshow": { "slide_type": "-" @@ -6061,16 +5853,16 @@ { "data": { "text/plain": [ - "(2.2-0.4j)" + "(0.44+0.08j)" ] }, - "execution_count": 199, + "execution_count": 190, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "f / e" + "c1 / c2" ] }, { @@ -6081,12 +5873,12 @@ } }, "source": [ - "The absolute value of a `complex` number is commonly defined as the Pythagoram Theorem where $\\lVert x \\rVert = \\sqrt{a^2 + b^2}$ and the [abs()](https://docs.python.org/3/library/functions.html#abs) built-in function works implements that in Python." + "The absolute value of a `complex` number $x$ is defined with the Pythagorean Theorem where $\\lVert x \\rVert = \\sqrt{a^2 + b^2}$ and $a$ and $b$ are the real and imaginary parts. The [abs()](https://docs.python.org/3/library/functions.html#abs) built-in function implements that in Python." ] }, { "cell_type": "code", - "execution_count": 200, + "execution_count": 191, "metadata": { "slideshow": { "slide_type": "slide" @@ -6099,7 +5891,7 @@ "5.0" ] }, - "execution_count": 200, + "execution_count": 191, "metadata": {}, "output_type": "execute_result" } @@ -6121,7 +5913,7 @@ }, { "cell_type": "code", - "execution_count": 201, + "execution_count": 192, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6134,18 +5926,18 @@ "1.0" ] }, - "execution_count": 201, + "execution_count": 192, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "e.real" + "c1.real" ] }, { "cell_type": "code", - "execution_count": 202, + "execution_count": 193, "metadata": { "slideshow": { "slide_type": "-" @@ -6158,13 +5950,13 @@ "2.0" ] }, - "execution_count": 202, + "execution_count": 193, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "e.imag" + "c1.imag" ] }, { @@ -6180,10 +5972,10 @@ }, { "cell_type": "code", - "execution_count": 203, + "execution_count": 194, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -6193,13 +5985,13 @@ "(1-2j)" ] }, - "execution_count": 203, + "execution_count": 194, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "e.conjugate()" + "c1.conjugate()" ] }, { @@ -6232,14 +6024,19 @@ } }, "source": [ - "There are three numeric types that are part of core Python:\n", - "- `int`: perfect model for whole numbers (i.e., the set $\\mathbb{Z}$); inherently precise\n", - "- `float`: standard to approximate real numbers (i.e., the set $\\mathbb{R}$); inherently imprecise\n", - "- `complex`: layer on top of the `float` type\n", + "There exist three numeric types in core Python:\n", + "- `int`: a perfect model for whole numbers (i.e., the set $\\mathbb{Z}$); inherently precise\n", + "- `float`: the \"gold\" standard to approximate real numbers (i.e., the set $\\mathbb{R}$); inherently imprecise\n", + "- `complex`: layer on top of the `float` type; therefore inherently imprecise\n", "\n", "Furthermore, the [standard library](https://docs.python.org/3/library/) adds two more types that can be used as substitutes for `float` objects:\n", "- `Decimal`: similar to `float` but allows customizing the precision; still inherently imprecise\n", - "- `Fraction`: perfect model for rational numbers (i.e., the set $\\mathbb{Q}$)" + "- `Fraction`: a perfect model for rational numbers (i.e., the set $\\mathbb{Q}$); built on top of the `int` type and therefore inherently precise\n", + "\n", + "The important takeaways for the data science practicioner 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**." ] } ], From 76350cf895cfe572329ec0a2bf68ea643f46ba5a Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 14 Oct 2019 18:27:42 +0200 Subject: [PATCH 032/242] Add section on the numeric tower --- 05_numbers.ipynb | 913 ++++++++++++++++++++++++++++++++++++++++++++- static/numbers.png | Bin 0 -> 90958 bytes 2 files changed, 903 insertions(+), 10 deletions(-) create mode 100644 static/numbers.png diff --git a/05_numbers.ipynb b/05_numbers.ipynb index ccb1412..df7598c 100644 --- a/05_numbers.ipynb +++ b/05_numbers.ipynb @@ -92,7 +92,7 @@ { "data": { "text/plain": [ - "139927641322704" + "140218620396752" ] }, "execution_count": 2, @@ -2115,7 +2115,7 @@ { "data": { "text/plain": [ - "139927641500360" + "140218620574432" ] }, "execution_count": 63, @@ -3985,7 +3985,7 @@ } }, "source": [ - "As the exact implementation of floats may vary and be dependent on a particular Python installation, we look up the [float_info](https://docs.python.org/3/library/sys.html#sys.float_info) attribute in the [sys](https://docs.python.org/3/library/sys.html) module in the [standard library](https://docs.python.org/3/library/) to check the details. Usually, this is not necessary." + "As the exact implementation of floats may vary and be dependent on a particular Python installation, we look up the [float_info](https://docs.python.org/3/library/sys.html#sys.float_info) attribute in the [sys](https://docs.python.org/3/library/sys.html) module in the [standard library](https://docs.python.org/3/library/index.html) to check the details. Usually, this is not necessary." ] }, { @@ -4044,7 +4044,7 @@ } }, "source": [ - "The [decimal](https://docs.python.org/3/library/decimal.html) module in the standard library provides a [Decimal](https://docs.python.org/3/library/decimal.html#decimal.Decimal) type that may be used to represent any real number to a user-defined level of precision: \"User-defined\" does *not* mean an infinite or \"exact\" precision! The `Decimal` type merely allows us to work with a number of bits *different* from the $64$ as specified for the `float` type and also to customize the rounding rules and some other settings.\n", + "The [decimal](https://docs.python.org/3/library/decimal.html) module in the [standard library](https://docs.python.org/3/library/index.html) provides a [Decimal](https://docs.python.org/3/library/decimal.html#decimal.Decimal) type that may be used to represent any real number to a user-defined level of precision: \"User-defined\" does *not* mean an infinite or \"exact\" precision! The `Decimal` type merely allows us to work with a number of bits *different* from the $64$ as specified for the `float` type and also to customize the rounding rules and some other settings.\n", "\n", "We import the `Decimal` type and also the [getcontext()](https://docs.python.org/3/library/decimal.html#decimal.getcontext) function from the [decimal](https://docs.python.org/3/library/decimal.html) module." ] @@ -4460,7 +4460,7 @@ } }, "source": [ - "To preserve the precision for more advanced mathematical functions, `Decimal` objects come with many methods bound on them. For example, [ln()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.ln) and [log10()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.log10) take the logarithm while [sqrt()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.sqrt) calculates the square root. In general, the functions in the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/) should only be used with `float` objects as they do *not* preserve precision." + "To preserve the precision for more advanced mathematical functions, `Decimal` objects come with many methods bound on them. For example, [ln()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.ln) and [log10()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.log10) take the logarithm while [sqrt()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.sqrt) calculates the square root. In general, the functions in the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html) should only be used with `float` objects as they do *not* preserve precision." ] }, { @@ -4903,7 +4903,7 @@ } }, "source": [ - "If the numbers in an application can be expressed as [rational numbers](https://en.wikipedia.org/wiki/Rational_number) (i.e., the set $\\mathbb{Q}$), we may model them as a [Fraction](https://docs.python.org/3/library/fractions.html#fractions.Fraction) type from the [fractions](https://docs.python.org/3/library/fractions.html) module in the [standard library](https://docs.python.org/3/library/). As any fraction can always be formulated as the division of one integer by another, `Fraction` objects are inherently precise, just as `int` objects on their own. Further, we maintain the precision as long as we do not use them in a mathematical operation that could result in an irrational number (e.g., taking the square root).\n", + "If the numbers in an application can be expressed as [rational numbers](https://en.wikipedia.org/wiki/Rational_number) (i.e., the set $\\mathbb{Q}$), we may model them as a [Fraction](https://docs.python.org/3/library/fractions.html#fractions.Fraction) type from the [fractions](https://docs.python.org/3/library/fractions.html) module in the [standard library](https://docs.python.org/3/library/index.html). As any fraction can always be formulated as the division of one integer by another, `Fraction` objects are inherently precise, just as `int` objects on their own. Further, we maintain the precision as long as we do not use them in a mathematical operation that could result in an irrational number (e.g., taking the square root).\n", "\n", "We import the `Fraction` type from the [fractions](https://docs.python.org/3/library/fractions.html) module." ] @@ -5411,7 +5411,7 @@ { "data": { "text/plain": [ - "139927632267248" + "140218611341648" ] }, "execution_count": 173, @@ -6002,7 +6002,898 @@ } }, "source": [ - "The [cmath](https://docs.python.org/3/library/cmath.html) module in the [standard library](https://docs.python.org/3/library/) implements many of the functions from the [math](https://docs.python.org/3/library/math.html) module such that they work with complex numbers." + "The [cmath](https://docs.python.org/3/library/cmath.html) module in the [standard library](https://docs.python.org/3/library/index.html) implements many of the functions from the [math](https://docs.python.org/3/library/math.html) module such that they work with complex numbers." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The Numeric Tower" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Analogous to the discussion of containers and iterables in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Containers-vs.-Iterables), we contrast the *concrete* numeric data types in this chapter with the *abstract* ideas behind [numbers in mathematics](https://en.wikipedia.org/wiki/Number).\n", + "\n", + "The figure below summarizes five *major* sets of [numbers in mathematics](https://en.wikipedia.org/wiki/Number) as we know them from high school:\n", + "\n", + "- $\\mathbb{N}$: [Natural numbers](https://en.wikipedia.org/wiki/Natural_number) are all non-negative count numbers, e.g., $0, 1, 2, ...$\n", + "- $\\mathbb{Z}$: [Integers](https://en.wikipedia.org/wiki/Integer) are all numbers *without* a fractional component, e.g., $-1, 0, 1, ...$\n", + "- $\\mathbb{Q}$: [Rational numbers](https://en.wikipedia.org/wiki/Rational_number) are all numbers that can be expressed as a quotient of two integers, e.g., $-\\frac{1}{2}, 0, \\frac{1}{2}, ...$\n", + "- $\\mathbb{R}$: [Real numbers](https://en.wikipedia.org/wiki/Real_number) are all numbers that can be represented as a distance along a line (negative means \"reversed\"), e.g., $\\sqrt{2}, \\pi, \\text{e}, ...$\n", + "- $\\mathbb{C}$: [Complex numbers](https://en.wikipedia.org/wiki/Complex_number) are all numbers of the form $a + b\\textbf{i}$ where $a$ and $b$ are real numbers and $\\textbf{i}$ is the [imaginary number](https://en.wikipedia.org/wiki/Imaginary_number), e.g., $0, \\textbf{i}, 1 + \\textbf{i}, ...$\n", + "\n", + "In the listed order, the five sets are perfect subsets and $\\mathbb{C}$ is the largest set (to be precise, all sets are infinite, but they still have a different number of elements)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The *concrete* data types introduced in this chapter are all *imperfect* models of *abstract* mathematical ideas.\n", + "\n", + "The `int` and `Fraction` types are the models \"closest\" to the idea they implement: Whereas $\\mathbb{Z}$ and $\\mathbb{Q}$ are, by definition, infinite, every computer runs out of bits when representing sufficiently large integers or fractions with a sufficiently large number of decimals. However, within a system-dependent minimum and maximum integer range, we can model an integer or fraction without any loss in precision.\n", + "\n", + "For the other types, in particular, the `float` type, the implications of their imprecision are discussed in detail above.\n", + "\n", + "The abstract concepts behind the four outer-most mathematical sets are part of Python since [PEP 3141](https://www.python.org/dev/peps/pep-3141/) in 2007. The [numbers](https://docs.python.org/3/library/numbers.html) module in the [standard library](https://docs.python.org/3/library/index.html) defines what Pythonistas call the **numeric tower**, a collection of five **[abstract data types](https://en.wikipedia.org/wiki/Abstract_data_type)**, or **abstract base classes** as they are called in Python slang:\n", + "\n", + "- `numbers.Number`: \"any number\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Number))\n", + "- `numbers.Complex`: \"all complex numbers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Complex))\n", + "- `numbers.Real`: \"all real numbers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Real))\n", + "- `numbers.Rational`: \"all rational numbers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Rational))\n", + "- `numbers.Integral`: \"all integers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Integral))" + ] + }, + { + "cell_type": "code", + "execution_count": 195, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 196, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['ABCMeta',\n", + " 'Complex',\n", + " 'Integral',\n", + " 'Number',\n", + " 'Rational',\n", + " 'Real',\n", + " '__all__',\n", + " '__builtins__',\n", + " '__cached__',\n", + " '__doc__',\n", + " '__file__',\n", + " '__loader__',\n", + " '__name__',\n", + " '__package__',\n", + " '__spec__',\n", + " 'abstractmethod']" + ] + }, + "execution_count": 196, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dir(numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As a reminder, the built-in [help()](https://docs.python.org/3/library/functions.html#help) function is always our friend.\n", + "\n", + "The abstract types' docstrings are unsurprisingly similar to the corresponding concrete types' docstrings (for now, let's not worry about the dunder-style names in the docstrings).\n", + "\n", + "For example, both `numbers.Complex` and `complex` list the `imag` and `real` attributes." + ] + }, + { + "cell_type": "code", + "execution_count": 197, + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on class Complex in module numbers:\n", + "\n", + "class Complex(Number)\n", + " | Complex defines the operations that work on the builtin complex type.\n", + " | \n", + " | In short, those are: a conversion to complex, .real, .imag, +, -,\n", + " | *, /, abs(), .conjugate, ==, and !=.\n", + " | \n", + " | If it is given heterogeneous arguments, and doesn't have special\n", + " | knowledge about them, it should fall back to the builtin complex\n", + " | type as described below.\n", + " | \n", + " | Method resolution order:\n", + " | Complex\n", + " | Number\n", + " | builtins.object\n", + " | \n", + " | Methods defined here:\n", + " | \n", + " | __abs__(self)\n", + " | Returns the Real distance from 0. Called for abs(self).\n", + " | \n", + " | __add__(self, other)\n", + " | self + other\n", + " | \n", + " | __bool__(self)\n", + " | True if self != 0. Called for bool(self).\n", + " | \n", + " | __complex__(self)\n", + " | Return a builtin complex instance. Called for complex(self).\n", + " | \n", + " | __eq__(self, other)\n", + " | self == other\n", + " | \n", + " | __mul__(self, other)\n", + " | self * other\n", + " | \n", + " | __neg__(self)\n", + " | -self\n", + " | \n", + " | __pos__(self)\n", + " | +self\n", + " | \n", + " | __pow__(self, exponent)\n", + " | self**exponent; should promote to float or complex when necessary.\n", + " | \n", + " | __radd__(self, other)\n", + " | other + self\n", + " | \n", + " | __rmul__(self, other)\n", + " | other * self\n", + " | \n", + " | __rpow__(self, base)\n", + " | base ** self\n", + " | \n", + " | __rsub__(self, other)\n", + " | other - self\n", + " | \n", + " | __rtruediv__(self, other)\n", + " | other / self\n", + " | \n", + " | __sub__(self, other)\n", + " | self - other\n", + " | \n", + " | __truediv__(self, other)\n", + " | self / other: Should promote to float when necessary.\n", + " | \n", + " | conjugate(self)\n", + " | (x+y*i).conjugate() returns (x-y*i).\n", + " | \n", + " | ----------------------------------------------------------------------\n", + " | Data descriptors defined here:\n", + " | \n", + " | imag\n", + " | Retrieve the imaginary component of this number.\n", + " | \n", + " | This should subclass Real.\n", + " | \n", + " | real\n", + " | Retrieve the real component of this number.\n", + " | \n", + " | This should subclass Real.\n", + " | \n", + " | ----------------------------------------------------------------------\n", + " | Data and other attributes defined here:\n", + " | \n", + " | __abstractmethods__ = frozenset({'__abs__', '__add__', '__complex__', ...\n", + " | \n", + " | __hash__ = None\n", + "\n" + ] + } + ], + "source": [ + "help(numbers.Complex)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For sure, Python understands the built-in types as literals." + ] + }, + { + "cell_type": "code", + "execution_count": 198, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on class complex in module builtins:\n", + "\n", + "class complex(object)\n", + " | complex(real=0, imag=0)\n", + " | \n", + " | Create a complex number from a real part and an optional imaginary part.\n", + " | \n", + " | This is equivalent to (real + imag*1j) where imag defaults to 0.\n", + " | \n", + " | Methods defined here:\n", + " | \n", + " | __abs__(self, /)\n", + " | abs(self)\n", + " | \n", + " | __add__(self, value, /)\n", + " | Return self+value.\n", + " | \n", + " | __bool__(self, /)\n", + " | self != 0\n", + " | \n", + " | __divmod__(self, value, /)\n", + " | Return divmod(self, value).\n", + " | \n", + " | __eq__(self, value, /)\n", + " | Return self==value.\n", + " | \n", + " | __float__(self, /)\n", + " | float(self)\n", + " | \n", + " | __floordiv__(self, value, /)\n", + " | Return self//value.\n", + " | \n", + " | __format__(...)\n", + " | complex.__format__() -> str\n", + " | \n", + " | Convert to a string according to format_spec.\n", + " | \n", + " | __ge__(self, value, /)\n", + " | Return self>=value.\n", + " | \n", + " | __getattribute__(self, name, /)\n", + " | Return getattr(self, name).\n", + " | \n", + " | __getnewargs__(...)\n", + " | \n", + " | __gt__(self, value, /)\n", + " | Return self>value.\n", + " | \n", + " | __hash__(self, /)\n", + " | Return hash(self).\n", + " | \n", + " | __int__(self, /)\n", + " | int(self)\n", + " | \n", + " | __le__(self, value, /)\n", + " | Return self<=value.\n", + " | \n", + " | __lt__(self, value, /)\n", + " | Return self complex\n", + " | \n", + " | Return the complex conjugate of its argument. (3-4j).conjugate() == 3+4j.\n", + " | \n", + " | ----------------------------------------------------------------------\n", + " | Static methods defined here:\n", + " | \n", + " | __new__(*args, **kwargs) from builtins.type\n", + " | Create and return a new object. See help(type) for accurate signature.\n", + " | \n", + " | ----------------------------------------------------------------------\n", + " | Data descriptors defined here:\n", + " | \n", + " | imag\n", + " | the imaginary part of a complex number\n", + " | \n", + " | real\n", + " | the real part of a complex number\n", + "\n" + ] + } + ], + "source": [ + "help(complex)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "One way to use *abstract* data types is to use them in place of a *concrete* data type.\n", + "\n", + "For example, we may pass them as arguments to the built-in [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function and check in which of the five mathematical sets the object `1 / 10` is." + ] + }, + { + "cell_type": "code", + "execution_count": 199, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 199, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(1 / 10, float)" + ] + }, + { + "cell_type": "code", + "execution_count": 200, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 200, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(1 / 10, numbers.Number)" + ] + }, + { + "cell_type": "code", + "execution_count": 201, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 201, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(1 / 10, numbers.Complex)" + ] + }, + { + "cell_type": "code", + "execution_count": 202, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 202, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(1 / 10, numbers.Real)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Due to the `float` type's inherent imprecision, `1 / 10` is *not* a rational number." + ] + }, + { + "cell_type": "code", + "execution_count": 203, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 203, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(1 / 10, numbers.Rational)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `Fraction` type qualifies as a rational number; however, the `Decimal` type does not." + ] + }, + { + "cell_type": "code", + "execution_count": 204, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 204, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(Fraction(1, 10), numbers.Rational)" + ] + }, + { + "cell_type": "code", + "execution_count": 205, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 205, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(Decimal(\"0.1\"), numbers.Rational)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Example: [Factorial](https://en.wikipedia.org/wiki/Factorial) (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Replacing *concrete* data types with *abstract* ones is particularly valuable in the context of input validation: The revised version of the `factorial()` function below allows its user to *take advantage of duck typing*. If a real but non-integer argument `n` is passed in, `factorial()` tries to cast `n` as an `int` object with the [trunc()](https://docs.python.org/3/library/math.html#math.trunc) function from the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html). [trunc()](https://docs.python.org/3/library/math.html#math.trunc) cuts off all decimals and any *concrete* numeric type implementing the *abstract* `numbers.Real` type supports it (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Real))." + ] + }, + { + "cell_type": "code", + "execution_count": 206, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import math" + ] + }, + { + "cell_type": "code", + "execution_count": 207, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 207, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.trunc(1 / 10)" + ] + }, + { + "cell_type": "code", + "execution_count": 208, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def factorial(n, *, strict=True):\n", + " \"\"\"Calculate the factorial of a number.\n", + "\n", + " Args:\n", + " n (int): number to calculate the factorial for; must be positive\n", + " strict (bool): if n must not contain decimals; defaults to True\n", + "\n", + " Returns:\n", + " factorial (int)\n", + "\n", + " Raises:\n", + " TypeError: if n is not an integer or cannot be cast as such\n", + " ValueError: if n is negative\n", + " \"\"\"\n", + " if not isinstance(n, numbers.Integral):\n", + " if isinstance(n, numbers.Real):\n", + " if n != math.trunc(n) and strict:\n", + " raise ValueError(\"n is not an integer-like value; it has decimals\")\n", + " n = math.trunc(n)\n", + " else:\n", + " raise TypeError(\"Factorial is only defined for integers\")\n", + "\n", + " if n < 0:\n", + " raise ValueError(\"Factorial is not defined for negative integers\")\n", + " elif n == 0:\n", + " return 1\n", + " return n * factorial(n - 1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`factorial()` works as before, but now also accepts, for example, `float` numbers." + ] + }, + { + "cell_type": "code", + "execution_count": 209, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 209, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 210, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 210, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(3)" + ] + }, + { + "cell_type": "code", + "execution_count": 211, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 211, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(3.0)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With the keyword-only argument `strict`, we can control whether or not a passed in `float` object may be rounded. By default, this is not allowed." + ] + }, + { + "cell_type": "code", + "execution_count": 212, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "n is not an integer-like value; it has decimals", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mReal\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mstrict\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 18\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"n is not an integer-like value; it has decimals\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 19\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: n is not an integer-like value; it has decimals" + ] + } + ], + "source": [ + "factorial(3.1)" + ] + }, + { + "cell_type": "code", + "execution_count": 213, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 213, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(3.1, strict=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For `complex` numbers, `factorial()` still raises a `TypeError`." + ] + }, + { + "cell_type": "code", + "execution_count": 214, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "Factorial is only defined for integers", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m2j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 21\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 22\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: Factorial is only defined for integers" + ] + } + ], + "source": [ + "factorial(1 + 2j)" ] }, { @@ -6029,14 +6920,16 @@ "- `float`: the \"gold\" standard to approximate real numbers (i.e., the set $\\mathbb{R}$); inherently imprecise\n", "- `complex`: layer on top of the `float` type; therefore inherently imprecise\n", "\n", - "Furthermore, the [standard library](https://docs.python.org/3/library/) adds two more types that can be used as substitutes for `float` objects:\n", + "Furthermore, the [standard library](https://docs.python.org/3/library/index.html) adds two more types that can be used as substitutes for `float` objects:\n", "- `Decimal`: similar to `float` but allows customizing the precision; still inherently imprecise\n", "- `Fraction`: a perfect model for rational numbers (i.e., the set $\\mathbb{Q}$); built on top of the `int` type and therefore inherently precise\n", "\n", "The important takeaways for the data science practicioner 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**." + "2. actively expect `nan` results when working with `float` numbers as there are no **loud failures**.\n", + "\n", + "The **numeric tower** is Python's way of implementing the various **abstract** ideas of what a number is in mathematics." ] } ], diff --git a/static/numbers.png b/static/numbers.png new file mode 100644 index 0000000000000000000000000000000000000000..6d757269ffb7f0215648cd5cc4b153c7701b3e40 GIT binary patch literal 90958 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 RA1h$c55)T&dT@Z6{$EL>4blJr literal 0 HcmV?d00001 From 61f6981ffd5ec7a9d2ea1a6f9c14f5e094954bef Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 15 Oct 2019 13:08:05 +0200 Subject: [PATCH 033/242] Add review and exercises for notebook 05 --- 05_numbers.ipynb | 685 +++++++++++++----------- 05_numbers_review_and_exercises.ipynb | 722 ++++++++++++++++++++++++++ 2 files changed, 1108 insertions(+), 299 deletions(-) create mode 100644 05_numbers_review_and_exercises.ipynb diff --git a/05_numbers.ipynb b/05_numbers.ipynb index df7598c..d6409ba 100644 --- a/05_numbers.ipynb +++ b/05_numbers.ipynb @@ -92,7 +92,7 @@ { "data": { "text/plain": [ - "140218620396752" + "139928698315984" ] }, "execution_count": 2, @@ -2115,7 +2115,7 @@ { "data": { "text/plain": [ - "140218620574432" + "139928698489496" ] }, "execution_count": 63, @@ -4070,7 +4070,7 @@ } }, "source": [ - "[getcontext()](https://docs.python.org/3/library/decimal.html#decimal.getcontext) shows us how the [decimal](https://docs.python.org/3/library/decimal.html) module is set up. By default, the precision is set to $28$ significant digits, which is roughly twice as many as with `float` objects." + "[getcontext()](https://docs.python.org/3/library/decimal.html#decimal.getcontext) shows us how the [decimal](https://docs.python.org/3/library/decimal.html) module is set up. By default, the precision is set to `28` significant digits, which is roughly twice as many as with `float` objects." ] }, { @@ -4282,7 +4282,7 @@ } }, "source": [ - "Arithmetic operations between `Decimal` and `int` objects work as the latter are inherently precise: The results are *new* `Decimal` objects." + "`Decimal` numbers *preserve* the **significant digits**, even in cases where this is not needed." ] }, { @@ -4290,14 +4290,14 @@ "execution_count": 137, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "Decimal('42')" + "Decimal('0.30000')" ] }, "execution_count": 137, @@ -4306,7 +4306,7 @@ } ], "source": [ - "42 + Decimal(42) - 42" + "Decimal(\"0.10000\") + Decimal(\"0.20000\")" ] }, { @@ -4321,7 +4321,7 @@ { "data": { "text/plain": [ - "Decimal('42')" + "True" ] }, "execution_count": 138, @@ -4330,7 +4330,18 @@ } ], "source": [ - "10 * Decimal(42) / 10" + "Decimal(\"0.10000\") + Decimal(\"0.20000\") == Decimal(\"0.3\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Arithmetic operations between `Decimal` and `int` objects work as the latter are inherently precise: The results are *new* `Decimal` objects." ] }, { @@ -4345,7 +4356,7 @@ { "data": { "text/plain": [ - "Decimal('0.1')" + "Decimal('42')" ] }, "execution_count": 139, @@ -4353,6 +4364,54 @@ "output_type": "execute_result" } ], + "source": [ + "42 + Decimal(42) - 42" + ] + }, + { + "cell_type": "code", + "execution_count": 140, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('42')" + ] + }, + "execution_count": 140, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "10 * Decimal(42) / 10" + ] + }, + { + "cell_type": "code", + "execution_count": 141, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('0.1')" + ] + }, + "execution_count": 141, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "Decimal(1) / 10" ] @@ -4370,7 +4429,7 @@ }, { "cell_type": "code", - "execution_count": 140, + "execution_count": 142, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4383,7 +4442,7 @@ "'0.10000000000000000000000000000000000000000000000000'" ] }, - "execution_count": 140, + "execution_count": 142, "metadata": {}, "output_type": "execute_result" } @@ -4394,7 +4453,7 @@ }, { "cell_type": "code", - "execution_count": 141, + "execution_count": 143, "metadata": { "slideshow": { "slide_type": "-" @@ -4407,7 +4466,7 @@ "'0.10000000000000000555111512312578270211815834045410'" ] }, - "execution_count": 141, + "execution_count": 143, "metadata": {}, "output_type": "execute_result" } @@ -4429,7 +4488,7 @@ }, { "cell_type": "code", - "execution_count": 142, + "execution_count": 144, "metadata": { "slideshow": { "slide_type": "slide" @@ -4443,7 +4502,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m1.0\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m42\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m1.0\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m42\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for *: 'float' and 'decimal.Decimal'" ] } @@ -4465,7 +4524,7 @@ }, { "cell_type": "code", - "execution_count": 143, + "execution_count": 145, "metadata": { "slideshow": { "slide_type": "slide" @@ -4478,7 +4537,7 @@ "Decimal('2')" ] }, - "execution_count": 143, + "execution_count": 145, "metadata": {}, "output_type": "execute_result" } @@ -4489,7 +4548,7 @@ }, { "cell_type": "code", - "execution_count": 144, + "execution_count": 146, "metadata": { "slideshow": { "slide_type": "-" @@ -4502,7 +4561,7 @@ "Decimal('1.414213562373095048801688724')" ] }, - "execution_count": 144, + "execution_count": 146, "metadata": {}, "output_type": "execute_result" } @@ -4526,7 +4585,7 @@ }, { "cell_type": "code", - "execution_count": 145, + "execution_count": 147, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4539,7 +4598,7 @@ "Decimal('1.999999999999999999999999999')" ] }, - "execution_count": 145, + "execution_count": 147, "metadata": {}, "output_type": "execute_result" } @@ -4558,14 +4617,14 @@ } }, "source": [ - "The [quantize()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize) method allows us to cut off a `Decimal` number at any digit that is smaller than the set precision while adhering to the rounding rules from math.\n", + "The [quantize()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize) method allows us to [quantize](https://www.dictionary.com/browse/quantize) (i.e., \"round\") a `Decimal` number at any precision that is *smaller* than the set precision. It looks at the number of decimals (i.e., to the right of the period) of the numeric argument we pass in.\n", "\n", - "For example, as the overall imprecise value of `two` still has a precision of $28$ digits, we can correctly round it to the first $20$ digits." + "For example, as the overall imprecise value of `two` still has an internal precision of `28` digits, we can correctly round it to *four* decimals (i.e., `Decimal(\"0.0001\")` has four decimals)." ] }, { "cell_type": "code", - "execution_count": 146, + "execution_count": 148, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4575,16 +4634,44 @@ { "data": { "text/plain": [ - "Decimal('2')" + "Decimal('2.0000')" ] }, - "execution_count": 146, + "execution_count": 148, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "two.quantize(20)" + "two.quantize(Decimal(\"0.0001\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can never round a `Decimal` number and obtain a greater precision than before: The `InvalidOperation` exception tells us that *loudly*." + ] + }, + { + "cell_type": "code", + "execution_count": 149, + "metadata": {}, + "outputs": [ + { + "ename": "InvalidOperation", + "evalue": "[]", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mInvalidOperation\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtwo\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquantize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"0.1\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m**\u001b[0m \u001b[0;36m28\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mInvalidOperation\u001b[0m: []" + ] + } + ], + "source": [ + "two.quantize(Decimal(\"0.1\") ** 28)" ] }, { @@ -4595,12 +4682,12 @@ } }, "source": [ - "Consequently, with this little workaround $\\sqrt{2}^2 = 2$ works even in Python." + "Consequently, with this little workaround $\\sqrt{2}^2 = 2$ works, even in Python." ] }, { "cell_type": "code", - "execution_count": 147, + "execution_count": 150, "metadata": { "slideshow": { "slide_type": "skip" @@ -4613,13 +4700,13 @@ "True" ] }, - "execution_count": 147, + "execution_count": 150, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "two.quantize(20) == 2" + "two.quantize(Decimal(\"0.0001\")) == 2" ] }, { @@ -4635,7 +4722,7 @@ }, { "cell_type": "code", - "execution_count": 148, + "execution_count": 151, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4648,13 +4735,13 @@ "True" ] }, - "execution_count": 148, + "execution_count": 151, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "(Decimal(2).sqrt() ** 2).quantize(20) == 2" + "(Decimal(2).sqrt() ** 2).quantize(Decimal(\"0.0001\")) == 2" ] }, { @@ -4670,7 +4757,7 @@ }, { "cell_type": "code", - "execution_count": 149, + "execution_count": 152, "metadata": { "slideshow": { "slide_type": "skip" @@ -4683,85 +4770,13 @@ "Decimal('NaN')" ] }, - "execution_count": 149, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "Decimal(\"nan\")" - ] - }, - { - "cell_type": "code", - "execution_count": 150, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 150, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "Decimal(\"nan\") == Decimal(\"nan\") # nan's never compare equal to anything, not even to themselves" - ] - }, - { - "cell_type": "code", - "execution_count": 151, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "Decimal('Infinity')" - ] - }, - "execution_count": 151, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "Decimal(\"inf\")" - ] - }, - { - "cell_type": "code", - "execution_count": 152, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "Decimal('-Infinity')" - ] - }, "execution_count": 152, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "Decimal(\"-inf\")" + "Decimal(\"nan\")" ] }, { @@ -4776,7 +4791,7 @@ { "data": { "text/plain": [ - "Decimal('Infinity')" + "False" ] }, "execution_count": 153, @@ -4785,7 +4800,7 @@ } ], "source": [ - "Decimal(\"inf\") + 42 # Infinity is infinity, concrete numbers loose its meaning" + "Decimal(\"nan\") == Decimal(\"nan\") # nan's never compare equal to anything, not even to themselves" ] }, { @@ -4800,7 +4815,7 @@ { "data": { "text/plain": [ - "True" + "Decimal('Infinity')" ] }, "execution_count": 154, @@ -4808,6 +4823,78 @@ "output_type": "execute_result" } ], + "source": [ + "Decimal(\"inf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 155, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('-Infinity')" + ] + }, + "execution_count": 155, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"-inf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 156, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('Infinity')" + ] + }, + "execution_count": 156, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"inf\") + 42 # Infinity is infinity, concrete numbers loose its meaning" + ] + }, + { + "cell_type": "code", + "execution_count": 157, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 157, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "Decimal(\"inf\") + 42 == Decimal(\"inf\")" ] @@ -4825,7 +4912,7 @@ }, { "cell_type": "code", - "execution_count": 155, + "execution_count": 158, "metadata": { "slideshow": { "slide_type": "skip" @@ -4839,7 +4926,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"-inf\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"-inf\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m: []" ] } @@ -4850,7 +4937,7 @@ }, { "cell_type": "code", - "execution_count": 156, + "execution_count": 159, "metadata": { "slideshow": { "slide_type": "skip" @@ -4864,7 +4951,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m: []" ] } @@ -4910,7 +4997,7 @@ }, { "cell_type": "code", - "execution_count": 157, + "execution_count": 160, "metadata": { "slideshow": { "slide_type": "slide" @@ -4934,7 +5021,7 @@ }, { "cell_type": "code", - "execution_count": 158, + "execution_count": 161, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4947,7 +5034,7 @@ "Fraction(1, 3)" ] }, - "execution_count": 158, + "execution_count": 161, "metadata": {}, "output_type": "execute_result" } @@ -4958,7 +5045,7 @@ }, { "cell_type": "code", - "execution_count": 159, + "execution_count": 162, "metadata": { "slideshow": { "slide_type": "-" @@ -4971,7 +5058,7 @@ "Fraction(1, 3)" ] }, - "execution_count": 159, + "execution_count": 162, "metadata": {}, "output_type": "execute_result" } @@ -4982,7 +5069,7 @@ }, { "cell_type": "code", - "execution_count": 160, + "execution_count": 163, "metadata": { "slideshow": { "slide_type": "-" @@ -4995,7 +5082,7 @@ "Fraction(3333333333, 10000000000)" ] }, - "execution_count": 160, + "execution_count": 163, "metadata": {}, "output_type": "execute_result" } @@ -5006,7 +5093,7 @@ }, { "cell_type": "code", - "execution_count": 161, + "execution_count": 164, "metadata": { "slideshow": { "slide_type": "-" @@ -5019,7 +5106,7 @@ "Fraction(3333333333, 10000000000)" ] }, - "execution_count": 161, + "execution_count": 164, "metadata": {}, "output_type": "execute_result" } @@ -5041,7 +5128,7 @@ }, { "cell_type": "code", - "execution_count": 162, + "execution_count": 165, "metadata": { "slideshow": { "slide_type": "slide" @@ -5054,7 +5141,7 @@ "Fraction(3, 2)" ] }, - "execution_count": 162, + "execution_count": 165, "metadata": {}, "output_type": "execute_result" } @@ -5065,7 +5152,7 @@ }, { "cell_type": "code", - "execution_count": 163, + "execution_count": 166, "metadata": { "slideshow": { "slide_type": "-" @@ -5078,7 +5165,7 @@ "Fraction(3, 2)" ] }, - "execution_count": 163, + "execution_count": 166, "metadata": {}, "output_type": "execute_result" } @@ -5100,7 +5187,7 @@ }, { "cell_type": "code", - "execution_count": 164, + "execution_count": 167, "metadata": { "slideshow": { "slide_type": "slide" @@ -5113,7 +5200,7 @@ "Fraction(1, 10)" ] }, - "execution_count": 164, + "execution_count": 167, "metadata": {}, "output_type": "execute_result" } @@ -5135,7 +5222,7 @@ }, { "cell_type": "code", - "execution_count": 165, + "execution_count": 168, "metadata": { "slideshow": { "slide_type": "-" @@ -5148,7 +5235,7 @@ "Fraction(3602879701896397, 36028797018963968)" ] }, - "execution_count": 165, + "execution_count": 168, "metadata": {}, "output_type": "execute_result" } @@ -5170,7 +5257,7 @@ }, { "cell_type": "code", - "execution_count": 166, + "execution_count": 169, "metadata": { "slideshow": { "slide_type": "slide" @@ -5183,7 +5270,7 @@ "Fraction(7, 4)" ] }, - "execution_count": 166, + "execution_count": 169, "metadata": {}, "output_type": "execute_result" } @@ -5194,7 +5281,7 @@ }, { "cell_type": "code", - "execution_count": 167, + "execution_count": 170, "metadata": { "slideshow": { "slide_type": "-" @@ -5207,7 +5294,7 @@ "Fraction(1, 2)" ] }, - "execution_count": 167, + "execution_count": 170, "metadata": {}, "output_type": "execute_result" } @@ -5218,7 +5305,7 @@ }, { "cell_type": "code", - "execution_count": 168, + "execution_count": 171, "metadata": { "slideshow": { "slide_type": "-" @@ -5231,7 +5318,7 @@ "Fraction(1, 1)" ] }, - "execution_count": 168, + "execution_count": 171, "metadata": {}, "output_type": "execute_result" } @@ -5242,7 +5329,7 @@ }, { "cell_type": "code", - "execution_count": 169, + "execution_count": 172, "metadata": { "slideshow": { "slide_type": "-" @@ -5255,7 +5342,7 @@ "Fraction(1, 1)" ] }, - "execution_count": 169, + "execution_count": 172, "metadata": {}, "output_type": "execute_result" } @@ -5277,7 +5364,7 @@ }, { "cell_type": "code", - "execution_count": 170, + "execution_count": 173, "metadata": { "slideshow": { "slide_type": "slide" @@ -5290,7 +5377,7 @@ "0.1" ] }, - "execution_count": 170, + "execution_count": 173, "metadata": {}, "output_type": "execute_result" } @@ -5301,7 +5388,7 @@ }, { "cell_type": "code", - "execution_count": 171, + "execution_count": 174, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5314,7 +5401,7 @@ "'0.10000000000000000555111512312578270211815834045410'" ] }, - "execution_count": 171, + "execution_count": 174, "metadata": {}, "output_type": "execute_result" } @@ -5383,12 +5470,12 @@ "source": [ "`complex` numbers are part of core Python. The simplest way to create one is to write an arithmetic expression with the literal `j` notation for $\\textbf{i}$. The `j` is commonly used in many engineering disciplines instead of the symbol $\\textbf{i}$ from math as $I$ in engineering more often than not means [electric current](https://en.wikipedia.org/wiki/Electric_current).\n", "\n", - "For example, the answer to $x^2 = -1$ can be written in Python as `1j` like below. This creates a `complex` object with value `1j`. The same syntactic rules apply as with the above `e` notation: No spaces are allowed between the number and the `j`, and the number may be any `int` or `float` literal." + "For example, the answer to $x^2 = -1$ can be written in Python as `1j` like below. This creates a `complex` object with value `1j`. The same syntactic rules apply as with the above `e` notation: No spaces are allowed between the number and the `j`. The number may be any `int` or `float` literal; however, it is stored as a `float` internally. So, `complex` numbers suffer from the same imprecision as `float` numbers." ] }, { "cell_type": "code", - "execution_count": 172, + "execution_count": 175, "metadata": { "slideshow": { "slide_type": "slide" @@ -5401,7 +5488,7 @@ }, { "cell_type": "code", - "execution_count": 173, + "execution_count": 176, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5411,10 +5498,10 @@ { "data": { "text/plain": [ - "140218611341648" + "139928697740272" ] }, - "execution_count": 173, + "execution_count": 176, "metadata": {}, "output_type": "execute_result" } @@ -5425,7 +5512,7 @@ }, { "cell_type": "code", - "execution_count": 174, + "execution_count": 177, "metadata": { "slideshow": { "slide_type": "-" @@ -5438,7 +5525,7 @@ "complex" ] }, - "execution_count": 174, + "execution_count": 177, "metadata": {}, "output_type": "execute_result" } @@ -5449,7 +5536,7 @@ }, { "cell_type": "code", - "execution_count": 175, + "execution_count": 178, "metadata": { "slideshow": { "slide_type": "-" @@ -5462,7 +5549,7 @@ "1j" ] }, - "execution_count": 175, + "execution_count": 178, "metadata": {}, "output_type": "execute_result" } @@ -5484,7 +5571,7 @@ }, { "cell_type": "code", - "execution_count": 176, + "execution_count": 179, "metadata": { "slideshow": { "slide_type": "slide" @@ -5497,7 +5584,7 @@ "True" ] }, - "execution_count": 176, + "execution_count": 179, "metadata": {}, "output_type": "execute_result" } @@ -5519,7 +5606,7 @@ }, { "cell_type": "code", - "execution_count": 177, + "execution_count": 180, "metadata": { "slideshow": { "slide_type": "slide" @@ -5532,7 +5619,7 @@ "(2+0.5j)" ] }, - "execution_count": 177, + "execution_count": 180, "metadata": {}, "output_type": "execute_result" } @@ -5554,7 +5641,7 @@ }, { "cell_type": "code", - "execution_count": 178, + "execution_count": 181, "metadata": { "slideshow": { "slide_type": "-" @@ -5567,7 +5654,7 @@ "(2+0.5j)" ] }, - "execution_count": 178, + "execution_count": 181, "metadata": {}, "output_type": "execute_result" } @@ -5578,7 +5665,7 @@ }, { "cell_type": "code", - "execution_count": 179, + "execution_count": 182, "metadata": { "slideshow": { "slide_type": "-" @@ -5591,7 +5678,7 @@ "(2+0j)" ] }, - "execution_count": 179, + "execution_count": 182, "metadata": {}, "output_type": "execute_result" } @@ -5602,7 +5689,7 @@ }, { "cell_type": "code", - "execution_count": 180, + "execution_count": 183, "metadata": { "slideshow": { "slide_type": "skip" @@ -5615,7 +5702,7 @@ "(2+0.5j)" ] }, - "execution_count": 180, + "execution_count": 183, "metadata": {}, "output_type": "execute_result" } @@ -5626,7 +5713,7 @@ }, { "cell_type": "code", - "execution_count": 181, + "execution_count": 184, "metadata": { "slideshow": { "slide_type": "skip" @@ -5639,7 +5726,7 @@ "(2+0.5j)" ] }, - "execution_count": 181, + "execution_count": 184, "metadata": {}, "output_type": "execute_result" } @@ -5661,7 +5748,7 @@ }, { "cell_type": "code", - "execution_count": 182, + "execution_count": 185, "metadata": { "slideshow": { "slide_type": "slide" @@ -5675,7 +5762,7 @@ }, { "cell_type": "code", - "execution_count": 183, + "execution_count": 186, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5688,7 +5775,7 @@ "(4+6j)" ] }, - "execution_count": 183, + "execution_count": 186, "metadata": {}, "output_type": "execute_result" } @@ -5699,7 +5786,7 @@ }, { "cell_type": "code", - "execution_count": 184, + "execution_count": 187, "metadata": { "slideshow": { "slide_type": "-" @@ -5712,85 +5799,13 @@ "(-2-2j)" ] }, - "execution_count": 184, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c1 - c2" - ] - }, - { - "cell_type": "code", - "execution_count": 185, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "(40+2j)" - ] - }, - "execution_count": 185, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "c1 + 39" - ] - }, - { - "cell_type": "code", - "execution_count": 186, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "-4j" - ] - }, - "execution_count": 186, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "3.0 - c2" - ] - }, - { - "cell_type": "code", - "execution_count": 187, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "(5+10j)" - ] - }, "execution_count": 187, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "5 * c1" + "c1 - c2" ] }, { @@ -5805,7 +5820,7 @@ { "data": { "text/plain": [ - "(0.5+0.6666666666666666j)" + "(40+2j)" ] }, "execution_count": 188, @@ -5813,13 +5828,85 @@ "output_type": "execute_result" } ], + "source": [ + "c1 + 39" + ] + }, + { + "cell_type": "code", + "execution_count": 189, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-4j" + ] + }, + "execution_count": 189, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "3.0 - c2" + ] + }, + { + "cell_type": "code", + "execution_count": 190, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(5+10j)" + ] + }, + "execution_count": 190, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "5 * c1" + ] + }, + { + "cell_type": "code", + "execution_count": 191, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(0.5+0.6666666666666666j)" + ] + }, + "execution_count": 191, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "c2 / 6" ] }, { "cell_type": "code", - "execution_count": 189, + "execution_count": 192, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5832,7 +5919,7 @@ "(-5+10j)" ] }, - "execution_count": 189, + "execution_count": 192, "metadata": {}, "output_type": "execute_result" } @@ -5843,7 +5930,7 @@ }, { "cell_type": "code", - "execution_count": 190, + "execution_count": 193, "metadata": { "slideshow": { "slide_type": "-" @@ -5856,7 +5943,7 @@ "(0.44+0.08j)" ] }, - "execution_count": 190, + "execution_count": 193, "metadata": {}, "output_type": "execute_result" } @@ -5878,7 +5965,7 @@ }, { "cell_type": "code", - "execution_count": 191, + "execution_count": 194, "metadata": { "slideshow": { "slide_type": "slide" @@ -5891,7 +5978,7 @@ "5.0" ] }, - "execution_count": 191, + "execution_count": 194, "metadata": {}, "output_type": "execute_result" } @@ -5913,7 +6000,7 @@ }, { "cell_type": "code", - "execution_count": 192, + "execution_count": 195, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5926,7 +6013,7 @@ "1.0" ] }, - "execution_count": 192, + "execution_count": 195, "metadata": {}, "output_type": "execute_result" } @@ -5937,7 +6024,7 @@ }, { "cell_type": "code", - "execution_count": 193, + "execution_count": 196, "metadata": { "slideshow": { "slide_type": "-" @@ -5950,7 +6037,7 @@ "2.0" ] }, - "execution_count": 193, + "execution_count": 196, "metadata": {}, "output_type": "execute_result" } @@ -5972,7 +6059,7 @@ }, { "cell_type": "code", - "execution_count": 194, + "execution_count": 197, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5985,7 +6072,7 @@ "(1-2j)" ] }, - "execution_count": 194, + "execution_count": 197, "metadata": {}, "output_type": "execute_result" } @@ -6062,7 +6149,7 @@ "\n", "For the other types, in particular, the `float` type, the implications of their imprecision are discussed in detail above.\n", "\n", - "The abstract concepts behind the four outer-most mathematical sets are part of Python since [PEP 3141](https://www.python.org/dev/peps/pep-3141/) in 2007. The [numbers](https://docs.python.org/3/library/numbers.html) module in the [standard library](https://docs.python.org/3/library/index.html) defines what Pythonistas call the **numeric tower**, a collection of five **[abstract data types](https://en.wikipedia.org/wiki/Abstract_data_type)**, or **abstract base classes** as they are called in Python slang:\n", + "The abstract concepts behind the four outer-most mathematical sets are part of Python since [PEP 3141](https://www.python.org/dev/peps/pep-3141/) in 2007. The [numbers](https://docs.python.org/3/library/numbers.html) module in the [standard library](https://docs.python.org/3/library/index.html) defines what Pythonistas call the **numeric tower**, a collection of five **[abstract data types](https://en.wikipedia.org/wiki/Abstract_data_type)**, or **abstract base classes** as they are called in Python jargon:\n", "\n", "- `numbers.Number`: \"any number\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Number))\n", "- `numbers.Complex`: \"all complex numbers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Complex))\n", @@ -6073,7 +6160,7 @@ }, { "cell_type": "code", - "execution_count": 195, + "execution_count": 198, "metadata": { "slideshow": { "slide_type": "slide" @@ -6086,7 +6173,7 @@ }, { "cell_type": "code", - "execution_count": 196, + "execution_count": 199, "metadata": { "slideshow": { "slide_type": "-" @@ -6114,7 +6201,7 @@ " 'abstractmethod']" ] }, - "execution_count": 196, + "execution_count": 199, "metadata": {}, "output_type": "execute_result" } @@ -6140,7 +6227,7 @@ }, { "cell_type": "code", - "execution_count": 197, + "execution_count": 200, "metadata": { "scrolled": true, "slideshow": { @@ -6262,7 +6349,7 @@ }, { "cell_type": "code", - "execution_count": 198, + "execution_count": 201, "metadata": { "slideshow": { "slide_type": "skip" @@ -6429,7 +6516,7 @@ }, { "cell_type": "code", - "execution_count": 199, + "execution_count": 202, "metadata": { "slideshow": { "slide_type": "slide" @@ -6442,7 +6529,7 @@ "True" ] }, - "execution_count": 199, + "execution_count": 202, "metadata": {}, "output_type": "execute_result" } @@ -6453,7 +6540,7 @@ }, { "cell_type": "code", - "execution_count": 200, + "execution_count": 203, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6466,7 +6553,7 @@ "True" ] }, - "execution_count": 200, + "execution_count": 203, "metadata": {}, "output_type": "execute_result" } @@ -6477,7 +6564,7 @@ }, { "cell_type": "code", - "execution_count": 201, + "execution_count": 204, "metadata": { "slideshow": { "slide_type": "-" @@ -6490,7 +6577,7 @@ "True" ] }, - "execution_count": 201, + "execution_count": 204, "metadata": {}, "output_type": "execute_result" } @@ -6501,7 +6588,7 @@ }, { "cell_type": "code", - "execution_count": 202, + "execution_count": 205, "metadata": { "slideshow": { "slide_type": "-" @@ -6514,7 +6601,7 @@ "True" ] }, - "execution_count": 202, + "execution_count": 205, "metadata": {}, "output_type": "execute_result" } @@ -6536,7 +6623,7 @@ }, { "cell_type": "code", - "execution_count": 203, + "execution_count": 206, "metadata": { "slideshow": { "slide_type": "slide" @@ -6549,7 +6636,7 @@ "False" ] }, - "execution_count": 203, + "execution_count": 206, "metadata": {}, "output_type": "execute_result" } @@ -6571,7 +6658,7 @@ }, { "cell_type": "code", - "execution_count": 204, + "execution_count": 207, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6584,7 +6671,7 @@ "True" ] }, - "execution_count": 204, + "execution_count": 207, "metadata": {}, "output_type": "execute_result" } @@ -6595,7 +6682,7 @@ }, { "cell_type": "code", - "execution_count": 205, + "execution_count": 208, "metadata": { "slideshow": { "slide_type": "-" @@ -6608,7 +6695,7 @@ "False" ] }, - "execution_count": 205, + "execution_count": 208, "metadata": {}, "output_type": "execute_result" } @@ -6641,7 +6728,7 @@ }, { "cell_type": "code", - "execution_count": 206, + "execution_count": 209, "metadata": { "slideshow": { "slide_type": "slide" @@ -6654,7 +6741,7 @@ }, { "cell_type": "code", - "execution_count": 207, + "execution_count": 210, "metadata": { "slideshow": { "slide_type": "-" @@ -6667,7 +6754,7 @@ "0" ] }, - "execution_count": 207, + "execution_count": 210, "metadata": {}, "output_type": "execute_result" } @@ -6678,7 +6765,7 @@ }, { "cell_type": "code", - "execution_count": 208, + "execution_count": 211, "metadata": { "slideshow": { "slide_type": "slide" @@ -6703,7 +6790,7 @@ " if not isinstance(n, numbers.Integral):\n", " if isinstance(n, numbers.Real):\n", " if n != math.trunc(n) and strict:\n", - " raise ValueError(\"n is not an integer-like value; it has decimals\")\n", + " raise TypeError(\"n is not an integer-like value; it has decimals\")\n", " n = math.trunc(n)\n", " else:\n", " raise TypeError(\"Factorial is only defined for integers\")\n", @@ -6728,7 +6815,7 @@ }, { "cell_type": "code", - "execution_count": 209, + "execution_count": 212, "metadata": { "slideshow": { "slide_type": "slide" @@ -6741,7 +6828,7 @@ "1" ] }, - "execution_count": 209, + "execution_count": 212, "metadata": {}, "output_type": "execute_result" } @@ -6752,7 +6839,7 @@ }, { "cell_type": "code", - "execution_count": 210, + "execution_count": 213, "metadata": { "slideshow": { "slide_type": "-" @@ -6765,7 +6852,7 @@ "6" ] }, - "execution_count": 210, + "execution_count": 213, "metadata": {}, "output_type": "execute_result" } @@ -6776,7 +6863,7 @@ }, { "cell_type": "code", - "execution_count": 211, + "execution_count": 214, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6789,7 +6876,7 @@ "6" ] }, - "execution_count": 211, + "execution_count": 214, "metadata": {}, "output_type": "execute_result" } @@ -6811,7 +6898,7 @@ }, { "cell_type": "code", - "execution_count": 212, + "execution_count": 215, "metadata": { "slideshow": { "slide_type": "slide" @@ -6819,15 +6906,15 @@ }, "outputs": [ { - "ename": "ValueError", + "ename": "TypeError", "evalue": "n is not an integer-like value; it has decimals", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mReal\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mstrict\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 18\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"n is not an integer-like value; it has decimals\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 19\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mValueError\u001b[0m: n is not an integer-like value; it has decimals" + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mReal\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mstrict\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 18\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"n is not an integer-like value; it has decimals\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 19\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: n is not an integer-like value; it has decimals" ] } ], @@ -6837,7 +6924,7 @@ }, { "cell_type": "code", - "execution_count": 213, + "execution_count": 216, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6850,7 +6937,7 @@ "6" ] }, - "execution_count": 213, + "execution_count": 216, "metadata": {}, "output_type": "execute_result" } @@ -6872,7 +6959,7 @@ }, { "cell_type": "code", - "execution_count": 214, + "execution_count": 217, "metadata": { "slideshow": { "slide_type": "slide" @@ -6886,8 +6973,8 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m2j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 21\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 22\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m2j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 21\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 22\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mTypeError\u001b[0m: Factorial is only defined for integers" ] } @@ -6924,7 +7011,7 @@ "- `Decimal`: similar to `float` but allows customizing the precision; still inherently imprecise\n", "- `Fraction`: a perfect model for rational numbers (i.e., the set $\\mathbb{Q}$); built on top of the `int` type and therefore inherently precise\n", "\n", - "The important takeaways for the data science practicioner are:\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", diff --git a/05_numbers_review_and_exercises.ipynb b/05_numbers_review_and_exercises.ipynb new file mode 100644 index 0000000..4246965 --- /dev/null +++ b/05_numbers_review_and_exercises.ipynb @@ -0,0 +1,722 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "# Chapter 5: Numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Content Review" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb) of the book. Then work through the eighteen review questions." + ] + }, + { + "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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: Why may objects of type `bool` be regarded a **numeric type** as well?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: Why is it *inefficient* to store `bool` objects in bits resembling a **hexadecimal representation**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: Colors are commonly expressed in the **hexadecimal system** in websites (cf., the [HTML](https://en.wikipedia.org/wiki/HTML) and [CSS](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](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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: What does it mean for a code fragment to **fail silently**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: How do we deal with a `float` object's imprecision if we need to **check for equality**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: What data type, built-in or from the [standard library](https://docs.python.org/3/library/index.html), is best suited to represent the [transcendental numbers](https://en.wikipedia.org/wiki/Transcendental_number) $\\pi$ and $\\text{e}$?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: How can **abstract data types**, for example, as defined in the **numeric tower**, be helpful in enabling **duck typing**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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": [ + "**Q10**: 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: With the built-in [round()](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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q12**: 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q13**: The [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) standard's **special values** provide no benefit in practice as we could always use a **[sentinel](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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q14**: 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15**: 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q16**: From a practitioner's point of view, the built-in [format()](https://docs.python.org/3/library/functions.html#format) function does the *same* as the built-in [round()](https://docs.python.org/3/library/functions.html#round) function." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q17**: The `Decimal` type from the [decimal](https://docs.python.org/3/library/decimal.html) module in the [standard library](https://docs.python.org/3/library/index.html) allows us to model the set of the real numbers $\\mathbb{R}$ *precisely*." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q18**: The `Fraction` type from the [fractions](https://docs.python.org/3/library/fractions.html) module in the [standard library](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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Coding Exercises" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Discounting Customer Orders (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11** in [Chapter 2's Review & Exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions_review_and_exercises.ipynb#Volume-of-a-Sphere) section already revealed that we must consider the effects of the `float` type's imprecision.\n", + "\n", + "This becomes even more important when we deal with numeric data modeling accounting or finance data (cf., [this comment](https://stackoverflow.com/a/24976426) on \"falsehoods programmers believe about money\").\n", + "\n", + "In addition to the *inherent imprecision* of numbers in general, the topic of **[rounding numbers](https://en.wikipedia.org/wiki/Rounding)** is also not as trivial as we might expect! [This article](https://realpython.com/python-rounding/) summarizes everything the data science practitioner needs to know.\n", + "\n", + "In this exercise, we revisit **Q9** from [Chapter 3's Review & Exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_review_and_exercises.ipynb#Discounting-Customer-Orders) section, and make the `discounted_price()` function work *correctly* for real-life sales data." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q19.1**: Execute the code cells below! What results would you have *expected*, and why?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "round(1.5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "round(2.5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "round(2.675, 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q19.2**: The built-in [round()](https://docs.python.org/3/library/functions.html#round) function implements the \"**[round half to even](https://en.wikipedia.org/wiki/Rounding#Round_half_to_even)**\" strategy. Describe in one or two sentences what that means!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q19.3**: For the revised `discounted_price()` function, we have to tackle *two* issues: First, we have to replace the built-in `float` type with a data type that allows us to control the precision. Second, the discounted price should be rounded according to a more human-friendly rounding strategy, namely \"**[round half away from zero](https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero)**.\"\n", + "\n", + "Describe in one or two sentences how \"**[round half away from zero](https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero)**\" is more in line with how humans think of rounding!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q19.4**: We use the `Decimal` type from the [decimal](https://docs.python.org/3/library/decimal.html) module in the [standard library](https://docs.python.org/3/library/index.html) to tackle *both* issues simultaneously.\n", + "\n", + "Assign `euro` a numeric object such that both `Decimal(\"1.5\")` and `Decimal(\"2.5\")` are rounded to `Decimal(\"2\")` (i.e., no decimal) with the [quantize()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize) method!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from decimal import Decimal" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "euro = ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Decimal(\"1.5\").quantize(...)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Decimal(\"2.5\").quantize(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q19.5**: Obviously, the two preceding code cells still [round half to even](https://en.wikipedia.org/wiki/Rounding#Round_half_to_even).\n", + "\n", + "The [decimal](https://docs.python.org/3/library/decimal.html) module defines a `ROUND_HALF_UP` flag that we can pass as the second argument to the [quantize()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize) method. Then, it [rounds half away from zero](https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero).\n", + "\n", + "Add `ROUND_HALF_UP` to the code cells! `Decimal(\"2.5\")` should now be rounded to `Decimal(\"3\")`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from decimal import ROUND_HALF_UP" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Decimal(\"1.5\").quantize(...)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Decimal(\"2.5\").quantize(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q19.6**: Instead of `euro`, define `cents` such that rounding occurs to *two* decimals! `Decimal(\"2.675\")` should now be rounded to `Decimal(\"2.68\")`. Do *not* forget to include the `ROUND_HALF_UP` flag!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cents = ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Decimal(\"2.675\").quantize(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q19.7**: Re-write the function `discounted_price()` from [Chapter 3's Review & Exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_review_and_exercises.ipynb#Discounting-Customer-Orders) section!\n", + "\n", + "It takes the *positional* arguments `unit_price` and `quantity` and implements a discount scheme for a line item in a customer order as follows:\n", + "\n", + "- 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 then returns the overall price for the line item as a `Decimal` number with a precision of *two* decimals.\n", + "\n", + "Enable **duck typing** by allowing the function to be called with various numeric types as the arguments, in particular, `quantity` may be a non-integer as well: Use an appropriate **abstract data type** from the [numbers](https://docs.python.org/3/library/numbers.html) module in the [standard library](https://docs.python.org/3/library/index.html) to verify the arguments' types and also that they are both positive!\n", + "\n", + "It is considered a *best practice* to only round towards the *end* of the calculations." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numbers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def discounted_price(...):\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q19.8**: Execute the code cells below and verify the final price for the following four test cases:\n", + "\n", + "- $7$ smartphones @ $99.00$ USD\n", + "- $3$ workstations @ $999.00$ USD\n", + "- $19$ GPUs @ $879.95$ USD\n", + "- $14$ Raspberry Pis @ $35.00$ USD\n", + "\n", + "The output should now *always* be a `Decimal` number with *two* decimals!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(99, 7)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(999, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(879.95, 19)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(35, 14)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This also works if `quantity` is passed in as a `float` type." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(99, 7.0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Decimals beyond the first two are gracefully discarded (i.e., *without* rounding errors accumulating)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(99.0001, 7)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The basic input validation ensures that the user of `discounted_price()` does not pass in invalid data. Here, the `\"abc\"` creates a `TypeError`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(\"abc\", 7)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A `-1` passed in as `unit_price` results in a `ValueError`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(-1, 7)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "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": 2 +} From 4838c077f056c053b824161adad859b2cc509fcd Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 15 Oct 2019 19:36:16 +0200 Subject: [PATCH 034/242] Streamline previous content --- 02_functions.ipynb | 2 +- 05_numbers.ipynb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/02_functions.ipynb b/02_functions.ipynb index 6ab7c32..941c71b 100644 --- a/02_functions.ipynb +++ b/02_functions.ipynb @@ -57,7 +57,7 @@ "\n", "A function may come with an *explicit* **[return value](https://docs.python.org/3/reference/simple_stmts.html#the-return-statement)** (i.e., \"result\" or \"output\") specified with the `return` statement: Functions that have one are considered **fruitful**; otherwise, they are **void**. Functions of the latter kind are still useful because of their **side effects** (e.g., the [print()](https://docs.python.org/3/library/functions.html#print) built-in). Strictly speaking, they also have an *implicit* return value of `None` that is different from the `False` we saw in Chapter 1.\n", "\n", - "A function should define a **docstring** that describes what it does in a short subject line, what parameters it expects (i.e., their types), and what it returns (if anything). A docstring is a syntactically valid multi-line string (i.e., type `str`) defined within **triple-double quotes** `\"\"\"`. Strings are covered in depth in Chapter 6. Widely adopted standards as to how to format a docstring are [PEP 257](https://www.python.org/dev/peps/pep-0257/) and section 3.8 in [Google's Python Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md)." + "A function should define a **docstring** that describes what it does in a short subject line, what parameters it expects (i.e., their types), and what it returns (if anything). A docstring is a syntactically valid multi-line string (i.e., type `str`) defined within **triple-double quotes** `\"\"\"`. Strings are covered in depth in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb). Widely adopted standards as to how to format a docstring are [PEP 257](https://www.python.org/dev/peps/pep-0257/) and section 3.8 in [Google's Python Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md)." ] }, { diff --git a/05_numbers.ipynb b/05_numbers.ipynb index d6409ba..73daebb 100644 --- a/05_numbers.ipynb +++ b/05_numbers.ipynb @@ -21,7 +21,7 @@ "source": [ "After learning about the basic building blocks of expressing and structuring the business logic in programs, we focus our attention on the **data types** Python offers us, both built-in and available via the [standard library](https://docs.python.org/3/library/index.html) or third-party packages.\n", "\n", - "We start with the \"simple\" ones: Numeric types in this chapter and textual data in Chapter 6. An important fact that holds for all objects of these types is that they are **immutable**. To re-use the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Objects-vs.-Types-vs.-Values), this means that the $0$s and $1$s making up an object's *value* cannot be changed once the bag is created in memory, implying that any operation with or method on the object creates a *new* object in a *different* memory location.\n", + "We start with the \"simple\" ones: Numeric types in this chapter and textual data in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb). An important fact that holds for all objects of these types is that they are **immutable**. To re-use the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Objects-vs.-Types-vs.-Values), this means that the $0$s and $1$s making up an object's *value* cannot be changed once the bag is created in memory, implying that any operation with or method on the object creates a *new* object in a *different* memory location.\n", "\n", "Chapters 7, 8, and 9 then cover the more \"complex\" data types, including, for example, the `list` type. Finally, Chapter 10 completes the picture by introducing language constructs to create custom types.\n", "\n", @@ -4519,7 +4519,7 @@ } }, "source": [ - "To preserve the precision for more advanced mathematical functions, `Decimal` objects come with many methods bound on them. For example, [ln()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.ln) and [log10()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.log10) take the logarithm while [sqrt()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.sqrt) calculates the square root. In general, the functions in the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html) should only be used with `float` objects as they do *not* preserve precision." + "To preserve the precision for more advanced mathematical functions, `Decimal` objects come with many **methods bound** on them. For example, [ln()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.ln) and [log10()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.log10) take the logarithm while [sqrt()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.sqrt) calculates the square root. In general, the functions in the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html) should only be used with `float` objects as they do *not* preserve precision." ] }, { From e309f19d6ca24ea15b4a849ba3b5a2b1dde8a36b Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 15 Oct 2019 19:36:37 +0200 Subject: [PATCH 035/242] Add initial version of notebook 06 --- 06_text.ipynb | 2787 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2787 insertions(+) create mode 100644 06_text.ipynb diff --git a/06_text.ipynb b/06_text.ipynb new file mode 100644 index 0000000..4cdd4ab --- /dev/null +++ b/06_text.ipynb @@ -0,0 +1,2787 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 6: Text" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In this chapter, we continue the study of the built-in data types. Building on our knowledge of numbers, the next layer consists of textual data that are modeled primarily with the `str` type in Python. `str` objects are naturally more \"complex\" than numeric objects as any text consists of an arbitrary and possibly large number of individual characters that may be chosen from any alphabet in the history of humankind. Luckily, Python abstracts away most of this complexity." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The `str` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `str` type is the default way of modeling **textual data**. To create a `str` object, we use a **literal notation** and type the text between enclosing **double quotes** `\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "school = \"WHU - Otto Beisheim School of Management\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Like everything in Python, `school` is an object." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140148947850512" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(school)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "str" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(school)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A `str` object evaluates to itself in a literal notation with enclosing **single quotes** `'` by default. In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb), we already specified the double quotes `\"` convention we stick to in this book. Yet, single quotes `'` and double quotes `\"` are *perfect* substitutes for all `str` objects that do *not* contain any of the two symbols in it. We could have used the reverse convention, as well.\n", + "\n", + "As [this discussion](https://stackoverflow.com/questions/56011/single-quotes-vs-double-quotes-in-python) shows, many programmers have *strong* opinions about that and make up *new* conventions for their projects. Consequently, the discussion was \"closed as not constructive\" by the moderators." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'WHU - Otto Beisheim School of Management'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As the single quote `'` is often used in the English language as a shortener, we could make an argument in favor of using the double quotes `\"`: There are possibly fewer situations like in the two code cells below, in which we must revert to using a `\\` to **escape** a single quote `'` in a text (cf., the section on special characters further below). However, double quotes `\"` are often used as well. So, this argument is somewhat not convincing.\n", + "\n", + "Many proponents of the single quote `'` usage claim that double quotes `\"` make more **visual noise** on the screen. This argument is also not convincing. On the contrary, one could claim that *two* single quotes `''` look so similar to *one* double quote `\"` that it might not be apparent right away what we are looking at. By sticking to double quotes `\"`, we avoid such danger of confusion.\n", + "\n", + "This discussion is an exellent example of a [flame war](https://en.wikipedia.org/wiki/Flaming_%28Internet%29#Flame_war) in the programming world that leads to *no* result.\n", + "\n", + "An *important* fact to know is that enclosing quotes of either kind are *not* part of the `str` object's *value*! They are merely *syntax* to make the text in a code cell a *literal* that Python converts into a `str` object upon reading." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"It's cool that strings are so versatile in Python!\"" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"It's cool that strings are so versatile in Python!\"" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"It's cool that strings are so versatile in Python!\"" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "'It\\'s cool that strings are so versatile in Python!'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Alternatively, we can use the [str()](https://docs.python.org/3/library/stdtypes.html#str) built-in to **cast** non-`str` objects as a `str`." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'123'" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "str(123)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## A \"String\" of Characters" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The idea of a **sequence** is yet another *abstract* concept.\n", + "\n", + "It unifies *four* orthogonal *abstract* concepts into one: Any *concrete* data type, such as `str` in this chapter, is considered a sequence if it simultaneously\n", + "\n", + "1. behaves like a **container** and\n", + "2. an **iterable**, and \n", + "3. comes with a predictable **order** of its\n", + "4. **finite** number of elements.\n", + "\n", + "Chapter 7 discusses sequences in a generic sense and in greater detail. Here, we keep our focus on the `str` type that historically received its name as it models a \"**[string of characters](https://en.wikipedia.org/wiki/String_%28computer_science%29)**,\" and a \"string\" is more formally called a sequence in the computer science literature.\n", + "\n", + "Behaving like a sequence, `str` objects may be treated like `list` objects in many cases. For example, the built-in [len()](https://docs.python.org/3/library/functions.html#len) function tells us how many elements (i.e., characters) make up `school`. [len()](https://docs.python.org/3/library/functions.html#len) would not work on an \"*infinite*\" object: As anything modeled in a program must fit into a computer's finite memory at runtime, there cannot exist objects containing a truly infinite number of elements; however, Chapter 7 introduces a *concrete* iterable data type that can be used to model an *infinite* series of elements and that, consequently, has no concept of \"length.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "40" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(school)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Being iterable, we can iterate over a `str` object, for example, with a `for`-loop, and do something with the individual characters, for example, print them out with extra space in between them.\n", + "\n", + "[Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Containers-vs.-Iterables) already shows that we can loop over *different* concrete types: The example there first loops over the `list` object `[0, 1, 2, 3, 4]` and then the `range` object `range(5)`, with the same outcome. Now, we add the `str` type to the list of *concrete* types we can loop over. *Abstractly* speaking, all three are *iterable*, and there are many more iterable types in Python." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "W H U - O t t o B e i s h e i m S c h o o l o f M a n a g e m e n t " + ] + } + ], + "source": [ + "for letter in school:\n", + " print(letter, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Being a container, we can check if a given object is a member of a sequence with the `in` operator. In the context of strings, the `in` operator has *two* usages: First, it checks if a *single* character is contained in a `str` object. Second, it may also check if a shorter `str` object, then called a **substring**, is contained in a longer one." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"O\" in school" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"WHU\" in school" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"EBS\" in school" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Indexing" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As `str` objects have the additional property of being *ordered*, we may **index** into them to obtain individual characters with the **indexing operator** `[]`. This is analogous to how we obtained individual elements of a `list` object in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?)." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'W'" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'H'" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school[1]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The index must be of type `int`." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "string indices must be integers", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mschool\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: string indices must be integers" + ] + } + ], + "source": [ + "school[1.0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The last index is one less than the above \"length\" of the `str` object as we start counting at 0." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'t'" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school[39]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "An `IndexError` is raised whenever the index is too large." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "ename": "IndexError", + "evalue": "string index out of range", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mschool\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m40\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mIndexError\u001b[0m: string index out of range" + ] + } + ], + "source": [ + "school[40]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We may use *negative* indexes to start counting from the end of the `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'t'" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school[-1]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "One reason why programmers like to start counting at 0 is that a positive index and its *corresponding* negative index always add up to the length of the sequence." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'O'" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school[6]" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'O'" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school[-34]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Slicing" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A **slice** is a substring of a `str` object.\n", + "\n", + "The **slicing operator** is a generalization of the indexing operator: We can put one, two, or three integers within the brackets, separated by colons `:`. The three integers are then referred to as the *start*, *end*, and *step* values." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'WHU'" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school[0:3]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Whereas the *start* is always included in the result, the *end* is not. Counter-intuitive at first, this makes working with individual slices easier as they add up to the original `str` object again. As the *end* is not included, we must end the second slice with `len(school)` or `40` below." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'WHU - Otto Beisheim School of Management'" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school[0:3] + school[3:40]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For convenience, the indexes do not need to lie in the range from 0 to the string's \"length\" when slicing. This is *not* the case for indexing as the `IndexError` above shows." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'WHU - Otto Beisheim School of Management'" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school[0:999]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Commonly, we leave out the `0` for the *start* and the *end* if it is equal to the \"length.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'WHU - Otto Beisheim School of Management'" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school[:3] + school[3:]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Slicing makes it easy to obtain shorter versions of the original `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'WHU Otto Beisheim School'" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school[:3] + school[5:26]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A *step* value of $i$ can be used to obtain only every $i$th letter." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'WU-Ot esemSho fMngmn'" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school[::2]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A negative *step* size reverses the order of the characters." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'tnemeganaM fo loohcS miehsieB ottO - UHW'" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school[::-1]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Immutability" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Whereas elements of a `list` object *may* be *re-assigned*, as shortly hinted at in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?), this is *not* allowed for `str` objects. Once created, they *cannot* be *changed*. Formally, we say that they are **immutable**. In that regard, `str` objects and all the numeric types in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb) are alike.\n", + "\n", + "On the contrary, objects that may be changed after creation, are called **mutable**. We already saw in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?) how mutable objects are more difficult to reason about for a beginner, in particular, if more than *one* variable point to one. Yet, mutability does have its place in a programmer's toolbox, and we revisit this idea in the next chapters.\n", + "\n", + "`str` objects are *immutable* as the `TypeError` indicates: Assignment to an index is *not* supported by this type." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'str' object does not support item assignment", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mschool\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"E\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: 'str' object does not support item assignment" + ] + } + ], + "source": [ + "school[0] = \"E\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The only thing we can do is to create a *new* `str` object in memory." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "new_school = \"EBS\" + school[3:]" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'EBS - Otto Beisheim School of Management'" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "new_school" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140148946876144" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(new_school)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140148947850512" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(school)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## String Operations" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As mentioned before, the `+` and `*` operators are *overloaded* and used for **string concatenation**." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "greeting = \"Hello \"" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Hello WHU'" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "greeting + school[:3]" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'WHU WHU WHU WHU WHU WHU WHU WHU WHU WHU '" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "10 * school[:4]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## String Methods" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Objects of type `str` come with many **methods** bound on them (cf., the [documentation](https://docs.python.org/3/library/stdtypes.html#string-methods) for a full list). As seen before, they work like *normal* functions and are accessed via the **dot operator** `.`. Calling a method is also referred to as **method invocation**.\n", + "\n", + "The [find()](https://docs.python.org/3/library/stdtypes.html#str.find) method returns the index of the first occurrence of a character or a substring. If no match is found, it returns `-1`." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school.find(\"O\")" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school.find(\"Z\")" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "11" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school.find(\"Beisheim\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[find()](https://docs.python.org/3/library/stdtypes.html#str.find) takes optional *start* and *end* arguments that allow us to find occurrences other than the first one." + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school.find(\"e\")" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "16" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school.find(\"e\", 13) # 13 not 12 as otherwise the same character is found" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school.find(\"e\", 13, 15) # \"e\" does not occur in the specified slice" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[count()](https://docs.python.org/3/library/stdtypes.html#str.count) does what we expect." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school.count(\"o\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As [count()](https://docs.python.org/3/library/stdtypes.html#str.count) is *case-sensitive*, we must **chain** it with the [lower()](https://docs.python.org/3/library/stdtypes.html#str.lower) method to get the count of all \"O\"s and \"o\"s." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "5" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school.lower().count(\"o\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Alternatively, we may use the [upper()](https://docs.python.org/3/library/stdtypes.html#str.upper) method and search for \"O\"s." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "5" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "school.upper().count(\"O\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Because `str` objects are *immutable*, the methods always return *new* objects, even if a method does *not* change the value of the `str` object at all." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "example = \"test\"" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140149036799680" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(example)" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "lower = example.lower()" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140148946626016" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(lower)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`example` and `lower` are *different* objects with the *same* value." + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "example is lower" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "example == lower" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Another popular string method is [split()](https://docs.python.org/3/library/stdtypes.html#str.split): It separates a longer `str` object into a list of smaller ones. By default, groups of whitespace are used as the *separator*." + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "WHU\n", + "-\n", + "Otto\n", + "Beisheim\n", + "School\n", + "of\n", + "Management\n" + ] + } + ], + "source": [ + "for word in school.split():\n", + " print(word)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The opposite of splitting is done with the [join()](https://docs.python.org/3/library/stdtypes.html#str.join) method. It is typically invoked on a `str` object that represents a separator (e.g., `\" \"` or `\", \"`) and connects the elements of an *iterable* argument passed in (e.g., `words` below) into one *new* `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "words = [\"This\", \"will\", \"become\", \"a\", \"sentence\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "sentence = \" \".join(words)" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'This will become a sentence'" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sentence" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As the `str` object `\"abcde\"` below is an *iterable* itself, its elements (i.e., characters) are joined together with a space `\" \"` in between." + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'a b c d e'" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\" \".join(\"abcde\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [replace()](https://docs.python.org/3/library/stdtypes.html#str.replace) method creates a *new* `str` object with parts of the text replaced." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'This is a sentence'" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sentence.replace(\"will become\", \"is\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## String Comparison" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The *relational* operators also work with `str` objects, another example of operator overloading. Comparison is done one character at a time until the first pair differs or one operand ends. However, `str` objects are sorted in a \"weird\" way. The reason for this is that computers store characters internally as numbers (i.e., $0$s and $1$s). Depending on the character encoding, these numbers vary. Commonly, characters and symbols used in the American language are encoded with the numbers 0 through 127, the so-called [ASCII standard](https://en.wikipedia.org/wiki/ASCII). However, Python works with the more general [Unicode/UTF-8 standard](https://en.wikipedia.org/wiki/UTF-8) that understands every language ever used by humans, even emojis." + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "A = \"Apple\" # ignore snake_case for variable names in this example\n", + "a = \"apple\"\n", + "B = \"Banana\"" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "A < B" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a < B" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "One way to fix this is to only compare lower-case strings." + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a < B.lower()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To provide a simple intuition for the \"weird\" sorting above, let's think of the American alphabet as being represented by the numbers as listed below. Then `\"Banana\"` is clearly \"smaller\" than `\"apple\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "A -> 65 \t a -> 97\n", + "B -> 66 \t b -> 98\n", + "C -> 67 \t c -> 99\n", + "D -> 68 \t d -> 100\n", + "E -> 69 \t e -> 101\n", + "F -> 70 \t f -> 102\n", + "G -> 71 \t g -> 103\n", + "H -> 72 \t h -> 104\n", + "I -> 73 \t i -> 105\n", + "J -> 74 \t j -> 106\n", + "K -> 75 \t k -> 107\n", + "L -> 76 \t l -> 108\n", + "M -> 77 \t m -> 109\n", + "N -> 78 \t n -> 110\n", + "O -> 79 \t o -> 111\n", + "P -> 80 \t p -> 112\n", + "Q -> 81 \t q -> 113\n", + "R -> 82 \t r -> 114\n", + "S -> 83 \t s -> 115\n", + "T -> 84 \t t -> 116\n", + "U -> 85 \t u -> 117\n", + "V -> 86 \t v -> 118\n", + "W -> 87 \t w -> 119\n", + "X -> 88 \t x -> 120\n", + "Y -> 89 \t y -> 121\n", + "Z -> 90 \t z -> 122\n" + ] + } + ], + "source": [ + "for lower_i in range(65, 91):\n", + " upper_i = lower_i + 32 # all the upper case characters are offset by 32\n", + " lower_char = chr(lower_i) # from their lower case counterpart\n", + " upper_char = chr(upper_i)\n", + " print(f\"{lower_char} -> {lower_i} \\t {upper_char} -> {upper_i}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## String Interpolation" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The previous code cell shows an example of a so-called **f-string**, as introduced by [PEP 498](https://www.python.org/dev/peps/pep-0498/) only in 2016.\n", + "\n", + "So far, we have used the built-in [print()](https://docs.python.org/3/library/functions.html#print) function only with \"plain\" `str` objects (e.g., `\"example\"`) or variables. Sometimes, it is more convenient to fill a value determined at runtime in a \"draft\" `str` object. This is called **string interpolation**. There are three ways to achieve that in Python, but only two are commonly used." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### f-strings" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "f-strings are the new and most readable way. Prepend the literal notation with an `f`, and put variables/expressions within curly braces. The latter are then filled in when the [print()](https://docs.python.org/3/library/functions.html#print) function is executed." + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "name = \"Alexander\"\n", + "time_of_day = \"morning\"" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello Alexander! Good morning.\n" + ] + } + ], + "source": [ + "print(f\"Hello {name}! Good {time_of_day}.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Separated by a colon `:`, various formatting options are available. In the beginning, only the ability to round is useful, and this can be achieved by adding `:.2f` to the variable name to cast the number as a float and round it to two digits." + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "pi = 3.141592653" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Pi is 3.14\n" + ] + } + ], + "source": [ + "print(f\"Pi is {pi:.2f}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Pi is 3.142\n" + ] + } + ], + "source": [ + "print(f\"Pi is {pi:.3f}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### format() Method" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`str` objects also provide a [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method that accepts an arbitrary number of *positional* arguments that are inserted into the `str` object in the same order replacing curly brackets. See the official [documentation](https://docs.python.org/3/library/string.html#formatspec) for a full reference. This is the more traditional way of string interpolation and many code examples on the internet use it. f-strings are the officially recommended way going forward, but the usage of the [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method is most likely not going down any time soon." + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello Alexander! Good morning.\n" + ] + } + ], + "source": [ + "print(\"Hello {}! Good {}.\".format(name, time_of_day))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Use index numbers if the order is different in the `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Good morning, Alexander\n" + ] + } + ], + "source": [ + "print(\"Good {1}, {0}\".format(name, time_of_day))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method may alternatively be used with *keyword* arguments as well. Then, we must put the keyword names within the curly brackets." + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello Alexander! Good morning.\n" + ] + } + ], + "source": [ + "print(\"Hello {name}! Good {time}.\".format(name=name, time=time_of_day))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Numbers are treated as in the f-strings case." + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Pi is 3.14\n" + ] + } + ], + "source": [ + "print(\"Pi is {:.2f}\".format(pi))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Special Characters" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Some symbols have a special meaning within `str` objects. Popular examples are the newline `\\n` and tab `\\t` \"characters.\" The backslash symbol `\\` is also referred to as an **escape character** in this context, indicating that the following character has a meaning other than its literal meaning." + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This is a sentence\n", + "that is printed\n", + "on three lines.\n" + ] + } + ], + "source": [ + "print(\"This is a sentence\\nthat is printed\\non three lines.\")" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Words\taligned\twith\ttabs.\n" + ] + } + ], + "source": [ + "print(\"Words\\taligned\\twith\\ttabs.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As emojis are important as well, they can be inserted with the corresponding **unicode code point** number starting with `\\U`. See this [list](https://en.wikipedia.org/wiki/List_of_Unicode_characters) of unicode characters for an overview." + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "đź„\n" + ] + } + ], + "source": [ + "print(\"\\U0001f604\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Raw Strings" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Sometimes we do *not* want the backslash `\\` and its following character be interpreted as special characters.\n", + "\n", + "For example, let's print a typical installation path on a Windows systems. Obviously, the newline character `\\n` does *not* makes sense here." + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C:\\Programs\n", + "ew_application\n" + ] + } + ], + "source": [ + "print(\"C:\\Programs\\new_application\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Some strings even produce a `SyntaxError` because the `\\U` *cannot* be interpreted as a unicode code point." + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "(unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape (, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print(\"C:\\Users\\Administrator\\Desktop\\Project_Folder\")\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape\n" + ] + } + ], + "source": [ + "print(\"C:\\Users\\Administrator\\Desktop\\Project_Folder\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A simple solution would be to escape the escape character with a *second* backslash `\\`." + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C:\\Programs\\new_application\n" + ] + } + ], + "source": [ + "print(\"C:\\\\Programs\\\\new_application\")" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C:\\Users\\Administrator\\Desktop\\Project_Folder\n" + ] + } + ], + "source": [ + "print(\"C:\\\\Users\\\\Administrator\\\\Desktop\\\\Project_Folder\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, this is tedious to remember and type. Luckily, Python allows treating any string literal as a \"raw\" string, and this is indicated in the string literal by a `r` prefix." + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C:\\Programs\\new_application\n" + ] + } + ], + "source": [ + "print(r\"C:\\Programs\\new_application\")" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C:\\Users\\Administrator\\Desktop\\Project_Folder\n" + ] + } + ], + "source": [ + "print(r\"C:\\Users\\Administrator\\Desktop\\Project_Folder\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Multi-line Strings" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Sometimes, it is convenient to split text across multiple lines in source code. For example, to make lines fit into the 79 characters requirement of [PEP 8](https://www.python.org/dev/peps/pep-0008/) or because the text naturally contains many newlines. Using double quotes `\"` around multiple lines results in a `SyntaxError`." + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "EOL while scanning string literal (, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m \"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m EOL while scanning string literal\n" + ] + } + ], + "source": [ + "\"\n", + "Do not break the lines like this\n", + "\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, by enclosing a string literal with either **triple-double** quotes `\"\"\"` or **triple-single** quotes `'''`, Python creates a \"plain\" `str` object. Docstrings are precisely that, and, by convention, always written in triple-double quotes `\"\"\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "multi_line = \"\"\"\n", + "I am a multi-line string\n", + "consisting of 4 lines.\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Linebreaks are kept and implicitly converted into `\\n` characters." + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'\\nI am a multi-line string\\nconsisting of 4 lines.\\n'" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "multi_line" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The built-in [print()](https://docs.python.org/3/library/functions.html#print) function correctly prints out the `\\n` characters." + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "I am a multi-line string\n", + "consisting of 4 lines.\n", + "\n" + ] + } + ], + "source": [ + "print(multi_line)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Using the [split()](https://docs.python.org/3/library/stdtypes.html#str.split) method with the optional *sep* argument, we confirm that `multi_line` consists of *four* lines with the first and last linebreaks being the first and last characters in the `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 \n", + "1 I am a multi-line string\n", + "2 consisting of 4 lines.\n", + "3 \n" + ] + } + ], + "source": [ + "for i, line in enumerate(multi_line.split(\"\\n\")):\n", + " print(i, line)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## 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 make up the idea of a **sequence**: Every `str` object is an iterable container of a finite number of ordered characters." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "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": 2 +} From ffd407b6a8104bb6c4cfee5cb106c42dacbb20ed Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 15 Oct 2019 20:07:57 +0200 Subject: [PATCH 036/242] Add review and exercises for notebook 06 --- 06_text_review_and_exercises.ipynb | 205 +++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 06_text_review_and_exercises.ipynb diff --git a/06_text_review_and_exercises.ipynb b/06_text_review_and_exercises.ipynb new file mode 100644 index 0000000..304a91d --- /dev/null +++ b/06_text_review_and_exercises.ipynb @@ -0,0 +1,205 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "# Chapter 6: Text" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Content Review" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb) of the book. Then work through the eight review questions." + ] + }, + { + "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 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: What is a direct consequence of the `str` type's property of being **ordered**? What operations could we *not* do with it if it were *unordered*?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: Describe in your own words what we mean with **string interpolation**!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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 model so-called **multi-line strings**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: A **substring** is a string that *subsumes* another string." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: Indexing into a `str` object with a *negative* index **fails silently**: It does *not* raise an error but also does *not* do anything useful." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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": [ + " " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "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": 2 +} From f38a3d1ea29b76e5a6df80188ad2c7a65a4b0d9e Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 15 Oct 2019 20:09:02 +0200 Subject: [PATCH 037/242] Adjust content overview in README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 57c513a..daa2e93 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ As such they can be viewed in a plain web browser: - [03 - Conditionals & Exceptions](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb) - [04 - Recursion & Looping](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb) - [05 - Numbers](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb) +- [06 - Text](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb) However, it is recommended that students **install Python and Jupyter locally** and run the code in the notebooks on their own. From 08e0ec7b7379415b83140952535cba5a087b0534 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 15 Oct 2019 20:12:11 +0200 Subject: [PATCH 038/242] Bump version number --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1e69fc5..c2a9bdc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "intro-to-python" -version = "0.1.0" +version = "0.4.0" description = "An introduction to Python and programming for wanna-be data scientists" authors = ["Alexander Hess "] license = "MIT" From 04d53956a3644375996a69f48847cbabcbd3d603 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 30 Oct 2019 11:04:59 +0100 Subject: [PATCH 039/242] Streamline previous content --- 00_start_up.ipynb | 9 +- 01_elements.ipynb | 32 +- 02_functions.ipynb | 32 +- 03_conditionals.ipynb | 225 +++-- 04_iteration.ipynb | 32 +- 04_iteration_review_and_exercises.ipynb | 2 +- 05_numbers.ipynb | 67 +- 05_numbers_review_and_exercises.ipynb | 4 +- 06_text.ipynb | 1088 ++++++++++++++++++----- 06_text_review_and_exercises.ipynb | 10 +- lorem_ipsum.txt | 6 + 11 files changed, 1097 insertions(+), 410 deletions(-) create mode 100644 lorem_ipsum.txt diff --git a/00_start_up.ipynb b/00_start_up.ipynb index 9ace23c..97fe84b 100644 --- a/00_start_up.ipynb +++ b/00_start_up.ipynb @@ -792,13 +792,12 @@ "**Part 2: Managing Data and Memory**\n", "\n", "- How is data stored in memory?\n", - " 5. [Numbers](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb)\n", - " 6. Text\n", - " 7. Sequences\n", + " 5. [Bits & Numbers](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb)\n", + " 6. [Bytes & Text](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb)\n", + " 7. [Sequential Data](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb)\n", " 8. Mappings & Sets\n", - " 9. Arrays\n", "- How can we create custom data types?\n", - " 10. Object-Orientation" + " 9. Object-Orientation" ] }, { diff --git a/01_elements.ipynb b/01_elements.ipynb index c941e43..f9c6131 100644 --- a/01_elements.ipynb +++ b/01_elements.ipynb @@ -46,11 +46,11 @@ } }, "source": [ - "As our introductory example, we want to calculate the *average* of all *even* numbers from *one* through *ten*.\n", + "As our introductory example, we want to calculate the *average* of all *even* numbers from `1` through `10`.\n", "\n", "While we could come up with an [analytical solution](https://math.stackexchange.com/questions/935405/what-s-the-difference-between-analytical-and-numerical-approaches-to-problems/935446#935446) (i.e., derive some equation with \"pen and paper\" from, e.g., one of [Faulhaber's formulas](https://en.wikipedia.org/wiki/Faulhaber%27s_formula)), we instead solve the task programmatically.\n", "\n", - "We start by creating a **list** called `numbers` that holds all the individual numbers." + "We start by creating a **list** called `numbers` that holds all the individual numbers between **brackets** `[` and `]`." ] }, { @@ -988,7 +988,7 @@ { "data": { "text/plain": [ - "139829040614096" + "139878568414128" ] }, "execution_count": 28, @@ -1012,7 +1012,7 @@ { "data": { "text/plain": [ - "139829040789352" + "139878568593256" ] }, "execution_count": 29, @@ -1036,7 +1036,7 @@ { "data": { "text/plain": [ - "139829040474736" + "139878567760368" ] }, "execution_count": 30, @@ -1080,7 +1080,7 @@ } }, "source": [ - "`a` and `d` indeed have the same value as is checked with the **equality operator** `==`. The resulting `True` (and the `False` further below) is yet another data type, a so-called **boolean**. We look into them closely in [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb)." + "`a` and `d` indeed have the same value as is checked with the **equality operator** `==`. The resulting `True` (and the `False` further below) is yet another data type, a so-called **boolean**. We look into them closely in [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb#Boolean-Expressions)." ] }, { @@ -1222,7 +1222,7 @@ "source": [ "Different types imply different behaviors for the objects. The `b` object, for example, can be \"asked\" if it could also be interpreted as an `int` with the [is_integer()](https://docs.python.org/3/library/stdtypes.html#float.is_integer) \"functionality\" that comes with every `float` object.\n", "\n", - "Formally, we call such type-specific functionalities **methods** (to differentiate them from functions) and we formally introduce them in Chapter 10. For now, it suffices to know that we access them using the **dot operator** `.`. Of course, `b` could be converted into an `int`, which the boolean value `True` tells us." + "Formally, we call such type-specific functionalities **methods** (to differentiate them from functions) and we formally introduce them in Chapter 9. For now, it suffices to know that we access them using the **dot operator** `.`. Of course, `b` could be converted into an `int`, which the boolean value `True` tells us." ] }, { @@ -1817,7 +1817,7 @@ " " ], "text/plain": [ - "" + "" ] }, "execution_count": 51, @@ -1838,7 +1838,7 @@ } }, "source": [ - "For example, while the above code to calculate the average of the even numbers from 1 through 10 is correct, a Pythonista would re-write it in a more \"Pythonic\" way and use the [sum()](https://docs.python.org/3/library/functions.html#sum) and [len()](https://docs.python.org/3/library/functions.html#len) (= \"length\") built-in functions (cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb)) as well as a so-called **list comprehension** (cf., Chapter 7). Pythonic code runs faster in many cases and is less error-prone." + "For example, while the above code to calculate the average of the even numbers from 1 through 10 is correct, a Pythonista would re-write it in a more \"Pythonic\" way and use the [sum()](https://docs.python.org/3/library/functions.html#sum) and [len()](https://docs.python.org/3/library/functions.html#len) (= \"length\") [built-in functions](https://docs.python.org/3/library/functions.html) (cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb#Built-in-Functions)) as well as a so-called **list comprehension** (cf., [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#List-Comprehensions)). Pythonic code runs faster in many cases and is less error-prone." ] }, { @@ -2040,7 +2040,7 @@ "\n", "At the same time, for a beginner's course, it is often easier to code linearly.\n", "\n", - "In real data science projects, one would probably employ a mixed approach and put re-usable code into so-called Python modules (i.e., *.py* files; cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb)) and then use Jupyter notebooks to build up a linear report or storyline for a business argument to be made." + "In real data science projects, one would probably employ a mixed approach and put re-usable code into so-called Python modules (i.e., *.py* files; cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb#Local-Modules-and-Packages)) and then use Jupyter notebooks to build up a linear report or storyline for a business argument to be made." ] }, { @@ -2330,7 +2330,7 @@ } }, "source": [ - "Some variables magically exist when we start a Python process or are added by Jupyter. We may safely ignore the former until Chapter 10 and the latter for good." + "Some variables magically exist when we start a Python process or are added by Jupyter. We may safely ignore the former until Chapter 9 and the latter for good." ] }, { @@ -2779,7 +2779,7 @@ "source": [ "Let's change the first element of `x`.\n", "\n", - "Chapter 7 discusses lists in more depth. For now, let's view a `list` object as some sort of **container** that holds an arbitrary number of pointers to other objects and treat the brackets `[]` attached to it as just another operator, called the **indexing operator**. `x[0]` instructs Python to first follow the pointer from the global list of all names to the `x` object. Then, it follows the first pointer it finds there to the `1` object. The indexing operator must be an operator as we merely read the first element and do not change anything in memory.\n", + "[Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#The-list-Type) discusses lists in more depth. For now, let's view a `list` object as some sort of **container** that holds an arbitrary number of pointers to other objects and treat the brackets `[]` attached to it as just another operator, called the **indexing operator**. `x[0]` instructs Python to first follow the pointer from the global list of all names to the `x` object. Then, it follows the first pointer it finds there to the `1` object. The indexing operator must be an operator as we merely read the first element and do not change anything in memory.\n", "\n", "Note how Python **begins counting at 0**. This is not the case for many other languages, for example, [MATLAB](https://en.wikipedia.org/wiki/MATLAB), [R](https://en.wikipedia.org/wiki/R_%28programming_language%29), or [Stata](https://en.wikipedia.org/wiki/Stata). To understand why this makes sense, see this short [note](https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html) by one of the all-time greats in computer science, the late [Edsger Dijkstra](https://en.wikipedia.org/wiki/Edsger_W._Dijkstra)." ] @@ -3189,7 +3189,7 @@ " " ], "text/plain": [ - "" + "" ] }, "execution_count": 94, @@ -3579,12 +3579,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "I change the display of the computer\n" + "I change the state of the computer's display\n" ] } ], "source": [ - "print(\"I change the display of the computer\")" + "print(\"I change the state of the computer's display\")" ] }, { @@ -3686,7 +3686,7 @@ } }, "source": [ - "We end each chapter with a summary of the main points. The essence in this first chapter is that just like 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." + "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." ] }, { diff --git a/02_functions.ipynb b/02_functions.ipynb index 941c71b..809982f 100644 --- a/02_functions.ipynb +++ b/02_functions.ipynb @@ -45,7 +45,7 @@ } }, "source": [ - "So-called **[user-defined functions](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)** may be created with the `def` statement. To extend an already familiar example, we re-use the introductory example from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) in its final Pythonic version and transform it into the function `average_evens()` below. \n", + "So-called **[user-defined functions](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)** may be created with the `def` statement. To extend an already familiar example, we re-use the introductory example from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Best-Practices) in its final Pythonic version and transform it into the function `average_evens()` below. \n", "\n", "A function's **name** must be chosen according to the same naming rules as ordinary variables as Python manages function names like variables. In this book, we further adopt the convention of ending function names with parentheses `()` in text cells for faster comprehension when reading (i.e., `average_evens()` vs. `average_evens`). These are *not* part of the name but must always be written out in the `def` statement for syntactic reasons.\n", "\n", @@ -55,9 +55,9 @@ "\n", "Together, the name and the list of parameters are also referred to as the function's **[signature](https://en.wikipedia.org/wiki/Type_signature)** (i.e., `average_evens(numbers)` below).\n", "\n", - "A function may come with an *explicit* **[return value](https://docs.python.org/3/reference/simple_stmts.html#the-return-statement)** (i.e., \"result\" or \"output\") specified with the `return` statement: Functions that have one are considered **fruitful**; otherwise, they are **void**. Functions of the latter kind are still useful because of their **side effects** (e.g., the [print()](https://docs.python.org/3/library/functions.html#print) built-in). Strictly speaking, they also have an *implicit* return value of `None` that is different from the `False` we saw in Chapter 1.\n", + "A function may come with an *explicit* **[return value](https://docs.python.org/3/reference/simple_stmts.html#the-return-statement)** (i.e., \"result\" or \"output\") specified with the `return` statement: Functions that have one are considered **fruitful**; otherwise, they are **void**. Functions of the latter kind are still useful because of their **side effects** (e.g., the [print()](https://docs.python.org/3/library/functions.html#print) built-in). Strictly speaking, they also have an *implicit* return value of `None` that is different from the `False` we saw in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Identity-/-\"Memory-Location\").\n", "\n", - "A function should define a **docstring** that describes what it does in a short subject line, what parameters it expects (i.e., their types), and what it returns (if anything). A docstring is a syntactically valid multi-line string (i.e., type `str`) defined within **triple-double quotes** `\"\"\"`. Strings are covered in depth in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb). Widely adopted standards as to how to format a docstring are [PEP 257](https://www.python.org/dev/peps/pep-0257/) and section 3.8 in [Google's Python Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md)." + "A function should define a **docstring** that describes what it does in a short subject line, what parameters it expects (i.e., their types), and what it returns (if anything). A docstring is a syntactically valid multi-line string (i.e., type `str`) defined within **triple-double quotes** `\"\"\"`. Strings are covered in depth in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb#The-str-Type). Widely adopted standards as to how to format a docstring are [PEP 257](https://www.python.org/dev/peps/pep-0257/) and section 3.8 in [Google's Python Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md)." ] }, { @@ -142,7 +142,7 @@ { "data": { "text/plain": [ - "140519730762208" + "139829511291360" ] }, "execution_count": 3, @@ -657,7 +657,7 @@ "source": [ "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010%5D%0A%0Adef%20average_wrong%28numbers%29%3A%0A%20%20%20%20evens%20%3D%20%5Bn%20for%20n%20in%20nums%20if%20n%20%25%202%20%3D%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28evens%29%20/%20len%28evens%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_wrong%28%5B123,%20456,%20789%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) is again helpful at visualizing the error interactively: Creating the `list` object `evens` eventually points to takes *12* computational steps, namely one for setting up an empty `list` object, *ten* for filling it with elements derived from `nums` in the global scope, and one to make `evens` point at it (cf., steps 6-18).\n", "\n", - "The frames logic shown by PythonTutor is the mechanism by which Python not only manages the names inside *one* function call but also for *many* potentially simultaneous* calls, as revealed in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb). It is the reason why we may re-use the same names for the parameters and variables inside both `average_evens()` and `average_wrong()` without Python mixing them up. So, as we already read in the [Zen of Python](https://www.python.org/dev/peps/pep-0020/), \"namespaces are one honking great idea\" (cf., `import this`), and a frame is just a special kind of namespace." + "The frames logic shown by PythonTutor is the mechanism by which Python not only manages the names inside *one* function call but also for *many* potentially *simultaneous* calls, as revealed in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Trivial-Example:-Countdown). It is the reason why we may re-use the same names for the parameters and variables inside both `average_evens()` and `average_wrong()` without Python mixing them up. So, as we already read in the [Zen of Python](https://www.python.org/dev/peps/pep-0020/), \"namespaces are one honking great idea\" (cf., `import this`), and a frame is just a special kind of namespace." ] }, { @@ -1170,7 +1170,7 @@ } }, "source": [ - "So far, we have only specified one parameter in each of our user-defined functions. In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb), however, we saw the built-in function [divmod()](https://docs.python.org/3/library/functions.html#divmod) take two arguments. And, the order of the numbers passed in mattered! Whenever we call a function and list its arguments in a comma separated manner, we say that we pass in the arguments by position or refer to them as **positional arguments**." + "So far, we have only specified one parameter in each of our user-defined functions. In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#%28Arithmetic%29-Operators), however, we saw the built-in function [divmod()](https://docs.python.org/3/library/functions.html#divmod) take two arguments. And, the order of the numbers passed in mattered! Whenever we call a function and list its arguments in a comma separated manner, we say that we pass in the arguments by position or refer to them as **positional arguments**." ] }, { @@ -1941,7 +1941,7 @@ "source": [ "The main point of having functions without a name is to use them in a situation where we know ahead of time that we use the function *once* only.\n", "\n", - "Popular applications of lambda expressions are with the **map-filter-reduce** paradigm in Chapter 7 or when we do \"number crunching\" with **arrays** and **data frames** in Chapter 9." + "Popular applications of lambda expressions occur in combination with the **map-filter-reduce** paradigm or when we do \"number crunching\" with **arrays** and **data frames**. We look at both in detail in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb)." ] }, { @@ -2084,7 +2084,7 @@ { "data": { "text/plain": [ - "140519828111832" + "139829616976344" ] }, "execution_count": 58, @@ -2356,7 +2356,7 @@ "source": [ "Observe how the arguments passed to functions do not need to be just variables or simple literals. Instead, we may pass in any *expression* that evaluates to a *new* object of the type the function expects.\n", "\n", - "So just as a reminder from the expression vs. statement discussion in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb): An expression is *any* syntactically correct combination of variables and literals with operators. And the call operator `()` is yet another operator. So both of the next two code cells are just expressions! They have no permanent side effects in memory. We may execute them as often as we want *without* changing the state of the program (i.e., this Jupyter notebook).\n", + "So just as a reminder from the expression vs. statement discussion in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Expressions): An expression is *any* syntactically correct combination of variables and literals with operators. And the call operator `()` is yet another operator. So both of the next two code cells are just expressions! They have no permanent side effects in memory. We may execute them as often as we want *without* changing the state of the program (i.e., this Jupyter notebook).\n", "\n", "So, regarding the very next cell in particular: Although the `2 ** 2` creates a *new* object `4` in memory that is then immediately passed into the [math.sqrt()](https://docs.python.org/3/library/math.html#math.sqrt) function, once that function call returns, \"all is lost\" and the newly created `4` object is forgotten again, as well as the return value of [math.sqrt()](https://docs.python.org/3/library/math.html#math.sqrt)." ] @@ -2537,7 +2537,7 @@ } }, "source": [ - "Besides the usual dunder-style attributes, the built-in [dir()](https://docs.python.org/3/library/functions.html#dir) function lists some attributes in an upper case naming convention and many others starting with a *single* underscore `_`. To understand the former, we must wait until Chapter 10, while the latter is explained further below." + "Besides the usual dunder-style attributes, the built-in [dir()](https://docs.python.org/3/library/functions.html#dir) function lists some attributes in an upper case naming convention and many others starting with a *single* underscore `_`. To understand the former, we must wait until Chapter 9, while the latter is explained further below." ] }, { @@ -2697,7 +2697,7 @@ { "data": { "text/plain": [ - "0.5291407120147841" + "0.4165845384207939" ] }, "execution_count": 75, @@ -2732,7 +2732,7 @@ { "data": { "text/plain": [ - ">" + ">" ] }, "execution_count": 76, @@ -2781,7 +2781,7 @@ { "data": { "text/plain": [ - "4" + "2" ] }, "execution_count": 78, @@ -2927,9 +2927,9 @@ } }, "source": [ - "[numpy](http://www.numpy.org/) is the de-facto standard in the Python world for handling **array-like** data. That is a fancy word for data that can be put into a matrix or vector format. We look at it in depth in Chapter 9.\n", + "[numpy](http://www.numpy.org/) is the de-facto standard in the Python world for handling **array-like** data. That is a fancy word for data that can be put into a matrix or vector format. We look at it in depth in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb).\n", "\n", - "As [numpy](http://www.numpy.org/) is *not* in the [standard library](https://docs.python.org/3/library/index.html), it must be *manually* installed, for example, with the [pip](https://pip.pypa.io/en/stable/) tool. As mentioned in [Chapter 0](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb), to execute terminal commands from within a Jupyter notebook, we start a code cell with an exclamation mark.\n", + "As [numpy](http://www.numpy.org/) is *not* in the [standard library](https://docs.python.org/3/library/index.html), it must be *manually* installed, for example, with the [pip](https://pip.pypa.io/en/stable/) tool. As mentioned in [Chapter 0](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb#Markdown-vs.-Code-Cells), to execute terminal commands from within a Jupyter notebook, we start a code cell with an exclamation mark.\n", "\n", "If you are running this notebook with an installation of the [Anaconda Distribution](https://www.anaconda.com/distribution/), then [numpy](http://www.numpy.org/) is probably already installed. Running the cell below confirms that." ] @@ -3433,7 +3433,7 @@ } }, "source": [ - "Packages are a generalization of modules, and we look at one in detail in Chapter 10. You may, however, already look at a [sample package](https://github.com/webartifex/intro-to-python/tree/master/sample_package) in the repository, which is nothing but a folder with *.py* files in it.\n", + "Packages are a generalization of modules, and we look at one in detail in Chapter 9. You may, however, already look at a [sample package](https://github.com/webartifex/intro-to-python/tree/master/sample_package) in the repository, which is nothing but a folder with *.py* files in it.\n", "\n", "As a further reading on modules and packages, we refer to the [official tutorial](https://docs.python.org/3/tutorial/modules.html)." ] diff --git a/03_conditionals.ipynb b/03_conditionals.ipynb index d0ed61d..90da64f 100644 --- a/03_conditionals.ipynb +++ b/03_conditionals.ipynb @@ -139,7 +139,7 @@ } }, "source": [ - "There are, however, cases where even well-behaved Python does not make us happy. [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb) provides more insights into this \"bug.\"" + "There are, however, cases where even well-behaved Python does not make us happy. [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb#Imprecision) provides more insights into this \"bug.\"" ] }, { @@ -189,7 +189,7 @@ { "data": { "text/plain": [ - "94906834637792" + "94731133531104" ] }, "execution_count": 5, @@ -213,7 +213,7 @@ { "data": { "text/plain": [ - "94906834637760" + "94731133531072" ] }, "execution_count": 6, @@ -281,7 +281,7 @@ } }, "source": [ - "Let's not confuse the boolean `False` with `None`, another built-in object! We saw the latter before in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) as the *implicit* return value of a function without a `return` statement.\n", + "Let's not confuse the boolean `False` with `None`, another built-in object! We saw the latter before in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb#Function-Definition) as the *implicit* return value of a function without a `return` statement.\n", "\n", "We might think of `None` in a boolean context indicating a \"maybe\" or even an \"unknown\" answer; however, for Python, there are no \"maybe\" or \"unknown\" objects, as we see further below!\n", "\n", @@ -313,7 +313,7 @@ { "data": { "text/plain": [ - "94906834624752" + "94731133518064" ] }, "execution_count": 10, @@ -357,7 +357,7 @@ } }, "source": [ - "`True`, `False`, and `None` have the property that they each exist in memory only *once*. Objects designed this way are so-called **singletons**. This **[design pattern](https://en.wikipedia.org/wiki/Design_Patterns)** was originally developed to keep a program's memory usage at a minimum. It may only be employed in situations where we know that an object does *not* mutate its value (i.e., to re-use the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb), no flipping of $0$s and $1$s in the bag is allowed). In languages \"closer\" to the memory like C, we would have to code this singleton logic ourselves, but Python has this built in for *some* types.\n", + "`True`, `False`, and `None` have the property that they each exist in memory only *once*. Objects designed this way are so-called **singletons**. This **[design pattern](https://en.wikipedia.org/wiki/Design_Patterns)** was originally developed to keep a program's memory usage at a minimum. It may only be employed in situations where we know that an object does *not* mutate its value (i.e., to re-use the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Objects-vs.-Types-vs.-Values), no flipping of $0$s and $1$s in the bag is allowed). In languages \"closer\" to the memory like C, we would have to code this singleton logic ourselves, but Python has this built in for *some* types.\n", "\n", "We verify this with either the `is` operator or by comparing memory addresses." ] @@ -897,9 +897,11 @@ } }, "source": [ - "The operands of the logical operators do not need to be *boolean* expressions as defined above but may be *any* expression. If a sub-expression does *not* evaluate to an object of type `bool`, Python automatically casts it as such.\n", + "The operands of the logical operators do not need to be *boolean* expressions but may be *any* expression. If a sub-expression does *not* evaluate to an object of type `bool`, Python automatically casts it as such.\n", "\n", - "For example, any non-zero numeric object becomes `True`. While this behavior allows writing more concise and thus more \"beautiful\" code, it is also a common source of confusion. `(x - 9)` is cast as `True` and then the overall expression evaluates to `True` as well." + "For example, any non-zero numeric object becomes `True`. While this behavior allows writing more concise and thus more \"beautiful\" code, it is also a common source of confusion.\n", + "\n", + "So, `(x - 9)` is cast as `True` and then the overall expression evaluates to `True` as well." ] }, { @@ -1122,7 +1124,7 @@ } }, "source": [ - "Pythonistas often use the terms **truthy** or **falsy** to describe a non-boolean expression's behavior when used in place of a boolean one." + "Pythonistas use the terms **truthy** or **falsy** to describe a non-boolean expression's behavior when evaluated in a boolean context." ] }, { @@ -1144,14 +1146,18 @@ } }, "source": [ - "When evaluating boolean expressions with logical operators in it, Python follows the **[short-circuiting](https://en.wikipedia.org/wiki/Short-circuit_evaluation)** strategy: First, the inner-most sub-expressions are evaluated. Second, with identical **[operator precedence](https://docs.python.org/3/reference/expressions.html#operator-precedence)**, evaluation goes from left to right. Once it is clear what the overall truth value is, no more sub-expressions are evaluated, and the result is *immediately* returned.\n", + "When evaluating expressions involving the `and` and `or` operators, Python follows the **[short-circuiting](https://en.wikipedia.org/wiki/Short-circuit_evaluation)** strategy: Once it is clear what the overall truth value is, no more sub-expressions are evaluated, and the result is *immediately* returned.\n", "\n", - "In summary, data science practitioners must know *how* the following two generic expressions are evaluated:\n", + "Also, if such expressions are evaluated in a non-boolean context, the result is returned as is and *not* cast as a `bool` type.\n", "\n", - "- `x or y`: The `y` expression is evaluated *only if* `x` evaluates to `False`, in which case `y` is returned; otherwise, `x` is returned *without* even looking at `y`.\n", - "- `x and y`: The `y` expression is evaluated *only if* `x` evaluates to `True`. Then, if `y` also evaluates to `True`, it is returned; otherwise, `x` is returned.\n", + "The two rules can be summarized as:\n", "\n", - "Let's look at a couple of examples." + "- `x or y`: If `x` is truthy, it is returned *without* evaluating `y`. Otherwise, `y` is evaluated *and* returned.\n", + "- `x and y`: If `x` is falsy, it is returned *without* evaluating `y`. Otherwise, `y` is evaluated *and* returned.\n", + "\n", + "The rules may also be chained or combined.\n", + "\n", + "Let's look at a couple of examples below. To visualize which sub-expressions are evaluated, we define a helper function `expr()` that prints out the only argument it is passed before returning it." ] }, { @@ -1163,32 +1169,6 @@ } }, "outputs": [], - "source": [ - "x = 0\n", - "y = 1\n", - "z = 2" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "We define a helper function `expr()` that prints out the only argument it is passed before returning it. With `expr()`, we can see if a sub-expression is evaluated or not." - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [], "source": [ "def expr(arg):\n", " \"\"\"Print and return the only argument.\"\"\"\n", @@ -1204,12 +1184,12 @@ } }, "source": [ - "With the `or` operator, the first sub-expression that evaluates to `True` is returned." + "With the `or` operator, the first truthy sub-expression is returned." ] }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 37, "metadata": { "slideshow": { "slide_type": "slide" @@ -1230,18 +1210,18 @@ "1" ] }, - "execution_count": 38, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "expr(x) or expr(y)" + "expr(0) or expr(1)" ] }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 38, "metadata": { "slideshow": { "slide_type": "-" @@ -1261,18 +1241,18 @@ "1" ] }, - "execution_count": 39, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "expr(y) or expr(z)" + "expr(1) or expr(2) # 2 is not evaluated due to short-circuiting" ] }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 39, "metadata": { "slideshow": { "slide_type": "-" @@ -1293,13 +1273,13 @@ "1" ] }, - "execution_count": 40, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "expr(x) or expr(y) or expr(z)" + "expr(0) or expr(1) or expr(2) # 2 is not evaluated due to short-circuiting" ] }, { @@ -1310,12 +1290,12 @@ } }, "source": [ - "If all sub-expressions evaluate to `False`, the last one is the result." + "If all sub-expressions are falsy, the last one is returned." ] }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 40, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1337,13 +1317,13 @@ "0" ] }, - "execution_count": 41, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "expr(False) or expr([]) or expr(x)" + "expr(False) or expr([]) or expr(0)" ] }, { @@ -1354,12 +1334,12 @@ } }, "source": [ - "With the `and` operator, the first sub-expression that evaluates to `False` is returned." + "With the `and` operator, the first falsy sub-expression is returned." ] }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 41, "metadata": { "slideshow": { "slide_type": "slide" @@ -1373,6 +1353,38 @@ "Arg: 0\n" ] }, + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr(0) and expr(1) # 1 is not evaluated due to short-circuiting" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Arg: 1\n", + "Arg: 0\n" + ] + }, { "data": { "text/plain": [ @@ -1385,7 +1397,7 @@ } ], "source": [ - "expr(x) and expr(y)" + "expr(1) and expr(0)" ] }, { @@ -1417,40 +1429,7 @@ } ], "source": [ - "expr(y) and expr(x)" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Arg: 2\n", - "Arg: 1\n", - "Arg: 0\n" - ] - }, - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 44, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "expr(z) and expr(y) and expr(x)" + "expr(1) and expr(0) and expr(2) # 2 is not evaluated due to short-circuiting" ] }, { @@ -1461,12 +1440,12 @@ } }, "source": [ - "If all sub-expressions evaluate to `True`, the last one is returned." + "If all sub-expressions are truthy, the last one is returned." ] }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 44, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1487,13 +1466,13 @@ "2" ] }, - "execution_count": 45, + "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "expr(y) and expr(z)" + "expr(1) and expr(2)" ] }, { @@ -1543,7 +1522,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 45, "metadata": { "code_folding": [], "slideshow": { @@ -1557,7 +1536,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 46, "metadata": { "code_folding": [], "slideshow": { @@ -1599,7 +1578,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 47, "metadata": { "slideshow": { "slide_type": "slide" @@ -1612,13 +1591,21 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 48, "metadata": { "slideshow": { "slide_type": "-" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "You read this just as often as you see heads when tossing a coin\n" + ] + } + ], "source": [ "if random.random() > 0.5:\n", " print(\"You read this just as often as you see heads when tossing a coin\")" @@ -1637,7 +1624,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 49, "metadata": { "slideshow": { "slide_type": "skip" @@ -1674,7 +1661,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 50, "metadata": { "slideshow": { "slide_type": "slide" @@ -1717,7 +1704,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 51, "metadata": { "slideshow": { "slide_type": "slide" @@ -1790,7 +1777,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 52, "metadata": { "slideshow": { "slide_type": "slide" @@ -1814,7 +1801,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 53, "metadata": { "slideshow": { "slide_type": "-" @@ -1830,7 +1817,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 54, "metadata": { "slideshow": { "slide_type": "-" @@ -1843,7 +1830,7 @@ "9" ] }, - "execution_count": 55, + "execution_count": 54, "metadata": {}, "output_type": "execute_result" } @@ -1865,7 +1852,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 55, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1878,7 +1865,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 56, "metadata": { "slideshow": { "slide_type": "skip" @@ -1891,7 +1878,7 @@ "9" ] }, - "execution_count": 57, + "execution_count": 56, "metadata": {}, "output_type": "execute_result" } @@ -1913,7 +1900,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 57, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1926,7 +1913,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 58, "metadata": { "slideshow": { "slide_type": "skip" @@ -1939,7 +1926,7 @@ "9" ] }, - "execution_count": 59, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } @@ -1956,7 +1943,7 @@ } }, "source": [ - "Conditional expressions may not only be used in the way described in this section. We already saw them as part of a *list comprehension* in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) and [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) and revisit this in Chapter 7 in greater detail." + "Conditional expressions may not only be used in the way described in this section. We already saw them as part of a *list comprehension* in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) and [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) and revisit this construct in greater detail in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#List-Comprehensions)." ] }, { @@ -1987,7 +1974,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 59, "metadata": { "slideshow": { "slide_type": "slide" @@ -2000,7 +1987,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 60, "metadata": { "slideshow": { "slide_type": "-" @@ -2039,7 +2026,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 61, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2078,7 +2065,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 62, "metadata": { "slideshow": { "slide_type": "slide" @@ -2089,7 +2076,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Yes, division worked smoothly.\n", + "Oops. Division by 0. How does that work?\n", "I am always printed\n" ] } diff --git a/04_iteration.ipynb b/04_iteration.ipynb index b760a25..3a2e42d 100644 --- a/04_iteration.ipynb +++ b/04_iteration.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "# Chapter 4: Iteration" + "# Chapter 4: Recursion & Looping" ] }, { @@ -859,7 +859,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "39.9 µs ± 5.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + "69.6 µs ± 25.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], @@ -881,7 +881,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "1.61 ms ± 8.84 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + "1.55 ms ± 11.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], @@ -903,7 +903,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "199 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "189 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -925,7 +925,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "2.21 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "2.07 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -947,7 +947,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "5.8 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "5.41 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -4289,7 +4289,7 @@ } }, "source": [ - "With everything *officially* introduced so far (i.e., without the introductory example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) that only served as an overview), Python would be what is called **[Turing complete](https://en.wikipedia.org/wiki/Turing_completeness)**. That means that anything that could be formulated as an algorithm could be expressed with all the language features we have seen. Note that, in particular, we have *not* yet formally *introduced* the `for` and `while` statements!" + "With everything *officially* introduced so far, Python would be what is called **[Turing complete](https://en.wikipedia.org/wiki/Turing_completeness)**. That means that anything that could be formulated as an algorithm could be expressed with all the language features we have seen. Note that, in particular, we have *not* yet formally *introduced* the `for` and `while` statements!" ] }, { @@ -4565,7 +4565,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "4.69 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "4.9 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -4877,7 +4877,7 @@ } }, "source": [ - "For sequences of integers, the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in makes the `for` statement even more convenient: It creates a list-like object of type `range` that generates integers \"on the fly,\" and we look closely at the underlying effects in memory in Chapter 7." + "For sequences of integers, the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in makes the `for` statement even more convenient: It creates a list-like object of type `range` that generates integers \"on the fly,\" and we look closely at the underlying effects in memory in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Mapping)." ] }, { @@ -5008,13 +5008,13 @@ "\n", "Now, just as we classify objects by their types, we also classify these **concrete data types** (e.g., `int`, `float`, `str`, or `list`) into **abstract concepts**.\n", "\n", - "We did this already in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) when we described a `list` object as \"some sort of container that holds [...] pointers to other objects\". So, abstractly speaking, **containers** are any objects that are \"composed\" of other objects and also \"manage\" how these objects are organized. `list` objects, for example, have the property that they model an internal order associated with its elements. There exist, however, other container types, many of which do *not* come with an order. So, containers primarily \"contain\" other objects and have *nothing* to do with looping.\n", + "We did this already in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?) when we described a `list` object as \"some sort of container that holds [...] pointers to other objects\". So, abstractly speaking, **containers** are any objects that are \"composed\" of other objects and also \"manage\" how these objects are organized. `list` objects, for example, have the property that they model an internal order associated with its elements. There exist, however, other container types, many of which do *not* come with an order. So, containers primarily \"contain\" other objects and have *nothing* to do with looping.\n", "\n", "On the contrary, the abstract concept of **iterables** is all about looping: Any object that we can loop over is, by definition, an iterable. So, `range` objects, for example, are iterables that do *not* contain other objects. Moreover, looping does *not* have to occur in a *predictable* order, although this is the case for both `list` and `range` objects.\n", "\n", - "Typically, containers are iterables, and iterables are containers. Yet, only because these two concepts coincide often, we must not think of them as the same. Chapter 10 finally gives an explanation as to how abstract concepts are implemented and play together.\n", + "Typically, containers are iterables, and iterables are containers. Yet, only because these two concepts coincide often, we must not think of them as the same. Chapter 9 finally gives an explanation as to how abstract concepts are implemented and play together.\n", "\n", - "So, `list` objects like `first_names` below are iterable containers. They implement even more abstract concepts, as Chapter 7 reveals." + "So, `list` objects like `first_names` below are iterable containers. They implement even more abstract concepts, as [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Collections-vs.-Sequences) reveals." ] }, { @@ -5165,7 +5165,7 @@ } }, "source": [ - "If we must have an index variable in the loop's body, we use the [enumerate()](https://docs.python.org/3/library/functions.html#enumerate) built-in that takes an *iterable* as its argument and then generates a \"stream\" of \"pairs\" consisting of an index variable and an object provided by the iterable. There is *no* need to ever revert to the `while` statement to loop over an iterable object." + "If we must have an index variable in the loop's body, we use the [enumerate()](https://docs.python.org/3/library/functions.html#enumerate) built-in that takes an *iterable* as its argument and then generates a \"stream\" of \"pairs\" of an index variable, `i` below, and an object provided by the iterable, `name`, separated by a `,`. There is *no* need to ever revert to the `while` statement with an explicitly managed index variable to loop over an iterable object." ] }, { @@ -5288,7 +5288,7 @@ } }, "source": [ - "In contrast to its recursive counterpart, the iterative `fibonacci()` function below is somewhat harder to read. For example, it is not so obvious as to how many iterations through the `for`-loop we need to make when implementing it. There is an increased risk of making an *off-by-one* error. Moreover, we need to track a `temp` variable along, at least until we have worked through Chapter 7. Do you understand what `temp` does?\n", + "In contrast to its recursive counterpart, the iterative `fibonacci()` function below is somewhat harder to read. For example, it is not so obvious as to how many iterations through the `for`-loop we need to make when implementing it. There is an increased risk of making an *off-by-one* error. Moreover, we need to track a `temp` variable along.\n", "\n", "However, one advantage of calculating Fibonacci numbers in a **forward** fashion with a `for` statement is that we could list the entire sequence in ascending order as we calculate the desired number. To show this, we added `print()` statements in `fibonacci()` below.\n", "\n", @@ -5843,7 +5843,7 @@ } }, "source": [ - "Often, we process some iterable with numeric data, for example, a list of numbers as in the introductory example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) or, more realistically, data from a CSV file with many rows and columns.\n", + "Often, we process some iterable with numeric data, for example, a list of numbers as in the introductory example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Example:-Average-of-a-Subset-of-Numbers) 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", @@ -5851,7 +5851,7 @@ "- **filtering**: throw away individual samples (e.g., statistical outliers)\n", "- **reducing**: collect individual samples into summary statistics\n", "\n", - "We study this **map-filter-reduce** paradigm extensively in Chapter 7 after introducing more advanced data types that are needed to work with \"big\" data.\n", + "We study this **map-filter-reduce** paradigm extensively in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#The-Map-Filter-Reduce-Paradigm) after introducing more advanced data types that are needed to work with \"big\" data.\n", "\n", "In the remainder of this section, we focus on *filtering out* some samples within a `for`-loop." ] diff --git a/04_iteration_review_and_exercises.ipynb b/04_iteration_review_and_exercises.ipynb index a953796..64d6903 100644 --- a/04_iteration_review_and_exercises.ipynb +++ b/04_iteration_review_and_exercises.ipynb @@ -5,7 +5,7 @@ "metadata": {}, "source": [ "\n", - "# Chapter 4: Iteration" + "# Chapter 4: Recursion & Looping" ] }, { diff --git a/05_numbers.ipynb b/05_numbers.ipynb index 73daebb..c545cde 100644 --- a/05_numbers.ipynb +++ b/05_numbers.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "# Chapter 5: Numbers" + "# Chapter 5: Bits & Numbers" ] }, { @@ -21,9 +21,9 @@ "source": [ "After learning about the basic building blocks of expressing and structuring the business logic in programs, we focus our attention on the **data types** Python offers us, both built-in and available via the [standard library](https://docs.python.org/3/library/index.html) or third-party packages.\n", "\n", - "We start with the \"simple\" ones: Numeric types in this chapter and textual data in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb). An important fact that holds for all objects of these types is that they are **immutable**. To re-use the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Objects-vs.-Types-vs.-Values), this means that the $0$s and $1$s making up an object's *value* cannot be changed once the bag is created in memory, implying that any operation with or method on the object creates a *new* object in a *different* memory location.\n", + "We start with the \"simple\" ones: Numerical types in this chapter and textual data in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb). An important fact that holds for all objects of these types is that they are **immutable**. To re-use the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Objects-vs.-Types-vs.-Values), this means that the $0$s and $1$s making up an object's *value* cannot be changed once the bag is created in memory, implying that any operation with or method on the object creates a *new* object in a *different* memory location.\n", "\n", - "Chapters 7, 8, and 9 then cover the more \"complex\" data types, including, for example, the `list` type. Finally, Chapter 10 completes the picture by introducing language constructs to create custom types.\n", + "[Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb) and Chapter 8 then cover the more \"complex\" data types, including, for example, the `list` type. Finally, Chapter 9 completes the picture by introducing language constructs to create custom types.\n", "\n", "We have already seen many hints indicating that numbers are not as trivial to work with as it seems at first sight:\n", "\n", @@ -31,7 +31,7 @@ "- [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb#Boolean-Expressions) raises questions regarding the **limited precision** of `float` numbers (e.g., `42 == 42.000000000000001` evaluates to `True`), and\n", "- [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Infinite-Recursion) shows that sometimes a `float` \"walks\" and \"quacks\" like an `int`, whereas the reverse is true in other cases.\n", "\n", - "This chapter introduces all the [built-in numeric types](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex): `int`, `float`, and `complex`. To mitigate the limited precision of floating-point numbers, we also look at two replacements for the `float` type in the [standard library](https://docs.python.org/3/library/index.html), namely the `Decimal` type in the [decimals](https://docs.python.org/3/library/decimal.html#decimal.Decimal) and the `Fraction` type in the [fractions](https://docs.python.org/3/library/fractions.html#fractions.Fraction) module." + "This chapter introduces all the [built-in numerical types](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex): `int`, `float`, and `complex`. To mitigate the limited precision of floating-point numbers, we also look at two replacements for the `float` type in the [standard library](https://docs.python.org/3/library/index.html), namely the `Decimal` type in the [decimals](https://docs.python.org/3/library/decimal.html#decimal.Decimal) and the `Fraction` type in the [fractions](https://docs.python.org/3/library/fractions.html#fractions.Fraction) module." ] }, { @@ -53,7 +53,7 @@ } }, "source": [ - "The simplest numeric type is the `int` type: It behaves like an [integer in ordinary math](https://en.wikipedia.org/wiki/Integer) (i.e., the set $\\mathbb{Z}$) and supports operators in the way we saw in the section on arithmetic operators in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#%28Arithmetic%29-Operators)." + "The simplest numerical type is the `int` type: It behaves like an [integer in ordinary math](https://en.wikipedia.org/wiki/Integer) (i.e., the set $\\mathbb{Z}$) and supports operators in the way we saw in the section on arithmetic operators in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#%28Arithmetic%29-Operators)." ] }, { @@ -92,7 +92,7 @@ { "data": { "text/plain": [ - "139928698315984" + "139630197982416" ] }, "execution_count": 2, @@ -160,7 +160,7 @@ } }, "source": [ - "A nice feature is using underscores `_` as (thousands) separator in numeric literals. For example, `1_000_000` evaluates to `1000000` in memory." + "A nice feature is using underscores `_` as (thousands) separator in numerical literals. For example, `1_000_000` evaluates to `1000000` in memory." ] }, { @@ -1165,7 +1165,7 @@ } }, "source": [ - "Whereas the boolean literals `True` and `False` are commonly *not* regarded as numeric types, they behave like `1` and `0` in an arithmetic context." + "Whereas the boolean literals `True` and `False` are commonly *not* regarded as numerical types, they behave like `1` and `0` in an arithmetic context." ] }, { @@ -2115,7 +2115,7 @@ { "data": { "text/plain": [ - "139928698489496" + "139630198164096" ] }, "execution_count": 63, @@ -2218,7 +2218,7 @@ } }, "source": [ - "In cases where the dot `.` is unnecessary from a mathematical point of view, we either need to end the number with it nevertheless or use the [float()](https://docs.python.org/3/library/functions.html#float) built-in to cast the number explicitly. [float()](https://docs.python.org/3/library/functions.html#float) can process any numeric object or a properly formatted `str` object." + "In cases where the dot `.` is unnecessary from a mathematical point of view, we either need to end the number with it nevertheless or use the [float()](https://docs.python.org/3/library/functions.html#float) built-in to cast the number explicitly. [float()](https://docs.python.org/3/library/functions.html#float) can process any numerical object or a properly formatted `str` object." ] }, { @@ -2242,7 +2242,7 @@ } ], "source": [ - "1. # on the contrary, 1 creates an int object" + "1. # 1 without a dot creates an int object" ] }, { @@ -3517,7 +3517,7 @@ "source": [ "The [format()](https://docs.python.org/3/library/functions.html#format) function does *not* round a `float` object in the mathematical sense! It just allows us to show an arbitrary number of the digits as stored in memory, and it also does *not* change these.\n", "\n", - "On the contrary, the built-in [round()](https://docs.python.org/3/library/functions.html#round) function creates a *new* numeric object that is a rounded version of the one passed in as the argument. It adheres to the common rules of math.\n", + "On the contrary, the built-in [round()](https://docs.python.org/3/library/functions.html#round) function creates a *new* numerical object that is a rounded version of the one passed in as the argument. It adheres to the common rules of math.\n", "\n", "For example, let's round `1 / 3` to five decimals. The obtained value for `roughly_a_third` is also *imprecise* but different from the \"exact\" representation of `1 / 3` above." ] @@ -4617,7 +4617,7 @@ } }, "source": [ - "The [quantize()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize) method allows us to [quantize](https://www.dictionary.com/browse/quantize) (i.e., \"round\") a `Decimal` number at any precision that is *smaller* than the set precision. It looks at the number of decimals (i.e., to the right of the period) of the numeric argument we pass in.\n", + "The [quantize()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize) method allows us to [quantize](https://www.dictionary.com/browse/quantize) (i.e., \"round\") a `Decimal` number at any precision that is *smaller* than the set precision. It looks at the number of decimals (i.e., to the right of the period) of the numerical argument we pass in.\n", "\n", "For example, as the overall imprecise value of `two` still has an internal precision of `28` digits, we can correctly round it to *four* decimals (i.e., `Decimal(\"0.0001\")` has four decimals)." ] @@ -5498,7 +5498,7 @@ { "data": { "text/plain": [ - "139928697740272" + "139630197402224" ] }, "execution_count": 176, @@ -5636,7 +5636,7 @@ } }, "source": [ - "Alternatively, we may use the [complex()](https://docs.python.org/3/library/functions.html#complex) built-in: This takes two parameters where the second is optional and defaults to `0`. We may either call it with one or two arguments of any numeric type or a `str` object in the format of the previous code cell without any spaces." + "Alternatively, we may use the [complex()](https://docs.python.org/3/library/functions.html#complex) built-in: This takes two parameters where the second is optional and defaults to `0`. We may either call it with one or two arguments of any numerical type or a `str` object in the format of the previous code cell without any spaces." ] }, { @@ -5708,7 +5708,7 @@ } ], "source": [ - "complex(Decimal(\"2.0\"), Fraction(1, 2)) # the arguments may be any numeric type" + "complex(Decimal(\"2.0\"), Fraction(1, 2)) # the arguments may be any numerical type" ] }, { @@ -5743,7 +5743,7 @@ } }, "source": [ - "Arithmetic expressions work with `complex` numbers. They may be mixed with the other numeric types, and the result is always a `complex` number." + "Arithmetic expressions work with `complex` numbers. They may be mixed with the other numerical types, and the result is always a `complex` number." ] }, { @@ -6100,7 +6100,7 @@ } }, "source": [ - "## The Numeric Tower" + "## The Numerical Tower" ] }, { @@ -6111,7 +6111,7 @@ } }, "source": [ - "Analogous to the discussion of containers and iterables in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Containers-vs.-Iterables), we contrast the *concrete* numeric data types in this chapter with the *abstract* ideas behind [numbers in mathematics](https://en.wikipedia.org/wiki/Number).\n", + "Analogous to the discussion of containers and iterables in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Containers-vs.-Iterables), we contrast the *concrete* numerical data types in this chapter with the *abstract* ideas behind [numbers in mathematics](https://en.wikipedia.org/wiki/Number).\n", "\n", "The figure below summarizes five *major* sets of [numbers in mathematics](https://en.wikipedia.org/wiki/Number) as we know them from high school:\n", "\n", @@ -6149,7 +6149,7 @@ "\n", "For the other types, in particular, the `float` type, the implications of their imprecision are discussed in detail above.\n", "\n", - "The abstract concepts behind the four outer-most mathematical sets are part of Python since [PEP 3141](https://www.python.org/dev/peps/pep-3141/) in 2007. The [numbers](https://docs.python.org/3/library/numbers.html) module in the [standard library](https://docs.python.org/3/library/index.html) defines what Pythonistas call the **numeric tower**, a collection of five **[abstract data types](https://en.wikipedia.org/wiki/Abstract_data_type)**, or **abstract base classes** as they are called in Python jargon:\n", + "The abstract concepts behind the four outer-most mathematical sets are part of Python since [PEP 3141](https://www.python.org/dev/peps/pep-3141/) in 2007. The [numbers](https://docs.python.org/3/library/numbers.html) module in the [standard library](https://docs.python.org/3/library/index.html) defines what programmers call the **[numerical tower](https://en.wikipedia.org/wiki/Numerical_tower)**, a collection of five **[abstract data types](https://en.wikipedia.org/wiki/Abstract_data_type)**, or **abstract base classes** as they are called in Python jargon:\n", "\n", "- `numbers.Number`: \"any number\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Number))\n", "- `numbers.Complex`: \"all complex numbers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Complex))\n", @@ -6712,7 +6712,7 @@ } }, "source": [ - "#### Example: [Factorial](https://en.wikipedia.org/wiki/Factorial) (revisited)" + "### Goose Typing" ] }, { @@ -6723,7 +6723,9 @@ } }, "source": [ - "Replacing *concrete* data types with *abstract* ones is particularly valuable in the context of input validation: The revised version of the `factorial()` function below allows its user to *take advantage of duck typing*. If a real but non-integer argument `n` is passed in, `factorial()` tries to cast `n` as an `int` object with the [trunc()](https://docs.python.org/3/library/math.html#math.trunc) function from the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html). [trunc()](https://docs.python.org/3/library/math.html#math.trunc) cuts off all decimals and any *concrete* numeric type implementing the *abstract* `numbers.Real` type supports it (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Real))." + "Replacing *concrete* data types with *abstract* ones is particularly valuable in the context of input validation: The revised version of the `factorial()` function below allows its user to take advantage of *duck typing*: If a real but non-integer argument `n` is passed in, `factorial()` tries to cast `n` as an `int` object with the [trunc()](https://docs.python.org/3/library/math.html#math.trunc) function from the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html). [trunc()](https://docs.python.org/3/library/math.html#math.trunc) cuts off all decimals and any *concrete* numerical type implementing the *abstract* `numbers.Real` type supports it (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Real)).\n", + "\n", + "Two popular and distinguished Pythonistas, [Luciano Ramalho](https://github.com/ramalho) and [Alex Martelli](https://en.wikipedia.org/wiki/Alex_Martelli), coin the term **goose typing** to specifically mean using the built-in [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function with an *abstract base class* (cf., Chapter 11 in this [book](https://www.amazon.com/Fluent-Python-Concise-Effective-Programming/dp/1491946008) or this [summary](https://dgkim5360.github.io/blog/python/2017/07/duck-typing-vs-goose-typing-pythonic-interfaces/) thereof)." ] }, { @@ -6763,6 +6765,17 @@ "math.trunc(1 / 10)" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### Example: [Factorial](https://en.wikipedia.org/wiki/Factorial) (revisited)" + ] + }, { "cell_type": "code", "execution_count": 211, @@ -6893,7 +6906,7 @@ } }, "source": [ - "With the keyword-only argument `strict`, we can control whether or not a passed in `float` object may be rounded. By default, this is not allowed." + "With the keyword-only argument `strict`, we can control whether or not a passed in `float` object may be rounded. By default, this is not allowed and results in a `TypeError`." ] }, { @@ -7002,21 +7015,21 @@ } }, "source": [ - "There exist three numeric types in core Python:\n", - "- `int`: a perfect model for whole numbers (i.e., the set $\\mathbb{Z}$); inherently precise\n", + "There exist three numerical types in core Python:\n", + "- `int`: a near-perfect model for whole numbers (i.e., the set $\\mathbb{Z}$); inherently precise\n", "- `float`: the \"gold\" standard to approximate real numbers (i.e., the set $\\mathbb{R}$); inherently imprecise\n", "- `complex`: layer on top of the `float` type; therefore inherently imprecise\n", "\n", "Furthermore, the [standard library](https://docs.python.org/3/library/index.html) adds two more types that can be used as substitutes for `float` objects:\n", "- `Decimal`: similar to `float` but allows customizing the precision; still inherently imprecise\n", - "- `Fraction`: a perfect model for rational numbers (i.e., the set $\\mathbb{Q}$); built on top of the `int` type and therefore inherently precise\n", + "- `Fraction`: a near-perfect model for rational numbers (i.e., the set $\\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 **numeric tower** is Python's way of implementing the various **abstract** ideas of what a number is in mathematics." + "The **numerical tower** is Python's way of implementing the various **abstract** ideas of what numbers are in mathematics." ] } ], diff --git a/05_numbers_review_and_exercises.ipynb b/05_numbers_review_and_exercises.ipynb index 4246965..90983d4 100644 --- a/05_numbers_review_and_exercises.ipynb +++ b/05_numbers_review_and_exercises.ipynb @@ -5,7 +5,7 @@ "metadata": {}, "source": [ "\n", - "# Chapter 5: Numbers" + "# Chapter 5: Bits & Numbers" ] }, { @@ -156,7 +156,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q9**: How can **abstract data types**, for example, as defined in the **numeric tower**, be helpful in enabling **duck typing**?" + "**Q9**: How can **abstract data types**, for example, as defined in the **numerical tower**, be helpful in enabling **duck typing**?" ] }, { diff --git a/06_text.ipynb b/06_text.ipynb index 4cdd4ab..9092188 100644 --- a/06_text.ipynb +++ b/06_text.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "# Chapter 6: Text" + "# Chapter 6: Bytes & Text" ] }, { @@ -19,7 +19,7 @@ } }, "source": [ - "In this chapter, we continue the study of the built-in data types. Building on our knowledge of numbers, the next layer consists of textual data that are modeled primarily with the `str` type in Python. `str` objects are naturally more \"complex\" than numeric objects as any text consists of an arbitrary and possibly large number of individual characters that may be chosen from any alphabet in the history of humankind. Luckily, Python abstracts away most of this complexity." + "In this chapter, we continue the study of the built-in data types. Building on our knowledge of numbers, the next layer consists of textual data that are modeled primarily with the `str` type in Python. `str` objects are naturally more \"complex\" than numerical objects as any text consists of an arbitrary and possibly large number of individual characters that may be chosen from any alphabet in the history of humankind. Luckily, Python abstracts away most of this complexity." ] }, { @@ -80,7 +80,7 @@ { "data": { "text/plain": [ - "140148947850512" + "140133793916176" ] }, "execution_count": 2, @@ -124,7 +124,7 @@ } }, "source": [ - "A `str` object evaluates to itself in a literal notation with enclosing **single quotes** `'` by default. In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb), we already specified the double quotes `\"` convention we stick to in this book. Yet, single quotes `'` and double quotes `\"` are *perfect* substitutes for all `str` objects that do *not* contain any of the two symbols in it. We could have used the reverse convention, as well.\n", + "A `str` object evaluates to itself in a literal notation with enclosing **single quotes** `'` by default. In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Value), we already specified the double quotes `\"` convention we stick to in this book. Yet, single quotes `'` and double quotes `\"` are *perfect* substitutes for all `str` objects that do *not* contain any of the two symbols in it. We could use the reverse convention, as well.\n", "\n", "As [this discussion](https://stackoverflow.com/questions/56011/single-quotes-vs-double-quotes-in-python) shows, many programmers have *strong* opinions about that and make up *new* conventions for their projects. Consequently, the discussion was \"closed as not constructive\" by the moderators." ] @@ -161,7 +161,7 @@ } }, "source": [ - "As the single quote `'` is often used in the English language as a shortener, we could make an argument in favor of using the double quotes `\"`: There are possibly fewer situations like in the two code cells below, in which we must revert to using a `\\` to **escape** a single quote `'` in a text (cf., the section on special characters further below). However, double quotes `\"` are often used as well. So, this argument is somewhat not convincing.\n", + "As the single quote `'` is often used in the English language as a shortener, we could make an argument in favor of using the double quotes `\"`: There are possibly fewer situations like in the two code cells below, in which we must revert to using a `\\` to **escape** a single quote `'` in a text (cf., the [Special Characters](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb#Special-Characters) section further below). However, double quotes `\"` are often used as well. So, this argument is somewhat not convincing.\n", "\n", "Many proponents of the single quote `'` usage claim that double quotes `\"` make more **visual noise** on the screen. This argument is also not convincing. On the contrary, one could claim that *two* single quotes `''` look so similar to *one* double quote `\"` that it might not be apparent right away what we are looking at. By sticking to double quotes `\"`, we avoid such danger of confusion.\n", "\n", @@ -226,7 +226,7 @@ } }, "source": [ - "Alternatively, we can use the [str()](https://docs.python.org/3/library/stdtypes.html#str) built-in to **cast** non-`str` objects as a `str`." + "We can always use the [str()](https://docs.python.org/3/library/stdtypes.html#str) built-in to cast non-`str` objects as a `str`." ] }, { @@ -253,6 +253,471 @@ "str(123)" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Another common situation where we obtain `str` objects is when reading the contents of a file with the [open()](https://docs.python.org/3/library/functions.html#open) built-in. In its simplest form, to open a [text file](https://en.wikipedia.org/wiki/Text_file) file in read-only mode, we pass in its path (i.e., \"filename\") as a `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "file = open(\"lorem_ipsum.txt\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[open()](https://docs.python.org/3/library/functions.html#open) returns a **[proxy](https://en.wikipedia.org/wiki/Proxy_pattern)** object of type `TextIOWrapper` that allows us to interact with the file on disk." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "<_io.TextIOWrapper name='lorem_ipsum.txt' mode='r' encoding='UTF-8'>" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "_io.TextIOWrapper" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(file)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While `file` provides, for example, the [read()](https://docs.python.org/3/library/io.html#io.TextIOBase.read), [readline()](https://docs.python.org/3/library/io.html#io.TextIOBase.readline), and [readlines()](https://docs.python.org/3/library/io.html#io.IOBase.readlines) methods to access its contents, it is also *iterable*, and we may loop over the individual lines with a `for` statement." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lorem Ipsum is simply dummy text of the printing and typesetting industry.\n", + "\n", + "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s\n", + "\n", + "when an unknown printer took a galley of type and scrambled it to make a type\n", + "\n", + "specimen book. It has survived not only five centuries but also the leap into\n", + "\n", + "electronic typesetting, remaining essentially unchanged. It was popularised in\n", + "\n", + "the 1960s with the release of Letraset sheets.\n", + "\n" + ] + } + ], + "source": [ + "for line in file:\n", + " print(line)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Once we looped over `file` the first time, it is **exhausted**: That means we do not see any output if we loop over it another time." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "for line in file:\n", + " print(line)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "After the `for`-loop, `line` is still set to the last line in the file, and we verify that it is indeed a `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'the 1960s with the release of Letraset sheets.\\n'" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "line" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "str" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(line)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "An important fact is that `file` is still associated with an *open* **[file descriptor](https://en.wikipedia.org/wiki/File_descriptor)**. Without going into any technical details, we note that an operating system can only handle a limited number of \"open files\" at the same time, and, therefore, we should always *close* the file once we are done processing it.\n", + "\n", + "`file` has a `closed` attribute on it that shows us if a file descriptor is open or closed, and with the [close()](https://docs.python.org/3/library/io.html#io.IOBase.close) method, we can \"manually\" close it." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.closed" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "file.close()" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.closed" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The more Pythonic way is to open a file with the `with` statement (cf., [reference](https://docs.python.org/3/reference/compound_stmts.html#the-with-statement)): The indented code block is said to be executed in the **context** of the header line that acts as a **[context manager](https://docs.python.org/3/reference/datamodel.html?highlight=context%20manager#with-statement-context-managers)**. Such objects may have many different purposes. Here, the context manager created with `with open(...) as file:` mainly ensures that the file descriptor gets automatically closed after the last line in the code block is executed." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lorem Ipsum is simply dummy text of the printing and typesetting industry.\n", + "\n", + "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s\n", + "\n", + "when an unknown printer took a galley of type and scrambled it to make a type\n", + "\n", + "specimen book. It has survived not only five centuries but also the leap into\n", + "\n", + "electronic typesetting, remaining essentially unchanged. It was popularised in\n", + "\n", + "the 1960s with the release of Letraset sheets.\n", + "\n" + ] + } + ], + "source": [ + "with open(\"lorem_ipsum.txt\") as file:\n", + " for line in file:\n", + " print(line)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.closed" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To use constructs familiar from [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb#The-try-Statement) to explain what `with open(...) as file:` does, below is a formulation with a `try` statement *equivalent* to the `with` statement above. The `finally`-branch is always executed, even if an exception is raised in the `for`-loop. So, `file` is sure to be closed too, with a somewhat less expressive formulation." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lorem Ipsum is simply dummy text of the printing and typesetting industry.\n", + "\n", + "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s\n", + "\n", + "when an unknown printer took a galley of type and scrambled it to make a type\n", + "\n", + "specimen book. It has survived not only five centuries but also the leap into\n", + "\n", + "electronic typesetting, remaining essentially unchanged. It was popularised in\n", + "\n", + "the 1960s with the release of Letraset sheets.\n", + "\n" + ] + } + ], + "source": [ + "try:\n", + " file = open(\"lorem_ipsum.txt\")\n", + " for line in file:\n", + " print(line)\n", + "finally:\n", + " file.close()" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.closed" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A subtlety to notice is that there is an empty line printed between each `line`. That is because each `line` ends with a `\"\\n\"` that results in a line break and that is explained further below. To print the text without empty lines in between, we pass a `end=\"\"` argument to the [print()](https://docs.python.org/3/library/functions.html#print) function." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lorem Ipsum is simply dummy text of the printing and typesetting industry.\n", + "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s\n", + "when an unknown printer took a galley of type and scrambled it to make a type\n", + "specimen book. It has survived not only five centuries but also the leap into\n", + "electronic typesetting, remaining essentially unchanged. It was popularised in\n", + "the 1960s with the release of Letraset sheets.\n" + ] + } + ], + "source": [ + "with open(\"lorem_ipsum.txt\") as file:\n", + " for line in file:\n", + " print(line, end=\"\")" + ] + }, { "cell_type": "markdown", "metadata": { @@ -274,21 +739,21 @@ "source": [ "The idea of a **sequence** is yet another *abstract* concept.\n", "\n", - "It unifies *four* orthogonal *abstract* concepts into one: Any *concrete* data type, such as `str` in this chapter, is considered a sequence if it simultaneously\n", + "It unifies *four* [orthogonal](https://en.wikipedia.org/wiki/Orthogonality) (i.e., \"independent\") *abstract* concepts into one: Any *concrete* data type, such as `str`, is considered a sequence if it simultaneously\n", "\n", - "1. behaves like a **container** and\n", - "2. an **iterable**, and \n", - "3. comes with a predictable **order** of its\n", + "1. **contains** other \"things,\"\n", + "2. is **iterable**, and \n", + "3. comes with a *predictable* **order** of its\n", "4. **finite** number of elements.\n", "\n", - "Chapter 7 discusses sequences in a generic sense and in greater detail. Here, we keep our focus on the `str` type that historically received its name as it models a \"**[string of characters](https://en.wikipedia.org/wiki/String_%28computer_science%29)**,\" and a \"string\" is more formally called a sequence in the computer science literature.\n", + "[Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Collections-vs.-Sequences) formalizes sequences in great detail. Here, we keep our focus on the `str` type that historically received its name as it models a \"**[string of characters](https://en.wikipedia.org/wiki/String_%28computer_science%29)**,\" and a \"string\" is more formally called a sequence in the computer science literature.\n", "\n", - "Behaving like a sequence, `str` objects may be treated like `list` objects in many cases. For example, the built-in [len()](https://docs.python.org/3/library/functions.html#len) function tells us how many elements (i.e., characters) make up `school`. [len()](https://docs.python.org/3/library/functions.html#len) would not work on an \"*infinite*\" object: As anything modeled in a program must fit into a computer's finite memory at runtime, there cannot exist objects containing a truly infinite number of elements; however, Chapter 7 introduces a *concrete* iterable data type that can be used to model an *infinite* series of elements and that, consequently, has no concept of \"length.\"" + "Behaving like a sequence, `str` objects may be treated like `list` objects in many cases. For example, the built-in [len()](https://docs.python.org/3/library/functions.html#len) function tells us how many elements (i.e., characters) make up `school`. [len()](https://docs.python.org/3/library/functions.html#len) would not work on an \"*infinite*\" object: As anything modeled in a program must fit into a computer's finite memory at runtime, there cannot exist objects containing a truly infinite number of elements; however, [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Collections-vs.-Sequences#Mapping) introduces *concrete* iterable data types that can be used to model an *infinite* series of elements and that, consequently, have no concept of \"length.\"" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 23, "metadata": { "slideshow": { "slide_type": "slide" @@ -301,7 +766,7 @@ "40" ] }, - "execution_count": 8, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -325,7 +790,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 24, "metadata": { "slideshow": { "slide_type": "fragment" @@ -353,12 +818,12 @@ } }, "source": [ - "Being a container, we can check if a given object is a member of a sequence with the `in` operator. In the context of strings, the `in` operator has *two* usages: First, it checks if a *single* character is contained in a `str` object. Second, it may also check if a shorter `str` object, then called a **substring**, is contained in a longer one." + "Being a container, we can check if a given object is a member of a sequence with the `in` operator. In the context of `str` objects, the `in` operator has *two* usages: First, it checks if a *single* character is contained in a `str` object. Second, it may also check if a shorter `str` object, then called a **substring**, is contained in a longer one." ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 25, "metadata": { "slideshow": { "slide_type": "fragment" @@ -371,7 +836,7 @@ "True" ] }, - "execution_count": 10, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } @@ -382,7 +847,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 26, "metadata": { "slideshow": { "slide_type": "-" @@ -395,7 +860,7 @@ "True" ] }, - "execution_count": 11, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -406,7 +871,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 27, "metadata": { "slideshow": { "slide_type": "-" @@ -419,7 +884,7 @@ "False" ] }, - "execution_count": 12, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -452,7 +917,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 28, "metadata": { "slideshow": { "slide_type": "slide" @@ -465,7 +930,7 @@ "'W'" ] }, - "execution_count": 13, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } @@ -476,7 +941,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 29, "metadata": { "slideshow": { "slide_type": "-" @@ -489,7 +954,7 @@ "'H'" ] }, - "execution_count": 14, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -511,7 +976,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 30, "metadata": { "slideshow": { "slide_type": "slide" @@ -525,7 +990,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mschool\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mschool\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: string indices must be integers" ] } @@ -547,7 +1012,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 31, "metadata": { "slideshow": { "slide_type": "slide" @@ -560,7 +1025,7 @@ "'t'" ] }, - "execution_count": 16, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } @@ -582,7 +1047,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 32, "metadata": { "slideshow": { "slide_type": "-" @@ -596,7 +1061,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mschool\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m40\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mschool\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m40\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mIndexError\u001b[0m: string index out of range" ] } @@ -618,7 +1083,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 33, "metadata": { "slideshow": { "slide_type": "slide" @@ -631,7 +1096,7 @@ "'t'" ] }, - "execution_count": 18, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" } @@ -653,7 +1118,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 34, "metadata": { "slideshow": { "slide_type": "fragment" @@ -666,7 +1131,7 @@ "'O'" ] }, - "execution_count": 19, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } @@ -677,7 +1142,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 35, "metadata": { "slideshow": { "slide_type": "-" @@ -690,7 +1155,7 @@ "'O'" ] }, - "execution_count": 20, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } @@ -725,7 +1190,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 36, "metadata": { "slideshow": { "slide_type": "slide" @@ -738,7 +1203,7 @@ "'WHU'" ] }, - "execution_count": 21, + "execution_count": 36, "metadata": {}, "output_type": "execute_result" } @@ -760,7 +1225,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 37, "metadata": { "slideshow": { "slide_type": "fragment" @@ -773,7 +1238,7 @@ "'WHU - Otto Beisheim School of Management'" ] }, - "execution_count": 22, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } @@ -790,12 +1255,12 @@ } }, "source": [ - "For convenience, the indexes do not need to lie in the range from 0 to the string's \"length\" when slicing. This is *not* the case for indexing as the `IndexError` above shows." + "For convenience, the indexes do not need to lie in the range from 0 to the `str` object's \"length\" when slicing. This is *not* the case for indexing as the `IndexError` above shows." ] }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 38, "metadata": { "slideshow": { "slide_type": "fragment" @@ -808,7 +1273,7 @@ "'WHU - Otto Beisheim School of Management'" ] }, - "execution_count": 23, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } @@ -830,7 +1295,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 39, "metadata": { "slideshow": { "slide_type": "fragment" @@ -843,7 +1308,7 @@ "'WHU - Otto Beisheim School of Management'" ] }, - "execution_count": 24, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } @@ -865,7 +1330,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 40, "metadata": { "slideshow": { "slide_type": "skip" @@ -878,7 +1343,7 @@ "'WHU Otto Beisheim School'" ] }, - "execution_count": 25, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } @@ -900,7 +1365,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 41, "metadata": { "slideshow": { "slide_type": "slide" @@ -913,7 +1378,7 @@ "'WU-Ot esemSho fMngmn'" ] }, - "execution_count": 26, + "execution_count": 41, "metadata": {}, "output_type": "execute_result" } @@ -935,7 +1400,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 42, "metadata": { "slideshow": { "slide_type": "fragment" @@ -948,7 +1413,7 @@ "'tnemeganaM fo loohcS miehsieB ottO - UHW'" ] }, - "execution_count": 27, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } @@ -976,7 +1441,7 @@ } }, "source": [ - "Whereas elements of a `list` object *may* be *re-assigned*, as shortly hinted at in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?), this is *not* allowed for `str` objects. Once created, they *cannot* be *changed*. Formally, we say that they are **immutable**. In that regard, `str` objects and all the numeric types in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb) are alike.\n", + "Whereas elements of a `list` object *may* be *re-assigned*, as shortly hinted at in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?), this is *not* allowed for `str` objects. Once created, they *cannot* be *changed*. Formally, we say that they are **immutable**. In that regard, `str` objects and all the numerical types in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb) are alike.\n", "\n", "On the contrary, objects that may be changed after creation, are called **mutable**. We already saw in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?) how mutable objects are more difficult to reason about for a beginner, in particular, if more than *one* variable point to one. Yet, mutability does have its place in a programmer's toolbox, and we revisit this idea in the next chapters.\n", "\n", @@ -985,7 +1450,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 43, "metadata": { "slideshow": { "slide_type": "slide" @@ -999,7 +1464,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mschool\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"E\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mschool\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"E\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: 'str' object does not support item assignment" ] } @@ -1021,7 +1486,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 44, "metadata": { "slideshow": { "slide_type": "slide" @@ -1034,7 +1499,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 45, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1047,7 +1512,7 @@ "'EBS - Otto Beisheim School of Management'" ] }, - "execution_count": 30, + "execution_count": 45, "metadata": {}, "output_type": "execute_result" } @@ -1058,7 +1523,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 46, "metadata": { "slideshow": { "slide_type": "-" @@ -1068,10 +1533,10 @@ { "data": { "text/plain": [ - "140148946876144" + "140133784610832" ] }, - "execution_count": 31, + "execution_count": 46, "metadata": {}, "output_type": "execute_result" } @@ -1082,7 +1547,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 47, "metadata": { "slideshow": { "slide_type": "-" @@ -1092,10 +1557,10 @@ { "data": { "text/plain": [ - "140148947850512" + "140133793916176" ] }, - "execution_count": 32, + "execution_count": 47, "metadata": {}, "output_type": "execute_result" } @@ -1128,7 +1593,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 48, "metadata": { "slideshow": { "slide_type": "slide" @@ -1141,7 +1606,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 49, "metadata": { "slideshow": { "slide_type": "-" @@ -1154,7 +1619,7 @@ "'Hello WHU'" ] }, - "execution_count": 34, + "execution_count": 49, "metadata": {}, "output_type": "execute_result" } @@ -1165,7 +1630,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 50, "metadata": { "slideshow": { "slide_type": "-" @@ -1178,7 +1643,7 @@ "'WHU WHU WHU WHU WHU WHU WHU WHU WHU WHU '" ] }, - "execution_count": 35, + "execution_count": 50, "metadata": {}, "output_type": "execute_result" } @@ -1213,7 +1678,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 51, "metadata": { "slideshow": { "slide_type": "slide" @@ -1226,7 +1691,7 @@ "6" ] }, - "execution_count": 36, + "execution_count": 51, "metadata": {}, "output_type": "execute_result" } @@ -1237,7 +1702,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 52, "metadata": { "slideshow": { "slide_type": "-" @@ -1250,7 +1715,7 @@ "-1" ] }, - "execution_count": 37, + "execution_count": 52, "metadata": {}, "output_type": "execute_result" } @@ -1261,7 +1726,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 53, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1274,7 +1739,7 @@ "11" ] }, - "execution_count": 38, + "execution_count": 53, "metadata": {}, "output_type": "execute_result" } @@ -1296,7 +1761,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 54, "metadata": { "slideshow": { "slide_type": "slide" @@ -1309,7 +1774,7 @@ "12" ] }, - "execution_count": 39, + "execution_count": 54, "metadata": {}, "output_type": "execute_result" } @@ -1320,7 +1785,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 55, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1333,7 +1798,7 @@ "16" ] }, - "execution_count": 40, + "execution_count": 55, "metadata": {}, "output_type": "execute_result" } @@ -1344,7 +1809,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 56, "metadata": { "slideshow": { "slide_type": "-" @@ -1357,7 +1822,7 @@ "-1" ] }, - "execution_count": 41, + "execution_count": 56, "metadata": {}, "output_type": "execute_result" } @@ -1379,7 +1844,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 57, "metadata": { "slideshow": { "slide_type": "slide" @@ -1392,7 +1857,7 @@ "4" ] }, - "execution_count": 42, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" } @@ -1414,7 +1879,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 58, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1427,7 +1892,7 @@ "5" ] }, - "execution_count": 43, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } @@ -1449,7 +1914,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 59, "metadata": { "slideshow": { "slide_type": "skip" @@ -1462,7 +1927,7 @@ "5" ] }, - "execution_count": 44, + "execution_count": 59, "metadata": {}, "output_type": "execute_result" } @@ -1484,7 +1949,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 60, "metadata": { "slideshow": { "slide_type": "slide" @@ -1497,7 +1962,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 61, "metadata": { "slideshow": { "slide_type": "-" @@ -1507,10 +1972,10 @@ { "data": { "text/plain": [ - "140149036799680" + "140133891261864" ] }, - "execution_count": 46, + "execution_count": 61, "metadata": {}, "output_type": "execute_result" } @@ -1521,7 +1986,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 62, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1534,7 +1999,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 63, "metadata": { "slideshow": { "slide_type": "-" @@ -1544,10 +2009,10 @@ { "data": { "text/plain": [ - "140148946626016" + "140133784910848" ] }, - "execution_count": 48, + "execution_count": 63, "metadata": {}, "output_type": "execute_result" } @@ -1569,7 +2034,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 64, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1582,7 +2047,7 @@ "False" ] }, - "execution_count": 49, + "execution_count": 64, "metadata": {}, "output_type": "execute_result" } @@ -1593,7 +2058,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 65, "metadata": { "slideshow": { "slide_type": "-" @@ -1606,7 +2071,7 @@ "True" ] }, - "execution_count": 50, + "execution_count": 65, "metadata": {}, "output_type": "execute_result" } @@ -1628,7 +2093,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 66, "metadata": { "slideshow": { "slide_type": "slide" @@ -1667,7 +2132,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 67, "metadata": { "slideshow": { "slide_type": "slide" @@ -1680,7 +2145,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 68, "metadata": { "slideshow": { "slide_type": "-" @@ -1693,7 +2158,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 69, "metadata": { "slideshow": { "slide_type": "-" @@ -1706,7 +2171,7 @@ "'This will become a sentence'" ] }, - "execution_count": 54, + "execution_count": 69, "metadata": {}, "output_type": "execute_result" } @@ -1728,7 +2193,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 70, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1741,7 +2206,7 @@ "'a b c d e'" ] }, - "execution_count": 55, + "execution_count": 70, "metadata": {}, "output_type": "execute_result" } @@ -1763,7 +2228,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 71, "metadata": { "slideshow": { "slide_type": "slide" @@ -1776,7 +2241,7 @@ "'This is a sentence'" ] }, - "execution_count": 56, + "execution_count": 71, "metadata": {}, "output_type": "execute_result" } @@ -1809,7 +2274,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 72, "metadata": { "slideshow": { "slide_type": "slide" @@ -1824,7 +2289,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 73, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1837,7 +2302,7 @@ "True" ] }, - "execution_count": 58, + "execution_count": 73, "metadata": {}, "output_type": "execute_result" } @@ -1848,7 +2313,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 74, "metadata": { "slideshow": { "slide_type": "-" @@ -1861,7 +2326,7 @@ "False" ] }, - "execution_count": 59, + "execution_count": 74, "metadata": {}, "output_type": "execute_result" } @@ -1878,12 +2343,12 @@ } }, "source": [ - "One way to fix this is to only compare lower-case strings." + "One way to fix this is to only compare lower-cased `str` objects." ] }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 75, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1896,7 +2361,7 @@ "True" ] }, - "execution_count": 60, + "execution_count": 75, "metadata": {}, "output_type": "execute_result" } @@ -1918,7 +2383,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 76, "metadata": { "slideshow": { "slide_type": "slide" @@ -1985,9 +2450,9 @@ } }, "source": [ - "The previous code cell shows an example of a so-called **f-string**, as introduced by [PEP 498](https://www.python.org/dev/peps/pep-0498/) only in 2016.\n", + "The previous code cell shows an example of a so-called **f-string**, as introduced by [PEP 498](https://www.python.org/dev/peps/pep-0498/) only in 2016, that is passed as the argument to the [print()](https://docs.python.org/3/library/functions.html#print) function.\n", "\n", - "So far, we have used the built-in [print()](https://docs.python.org/3/library/functions.html#print) function only with \"plain\" `str` objects (e.g., `\"example\"`) or variables. Sometimes, it is more convenient to fill a value determined at runtime in a \"draft\" `str` object. This is called **string interpolation**. There are three ways to achieve that in Python, but only two are commonly used." + "The \"f\" stands for \"formatted\", and we can think of the `str` object as a text \"draft\" that is filled in with values determined at runtime. This concept is formally called **string interpolation**, and there are three ways to achieve that in Python." ] }, { @@ -2009,12 +2474,12 @@ } }, "source": [ - "f-strings are the new and most readable way. Prepend the literal notation with an `f`, and put variables/expressions within curly braces. The latter are then filled in when the [print()](https://docs.python.org/3/library/functions.html#print) function is executed." + "f-strings, formally called **[formatted string literals](https://docs.python.org/3/reference/lexical_analysis.html#formatted-string-literals)**, are the least recently added and most readable way: We prepend a `str` in literal notation with an `f`, and put variables, or more generally, expressions, within curly braces. These are then filled in when a `str` object is evaluated." ] }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 77, "metadata": { "slideshow": { "slide_type": "slide" @@ -2028,7 +2493,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 78, "metadata": { "slideshow": { "slide_type": "-" @@ -2036,15 +2501,18 @@ }, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello Alexander! Good morning.\n" - ] + "data": { + "text/plain": [ + "'Hello Alexander! Good morning.'" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "print(f\"Hello {name}! Good {time_of_day}.\")" + "f\"Hello {name}! Good {time_of_day}.\"" ] }, { @@ -2055,12 +2523,12 @@ } }, "source": [ - "Separated by a colon `:`, various formatting options are available. In the beginning, only the ability to round is useful, and this can be achieved by adding `:.2f` to the variable name to cast the number as a float and round it to two digits." + "Separated by a colon `:`, various formatting options are available. In the beginning, the ability to round may be particularly useful: This can be achieved by adding `:.2f` to the variable name inside the curly braces, which casts the number as a `float` and rounds it to two digits. The `:.2f` is a so-called format specifier, and there exists a whole **[format specification mini-language](https://docs.python.org/3/library/string.html#formatspec)** to govern how specifiers work." ] }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 79, "metadata": { "slideshow": { "slide_type": "slide" @@ -2073,7 +2541,7 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 80, "metadata": { "slideshow": { "slide_type": "-" @@ -2081,20 +2549,23 @@ }, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Pi is 3.14\n" - ] + "data": { + "text/plain": [ + "'Pi is 3.14'" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "print(f\"Pi is {pi:.2f}\")" + "f\"Pi is {pi:.2f}\"" ] }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 81, "metadata": { "slideshow": { "slide_type": "-" @@ -2102,15 +2573,18 @@ }, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Pi is 3.142\n" - ] + "data": { + "text/plain": [ + "'Pi is 3.142'" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "print(f\"Pi is {pi:.3f}\")" + "f\"Pi is {pi:.3f}\"" ] }, { @@ -2121,7 +2595,7 @@ } }, "source": [ - "### format() Method" + "### [format()](https://docs.python.org/3/library/stdtypes.html#str.format) Method" ] }, { @@ -2132,12 +2606,12 @@ } }, "source": [ - "`str` objects also provide a [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method that accepts an arbitrary number of *positional* arguments that are inserted into the `str` object in the same order replacing curly brackets. See the official [documentation](https://docs.python.org/3/library/string.html#formatspec) for a full reference. This is the more traditional way of string interpolation and many code examples on the internet use it. f-strings are the officially recommended way going forward, but the usage of the [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method is most likely not going down any time soon." + "`str` objects also provide a [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method that accepts an arbitrary number of *positional* arguments that are inserted into the `str` object in the same order replacing empty curly brackets. String interpolation with the [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method is a more traditional and probably the most common way one as of today. While f-strings are the recommended way going forward, usage of the [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method is likely not declining any time soon." ] }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 82, "metadata": { "slideshow": { "slide_type": "slide" @@ -2145,15 +2619,18 @@ }, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello Alexander! Good morning.\n" - ] + "data": { + "text/plain": [ + "'Hello Alexander! Good morning.'" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "print(\"Hello {}! Good {}.\".format(name, time_of_day))" + "\"Hello {}! Good {}.\".format(name, time_of_day)" ] }, { @@ -2164,12 +2641,12 @@ } }, "source": [ - "Use index numbers if the order is different in the `str` object." + "We may use index numbers inside the curly braces if the order is different in the `str` object." ] }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 83, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2177,15 +2654,18 @@ }, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Good morning, Alexander\n" - ] + "data": { + "text/plain": [ + "'Good morning, Alexander'" + ] + }, + "execution_count": 83, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "print(\"Good {1}, {0}\".format(name, time_of_day))" + "\"Good {1}, {0}\".format(name, time_of_day)" ] }, { @@ -2196,12 +2676,12 @@ } }, "source": [ - "The [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method may alternatively be used with *keyword* arguments as well. Then, we must put the keyword names within the curly brackets." + "The [format()](https://docs.python.org/3/library/stdtypes.html#str.format) method may alternatively be used with *keyword* arguments as well. Then, we must put the keywords' names within the curly brackets." ] }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 84, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2209,15 +2689,18 @@ }, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello Alexander! Good morning.\n" - ] + "data": { + "text/plain": [ + "'Hello Alexander! Good morning.'" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "print(\"Hello {name}! Good {time}.\".format(name=name, time=time_of_day))" + "\"Hello {name}! Good {time}.\".format(name=name, time=time_of_day)" ] }, { @@ -2228,12 +2711,60 @@ } }, "source": [ - "Numbers are treated as in the f-strings case." + "Format specifiers work as in the f-string case." ] }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 85, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Pi is 3.14'" + ] + }, + "execution_count": 85, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Pi is {:.2f}\".format(pi)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### `%` Operator" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `%` operator that we saw in the context of modulo division in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#%28Arithmetic%29-Operators) is overloaded with string interpolation when its first operand is a `str` object. The second operand consists of all expressions to be filled in. Format specifiers work with a `%` instead of curly braces and according to a different set of rules referred to as **[printf-style string formatting](https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting)**. So, `{:.2f}` becomes `%.2f`.\n", + "\n", + "This way of string interpolation is the oldest and originates from the [C language](https://en.wikipedia.org/wiki/C_%28programming_language%29). It is still widely spread, but we should use one of the other two ways instead. We show it here mainly for completeness sake." + ] + }, + { + "cell_type": "code", + "execution_count": 86, "metadata": { "slideshow": { "slide_type": "skip" @@ -2241,15 +2772,53 @@ }, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Pi is 3.14\n" - ] + "data": { + "text/plain": [ + "'Pi is 3.14'" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "print(\"Pi is {:.2f}\".format(pi))" + "\"Pi is %.2f\" % pi" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To insert more than one expression, we must list them in order and between parenthesis `(` and `)`. As [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#The-tuple-Type) reveals, this literal syntax creates an object of type `tuple`. Also, to format an expression as text, we use the format specifier `%s`." + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Hello Alexander! Good morning.'" + ] + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Hello %s! Good %s.\" % (name, time_of_day)" ] }, { @@ -2271,12 +2840,14 @@ } }, "source": [ - "Some symbols have a special meaning within `str` objects. Popular examples are the newline `\\n` and tab `\\t` \"characters.\" The backslash symbol `\\` is also referred to as an **escape character** in this context, indicating that the following character has a meaning other than its literal meaning." + "Some symbols have a special meaning within `str` objects. Popular examples are the newline `\\n` and tab `\\t` \"characters.\" The backslash symbol `\\` is also referred to as an **escape character** in this context, indicating that the following character has a meaning other than its literal meaning.\n", + "\n", + "The built-in [print()](https://docs.python.org/3/library/functions.html#print) function then \"prints\" out these special characters accordingly." ] }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 88, "metadata": { "slideshow": { "slide_type": "slide" @@ -2299,7 +2870,7 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 89, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2331,7 +2902,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 90, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2350,6 +2921,41 @@ "print(\"\\U0001f604\")" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Outside the [print()](https://docs.python.org/3/library/functions.html#print) function, the special characters are not treated any different from non-special ones." + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'This is a sentence\\nthat is printed\\non three lines.'" + ] + }, + "execution_count": 91, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"This is a sentence\\nthat is printed\\non three lines.\"" + ] + }, { "cell_type": "markdown", "metadata": { @@ -2376,7 +2982,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 92, "metadata": { "slideshow": { "slide_type": "slide" @@ -2404,12 +3010,12 @@ } }, "source": [ - "Some strings even produce a `SyntaxError` because the `\\U` *cannot* be interpreted as a unicode code point." + "Some `str` objects even produce a `SyntaxError` because the `\\U` *cannot* be interpreted as a unicode code point." ] }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 93, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2418,10 +3024,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "(unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape (, line 1)", + "evalue": "(unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print(\"C:\\Users\\Administrator\\Desktop\\Project_Folder\")\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print(\"C:\\Users\\Administrator\\Desktop\\Project_Folder\")\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape\n" ] } ], @@ -2442,7 +3048,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 94, "metadata": { "slideshow": { "slide_type": "slide" @@ -2463,7 +3069,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 95, "metadata": { "slideshow": { "slide_type": "-" @@ -2490,12 +3096,12 @@ } }, "source": [ - "However, this is tedious to remember and type. Luckily, Python allows treating any string literal as a \"raw\" string, and this is indicated in the string literal by a `r` prefix." + "However, this is tedious to remember and type. Luckily, Python allows treating any string literal as \"raw,\" and this is indicated in the string literal by the prefix `r`." ] }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 96, "metadata": { "slideshow": { "slide_type": "slide" @@ -2516,7 +3122,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 97, "metadata": { "slideshow": { "slide_type": "-" @@ -2559,7 +3165,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 98, "metadata": { "slideshow": { "slide_type": "slide" @@ -2568,10 +3174,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "EOL while scanning string literal (, line 1)", + "evalue": "EOL while scanning string literal (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m \"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m EOL while scanning string literal\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m \"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m EOL while scanning string literal\n" ] } ], @@ -2594,7 +3200,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 99, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2616,12 +3222,12 @@ } }, "source": [ - "Linebreaks are kept and implicitly converted into `\\n` characters." + "Line breaks are kept and implicitly converted into `\\n` characters." ] }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 100, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2634,7 +3240,7 @@ "'\\nI am a multi-line string\\nconsisting of 4 lines.\\n'" ] }, - "execution_count": 82, + "execution_count": 100, "metadata": {}, "output_type": "execute_result" } @@ -2656,7 +3262,7 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 101, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2686,12 +3292,12 @@ } }, "source": [ - "Using the [split()](https://docs.python.org/3/library/stdtypes.html#str.split) method with the optional *sep* argument, we confirm that `multi_line` consists of *four* lines with the first and last linebreaks being the first and last characters in the `str` object." + "Using the [split()](https://docs.python.org/3/library/stdtypes.html#str.split) method with the optional *sep* argument, we confirm that `multi_line` consists of *four* lines with the first and last line breaks being the first and last characters in the `str` object." ] }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 102, "metadata": { "slideshow": { "slide_type": "slide" @@ -2714,6 +3320,82 @@ " print(i, line)" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The next code cell puts several constructs from this chapter together to create a multi-line `str` object `content`: The `with` statement provides a context that ensures `file` is not left open. Then, the [readlines()](https://docs.python.org/3/library/io.html#io.IOBase.readlines) method returns the contents of `file` as a `list` object holding as many `str` objects as there are lines in the file on disk. Lastly, we concatenate these together with the [join()](https://docs.python.org/3/library/stdtypes.html#str.join) method to obtain `content`. We do so on an empty `str` object `\"\"` as each line already ends with a `\"\\n\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "with open(\"lorem_ipsum.txt\") as file:\n", + " content = \"\".join(file.readlines())" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Lorem Ipsum is simply dummy text of the printing and typesetting industry.\\nLorem Ipsum has been the industry's standard dummy text ever since the 1500s\\nwhen an unknown printer took a galley of type and scrambled it to make a type\\nspecimen book. It has survived not only five centuries but also the leap into\\nelectronic typesetting, remaining essentially unchanged. It was popularised in\\nthe 1960s with the release of Letraset sheets.\\n\"" + ] + }, + "execution_count": 104, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "content" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lorem Ipsum is simply dummy text of the printing and typesetting industry.\n", + "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s\n", + "when an unknown printer took a galley of type and scrambled it to make a type\n", + "specimen book. It has survived not only five centuries but also the leap into\n", + "electronic typesetting, remaining essentially unchanged. It was popularised in\n", + "the 1960s with the release of Letraset sheets.\n", + "\n" + ] + } + ], + "source": [ + "print(content)" + ] + }, { "cell_type": "markdown", "metadata": { @@ -2735,7 +3417,7 @@ "source": [ "Textual data is modeled with the **immutable** `str` type.\n", "\n", - "The `str` type supports *four* orthogonal **abstract concepts** that together make up the idea of a **sequence**: Every `str` object is an iterable container of a finite number of ordered characters." + "The `str` type supports *four* orthogonal **abstract concepts** that together constitute the idea of a **sequence**: Every `str` object is an iterable container of a finite number of ordered characters." ] } ], diff --git a/06_text_review_and_exercises.ipynb b/06_text_review_and_exercises.ipynb index 304a91d..40ac8c4 100644 --- a/06_text_review_and_exercises.ipynb +++ b/06_text_review_and_exercises.ipynb @@ -5,7 +5,7 @@ "metadata": {}, "source": [ "\n", - "# Chapter 6: Text" + "# Chapter 6: Bytes & Text" ] }, { @@ -54,7 +54,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q2**: What is a direct consequence of the `str` type's property of being **ordered**? What operations could we *not* do with it if it were *unordered*?" + "**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*?" ] }, { @@ -87,7 +87,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q4**: Describe in your own words what we mean with **string interpolation**!" + "**Q4**: Describe in your own words what we mean by **string interpolation**!" ] }, { @@ -115,7 +115,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q5**: **Triple-double** quotes `\"\"\"` and **triple-single** quotes `'''` create a *new* object of type `text` that model so-called **multi-line strings**." + "**Q5**: **Triple-double** quotes `\"\"\"` and **triple-single** quotes `'''` create a *new* object of type `text` that models so-called **multi-line strings**." ] }, { @@ -143,7 +143,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q7**: Indexing into a `str` object with a *negative* index **fails silently**: It does *not* raise an error but also does *not* do anything useful." + "**Q7**: Indexing into a `str` object with a *negative* index **fails silently**: It does *neither* raise an error *nor* do anything useful." ] }, { diff --git a/lorem_ipsum.txt b/lorem_ipsum.txt new file mode 100644 index 0000000..c0e21ab --- /dev/null +++ b/lorem_ipsum.txt @@ -0,0 +1,6 @@ +Lorem Ipsum is simply dummy text of the printing and typesetting industry. +Lorem Ipsum has been the industry's standard dummy text ever since the 1500s +when an unknown printer took a galley of type and scrambled it to make a type +specimen book. It has survived not only five centuries but also the leap into +electronic typesetting, remaining essentially unchanged. It was popularised in +the 1960s with the release of Letraset sheets. From 00e37555633ac7d9155efa56015ed9c85a3be16d Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 30 Oct 2019 11:05:37 +0100 Subject: [PATCH 040/242] Adjust content overview in README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index daa2e93..3837323 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,9 @@ As such they can be viewed in a plain web browser: - [02 - Functions & Modularization](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) - [03 - Conditionals & Exceptions](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb) - [04 - Recursion & Looping](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb) -- [05 - Numbers](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb) -- [06 - Text](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb) +- [05 - Bits & Numbers](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb) +- [06 - Bytes & Text](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb) +- [07 - Sequential Data](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb) However, it is recommended that students **install Python and Jupyter locally** and run the code in the notebooks on their own. From 974362288ae7d7ccf75439c39aff5bfbdad988aa Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 30 Oct 2019 11:06:05 +0100 Subject: [PATCH 041/242] Add initial version of notebook 07 --- 07_sequences.ipynb | 10742 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 10742 insertions(+) create mode 100644 07_sequences.ipynb diff --git a/07_sequences.ipynb b/07_sequences.ipynb new file mode 100644 index 0000000..11add0a --- /dev/null +++ b/07_sequences.ipynb @@ -0,0 +1,10742 @@ +{ + "cells": [ + { + "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](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb)) and textual data (cf., [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb)) first, mainly because objects of the presented data types are \"simple,\" for two reasons: First, they are *immutable*, and, as we saw in the \"*Who am I? And how many?*\" section in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.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 and Chapter 8 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. Unsurprisingly, computer scientists refer to the ideas and theories behind these data types as **[data structures](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](https://en.wikipedia.org/wiki/Spreadsheet) or [matrices](https://en.wikipedia.org/wiki/Matrix_%28mathematics%29)/[vectors](https://en.wikipedia.org/wiki/Vector_%28mathematics_and_physics%29). Such 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](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb#A-\"String\"-of-Characters) already describes the *sequence* properties of `str` objects. Here, we take a step back and study these properties on their own before looking at bigger ideas.\n", + "\n", + "The [collections.abc](https://docs.python.org/3/library/collections.abc.html) module in the [standard library](https://docs.python.org/3/library/index.html) defines a variety of **abstract base classes** (ABCs). We saw ABCs already in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb#The-Numerical-Tower), where we use the ones from the [numbers](https://docs.python.org/3/library/numbers.html) module in the [standard library](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](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 `word` 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", + "word = \"random\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "They have in common 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": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "r a n d o m " + ] + } + ], + "source": [ + "for character in word:\n", + " print(character, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.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](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](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](https://docs.python.org/3/library/functions.html) include, among others, [enumerate()](https://docs.python.org/3/library/functions.html#enumerate), [sum()](https://docs.python.org/3/library/functions.html#sum), or [zip()](https://docs.python.org/3/library/functions.html#zip). So, they do *not* require the argument to be of a certain concrete 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 in the context of *goose typing* in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb#Goose-Typing), we can use ABCs with the built-in [isinstance()](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 `word`." + ] + }, + { + "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": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(word, 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": "fragment" + } + }, + "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)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0mdigit\u001b[0m \u001b[0;32min\u001b[0m \u001b[0;36m999\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdigit\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: '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 and the next chapter exhibit three [orthogonal](https://en.wikipedia.org/wiki/Orthogonality) (i.e., \"independent\") behaviors, formalized by ABCs in the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module as:\n", + "- `Iterable`: An object supports being 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": [ + "\"r\" in word" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Alternatively, we could also check if `numbers` and `word` are `Container` types with the [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function." + ] + }, + { + "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": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(word, 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": "fragment" + } + }, + "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)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m9\u001b[0m \u001b[0;32min\u001b[0m \u001b[0;36m999\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\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 `word` as the argument to the built-in [len()](https://docs.python.org/3/library/functions.html#len) function and obtain \"meaningful\" results. The exact meaning depends on the *concrete* data type: For `numbers`, [len()](https://docs.python.org/3/library/functions.html#len) tells us how many elements are in the `list` object; for `word`, it tells us how many [Unicode characters](https://en.wikipedia.org/wiki/Unicode) make up the `str` object. But, *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": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(word)" + ] + }, + { + "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": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(word, 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()](https://docs.python.org/3/library/functions.html#len) function raises a `TypeError`." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m999\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: 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](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", + "So, both `numbers` and `word` 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": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(word, 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](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", + "We add the [reversed()](https://docs.python.org/3/library/functions.html#reversed) built-in to the `for`-loop from above to iterate 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": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "m o d n a r " + ] + } + ], + "source": [ + "for character in reversed(word):\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": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(word, 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](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": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(word, abc.Sequence)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Most of the data types introduced in the remainder of this chapter are sequences. Nevertheless, we also look at some data types that are neither collections nor sequences but still useful to model sequential data in practice.\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 8." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The `list` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As already seen multiple times, to create a `list` object, we use the *literal notation* and list all elements within brackets `[` and `]`." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "empty = []" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "simple = [40, 50]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The elements do *not* need to be of the *same* type, and `list` objects may also be **nested**." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "nested = [empty, 10, 20.0, \"Thirty\", simple]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[PythonTutor](http://www.pythontutor.com/visualize.html#code=empty%20%3D%20%5B%5D%0Asimple%20%3D%20%5B40,%2050%5D%0Anested%20%3D%20%5Bempty,%2010,%2020.0,%20%22Thirty%22,%20simple%5D&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how `nested` holds references to the `empty` and `simple` objects. Technically, it holds three more references pointing to the `10`, `20.0`, and `\"Thirty\"` objects as well. However, to simplify the visualization, these three objects are shown right inside the `nested` object as they are immutable and of \"flat\" data types. In general, the $0$s and $1$s inside a `list` object in memory always constitute pointers to other objects only." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[[], 10, 20.0, 'Thirty', [40, 50]]" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's not forget that `nested` is an object on its own with an *identity* and *data type*." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140322034424136" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(nested)" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "list" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(nested)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Alternatively, we may use the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in to create a `list` object out of an iterable we pass to it as the argument.\n", + "\n", + "For example, we can wrap the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in with [list()](https://docs.python.org/3/library/functions.html#func-list): As described in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Containers-vs.-Iterables), `range` objects, like `range(1, 13)` below, are iterable and generate `int` objects \"on the fly\" (i.e., one by one). The [list()](https://docs.python.org/3/library/functions.html#func-list) around it acts like a `for`-loop and **materializes** twelve `int` objects in memory that then become the elements of the newly created `list` object. [PythonTutor](http://www.pythontutor.com/visualize.html#code=r%20%3D%20range%281,%2013%29%0Al%20%3D%20list%28range%281,%2013%29%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows this difference visually." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(range(1, 13))" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(range(1, 13), abc.Iterable)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Beware of passing a `range` object over a \"big\" horizon as the argument to [list()](https://docs.python.org/3/library/functions.html#func-list) as that may lead to a `MemoryError` and the computer crashing." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "MemoryError", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mMemoryError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m999_999_999_999\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mMemoryError\u001b[0m: " + ] + } + ], + "source": [ + "list(range(999_999_999_999))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As another example, we may also create a `list` object from a `str` object as the latter is iterable, as well. Then, the individual characters become the elements of the new `list` object!" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['W', 'H', 'U']" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(\"WHU\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Sequence Behaviors" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`list` objects are *sequences*. To reiterate that concept from above *without* the formal ABCs from the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module, we briefly summarize the *four* behaviors of a sequence and provide some more `list`-specific details below:" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "- **Container**:\n", + " - holds references to other objects in memory (with their own *identity* and *type*)\n", + " - implements membership testing via the `in` operator\n", + "- **Iterable**:\n", + " - supports being looped over\n", + " - works with the `for` or `while` statements\n", + "- **Reversible**:\n", + " - the elements come in a *predictable* order that we may traverse in a forward or backward fashion\n", + " - works with the [reversed()](https://docs.python.org/3/library/functions.html#reversed) built-in\n", + "- **Sized**:\n", + " - the number of elements is finite *and* known in advance\n", + " - works with the built-in [len()](https://docs.python.org/3/library/functions.html#len) function" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The \"length\" of `nested` is *five* because `simple` counts as only *one* element. In other words, `nested` holds five references to other objects." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "5" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(nested)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With a `for`-loop, we can traverse all elements in a *predictable* order, forward or backward. As `list` objects only hold references to other objects, these have a *indentity* and may be of *different* types; however, this is rarely, if ever, useful in practice." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[] \t140322025703432 \t\n", + "10 \t94360180081984 \t\n", + "20.0 \t140322034534104 \t\n", + "Thirty \t140322025251816 \t\n", + "[40, 50] \t140322034424072 \t\n" + ] + } + ], + "source": [ + "for element in nested:\n", + " print(element, id(element), type(element), sep=\" \\t\")" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[40, 50] Thirty 20.0 10 [] " + ] + } + ], + "source": [ + "for element in reversed(nested):\n", + " print(element, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `in` operator checks if a given object is \"contained\" in a `list` object. It uses the `==` operator behind the scenes (i.e., *not* the `is` operator) conducting a so-called **[linear search](https://en.wikipedia.org/wiki/Linear_search)**: So, Python implicitly loops over *all* elements and only stops prematurely if an element evaluates equal to the given object. A linear search may, therefore, be relatively *slow* for big `list` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "10 in nested" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`20` compares equal to the `20.0` in `nested`." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "20 in nested" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "30 in nested" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Indexing" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Because of the *predictable* order and the *finiteness*, each element in a sequence can be labeled with a unique *index* (i.e., an `int` object in the range $0 \\leq \\text{index} < \\lvert \\text{sequence} \\rvert$).\n", + "\n", + "Brackets, `[` and `]`, are the literal syntax for accessing individual elements of any sequence type. In this book, we also call them the *indexing operator*." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested[1]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The last index is one less than `len(nested)` above, and Python raises an `IndexError` if we look up an index that is not in the implied range." + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "ename": "IndexError", + "evalue": "list index out of range", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnested\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mIndexError\u001b[0m: list index out of range" + ] + } + ], + "source": [ + "nested[5]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Negative indices are used to count in reverse order from the end of a sequence, and brackets may be chained to access nested objects. So, to access the `50` inside `simple` via the `nested` object, we write `nested[-1][1]`." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "50" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested[-1][1]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Slicing" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Slicing `list` objects works analogously to slicing `str` objects: We use the literal syntax with either one or two colons `:` inside the brackets `[]` to separate the *start*, *stop*, and *step* values. Slicing creates a *new* `list` object with the elements chosen from the original one.\n", + "\n", + "For example, to obtain the three elements in the \"middle\" of `nested`, we slice from `1` (including) to `4` (excluding)." + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[10, 20.0, 'Thirty']" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested[1:4]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To obtain \"every other\" element, we slice from beginning to end, defaulting to `0` and `len(nested)`, in steps of `2`." + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[[], 20.0, [40, 50]]" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested[::2]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The literal notation with the colons `:` is *syntactic sugar*, and Python provides the [slice()](https://docs.python.org/3/library/functions.html#slice) built-in to slice with `slice` objects. [slice()](https://docs.python.org/3/library/functions.html#slice) takes *start*, *stop*, and *step* arguments in the same way as the already familiar [range()](https://docs.python.org/3/library/functions.html#func-range) built-in." + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "middle = slice(1, 4)" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "slice" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(middle)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In most cases, the literal notation is more convenient to use; however, with `slice` objects, we may give names to slices and re-use them across several sequences." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[10, 20.0, 'Thirty']" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested[middle]" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[11, 8, 5]" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers[middle]" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'and'" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "word[middle]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`slice` objects come with three read-only attributes `start`, `stop`, and `step` on them." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "middle.start" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "middle.stop" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If not passed to [slice()](https://docs.python.org/3/library/functions.html#slice), these attributes default to `None`. That is why the cell below has no output." + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "middle.step" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A good trick to know is taking a \"full\" slice: This copies *all* elements of a `list` object into a *new* `list` object." + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "nested_copy = nested[:]" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[[], 10, 20.0, 'Thirty', [40, 50]]" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested_copy" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "At first glance, `nested` and `nested_copy` seem to cause no pain. For `list` objects, the comparison operator `==` goes over the elements in both operands in a pairwise fashion and checks if they all evaluate equal.\n", + "\n", + "We confirm that `nested` and `nested_copy` compare equal as expected but also that they are *different* objects." + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested == nested_copy" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested is nested_copy" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, as [PythonTutor](http://pythontutor.com/visualize.html#code=nested%20%3D%20%5B%5B%5D,%2010,%2020.0,%20%22Thirty%22,%20%5B40,%2050%5D%5D%0Anested_copy%20%3D%20nested%5B%3A%5D&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) reveals, only the *pointers* to the elements are copied! That concept is called a **[shallow copy](https://en.wikipedia.org/wiki/Object_copying#Shallow_copy)**.\n", + "\n", + "We could also see this with the [id()](https://docs.python.org/3/library/functions.html#id) function: The respective first elements in both `nested` and `nested_copy` are the *same* `empty` object." + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested[0] is nested_copy[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Knowing this becomes critical if the elements in a `list` object are mutable objects. Then, because of the original `list` object and its copy both pointing at the *same* objects in memory, if some of them are mutated, these changes are visible to both! We already saw a similar kind of confusion in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?) in a \"simpler\" setting and look into this in detail in the next section.\n", + "\n", + "Instead of a shallow copy, we could also create a so-called **[deep copy](https://en.wikipedia.org/wiki/Object_copying#Deep_copy)** of `nested`: That concept recursively follows every pointer in a possible nested data structure and creates copies of *every* involved object.\n", + "\n", + "To explicitly create shallow or deep copies, the [copy](https://docs.python.org/3/library/copy.html) module in the [standard library](https://docs.python.org/3/library/index.html) provides two functions, [copy()](https://docs.python.org/3/library/copy.html#copy.copy) and [deepcopy()](https://docs.python.org/3/library/copy.html#copy.deepcopy). We must always remember that slicing creates *shallow* copies only." + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import copy" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "nested_deep_copy = copy.deepcopy(nested)" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested == nested_deep_copy" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Now, the first elements of `nested` and `nested_deep_copy` are *different* objects, and [PythonTutor](http://pythontutor.com/visualize.html#code=import%20copy%0Anested%20%3D%20%5B%5B%5D,%2010,%2020.0,%20%22Thirty%22,%20%5B40,%2050%5D%5D%0Anested_deep_copy%20%3D%20copy.deepcopy%28nested%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows that there are *six* `list` objects in memory." + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested[0] is nested_deep_copy[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As this [StackOverflow question](https://stackoverflow.com/questions/184710/what-is-the-difference-between-a-deep-copy-and-a-shallow-copy) shows, understanding shallow and deep copies is a common source of confusion independent of the programming language." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Mutability" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In contrast to `str` objects, `list` objects are *mutable*: We may assign new elements to indices or slices and also remove elements. That changes *parts* of a `list` object in memory." + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "nested[0] = 0" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[0, 10, 20.0, 'Thirty', [40, 50]]" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When we re-assign a slice, we can even change the size of the `list` object." + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "nested[:4] = [100, 100, 100] # assign three elements where there were four before" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[100, 100, 100, [40, 50]]" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(nested)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `list` object's identity does *not* change. That is the whole point behind mutable objects." + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140322034424136" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(nested)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`nested_copy` is still unchanged!" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[[], 10, 20.0, 'Thirty', [40, 50]]" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested_copy" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's change the nested `[40, 50]` via `nested_copy` into `[1, 2, 3]` by replacing all its elements." + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "nested_copy[-1][:] = [1, 2, 3]" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[[], 10, 20.0, 'Thirty', [1, 2, 3]]" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested_copy" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This has a surprising side effect on `nested`!" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[100, 100, 100, [1, 2, 3]]" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "That is because `nested_copy` is a shallow copy of `nested`. [PythonTutor](http://pythontutor.com/visualize.html#code=nested%20%3D%20%5B%5B%5D,%2010,%2020.0,%20%22Thirty%22,%20%5B40,%2050%5D%5D%0Anested_copy%20%3D%20nested%5B%3A%5D%0Anested%5B%3A4%5D%20%3D%20%5B100,%20100,%20100%5D%0Anested_copy%5B-1%5D%5B%3A%5D%20%3D%20%5B1,%202,%203%5D&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how both point to the *same* nested `list` object.\n", + "\n", + "Lastly, we use the `del` statement to remove an element." + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "del nested[-1]" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[100, 100, 100]" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `del` statement, of course, also works for slices." + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "del nested[:2]" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[100]" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### List Operations" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As with `str` objects, the `+` and `*` operators are overloaded for concatenation and always return *new* `list` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "first = [10, 20, 30]\n", + "second = [40, 50, 60]" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[10, 20, 30, 40, 50, 60]" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "first + second" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[10, 20, 30, 10, 20, 30]" + ] + }, + "execution_count": 85, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "2 * first" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[40, 50, 60, 40, 50, 60, 40, 50, 60]" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "second * 3" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Besides being an operator, the `*` symbol has a second syntactical use, as explained in [PEP 3132](https://www.python.org/dev/peps/pep-3132/) and [PEP 448](https://www.python.org/dev/peps/pep-0448/): It implements what is called **iterable unpacking**. It is *not* an operator syntactically but a notation that Python processes as a literal.\n", + "\n", + "In the example, Python interprets the expression as if the elements of the iterable `second` were placed between `30` and `70` one by one. So, we do not obtain a nested but a *flat* list." + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[30, 40, 50, 60, 70]" + ] + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[30, *second, 70]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### List Methods" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `list` type is an essential data structure in any real-world Python application, and many typical `list` related algorithms from computer science theory are already built into it at the C level (cf., the [documentation](https://docs.python.org/3/tutorial/datastructures.html#more-on-lists) for a full overview). So, understanding and applying the built-in methods of the `list` type not only speeds up the development process but also makes programs significantly faster.\n", + "\n", + "In contrast to the `str` type's methods, the `list` type's methods *always* mutate (i.e., \"change\") an object *in place*. They do *not* create a *new* `list` object and return `None` to indicate that. So, we must *never* assign the return value of `list` methods to the variable holding the list!\n", + "\n", + "Let's look at the following `names` example." + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "names = [\"Carl\", \"Berthold\", \"Achim\", \"Xavier\", \"Peter\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To add an object to the end of `names`, we use the append() method. The code cell shows no output indicating that `None` must have been returned." + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "names.append(\"Eckardt\")" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Carl', 'Berthold', 'Achim', 'Xavier', 'Peter', 'Eckardt']" + ] + }, + "execution_count": 90, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With the extend() method, we may also append multiple elements provided by an iterable at once. Here, the iterable is a `list` object itself holding two `str` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "names.extend([\"Karl\", \"Oliver\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Carl', 'Berthold', 'Achim', 'Xavier', 'Peter', 'Eckardt', 'Karl', 'Oliver']" + ] + }, + "execution_count": 92, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`list` objects may be sorted *in place* with the [sort()](https://docs.python.org/3/library/stdtypes.html#list.sort) method. That is different from the built-in [sorted()](https://docs.python.org/3/library/functions.html#sorted) function that takes any *finite* and *iterable* object and returns a *new* `list` object with the iterable's elements sorted." + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Achim', 'Berthold', 'Carl', 'Eckardt', 'Karl', 'Oliver', 'Peter', 'Xavier']" + ] + }, + "execution_count": 93, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sorted(names)" + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Carl', 'Berthold', 'Achim', 'Xavier', 'Peter', 'Eckardt', 'Karl', 'Oliver']" + ] + }, + "execution_count": 94, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "code", + "execution_count": 95, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "names.sort()" + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Achim', 'Berthold', 'Carl', 'Eckardt', 'Karl', 'Oliver', 'Peter', 'Xavier']" + ] + }, + "execution_count": 96, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To sort in reverse order, we pass a keyword-only `reverse=True` argument to either the [sort()](https://docs.python.org/3/library/stdtypes.html#list.sort) method or the [sorted()](https://docs.python.org/3/library/functions.html#sorted) function. In the latter case, we could also use the [reversed()](https://docs.python.org/3/library/functions.html#reversed) built-in instead; however, that *neither* returns a new `list` object *nor* changes the existing one in place. We revisit it at the end of this chapter." + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "names.sort(reverse=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Xavier', 'Peter', 'Oliver', 'Karl', 'Eckardt', 'Carl', 'Berthold', 'Achim']" + ] + }, + "execution_count": 98, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Both, the [sort()](https://docs.python.org/3/library/stdtypes.html#list.sort) method and the [sorted()](https://docs.python.org/3/library/functions.html#sorted) function, also accept a keyword-only `key` argument that must be a reference to a `function` object accepting one positional argument. Then, the elements in the `list` object are passed to that on a one-by-one basis, and the return values are used as the **sort keys**.\n", + "\n", + "For example, to sort `names` not by alphabet but by the names' lengths, we pass in a reference to the built-in [len()](https://docs.python.org/3/library/functions.html#len) function as `key=len`. Note that there are *no* parentheses after `len`!" + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "names.sort(key=len)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If two names have the same length, their relative order is kept as is. A [sorting algorithm](https://en.wikipedia.org/wiki/Sorting_algorithm) is called **[stable](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability)** if it has that property. That is why `\"Karl\"` comes before `\"Carl\" ` below.\n", + "\n", + "Sorting is an important topic in programming and we refer to the official [HOWTO](https://docs.python.org/3/howto/sorting.html) for a more comprehensive introduction." + ] + }, + { + "cell_type": "code", + "execution_count": 100, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Karl', 'Carl', 'Peter', 'Achim', 'Xavier', 'Oliver', 'Eckardt', 'Berthold']" + ] + }, + "execution_count": 100, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The pop() method removes the last element from a `list` object *and* returns it." + ] + }, + { + "cell_type": "code", + "execution_count": 101, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Berthold'" + ] + }, + "execution_count": 101, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names.pop()" + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Karl', 'Carl', 'Peter', 'Achim', 'Xavier', 'Oliver', 'Eckardt']" + ] + }, + "execution_count": 102, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "It takes an optional index argument and removes that instead." + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Carl'" + ] + }, + "execution_count": 103, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names.pop(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Karl', 'Peter', 'Achim', 'Xavier', 'Oliver', 'Eckardt']" + ] + }, + "execution_count": 104, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Instead of removing an element by its index, we can remove it by its value with the remove() method. Behind the scenes, Python then compares the object passed as its argument, `\"Peter\"` in the example, sequentially to each element with the `==` operator and removes the first one that evaluates equal." + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "names.remove(\"Peter\")" + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Karl', 'Achim', 'Xavier', 'Oliver', 'Eckardt']" + ] + }, + "execution_count": 106, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "remove() raises a `ValueError` if the value is not found." + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "list.remove(x): x not in list", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnames\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mremove\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Peter\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: list.remove(x): x not in list" + ] + } + ], + "source": [ + "names.remove(\"Peter\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`list` objects implement an index() method that returns the position of the first occurrence of an element. It fails loudly with a `ValueError` if the element cannot be found by value." + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Karl', 'Achim', 'Xavier', 'Oliver', 'Eckardt']" + ] + }, + "execution_count": 108, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 109, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names.index(\"Oliver\")" + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "'Carl' is not in list", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnames\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Carl\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: 'Carl' is not in list" + ] + } + ], + "source": [ + "names.index(\"Carl\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The count() method returns the number of occurrences of a value." + ] + }, + { + "cell_type": "code", + "execution_count": 111, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 111, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names.count(\"Xavier\")" + ] + }, + { + "cell_type": "code", + "execution_count": 112, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 112, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names.count(\"Yves\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### List Comparison" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The relational operators also work with `list` objects; yet another example of operator overloading.\n", + "\n", + "Comparison is made in a pairwise fashion until the first pair of elements does not evaluate equal or one of the `list` objects ends. The exact comparison rules depend on the elements and not the `list` object. We say that comparison is **[delegated](https://en.wikipedia.org/wiki/Delegation_(object-oriented_programming))** to the objects to be compared. Usually, all elements are of the *same* type. Then, the comparison is straightforward and conceptually the same as for string comparison in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb#String-Comparison)." + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Karl', 'Achim', 'Xavier', 'Oliver', 'Eckardt']" + ] + }, + "execution_count": 113, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "code", + "execution_count": 114, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 114, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names < [\"Karl\", \"Achim\", \"Oliver\", \"Xavier\", \"Eckardt\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 115, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 115, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names < [\"Karl\", \"Xavier\", \"Achim\", \"Oliver\", \"Eckardt\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The shorter `list` object is considered \"smaller,\" and vice versa." + ] + }, + { + "cell_type": "code", + "execution_count": 116, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 116, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names < [\"Karl\", \"Achim\", \"Xavier\", \"Oliver\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 117, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names < [\"Karl\", \"Achim\", \"Xavier\", \"Oliver\", \"Eckardt\", \"Peter\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Modifiers vs. Pure Functions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As `list` objects are mutable, the caller of a function can see the changes made to a `list` object passed to the function as an argument. That is often a surprising *side effect* and should be avoided.\n", + "\n", + "As an example, consider the `add_xyz()` function." + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "letters = [\"a\", \"b\", \"c\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 119, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "def add_xyz(arg):\n", + " \"\"\"Append letters to a list.\"\"\"\n", + " arg.extend([\"x\", \"y\", \"z\"])\n", + " return arg" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While this function is being executed, two variables, namely `letters` in the global scope and `arg` inside the function's local scope, point to the *same* `list` object in memory. Furthermore, the passed in `arg` is also the return value.\n", + "\n", + "So, after the function call, `letters_with_xyz` and `letters` are **aliases** as well, pointing to the *same* object." + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "letters_with_xyz = add_xyz(letters)" + ] + }, + { + "cell_type": "code", + "execution_count": 121, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['a', 'b', 'c', 'x', 'y', 'z']" + ] + }, + "execution_count": 121, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "letters_with_xyz" + ] + }, + { + "cell_type": "code", + "execution_count": 122, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['a', 'b', 'c', 'x', 'y', 'z']" + ] + }, + "execution_count": 122, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "letters" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A better practice is to first create a copy of `arg` within the function that is then modified and returned. If we are sure that `arg` contains immutable elements only, we get away with a shallow copy. The downside of this approach is the higher amount of memory necessary.\n", + "\n", + "The revised `add_xyz()` function below is more natural to reason about as it does *not* modify the passed in `arg` internally. This approach is following the **[functional programming](https://en.wikipedia.org/wiki/Functional_programming)** paradigm that is going through a \"renaissance\" currently. Two essential characteristics of functional programming are that a function *never* changes its inputs and *always* returns the same output given the same inputs.\n", + "\n", + "For a beginner, it is probably better to stick to this idea and not change any arguments as the original `add_xyz()` above. However, functions that modify and return the argument passed in are an important aspect of object-oriented programming, as explained in Chapter 9." + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "letters = [\"a\", \"b\", \"c\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "def add_xyz(arg):\n", + " \"\"\"Create a new list from an existing one.\"\"\"\n", + " new_arg = arg[:] # a shallow copy is good enough here\n", + " new_arg.extend([\"x\", \"y\", \"z\"])\n", + " return new_arg" + ] + }, + { + "cell_type": "code", + "execution_count": 125, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "letters_with_xyz = add_xyz(letters)" + ] + }, + { + "cell_type": "code", + "execution_count": 126, + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['a', 'b', 'c', 'x', 'y', 'z']" + ] + }, + "execution_count": 126, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "letters_with_xyz" + ] + }, + { + "cell_type": "code", + "execution_count": 127, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['a', 'b', 'c']" + ] + }, + "execution_count": 127, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "letters" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we want to modify the argument passed in, it is best to return `None` and not `arg`, as does the final version of `add_xyz()` below. Then, the user of our function cannot accidentally create two aliases to the same object. That is also why the list methods above all return `None`." + ] + }, + { + "cell_type": "code", + "execution_count": 128, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "letters = [\"a\", \"b\", \"c\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 129, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "def add_xyz(arg):\n", + " \"\"\"Append letters to a list.\"\"\"\n", + " arg.extend([\"x\", \"y\", \"z\"])\n", + " return # = None" + ] + }, + { + "cell_type": "code", + "execution_count": 130, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "add_xyz(letters)" + ] + }, + { + "cell_type": "code", + "execution_count": 131, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['a', 'b', 'c', 'x', 'y', 'z']" + ] + }, + "execution_count": 131, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "letters" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we call `add_xyz()` with `letters` as the argument again, we end up with an even longer `list` object." + ] + }, + { + "cell_type": "code", + "execution_count": 132, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "add_xyz(letters)" + ] + }, + { + "cell_type": "code", + "execution_count": 133, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['a', 'b', 'c', 'x', 'y', 'z', 'x', 'y', 'z']" + ] + }, + "execution_count": 133, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "letters" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Functions that only work on the argument passed in are called **modifiers**. Their primary purpose is to change the **state** of the argument. On the contrary, functions that have *no* side effects on the arguments are said to be **pure**." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The `tuple` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To create a `tuple` object, we can use the same literal notation as for `list` objects *without* the brackets and list all elements." + ] + }, + { + "cell_type": "code", + "execution_count": 134, + "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": 135, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4)" + ] + }, + "execution_count": 135, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, to be clearer, many Pythonistas write out the optional parentheses `(` and `)`." + ] + }, + { + "cell_type": "code", + "execution_count": 136, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "numbers = (7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4)" + ] + }, + { + "cell_type": "code", + "execution_count": 137, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4)" + ] + }, + "execution_count": 137, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As before, `numbers` is an object on its own." + ] + }, + { + "cell_type": "code", + "execution_count": 138, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140322025210648" + ] + }, + "execution_count": 138, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 139, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "tuple" + ] + }, + "execution_count": 139, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While we could use empty parentheses `()` to create an empty `tuple` object ..." + ] + }, + { + "cell_type": "code", + "execution_count": 140, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "empty_tuple = ()" + ] + }, + { + "cell_type": "code", + "execution_count": 141, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 141, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "empty_tuple" + ] + }, + { + "cell_type": "code", + "execution_count": 142, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "tuple" + ] + }, + "execution_count": 142, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(empty_tuple)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... we must use a *trailing comma* to create a `tuple` object holding one element. If we forget the comma, the parentheses are interpreted as the grouping operator and effectively useless!" + ] + }, + { + "cell_type": "code", + "execution_count": 143, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "one_tuple = (1,) # we could ommit the parentheses but not the comma" + ] + }, + { + "cell_type": "code", + "execution_count": 144, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1,)" + ] + }, + "execution_count": 144, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "one_tuple" + ] + }, + { + "cell_type": "code", + "execution_count": 145, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "tuple" + ] + }, + "execution_count": 145, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(one_tuple)" + ] + }, + { + "cell_type": "code", + "execution_count": 146, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "no_tuple = (1)" + ] + }, + { + "cell_type": "code", + "execution_count": 147, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 147, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "no_tuple" + ] + }, + { + "cell_type": "code", + "execution_count": 148, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "int" + ] + }, + "execution_count": 148, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(no_tuple)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Alternatively, we may use the [tuple()](https://docs.python.org/3/library/functions.html#func-tuple) built-in that takes any iterable as its argument and creates a new `tuple` from its elements." + ] + }, + { + "cell_type": "code", + "execution_count": 149, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1,)" + ] + }, + "execution_count": 149, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tuple([1])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Tuples are like \"Immutable Lists\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Most operations involving `tuple` objects work in the same way as with `list` objects. The main difference is that `tuple` objects are *immutable*. So, if our program does not depend on mutability, we may and should use `tuple` and not `list` objects to model sequential data. That way, we avoid the pitfalls seen above.\n", + "\n", + "`tuple` objects are *sequences* exhibiting the familiar *four* behaviors." + ] + }, + { + "cell_type": "code", + "execution_count": 150, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 150, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(numbers, abc.Sequence)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + " So, `numbers` holds a *finite* number of elements ..." + ] + }, + { + "cell_type": "code", + "execution_count": 151, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12" + ] + }, + "execution_count": 151, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... that we can obtain individually by looping over it in a predictable *forward* or *reverse* order." + ] + }, + { + "cell_type": "code", + "execution_count": 152, + "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": 153, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "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": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To check if a given object is *contained* in `numbers`, we use the `in` operator and conduct a linear search." + ] + }, + { + "cell_type": "code", + "execution_count": 154, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 154, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0 in numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 155, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 155, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 in numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 156, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 156, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1.0 in numbers # in relies on == behind the scenes" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We may index and slice with the `[]` operator. The latter returns *new* `tuple` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 157, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7" + ] + }, + "execution_count": 157, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 158, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 158, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers[-1]" + ] + }, + { + "cell_type": "code", + "execution_count": 159, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(2, 6, 9, 10, 1, 4)" + ] + }, + "execution_count": 159, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers[6:]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Index assignment does *not* work as tuples are *immutable* and results in a `TypeError`." + ] + }, + { + "cell_type": "code", + "execution_count": 160, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'tuple' object does not support item assignment", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnumbers\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m99\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: 'tuple' object does not support item assignment" + ] + } + ], + "source": [ + "numbers[-1] = 99" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we need to \"modify\" the `tuple` object, we must create a *new* `tuple` object, for example, like so: We take a slice of the elements we want to keep and use the overloaded `+` operator to concatenate the slice with another `tuple` object." + ] + }, + { + "cell_type": "code", + "execution_count": 161, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "new_numbers = numbers[:-1] + (99,)" + ] + }, + { + "cell_type": "code", + "execution_count": 162, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 99)" + ] + }, + "execution_count": 162, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "new_numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `*` operator works as well." + ] + }, + { + "cell_type": "code", + "execution_count": 163, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4, 7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4)" + ] + }, + "execution_count": 163, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "2 * numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Being immutable, `tuple` objects only provide the count() and index() methods." + ] + }, + { + "cell_type": "code", + "execution_count": 164, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 164, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers.count(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 165, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10" + ] + }, + "execution_count": 165, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers.index(1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The relational operators compare the elements of two `tuple` objects in a pairwise fashion as above." + ] + }, + { + "cell_type": "code", + "execution_count": 166, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 166, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers < new_numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While `tuple` objects are immutable, this only relates to the references they hold. If a `tuple` object contains mutable objects, the entire nested structure is *not* immutable as a whole.\n", + "\n", + "Consider the following stylized example `not_immutable`: It contains *three* elements, `1`, `[2, ..., 11]`, and `12`, and the elements of the nested `list` object may be changed. While it is not practical to mix data types in a `tuple` object that is used as an \"immutable list,\" we want to make the point that the mere usage of the `tuple` type does *not* guarantee a nested object to be immutable." + ] + }, + { + "cell_type": "code", + "execution_count": 167, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "not_immutable = (1, [2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 12)" + ] + }, + { + "cell_type": "code", + "execution_count": 168, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "not_immutable[1][:] = [99, 99, 99]" + ] + }, + { + "cell_type": "code", + "execution_count": 169, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, [99, 99, 99], 12)" + ] + }, + "execution_count": 169, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "not_immutable" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Packing & Unpacking" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In the \"*List Operations*\" section above, the `*` symbol **unpacks** the elements of a `list` object into another one. This idea of *iterable unpacking* is built into Python at various places, even *without* the `*` symbol.\n", + "\n", + "For example, we may write variables on the left-hand side of a `=` statement in a `tuple` style. Then, any *finite* iterable on the right-hand side is unpacked. So, `numbers` is unpacked into *twelve* variables below." + ] + }, + { + "cell_type": "code", + "execution_count": 170, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12 = numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 171, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7" + ] + }, + "execution_count": 171, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "n1" + ] + }, + { + "cell_type": "code", + "execution_count": 172, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "11" + ] + }, + "execution_count": 172, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "n2" + ] + }, + { + "cell_type": "code", + "execution_count": 173, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "8" + ] + }, + "execution_count": 173, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "n3" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Having to type twelve variables on the left is already tedious. Furthermore, if the iterable on the right yields a number of elements *different* from the number of variables, we get a `ValueError`." + ] + }, + { + "cell_type": "code", + "execution_count": 174, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "too many values to unpack (expected 11)", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mn1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn5\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn6\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn7\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn8\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn9\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn10\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn11\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: too many values to unpack (expected 11)" + ] + } + ], + "source": [ + "n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11 = numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 175, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "not enough values to unpack (expected 13, got 12)", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mn1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn5\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn6\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn7\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn8\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn9\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn10\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn11\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn12\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn13\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: not enough values to unpack (expected 13, got 12)" + ] + } + ], + "source": [ + "n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13 = numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "So, to make iterable unpacking useful, we prepend the `*` symbol to *one* of the variables on the left: That variable then becomes a `list` object holding the elements not captured by the other variables. We say that the excess elements from the iterable are **packed** into this variable.\n", + "\n", + "For example, let's get the `first` and `last` element of `numbers` and collect the rest in `middle`." + ] + }, + { + "cell_type": "code", + "execution_count": 176, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "first, *middle, last = numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 177, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7" + ] + }, + "execution_count": 177, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "first" + ] + }, + { + "cell_type": "code", + "execution_count": 178, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[11, 8, 5, 3, 12, 2, 6, 9, 10, 1]" + ] + }, + "execution_count": 178, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "middle" + ] + }, + { + "cell_type": "code", + "execution_count": 179, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 179, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "last" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we do not need the `middle` elements, we go with the underscore `_` convention and \"throw\" them away." + ] + }, + { + "cell_type": "code", + "execution_count": 180, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "first, *_, last = numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 181, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7" + ] + }, + "execution_count": 181, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "first" + ] + }, + { + "cell_type": "code", + "execution_count": 182, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 182, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "last" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We already used unpacking before this section without knowing it. Whenever we write a `for`-loop over the [zip()](https://docs.python.org/3/library/functions.html#zip) built-in, that generates a new `tuple` object in each iteration, which we unpack by listing several loop variables.\n", + "\n", + "So, the `name, position` acts like a left-hand side of an `=` statement and unpacks the `tuple` objects generated from \"zipping\" the `names` list and the `positions` tuple together." + ] + }, + { + "cell_type": "code", + "execution_count": 183, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "positions = (\"goalkeeper\", \"defender\", \"midfielder\", \"striker\", \"coach\")" + ] + }, + { + "cell_type": "code", + "execution_count": 184, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Karl is a goalkeeper\n", + "Achim is a defender\n", + "Xavier is a midfielder\n", + "Oliver is a striker\n", + "Eckardt is a coach\n" + ] + } + ], + "source": [ + "for name, position in zip(names, positions):\n", + " print(name, \"is a\", position)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Without unpacking, [zip()](https://docs.python.org/3/library/functions.html#zip) generates a series of `tuple` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 185, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ('Karl', 'goalkeeper')\n", + " ('Achim', 'defender')\n", + " ('Xavier', 'midfielder')\n", + " ('Oliver', 'striker')\n", + " ('Eckardt', 'coach')\n" + ] + } + ], + "source": [ + "for pair in zip(names, positions):\n", + " print(type(pair), pair, sep=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Unpacking also works for nested objects. Below, we wrap [zip()](https://docs.python.org/3/library/functions.html#zip) with the [enumerate()](https://docs.python.org/3/library/functions.html#enumerate) built-in to have an index variable `i` inside the `for`-loop. In each iteration, a `tuple` object consisting of `i` and another `tuple` object is created. The inner one then holds the `name` and `position`." + ] + }, + { + "cell_type": "code", + "execution_count": 186, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 -> Karl is a goalkeeper\n", + "1 -> Achim is a defender\n", + "2 -> Xavier is a midfielder\n", + "3 -> Oliver is a striker\n", + "4 -> Eckardt is a coach\n" + ] + } + ], + "source": [ + "for i, (name, position) in enumerate(zip(names, positions)):\n", + " print(i, \"->\", name, \"is a\", position)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Swapping Variables" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A popular use case of unpacking is **swapping** two variables.\n", + "\n", + "Consider `a` and `b` below." + ] + }, + { + "cell_type": "code", + "execution_count": 187, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "a = 0\n", + "b = 1" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Without unpacking, we must use a temporary variable `temp` to swap `a` and `b`." + ] + }, + { + "cell_type": "code", + "execution_count": 188, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "temp = a\n", + "a = b\n", + "b = temp" + ] + }, + { + "cell_type": "code", + "execution_count": 189, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 0)" + ] + }, + "execution_count": 189, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a, b" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With unpacking, the solution is more elegant, and also a bit faster as well. *All* expressions on the right-hand side are evaluated *before* any assignment takes place." + ] + }, + { + "cell_type": "code", + "execution_count": 190, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "a = 0\n", + "b = 1" + ] + }, + { + "cell_type": "code", + "execution_count": 191, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "a, b = b, a" + ] + }, + { + "cell_type": "code", + "execution_count": 192, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 0)" + ] + }, + "execution_count": 192, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a, b" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### Example: [Fibonacci Numbers](https://en.wikipedia.org/wiki/Fibonacci_number) (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Unpacking allows us to rewrite the iterative `fibonacci()` function from [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#\"Hard-at-first-Glance\"-Example:-Fibonacci-Numbers-%28revisited%29) in a concise way, now also supporting *goose typing* with the [numbers](https://docs.python.org/3/library/numbers.html) module from the [standard library](https://docs.python.org/3/library/index.html)." + ] + }, + { + "cell_type": "code", + "execution_count": 193, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 194, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "def fibonacci(i):\n", + " \"\"\"Calculate the ith Fibonacci number.\n", + "\n", + " Args:\n", + " i (int): index of the Fibonacci number to calculate\n", + "\n", + " Returns:\n", + " ith_fibonacci (int)\n", + "\n", + " Raises:\n", + " TypeError: if i is not an integer\n", + " ValueError: if i is not positive\n", + " \"\"\"\n", + " if not isinstance(i, numbers.Integral):\n", + " raise TypeError(\"i must be an integer\")\n", + " elif i < 0:\n", + " raise ValueError(\"i must be non-negative\")\n", + "\n", + " a, b = 0, 1\n", + "\n", + " for _ in range(i - 1):\n", + " a, b = b, a + b\n", + "\n", + " return b" + ] + }, + { + "cell_type": "code", + "execution_count": 195, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "144" + ] + }, + "execution_count": 195, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fibonacci(12)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Function Definitions & Calls" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The concepts of packing and unpacking are also helpful when writing and using functions.\n", + "\n", + "For example, let's look at the `product()` function below. Its implementation suggests that `args` must be a sequence type. Otherwise, it would not make sense to index into it with `[0]` or take a slice with `[1:]`. In line with the function's name, the `for`-loop multiplies all elements of the `args` sequence. So, what does the `*` do in the header line, and what is the exact data type of `args`?\n", + "\n", + "The `*` is again *not* an operator in this context but a special syntax that makes Python *pack* all *positional* arguments passed to `product()` into a single `tuple` object called `args`." + ] + }, + { + "cell_type": "code", + "execution_count": 196, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "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": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "So, we can pass an *arbitrary* (i.e., also none) number of *positional* arguments to `product()`.\n", + "\n", + "The product of just one number is the number itself." + ] + }, + { + "cell_type": "code", + "execution_count": 197, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 197, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "product(42)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Passing in several numbers works as expected." + ] + }, + { + "cell_type": "code", + "execution_count": 198, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "100" + ] + }, + "execution_count": 198, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "product(2, 5, 10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, this implementation of `product()` needs *at least* one argument passed in due to the expression `args[0]` used internally. Otherwise, we see a *runtime* error, namely an `IndexError`. We emphasize that this error is *not* caused in the header line." + ] + }, + { + "cell_type": "code", + "execution_count": 199, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "IndexError", + "evalue": "tuple index out of range", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mproduct\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mproduct\u001b[0;34m(*args)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mproduct\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\"\"\"Multiply all arguments.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0marg\u001b[0m \u001b[0;32min\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mIndexError\u001b[0m: tuple index out of range" + ] + } + ], + "source": [ + "product()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Another downside of this implementation is that we can easily generate *semantic* errors: For example, if we pass in an iterable object like the `one_hundred` list, *no* exception is raised. However, the return value is also not a numeric object as we expect. The reason for this is that during the function call, `args` becomes a `tuple` object holding *one* element, which is `one_hundred`, a `list` object. So, we created a nested structure by accident." + ] + }, + { + "cell_type": "code", + "execution_count": 200, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "one_hundred = [2, 5, 10]" + ] + }, + { + "cell_type": "code", + "execution_count": 201, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[2, 5, 10]" + ] + }, + "execution_count": 201, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "product(one_hundred)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This error does not occur if we unpack `one_hundred` upon passing it as the argument." + ] + }, + { + "cell_type": "code", + "execution_count": 202, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "100" + ] + }, + "execution_count": 202, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "product(*one_hundred)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "That is the equivalent of writing out the following tedious expression. Yet, that does *not* scale for iterables with many elements in them." + ] + }, + { + "cell_type": "code", + "execution_count": 203, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "100" + ] + }, + "execution_count": 203, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "product(one_hundred[0], one_hundred[1], one_hundred[2])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While we needed to unpack `one_hundred` above to avoid the semantic error, unpacking an argument in a function call may also be a convenience in general.\n", + "\n", + "For example, to print the elements of `one_hundred` in one line, we need to use a `for` statement, until now. With unpacking, we get away *without* a loop." + ] + }, + { + "cell_type": "code", + "execution_count": 204, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[2, 5, 10]\n" + ] + } + ], + "source": [ + "print(one_hundred) # prints the tuple; we do not want that" + ] + }, + { + "cell_type": "code", + "execution_count": 205, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2 5 10 " + ] + } + ], + "source": [ + "for number in one_hundred:\n", + " print(number, end=\" \")" + ] + }, + { + "cell_type": "code", + "execution_count": 206, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2 5 10\n" + ] + } + ], + "source": [ + "print(*one_hundred)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The `namedtuple` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Above, 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": 207, + "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.\n", + "\n", + "A better way is to create a *custom* data type. While that is covered in depth in Chapter 9, the [collections](https://docs.python.org/3/library/collections.html) module in the [standard library](https://docs.python.org/3/library/index.html) provides a [namedtuple()](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": 208, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "from collections import namedtuple" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[namedtuple()](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": 209, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "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`." + ] + }, + { + "cell_type": "code", + "execution_count": 210, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140321614839000" + ] + }, + "execution_count": 210, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(Point) # classes are objects as well" + ] + }, + { + "cell_type": "code", + "execution_count": 211, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "type" + ] + }, + "execution_count": 211, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(Point)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To create a `Point` object, we use the same *literal syntax* as for `current_position` above and prepend it with `Point`." + ] + }, + { + "cell_type": "code", + "execution_count": 212, + "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": 213, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Point(x=4, y=2)" + ] + }, + "execution_count": 213, + "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": 214, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140322025282656" + ] + }, + "execution_count": 214, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(current_position)" + ] + }, + { + "cell_type": "code", + "execution_count": 215, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "__main__.Point" + ] + }, + "execution_count": 215, + "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": 216, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 216, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "current_position.x" + ] + }, + { + "cell_type": "code", + "execution_count": 217, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 217, + "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": 218, + "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)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mcurrent_position\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mz\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\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." + ] + }, + { + "cell_type": "code", + "execution_count": 219, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 219, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(current_position, abc.Sequence)" + ] + }, + { + "cell_type": "code", + "execution_count": 220, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 220, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "current_position[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 221, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 221, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "current_position[1]" + ] + }, + { + "cell_type": "code", + "execution_count": 222, + "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": 223, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2\n", + "4\n" + ] + } + ], + "source": [ + "for number in reversed(current_position):\n", + " print(number)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The Map-Filter-Reduce Paradigm" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Whenever we process sequential data, most tasks can be classified into one of the three categories **map**, **filter**, or **reduce**. This paradigm has caught attention in recent years as it enables **[parallel computing](https://en.wikipedia.org/wiki/Parallel_computing)**, and this gets important when dealing with big amounts of data.\n", + "\n", + "Let's look at a simple example." + ] + }, + { + "cell_type": "code", + "execution_count": 224, + "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": "slide" + } + }, + "source": [ + "### Mapping" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Mapping** refers to the idea of applying a transformation to every element in a sequence.\n", + "\n", + "For example, let's square each element in `numbers` and add `1` to it. In essence, we apply the transformation $y := x^2 + 1$ expressed as the `transform()` function below." + ] + }, + { + "cell_type": "code", + "execution_count": 225, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def transform(element):\n", + " \"\"\"Map elements to their squares plus 1.\"\"\"\n", + " return (element ** 2) + 1" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With the syntax we know so far, we revert to a `for`-loop that iteratively appends the transformed elements to the initially empty `transformed_numbers` list." + ] + }, + { + "cell_type": "code", + "execution_count": 226, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "transformed_numbers = []\n", + "\n", + "for old in numbers:\n", + " new = transform(old)\n", + " transformed_numbers.append(new)" + ] + }, + { + "cell_type": "code", + "execution_count": 227, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[50, 122, 65, 26, 10, 145, 5, 37, 82, 101, 2, 17]" + ] + }, + "execution_count": 227, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "transformed_numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As this kind of data processing is so common, Python provides the [map()](https://docs.python.org/3/library/functions.html#map) built-in. In its simplest usage form, it takes two arguments: A transformation function that takes one positional argument and an iterable.\n", + "\n", + "We call [map()](https://docs.python.org/3/library/functions.html#map) with the `transform()` function and the `numbers` list as the arguments and store the result in the variable `transformer` to inspect it." + ] + }, + { + "cell_type": "code", + "execution_count": 228, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "transformer = map(transform, numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We might expect to get back a materialized sequence (i.e., all elements exist in memory), and a `list` object would feel the most natural because of the type of the `numbers` argument. However, `transformer` is an object of type `map`." + ] + }, + { + "cell_type": "code", + "execution_count": 229, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 229, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "transformer" + ] + }, + { + "cell_type": "code", + "execution_count": 230, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "map" + ] + }, + "execution_count": 230, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(transformer)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Like `range` objects, `map` objects generate a series of objects \"on the fly\" (i.e., one by one), and we use the built-in [next()](https://docs.python.org/3/library/functions.html#next) function to obtain the next object in line. So, we should think of a `map` object as a \"rule\" stored in memory that only knows how to calculate the next object of possibly *infinitely* many.\n", + "\n", + "It is essential to understand that by creating a `map` object with the [map()](https://docs.python.org/3/library/functions.html#map) built-in, nothing happens in memory except the creation of the `map` object. In particular, no second `list` object derived from `numbers` is created. Also, we may view `range` objects as a special case of `map` objects: They are constrained to generating `int` objects only, and the *iterable* argument is replaced with *start*, *stop*, and *step* arguments." + ] + }, + { + "cell_type": "code", + "execution_count": 231, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "50" + ] + }, + "execution_count": 231, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(transformer)" + ] + }, + { + "cell_type": "code", + "execution_count": 232, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "122" + ] + }, + "execution_count": 232, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(transformer)" + ] + }, + { + "cell_type": "code", + "execution_count": 233, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "65" + ] + }, + "execution_count": 233, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(transformer)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we are sure that a `map` object generates a *finite* number of elements, we may materialize them into a `list` object with the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in. In the example, this is the case as `transformer` is derived from a *finite* `list` object.\n", + "\n", + "In summary, instead of creating an empty list first and appending it in a `for`-loop as above, we write the following one-liner and obtain an equal `transformed_numbers` list." + ] + }, + { + "cell_type": "code", + "execution_count": 234, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "transformed_numbers = list(map(transform, numbers))" + ] + }, + { + "cell_type": "code", + "execution_count": 235, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[50, 122, 65, 26, 10, 145, 5, 37, 82, 101, 2, 17]" + ] + }, + "execution_count": 235, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "transformed_numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Filtering" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Filtering** refers to the idea of creating a subset of a sequence with a **boolean filter** function that indicates if an element should be kept or not.\n", + "\n", + "In the example, let's only keep the even elements in `numbers`. The `is_even()` function implements that as a filter." + ] + }, + { + "cell_type": "code", + "execution_count": 236, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def is_even(element):\n", + " \"\"\"Filter out odd numbers.\"\"\"\n", + " if element % 2 == 0:\n", + " return True\n", + " return False" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As before, we must revert to a `for`-loop that appends the elements to be kept iteratively to an initially empty `even_numbers` list." + ] + }, + { + "cell_type": "code", + "execution_count": 237, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "even_numbers = []\n", + "\n", + "for number in transformed_numbers:\n", + " if is_even(number):\n", + " even_numbers.append(number)" + ] + }, + { + "cell_type": "code", + "execution_count": 238, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[50, 122, 26, 10, 82, 2]" + ] + }, + "execution_count": 238, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "even_numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As filtering is also a common task, we use the [filter()](https://docs.python.org/3/library/functions.html#filter) built-in that returns an object of type `filter` stored in the `evens` variable." + ] + }, + { + "cell_type": "code", + "execution_count": 239, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "evens = filter(is_even, transformed_numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 240, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 240, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "evens" + ] + }, + { + "cell_type": "code", + "execution_count": 241, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "filter" + ] + }, + "execution_count": 241, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(evens)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`evens` works like `transformer` above, and we use the built-in [next()](https://docs.python.org/3/library/functions.html#next) function to obtain the even numbers one by one. So, the \"next\" element in line is simply the next even `int` object the `filter` object encounters." + ] + }, + { + "cell_type": "code", + "execution_count": 242, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[50, 122, 65, 26, 10, 145, 5, 37, 82, 101, 2, 17]" + ] + }, + "execution_count": 242, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "transformed_numbers # for quick reference" + ] + }, + { + "cell_type": "code", + "execution_count": 243, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "50" + ] + }, + "execution_count": 243, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(evens)" + ] + }, + { + "cell_type": "code", + "execution_count": 244, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "122" + ] + }, + "execution_count": 244, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(evens)" + ] + }, + { + "cell_type": "code", + "execution_count": 245, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "26" + ] + }, + "execution_count": 245, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(evens)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As above, we must explicitly create a materialized `list` object with the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in." + ] + }, + { + "cell_type": "code", + "execution_count": 246, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[50, 122, 26, 10, 82, 2]" + ] + }, + "execution_count": 246, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(filter(is_even, transformed_numbers))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We may also chain mappings and filters based on the original `numbers` list." + ] + }, + { + "cell_type": "code", + "execution_count": 247, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[50, 122, 26, 10, 82, 2]" + ] + }, + "execution_count": 247, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(filter(is_even, map(transform, numbers)))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Using the [map()](https://docs.python.org/3/library/functions.html#map) and [filter()](https://docs.python.org/3/library/functions.html#filter) built-ins, we can quickly switch the order: Filter first and then transform the remaining elements. This variant equals the \"*A simple Filter*\" example from [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Example:-A-simple-Filter). On the contrary, code with `for`-loops and `if` statements is more tedious to adapt. Additionally, `map` and `filter` objects are optimized at the C level and, therefore, a lot faster as well." + ] + }, + { + "cell_type": "code", + "execution_count": 248, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[65, 145, 5, 37, 101, 17]" + ] + }, + "execution_count": 248, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(map(transform, filter(is_even, numbers)))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Reducing" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Lastly, **reducing** sequential data means to summarize the elements into a single statistic.\n", + "\n", + "A simple example is the built-in [sum()](https://docs.python.org/3/library/functions.html#sum) function." + ] + }, + { + "cell_type": "code", + "execution_count": 249, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "370" + ] + }, + "execution_count": 249, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(map(transform, filter(is_even, numbers)))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Other straightforward examples are the built-in [min()](https://docs.python.org/3/library/functions.html#min) or [max()](https://docs.python.org/3/library/functions.html#max) functions." + ] + }, + { + "cell_type": "code", + "execution_count": 250, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "5" + ] + }, + "execution_count": 250, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "min(map(transform, filter(is_even, numbers)))" + ] + }, + { + "cell_type": "code", + "execution_count": 251, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "145" + ] + }, + "execution_count": 251, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "max(map(transform, filter(is_even, numbers)))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[sum()](https://docs.python.org/3/library/functions.html#sum), [min()](https://docs.python.org/3/library/functions.html#min), and [max()](https://docs.python.org/3/library/functions.html#max) can be regarded as special cases.\n", + "\n", + "The generic way of reducing a sequence is to apply a function of *two* arguments on a rolling horizon: Its first argument is the reduction of the elements processed so far, and the second the next element to be reduced.\n", + "\n", + "For illustration, let's replicate [sum()](https://docs.python.org/3/library/functions.html#sum) as such a function, called `add()`. Its implementation only adds two numbers." + ] + }, + { + "cell_type": "code", + "execution_count": 252, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def add(sum_so_far, next_number):\n", + " \"\"\"Reduce a sequence by addition.\"\"\"\n", + " return sum_so_far + next_number" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Further, we create a *new* `map` object derived from `numbers` ..." + ] + }, + { + "cell_type": "code", + "execution_count": 253, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "evens_transformed = map(transform, filter(is_even, numbers))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... and loop over all *but* the first element it generates. That we capture separately as the initial `result` with the [next()](https://docs.python.org/3/library/functions.html#next) function. So, `map` objects must be *iterable* as we may loop over them.\n", + "\n", + "We know that `evens_transformed` generates *six* elements. That is why we see *five* growing `result` values resembling a [cumulative sum](http://mathworld.wolfram.com/CumulativeSum.html)." + ] + }, + { + "cell_type": "code", + "execution_count": 254, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "65 210 215 252 353 " + ] + } + ], + "source": [ + "result = next(evens_transformed) # first element is the initial value\n", + "\n", + "for number in evens_transformed: # iterate over the remaining elements\n", + " print(result, end=\" \") # line added for didactical purposes\n", + " result = add(result, number)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The final `result` is the same `370` as above." + ] + }, + { + "cell_type": "code", + "execution_count": 255, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "370" + ] + }, + "execution_count": 255, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "result" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [reduce()](https://docs.python.org/3/library/functools.html#functools.reduce) function in the [functools](https://docs.python.org/3/library/functools.html) module in the [standard library](https://docs.python.org/3/library/index.html) provides more convenience replacing the `for`-loop. It takes two arguments in the same way as the [map()](https://docs.python.org/3/library/functions.html#map) and [filter()](https://docs.python.org/3/library/functions.html#filter) built-ins.\n", + "\n", + "[reduce()](https://docs.python.org/3/library/functools.html#functools.reduce) is **[eager](https://en.wikipedia.org/wiki/Eager_evaluation)** meaning that all computations implied by the contained `map` and `filter` \"rules\" are executed right away, and the code cell returns `370`. On the contrary, [map()](https://docs.python.org/3/library/functions.html#map) and [filter()](https://docs.python.org/3/library/functions.html#filter) create **[lazy](https://en.wikipedia.org/wiki/Lazy_evaluation)** `map` and `filter` objects, and we have to use the [next()](https://docs.python.org/3/library/functions.html#next) function to obtain the elements." + ] + }, + { + "cell_type": "code", + "execution_count": 256, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "from functools import reduce" + ] + }, + { + "cell_type": "code", + "execution_count": 257, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "370" + ] + }, + "execution_count": 257, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reduce(add, map(transform, filter(is_even, numbers)))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Lambda Expressions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[map()](https://docs.python.org/3/library/functions.html#map), [filter()](https://docs.python.org/3/library/functions.html#filter), and [reduce()](https://docs.python.org/3/library/functools.html#functools.reduce) take a `function` object as their first argument, and we defined `transform()`, `is_even()`, and `add()` to be used precisely for that.\n", + "\n", + "Often, such functions are used *only once* in a program. However, the primary purpose of functions is to *re-use* them. In such cases, it makes more sense to define them \"anonymously\" right at the position where the first argument goes.\n", + "\n", + "As mentioned in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb#Anonymous-Functions), Python provides `lambda` expressions to create `function` objects *without* a variable pointing to them.\n", + "\n", + "So, the above `add()` function could be rewritten as a `lambda` expression like so ..." + ] + }, + { + "cell_type": "code", + "execution_count": 258, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(sum_so_far, next_number)>" + ] + }, + "execution_count": 258, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "lambda sum_so_far, next_number: sum_so_far + next_number" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... or even shorter." + ] + }, + { + "cell_type": "code", + "execution_count": 259, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(x, y)>" + ] + }, + "execution_count": 259, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "lambda x, y: x + y" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With the new concepts in this section, we can rewrite the entire example in just a few lines of code *without* any `for`, `if`, and `def` statements. The resulting code is concise, easy to read, quick to modify, and even faster in execution. Most importantly, it is optimized to handle big amounts of data as *no* temporary `list` objects are materialized in memory." + ] + }, + { + "cell_type": "code", + "execution_count": 260, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "370" + ] + }, + "execution_count": 260, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]\n", + "evens = filter(lambda x: x % 2 == 0, numbers)\n", + "transformed = map(lambda x: (x ** 2) + 1, evens)\n", + "sum(transformed)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If `numbers` comes as a sorted sequence of whole numbers, we may use the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in and get away *without any* `list` object in memory at all!" + ] + }, + { + "cell_type": "code", + "execution_count": 261, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "370" + ] + }, + "execution_count": 261, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers = range(1, 13)\n", + "evens = filter(lambda x: x % 2 == 0, numbers)\n", + "transformed = map(lambda x: (x ** 2) + 1, evens)\n", + "sum(transformed)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To additionally save the temporary variables, `numbers`, `evens`, and `transformed`, we write the entire computation as *one* expression." + ] + }, + { + "cell_type": "code", + "execution_count": 262, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "370" + ] + }, + "execution_count": 262, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(map(lambda x: (x ** 2) + 1, filter(lambda x: x % 2 == 0, range(1, 13))))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "PythonTutor visualizes the differences in the number of computational steps and memory usage:\n", + "- [Version 1](http://pythontutor.com/visualize.html#code=def%20is_even%28element%29%3A%0A%20%20%20%20if%20element%20%25%202%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%20True%0A%20%20%20%20return%20False%0A%0Adef%20transform%28element%29%3A%0A%20%20%20%20return%20%28element%20**%202%29%20%2B%201%0A%0Anumbers%20%3D%20list%28range%281,%2013%29%29%0A%0Aevens%20%3D%20%5B%5D%0Afor%20number%20in%20numbers%3A%0A%20%20%20%20if%20is_even%28number%29%3A%0A%20%20%20%20%20%20%20%20evens.append%28number%29%0A%0Atransformed%20%3D%20%5B%5D%0Afor%20number%20in%20evens%3A%0A%20%20%20%20transformed.append%28transform%28number%29%29%0A%0Aresult%20%3D%20sum%28transformed%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false): With `for`-loops, `if` statements, and named functions -> **116** steps\n", + "- [Version 2](http://pythontutor.com/visualize.html#code=numbers%20%3D%20range%281,%2013%29%0Aevens%20%3D%20filter%28lambda%20x%3A%20x%20%25%202%20%3D%3D%200,%20numbers%29%0Atransformed%20%3D%20map%28lambda%20x%3A%20%28x%20**%202%29%20%2B%201,%20evens%29%0Aresult%20%3D%20sum%28transformed%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false): With named `map` and `filter` objects -> **58** steps\n", + "- [Version 3](http://pythontutor.com/visualize.html#code=result%20%3D%20sum%28map%28lambda%20x%3A%20%28x%20**%202%29%20%2B%201,%20filter%28lambda%20x%3A%20x%20%25%202%20%3D%3D%200,%20range%281,%2013%29%29%29%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false): Everything in *one* expression -> **55** steps\n", + "\n", + "Versions 2 and 3 are the same, except for the three additional steps required to create the temporary variables. The *major* downside of Version 1 is that, in the worst case, it may need *three times* the memory as compared to the other two versions!\n", + "\n", + "An experienced Pythonista would probably go with Version 2 in a production system to keep the code readable and maintainable." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### List Comprehensions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For [map()](https://docs.python.org/3/library/functions.html#map) and [filter()](https://docs.python.org/3/library/functions.html#filter), Python provides a nice syntax appealing to people who like mathematics.\n", + "\n", + "Consider again the \"*A simple Filter*\" example from [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Example:-A-simple-Filter), written with combined `for` and `if` statements. So, the mapping and filtering steps happen simultaneously." + ] + }, + { + "cell_type": "code", + "execution_count": 263, + "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": 264, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "evens_transformed = []\n", + "\n", + "for number in numbers:\n", + " if number % 2 == 0:\n", + " evens_transformed.append((number ** 2) + 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 265, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[65, 145, 5, 37, 101, 17]" + ] + }, + "execution_count": 265, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "evens_transformed" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**List comprehensions**, or **list-comps** for short, are *expressions* to derive *new* `list` objects out of *existing* ones (cf., [reference](https://docs.python.org/3/reference/expressions.html#displays-for-lists-sets-and-dictionaries)). A single *expression* like below can replace the compound `for` *statement* from above." + ] + }, + { + "cell_type": "code", + "execution_count": 266, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[65, 145, 5, 37, 101, 17]" + ] + }, + "execution_count": 266, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[(n ** 2) + 1 for n in numbers if n % 2 == 0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A list comprehension may be used in place of any `list` object.\n", + "\n", + "For example, let's add up all the elements with [sum()](https://docs.python.org/3/library/functions.html#sum). The code below *materializes* all elements in memory *before* summing them up. So, this code might cause a `MemoryError` when executed with a bigger `numbers` list. [PythonTutor](http://pythontutor.com/visualize.html#code=numbers%20%3D%20range%281,%2013%29%0Aresult%20%3D%20sum%28%5B%28n%20**%202%29%20%2B%201%20for%20n%20in%20numbers%20if%20n%20%25%202%20%3D%3D%200%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how a `list` object exists in memory at step 17 and then \"gets lost\" right after. " + ] + }, + { + "cell_type": "code", + "execution_count": 267, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "370" + ] + }, + "execution_count": 267, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum([(n ** 2) + 1 for n in numbers if n % 2 == 0])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### Example: Nested Lists" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "List comprehensions may come with several `for`'s and `if`'s.\n", + "\n", + "The cell below creates a `list` object that contains other `list` objects with numbers in them. The starting number in each inner `list` object is offset by `1`." + ] + }, + { + "cell_type": "code", + "execution_count": 268, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "nested_numbers = [list(range(x, y + 1)) for x, y in zip([1, 2, 3], [7, 8, 9])]" + ] + }, + { + "cell_type": "code", + "execution_count": 269, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[[1, 2, 3, 4, 5, 6, 7], [2, 3, 4, 5, 6, 7, 8], [3, 4, 5, 6, 7, 8, 9]]" + ] + }, + "execution_count": 269, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested_numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To do something meaningful with the numbers, we have to get rid of the inner layer of `list` objects and flatten the data.\n", + "\n", + "Without list comprehensions, we would probably write two nested `for`-loops." + ] + }, + { + "cell_type": "code", + "execution_count": 270, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "flat_numbers = []\n", + "\n", + "for inner_numbers in nested_numbers:\n", + " for number in inner_numbers:\n", + " flat_numbers.append(number)" + ] + }, + { + "cell_type": "code", + "execution_count": 271, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 8, 9]" + ] + }, + "execution_count": 271, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "flat_numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "That translates into a list comprehension like below. The order of the `for`'s may be confusing at first but is the *same* as writing out the nested `for`-loops." + ] + }, + { + "cell_type": "code", + "execution_count": 272, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 8, 9]" + ] + }, + "execution_count": 272, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[number for inner_numbers in nested_numbers for number in inner_numbers]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Now, we may use the `list` object resulting from the list comprehension in any context we want.\n", + "\n", + "As an example, we add up the flattened numbers with [sum()](https://docs.python.org/3/library/functions.html#sum). The same caveat holds as before: The `list` object passed into [sum()](https://docs.python.org/3/library/functions.html#sum) is *materialized* before the sum is calculated!" + ] + }, + { + "cell_type": "code", + "execution_count": 273, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "105" + ] + }, + "execution_count": 273, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum([number for inner_numbers in nested_numbers for number in inner_numbers])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In this particular example, however, we can exploit the fact that any sum of numbers can be expressed as the sum of sums of mutually exclusive and collectively exhaustive subsets of these numbers and get away with just *one* `for` in the list comprehension." + ] + }, + { + "cell_type": "code", + "execution_count": 274, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "105" + ] + }, + "execution_count": 274, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum([sum(inner_numbers) for inner_numbers in nested_numbers])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### Example: Cartesian Products" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A popular use case of nested list comprehensions is applying a transformation to each $2$-tuple of the [Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product) of two iterables.\n", + "\n", + "For example, let's add `1` to each quotient obtained by taking the numerator from `[10, 20, 30]` and the denominator from `[40, 50, 60]`, and then find the product of all quotients. The table below visualizes the calculations: The result is the product of *nine* entries.\n", + "\n", + "||**10**|**20**|**30**|\n", + "|-|-|-|-|\n", + "|**40**|1.25|1.50|1.75|\n", + "|**50**|1.20|1.40|1.60|\n", + "|**60**|1.17|1.33|1.50|\n", + "\n", + "To express that in Python, we start by creating two `list` objects, `first` and `second`." + ] + }, + { + "cell_type": "code", + "execution_count": 275, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "first = [10, 20, 30]\n", + "second = [40, 50, 60]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For a Cartesian product, we loop over *all* possible $2$-tuples where one element is drawn from `first` and the other from `second`. That is equivalent to two nested `for`-loops." + ] + }, + { + "cell_type": "code", + "execution_count": 276, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1.25, 1.2, 1.1666666666666667, 1.5, 1.4, 1.3333333333333333, 1.75, 1.6, 1.5]" + ] + }, + "execution_count": 276, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cartesian_product = []\n", + "\n", + "for numerator in first:\n", + " for denominator in second:\n", + " quotient = numerator / denominator\n", + " cartesian_product.append(quotient + 1)\n", + "\n", + "cartesian_product" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We translate the two `for`-loops into one list comprehensions with two `for`'s in it and use `x` and `y` as shorter variable names." + ] + }, + { + "cell_type": "code", + "execution_count": 277, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1.25, 1.2, 1.1666666666666667, 1.5, 1.4, 1.3333333333333333, 1.75, 1.6, 1.5]" + ] + }, + "execution_count": 277, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[(x / y) + 1 for x in first for y in second]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The order of the `for`'s is *important*: The list comprehension above divides numbers from `first` by numbers from `second`, whereas the list comprehension below does the opposite." + ] + }, + { + "cell_type": "code", + "execution_count": 278, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[5.0, 3.0, 2.333333333333333, 6.0, 3.5, 2.666666666666667, 7.0, 4.0, 3.0]" + ] + }, + "execution_count": 278, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[(x / y) + 1 for x in second for y in first]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To find the overall product, we *unpack* the first list comprehension right into the `product()` function from the \"*Packing & Unpacking*\" section above." + ] + }, + { + "cell_type": "code", + "execution_count": 279, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "20.58" + ] + }, + "execution_count": 279, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "product(*[(x / y) + 1 for x in first for y in second])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Alternatively, we use a `lambda` expression with the [reduce()](https://docs.python.org/3/library/functools.html#functools.reduce) function from the [functools](https://docs.python.org/3/library/functools.html) module." + ] + }, + { + "cell_type": "code", + "execution_count": 280, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "20.58" + ] + }, + "execution_count": 280, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reduce(lambda x, y: x * y, [(x / y) + 1 for x in first for y in second])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While this example is stylized, Cartesian products are hidden in many applications, and it shows how the various language features introduced in this chapter can be seamlessly combined to process sequential data." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Generator Expressions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Pythonistas would forgo materialized `list` objects, and, thus, also list comprehensions, all together, and use a more memory-efficient approach with **[generator expressions](https://docs.python.org/3/reference/expressions.html#generator-expressions)**, or **genexps** for short. Syntactically, they work like list comprehensions except that parentheses replace the brackets.\n", + "\n", + "Let's go back to the original example in this section and find the transformation $y := x^2 + 1$ of all even elements in `numbers`." + ] + }, + { + "cell_type": "code", + "execution_count": 281, + "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": [ + "To filter and transform `numbers`, we wrote this list comprehension above ..." + ] + }, + { + "cell_type": "code", + "execution_count": 282, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[65, 145, 5, 37, 101, 17]" + ] + }, + "execution_count": 282, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[(n ** 2) + 1 for n in numbers if n % 2 == 0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... that now becomes a generator expression." + ] + }, + { + "cell_type": "code", + "execution_count": 283, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + " at 0x7f9f447618b8>" + ] + }, + "execution_count": 283, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "((n ** 2) + 1 for n in numbers if n % 2 == 0)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We can think of it as yet another \"rule\" in memory that knows how to generate the individual objects in a series one by one. Whereas a list comprehension materializes its elements in memory *when* it is evaluated, the opposite holds for generator expressions, and *no* object is created in memory except the \"rule\" itself. Because of this behavior, we describe generator expressions as *lazy* and list comprehensions as *eager*.\n", + "\n", + "So, to materialize all elements specified by a generator expression, we revert to the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in." + ] + }, + { + "cell_type": "code", + "execution_count": 284, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[65, 145, 5, 37, 101, 17]" + ] + }, + "execution_count": 284, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(((n ** 2) + 1 for n in numbers if n % 2 == 0))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Whenever a generator expression is the only argument to a function, we may leave out the parentheses." + ] + }, + { + "cell_type": "code", + "execution_count": 285, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[65, 145, 5, 37, 101, 17]" + ] + }, + "execution_count": 285, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list((n ** 2) + 1 for n in numbers if n % 2 == 0)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A common use case is to reduce the elements into a single object instead, for example, by adding them up with [sum()](https://docs.python.org/3/library/functions.html#sum). [PythonTutor](http://pythontutor.com/visualize.html#code=numbers%20%3D%20range%281,%2013%29%0Asum_with_list%20%3D%20sum%28%5B%28n%20**%202%29%20%2B%201%20for%20n%20in%20numbers%20if%20n%20%25%202%20%3D%3D%200%5D%29%0Asum_with_gen%20%3D%20sum%28%28n%20**%202%29%20%2B%201%20for%20n%20in%20numbers%20if%20n%20%25%202%20%3D%3D%200%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how the code cell below does *not* create a temporary `list` object in memory, whereas a list comprehension would (cf., step 17)." + ] + }, + { + "cell_type": "code", + "execution_count": 286, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "370" + ] + }, + "execution_count": 286, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum((n ** 2) + 1 for n in numbers if n % 2 == 0)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's assign the object returned from a generator expression to a variable and inspect it." + ] + }, + { + "cell_type": "code", + "execution_count": 287, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "gen = ((n ** 2) + 1 for n in numbers if n % 2 == 0)" + ] + }, + { + "cell_type": "code", + "execution_count": 288, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + " at 0x7f9f44761d68>" + ] + }, + "execution_count": 288, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gen" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Unsurprisingly, generator expressions create objects of type `generator`." + ] + }, + { + "cell_type": "code", + "execution_count": 289, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "generator" + ] + }, + "execution_count": 289, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(gen)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With the [next()](https://docs.python.org/3/library/functions.html#next) function, we can retrieve the generated elements one by one." + ] + }, + { + "cell_type": "code", + "execution_count": 290, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "65" + ] + }, + "execution_count": 290, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(gen)" + ] + }, + { + "cell_type": "code", + "execution_count": 291, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "145" + ] + }, + "execution_count": 291, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(gen)" + ] + }, + { + "cell_type": "code", + "execution_count": 292, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "5" + ] + }, + "execution_count": 292, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(gen)" + ] + }, + { + "cell_type": "code", + "execution_count": 293, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "37" + ] + }, + "execution_count": 293, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(gen)" + ] + }, + { + "cell_type": "code", + "execution_count": 294, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "101" + ] + }, + "execution_count": 294, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(gen)" + ] + }, + { + "cell_type": "code", + "execution_count": 295, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "17" + ] + }, + "execution_count": 295, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(gen)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Once a `generator` object runs out of elements, it raises a `StopIteration` exception. We say that the `generator` object is **exhausted**, and to loop over its elements again, we must create a *new* one." + ] + }, + { + "cell_type": "code", + "execution_count": 296, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "StopIteration", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mStopIteration\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgen\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mStopIteration\u001b[0m: " + ] + } + ], + "source": [ + "next(gen)" + ] + }, + { + "cell_type": "code", + "execution_count": 297, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "StopIteration", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mStopIteration\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgen\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mStopIteration\u001b[0m: " + ] + } + ], + "source": [ + "next(gen)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Calling the [next()](https://docs.python.org/3/library/functions.html#next) function repeatedly with the *same* `generator` object as the argument is essentially what a `for`-loop automates for us. So, `generator` objects are *iterable*." + ] + }, + { + "cell_type": "code", + "execution_count": 298, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "65 145 5 37 101 17 " + ] + } + ], + "source": [ + "for number in ((n ** 2) + 1 for n in numbers if n % 2 == 0):\n", + " print(number, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### Example: Nested Lists (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we are only interested in a *reduction* of `nested_numbers` into a single statistic, as the overall sum in the \"*Nested Lists*\" example, we should replace lists or list comprehensions with generator expressions wherever possible.\n", + "\n", + "The result is the *same*, but no intermediate lists are materialized! That makes our code scale to larger amounts of data and uses the available hardware more efficiently.\n", + "\n", + "Let's adapt the example but keep `nested_numbers` unchanged for now." + ] + }, + { + "cell_type": "code", + "execution_count": 299, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "nested_numbers = [list(range(x, y + 1)) for x, y in zip([1, 2, 3], [7, 8, 9])]" + ] + }, + { + "cell_type": "code", + "execution_count": 300, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[[1, 2, 3, 4, 5, 6, 7], [2, 3, 4, 5, 6, 7, 8], [3, 4, 5, 6, 7, 8, 9]]" + ] + }, + "execution_count": 300, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested_numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We leave out the brackets and keep everything else as-is: The argument to [sum()](https://docs.python.org/3/library/functions.html#sum), a list comprehension in the initial implementation above, becomes a generator expression." + ] + }, + { + "cell_type": "code", + "execution_count": 301, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "105" + ] + }, + "execution_count": 301, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(number for inner_numbers in nested_numbers for number in inner_numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "That also holds for the alternative formulation as a sum of sums." + ] + }, + { + "cell_type": "code", + "execution_count": 302, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "105" + ] + }, + "execution_count": 302, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(sum(inner_numbers) for inner_numbers in nested_numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Because `nested_numbers` has an internal structure, we can make it **memoryless** by expressing it as a generator expression derived from `range` objects. [PythonTutor](http://pythontutor.com/visualize.html#code=nested_numbers%20%3D%20%28%28range%28x,%20y%20%2B%201%29%29%20for%20x,%20y%20in%20zip%28range%281,%204%29,%20range%287,%2010%29%29%29%0Aresult%20%3D%20sum%28number%20for%20inner_numbers%20in%20nested_numbers%20for%20number%20in%20inner_numbers%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) confirms that no `list` object materializes at any point in time." + ] + }, + { + "cell_type": "code", + "execution_count": 303, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "nested_numbers = ((range(x, y + 1)) for x, y in zip(range(1, 4), range(7, 10)))" + ] + }, + { + "cell_type": "code", + "execution_count": 304, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + " at 0x7f9f44761e58>" + ] + }, + "execution_count": 304, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested_numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 305, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "105" + ] + }, + "execution_count": 305, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(number for inner_numbers in nested_numbers for number in inner_numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We must be careful when assigning a `generator` object to a variable: If we use `nested_numbers` again, for example, in the alternative formulation below, [sum()](https://docs.python.org/3/library/functions.html#sum) returns `0` because `nested_numbers` is exhausted after executing the previous code cell. [PythonTutor](http://pythontutor.com/visualize.html#code=nested_numbers%20%3D%20%28%28range%28x,%20y%20%2B%201%29%29%20for%20x,%20y%20in%20zip%28range%281,%204%29,%20range%287,%2010%29%29%29%0Aresult%20%3D%20sum%28number%20for%20inner_numbers%20in%20nested_numbers%20for%20number%20in%20inner_numbers%29%0Ano_result%20%3D%20sum%28sum%28inner_numbers%29%20for%20inner_numbers%20in%20nested_numbers%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) also shows that." + ] + }, + { + "cell_type": "code", + "execution_count": 306, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 306, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(sum(inner_numbers) for inner_numbers in nested_numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### Example: Cartesian Products (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's also rewrite the \"*Cartesian Products*\" example from above with generator expressions.\n", + "\n", + "As a first optimization, we replace the materialized `list` objects, `first` and `second`, with memoryless `range` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 307, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "first = range(10, 31, 10) # = [10, 20, 30]\n", + "second = range(40, 61, 10) # = [40, 50, 60]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Now, the first of the two alternatives may be more appealing to many readers. In general, many practitioners seem to dislike `lambda` expressions.\n", + "\n", + "The code cell below *unpacks* the elements produced by `((x / y) + 1 for x in first for y in second)` into the `product()` function from the \"*Packing & Unpacking*\" section above. However, inside `product()`, the elements are *packed* into `args`, a materialized `tuple` object. So, all the memory efficiency gained with the generator expression is voided! [PythonTutor](http://pythontutor.com/visualize.html#code=def%20product%28*args%29%3A%0A%20%20%20%20result%20%3D%20args%5B0%5D%0A%20%20%20%20for%20arg%20in%20args%5B1%3A%5D%3A%0A%20%20%20%20%20%20%20%20result%20*%3D%20arg%0A%20%20%20%20return%20result%0A%0Afirst%20%3D%20range%2810,%2031,%2010%29%0Asecond%20%3D%20range%2840,%2061,%2010%29%0A%0Aresult%20%3D%20product%28*%28%28x%20/%20y%29%20%2B%201%20for%20x%20in%20first%20for%20y%20in%20second%29%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how a `tuple` object exists in steps 38-58." + ] + }, + { + "cell_type": "code", + "execution_count": 308, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "20.58" + ] + }, + "execution_count": 308, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "product(*((x / y) + 1 for x in first for y in second))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "On the contrary, the solution with the [reduce()](https://docs.python.org/3/library/functools.html#functools.reduce) function from the [functools](https://docs.python.org/3/library/functools.html) module and the `lambda` expression works *without* all elements materialized at the same time, and [PythonTutor](http://pythontutor.com/visualize.html#code=from%20functools%20import%20reduce%0A%0Afirst%20%3D%20range%2810,%2031,%2010%29%0Asecond%20%3D%20range%2840,%2061,%2010%29%0A%0Aresult%20%3D%20reduce%28%0A%20%20%20%20lambda%20x,%20y%3A%20x%20*%20y,%0A%20%20%20%20%28%28x%20/%20y%29%20%2B%201%20for%20x%20in%20first%20for%20y%20in%20second%29%0A%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) confirms that. So, only the second alternative is truly memory-efficient." + ] + }, + { + "cell_type": "code", + "execution_count": 309, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "20.58" + ] + }, + "execution_count": 309, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reduce(lambda x, y: x * y, ((x / y) + 1 for x in first for y in second))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In summary, we learn from this example that unpacking generator expressions may be a *bad* idea." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### Tuple Comprehensions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "There is no syntax to derive *new* `tuple` objects out of existing ones. However, we can mimic such a construct by combining the [tuple()](https://docs.python.org/3/library/functions.html#func-tuple) built-in with a generator expression.\n", + "\n", + "So, to convert the list comprehension `[(n ** 2) + 1 for n in numbers if n % 2 == 0]` from above into a \"tuple comprehension,\" we write the following." + ] + }, + { + "cell_type": "code", + "execution_count": 310, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(65, 145, 5, 37, 101, 17)" + ] + }, + "execution_count": 310, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tuple((n ** 2) + 1 for n in numbers if n % 2 == 0)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Boolean Reducers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Besides [min()](https://docs.python.org/3/library/functions.html#min), [max()](https://docs.python.org/3/library/functions.html#max), and [sum()](https://docs.python.org/3/library/functions.html#sum), Python provides two boolean reduce functions: [all()](https://docs.python.org/3/library/functions.html#all) and [any()](https://docs.python.org/3/library/functions.html#any).\n", + "\n", + "Let's look at straightforward examples involving `numbers` again." + ] + }, + { + "cell_type": "code", + "execution_count": 311, + "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": [ + "[all()](https://docs.python.org/3/library/functions.html#all) takes an *iterable* argument and returns `True` if *all* elements are *truthy*.\n", + "\n", + "For example, let's check if the square of each element in `numbers` is below `100` or `150`, respectively. We express the computation with a generator expression passed as the only argument to [all()](https://docs.python.org/3/library/functions.html#all)." + ] + }, + { + "cell_type": "code", + "execution_count": 312, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 312, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "all(x ** 2 < 100 for x in numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 313, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 313, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "all(x ** 2 < 150 for x in numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[all()](https://docs.python.org/3/library/functions.html#all) can be viewed as syntactic sugar replacing a `for`-loop: Internally, [all()](https://docs.python.org/3/library/functions.html#all) implements the *short-circuiting* strategy from [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb#Short-Circuiting), and we mimic that by testing for the *opposite* condition in the `if` statement and leaving the `for`-loop early with the `break` statement. In the worst case, if `threshold` were, for example, `150`, we would loop over *all* elements in the *iterable*, which must be *finite* for the code to work. So, [all()](https://docs.python.org/3/library/functions.html#all) is a *linear search* in disguise." + ] + }, + { + "cell_type": "code", + "execution_count": 314, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 314, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "threshold = 100\n", + "\n", + "for number in numbers:\n", + " if number ** 2 >= threshold: # = the opposite of what we are checking for\n", + " all_below_threshold = False\n", + " break\n", + "else:\n", + " all_below_threshold = True\n", + "\n", + "all_below_threshold" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The documentation of [all()](https://docs.python.org/3/library/functions.html#all) shows in another way what it does with code: By placing a `return` statement inside the `for`-loop of a function's body, iteration is stopped prematurely once an element does *not* meet the condition. That is the familiar *early exit* pattern at work." + ] + }, + { + "cell_type": "code", + "execution_count": 315, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "def all_alt(iterable):\n", + " \"\"\"Alternative implementation of the built-in all() function.\"\"\"\n", + " for element in iterable:\n", + " if not element: # = the opposite of what we are checking for\n", + " return False\n", + " return True" + ] + }, + { + "cell_type": "code", + "execution_count": 316, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 316, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "all_alt(x ** 2 < 100 for x in numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 317, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 317, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "all_alt(x ** 2 < 150 for x in numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Similarly, [any()](https://docs.python.org/3/library/functions.html#any) checks if *at least* one element in the *iterable* argument is *truthy*.\n", + "\n", + "To continue the example, let's check if the square of *any* element in `numbers` is above `100` or `150`, respectively." + ] + }, + { + "cell_type": "code", + "execution_count": 318, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 318, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "any(x ** 2 > 100 for x in numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 319, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 319, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "any(x ** 2 > 150 for x in numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Expressed with a `for`-loop, the implementation below reveals that [any()](https://docs.python.org/3/library/functions.html#any) follows the *short-circuiting* strategy as well. Here, we do *not* need to check for the opposite condition." + ] + }, + { + "cell_type": "code", + "execution_count": 320, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 320, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "threshold = 100\n", + "\n", + "for number in numbers:\n", + " if number ** 2 > threshold:\n", + " any_above_threshold = True\n", + " break\n", + "else:\n", + " any_above_threshold = False\n", + "\n", + "any_above_threshold" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The alternative formulation in the documentation of [any()](https://docs.python.org/3/library/functions.html#any) is straightforward." + ] + }, + { + "cell_type": "code", + "execution_count": 321, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "def any_alt(iterable):\n", + " \"\"\"Alternative implementation of the built-in any() function.\"\"\"\n", + " for element in iterable:\n", + " if element:\n", + " return True\n", + " return False" + ] + }, + { + "cell_type": "code", + "execution_count": 322, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 322, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "any_alt(x ** 2 > 100 for x in numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 323, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 323, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "any_alt(x ** 2 > 150 for x in numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### Example: Averaging Even Numbers (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With the new concepts in this chapter, let's rewrite the book's introductory \"*Averaging Even Numbers*\" example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Example:-Averaging-Even-Numbers) such that it efficiently handles a large sequence of numbers.\n", + "\n", + "We assume the `average_evens()` function below is called with a *finite* and *iterable* object, which generates a \"stream\" of numeric objects that can be cast as `int` objects because the idea of even and odd numbers only makes sense in the context of whole numbers.\n", + "\n", + "The generator expression `(int(n) for n in numbers)` implements the type casting, and when it is evaluated, *nothing* happens except that a `generator` object is stored in `integers`. Then, with the [reduce()](https://docs.python.org/3/library/functools.html#functools.reduce) function from the [functools](https://docs.python.org/3/library/functools.html) module, we *simultaneously* add up *and* count the even numbers produced by the inner generator expression `((n, 1) for n in integers if n % 2 == 0)`. That results in a `generator` object producing `tuple` objects consisting of the next *even* number in line and `1`. Two such `tuple` objects are then iteratively passed to the `lambda` expression as the `x` and `y` arguments. `x` represents the total and the count of the even numbers processed so far, while `y`'s first element, `y[0]`, is the next even number to be added to the running total. The result of the [reduce()](https://docs.python.org/3/library/functools.html#functools.reduce) function is again a `tuple` object, namely the final `total` and `count`. Lastly, we calculate the simple average.\n", + "\n", + "In summary, the implementation of `average_evens()` does *not* keep materialized `list` objects internally like its predecessors from [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb), but processes the elements of the `numbers` argument on a one-by-one basis." + ] + }, + { + "cell_type": "code", + "execution_count": 324, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "def average_evens(numbers):\n", + " \"\"\"Calculate the average of all even integers.\n", + "\n", + " Args:\n", + " numbers (iterable): a finite stream of numbers;\n", + " may be integers or floats; floats are truncated\n", + "\n", + " Returns:\n", + " float: average\n", + " \"\"\"\n", + " integers = (int(n) for n in numbers)\n", + " total, count = reduce(\n", + " lambda x, y: (x[0] + y[0], x[1] + y[1]),\n", + " ((n, 1) for n in integers if n % 2 == 0)\n", + " )\n", + " return total / count" + ] + }, + { + "cell_type": "code", + "execution_count": 325, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7.0" + ] + }, + "execution_count": 325, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens([7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "An argument generating `float` objects works as well." + ] + }, + { + "cell_type": "code", + "execution_count": 326, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7.0" + ] + }, + "execution_count": 326, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens([7., 11., 8., 5., 3., 12., 2., 6., 9., 10., 1., 4.])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To show that `average_evens()` can process a **stream** of data, we simulate `10_000_000` randomly drawn integers between `0` and `100` with the [randint()](https://docs.python.org/3/library/random.html#random.randint) function from the [random](https://docs.python.org/3/library/random.html) module. We use a generator expression derived from a `range` object as the `numbers` argument. So, at *no* point in time is there a materialized `list` or `tuple` object in memory. The result approaching `50` tells us that [randint()](https://docs.python.org/3/library/random.html#random.randint) must be based on a uniform distribution." + ] + }, + { + "cell_type": "code", + "execution_count": 327, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import random" + ] + }, + { + "cell_type": "code", + "execution_count": 328, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 329, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "49.994081434519636" + ] + }, + "execution_count": 329, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens(random.randint(0, 100) for _ in range(10_000_000))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To show that `average_evens()` filters out odd numbers, we simulate another stream of `10_000_000` randomly drawn odd integers between `1` and `99`. As no function in the [random](https://docs.python.org/3/library/random.html) module does that \"out of the box,\" we must be creative: Doubling a number drawn from `random.randint(0, 49)` results in an even number between `0` and `98`, and adding `1` makes it odd. Then, `average_evens()` raises a `TypeError`, essentially because `(int(n) for n in numbers)` does not generate any element." + ] + }, + { + "cell_type": "code", + "execution_count": 330, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "reduce() of empty sequence with no initial value", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0maverage_evens\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m2\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mrandom\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrandint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m49\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0m_\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m10_000_000\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36maverage_evens\u001b[0;34m(numbers)\u001b[0m\n\u001b[1;32m 12\u001b[0m total, count = reduce(\n\u001b[1;32m 13\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 14\u001b[0;31m \u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mn\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mintegers\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0;36m2\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 15\u001b[0m )\n\u001b[1;32m 16\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mtotal\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mcount\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: reduce() of empty sequence with no initial value" + ] + } + ], + "source": [ + "average_evens(2 * random.randint(0, 49) + 1 for _ in range(10_000_000))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Iterators vs. Iterables" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In the \"*Collections vs. Sequences*\" section above, we studied the three and four *behaviors* of collections and sequences. The latter two are *abstract* ideas, and we mainly use them to classify *concrete* data types.\n", + "\n", + "Similarly, we have introduced data types in this chapter that all share the \"behavior\" of modeling some \"rule\" in memory to generate objects \"on the fly:\" They are the `map`, `filter`, and `generator` types. Their main commonality is supporting the built-in [next()](https://docs.python.org/3/library/functions.html#next) function. In computer science terminology, such data types are called **[iterators](https://en.wikipedia.org/wiki/Iterator)**, and the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module formalizes them with the `Iterator` ABC.\n", + "\n", + "So, an example of an iterator is `evens_transformed` below, an object of type `map`." + ] + }, + { + "cell_type": "code", + "execution_count": 331, + "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": 332, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "evens_transformed = map(lambda x: (x ** 2) + 1, filter(lambda x: x % 2 == 0, numbers))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's first confirm that `evens_transformed` is indeed an `Iterator`, \"abstractly speaking.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 333, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 333, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(evens_transformed, abc.Iterator)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In Python, iterators are *always* also iterables. The reverse does *not* hold! To be precise, iterators are *specializations* of iterables. That is what the \"Inherits from\" columns means in the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module's documentation." + ] + }, + { + "cell_type": "code", + "execution_count": 334, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 334, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(evens_transformed, abc.Iterable)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Furthermore, we revise our definition of *iterables* from above: Just as we defined an *iterator* to be an object that supports the [next()](https://docs.python.org/3/library/functions.html#next) function, we define an *iterable* to be an object that supports the built-in [iter()](https://docs.python.org/3/library/functions.html#iter) function.\n", + "\n", + "The confused reader may now be wondering how the two concepts relate to each other.\n", + "\n", + "In short, the [iter()](https://docs.python.org/3/library/functions.html#iter) function is the general way to create an *iterator* object out of a given *iterable* object. In real-world code, we hardly ever see [iter()](https://docs.python.org/3/library/functions.html#iter) as Python calls it for us in the background. Then, the *iterator* object manages the iteration over the *iterable* object.\n", + "\n", + "For illustration, let's do that ourselves and create *two* iterators out of the iterable `numbers` and see what we can do with them." + ] + }, + { + "cell_type": "code", + "execution_count": 335, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "iterator1 = iter(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 336, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "iterator2 = iter(numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`iterator1` and `iterator2` are of type `list_iterator`." + ] + }, + { + "cell_type": "code", + "execution_count": 337, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "list_iterator" + ] + }, + "execution_count": 337, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(iterator1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "*Iterators* are useful for only *one* operation: Get the next object from the associated *iterable*.\n", + "\n", + "By calling [next()](https://docs.python.org/3/library/functions.html#next) three times with `iterator1` as the argument, we obtain the first three elements of `numbers`." + ] + }, + { + "cell_type": "code", + "execution_count": 338, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(7, 11, 8)" + ] + }, + "execution_count": 338, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(iterator1), next(iterator1), next(iterator1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`iterator1` and `iterator2` keep their *states* separate. So, we could \"manually\" loop over an *iterable* in parallel." + ] + }, + { + "cell_type": "code", + "execution_count": 339, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(5, 7)" + ] + }, + "execution_count": 339, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(iterator1), next(iterator2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We can also play a \"trick\" and exchange some elements in `numbers`. `iterator1` and `iterator2` do *not* see these changes and present us with the new elements. So, *iterators* not only have state on their own but also keep this separate from the underlying *iterable*." + ] + }, + { + "cell_type": "code", + "execution_count": 340, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers[1], numbers[4] = 99, 99" + ] + }, + { + "cell_type": "code", + "execution_count": 341, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(99, 99)" + ] + }, + "execution_count": 341, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(iterator1), next(iterator2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's re-assign the elements in `numbers` so that they are in order. Now, the numbers returned from [next()](https://docs.python.org/3/library/functions.html#next) also tell us how often [next()](https://docs.python.org/3/library/functions.html#next) was called with `iterator1` or `iterator2`. We conclude that `list_iterator` objects must be keeping track of the *last* index obtained from the underlying *iterable*." + ] + }, + { + "cell_type": "code", + "execution_count": 342, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "numbers[:] = list(range(1, 13))" + ] + }, + { + "cell_type": "code", + "execution_count": 343, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" + ] + }, + "execution_count": 343, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 344, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(6, 3)" + ] + }, + "execution_count": 344, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(iterator1), next(iterator2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With the concepts introduced in this section, we can now understand the first sentence in the documentation on the [zip()](https://docs.python.org/3/library/functions.html#zip) built-in better: \"Make an *iterator* that aggregates elements from each of the *iterables*.\"\n", + "\n", + "Because *iterators* are always also *iterables*, we pass `iterator1` and `iterator2` as arguments to [zip()](https://docs.python.org/3/library/functions.html#zip).\n", + "\n", + "The returned `zipper` object is of type `zip` and, \"abstractly speaking,\" an `Iterator` as well." + ] + }, + { + "cell_type": "code", + "execution_count": 345, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "zipper = zip(iterator1, iterator2)" + ] + }, + { + "cell_type": "code", + "execution_count": 346, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 346, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "zipper" + ] + }, + { + "cell_type": "code", + "execution_count": 347, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "zip" + ] + }, + "execution_count": 347, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(zipper)" + ] + }, + { + "cell_type": "code", + "execution_count": 348, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 348, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(zipper, abc.Iterator)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "So far, we have always used [zip()](https://docs.python.org/3/library/functions.html#zip) with a `for` statement and looped over it. That was our earlier definition of an *iterable*. Our revised definition in this section states that an *iterable* is an object that supports the [iter()](https://docs.python.org/3/library/functions.html#iter) function. So, let's see what happens if we pass `zipper` to [iter()](https://docs.python.org/3/library/functions.html#iter)." + ] + }, + { + "cell_type": "code", + "execution_count": 349, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "zipper_iterator = iter(zipper)" + ] + }, + { + "cell_type": "code", + "execution_count": 350, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 350, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "zipper_iterator" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`zipper_iterator` points to the *same* object as `zipper`! That holds for *iterators* in general: Any *iterator* created from an existing *iterator* with [iter()](https://docs.python.org/3/library/functions.html#iter) is the *iterator* itself." + ] + }, + { + "cell_type": "code", + "execution_count": 351, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 351, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "zipper is zipper_iterator" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The Python core developers made that design decision so that *iterators* may also be looped over.\n", + "\n", + "The `for`-loop below prints out *six* more `tuple` objects derived from the re-ordered `numbers` because the `iterator1` object hidden inside `zipper` already returned the first *six* elements. So, the respective first elements of the `tuple` objects printed range from `7` to `12`. Similarly, as `iterator2` already provided *three* elements from `numbers`, we see the respective second elements in the range from `4` to `9`." + ] + }, + { + "cell_type": "code", + "execution_count": 352, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "7 > 4 8 > 5 9 > 6 10 > 7 11 > 8 12 > 9 " + ] + } + ], + "source": [ + "for x, y in zipper:\n", + " print(x, \">\", y, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`zipper` is now *exhausted*. So, the `for`-loop below does *not* make any iteration at all." + ] + }, + { + "cell_type": "code", + "execution_count": 353, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "for x, y in zipper:\n", + " print(x, \">\", y, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We verify that `iterator1` is exhausted by passing it to [next()](https://docs.python.org/3/library/functions.html#next) again, which raises a `StopIteration` exception." + ] + }, + { + "cell_type": "code", + "execution_count": 354, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "StopIteration", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mStopIteration\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0miterator1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mStopIteration\u001b[0m: " + ] + } + ], + "source": [ + "next(iterator1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "On the contrary, `iterator2` is *not* yet exhausted." + ] + }, + { + "cell_type": "code", + "execution_count": 355, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10" + ] + }, + "execution_count": 355, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(iterator2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### The `for` Statement (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#The-for-Statement), we argue that the `for` statement is syntactic sugar, replacing the `while` statement in many scenarios. In particular, a `for`-loop saves us two tasks: Managing an index variable *and* obtaining the individual elements by indexing. In this sub-section, we look at a more realistic picture, using the new terminology as well.\n", + "\n", + "Let's print out the elements of a `list` object as the *iterable* to be looped over." + ] + }, + { + "cell_type": "code", + "execution_count": 356, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "iterable = [0, 1, 2, 3, 4]" + ] + }, + { + "cell_type": "code", + "execution_count": 357, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 1 2 3 4 " + ] + } + ], + "source": [ + "for element in iterable:\n", + " print(element, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Our previous and equivalent formulation with a `while` statement is like so." + ] + }, + { + "cell_type": "code", + "execution_count": 358, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 1 2 3 4 " + ] + } + ], + "source": [ + "index = 0\n", + "while index < len(iterable):\n", + " element = iterable[index]\n", + " print(element, end=\" \")\n", + " index += 1\n", + "del index" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "What happens behind the scenes in the Python interpreter is shown below.\n", + "\n", + "First, Python calls [iter()](https://docs.python.org/3/library/functions.html#iter) with the `iterable` to be looped over, and obtains an `iterator`. That contains the entire logic of how the `iterable` is looped over. In particular, the `iterator` may or may not pick the `iterable`'s elements in a predictable order. That is up to the \"rule\" it models.\n", + "\n", + "Second, Python enters an *indefinite* `while`-loop. It tries to obtain the next element with [next()](https://docs.python.org/3/library/functions.html#next). If that succeeds, the `for`-loop's code block is executed. Below, that code is placed within the `else`-clause that runs only if *no* exception is raised in the `try`-clause. Then, Python jumps into the next iteration and tries to obtain the next element from the `iterator`, and so on. Once the `iterator` is exhausted, it raises a `StopIteration` exception, and Python leaves the `while`-loop with the `break` statement." + ] + }, + { + "cell_type": "code", + "execution_count": 359, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 1 2 3 4 " + ] + } + ], + "source": [ + "iterator = iter(iterable)\n", + "\n", + "while True:\n", + " try:\n", + " element = next(iterator)\n", + " except StopIteration:\n", + " break\n", + " else:\n", + " print(element, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Understanding *iterators* and *iterables* is helpful for any data science practitioner that deals with large amounts of data. Even without that, these two terms occur everywhere in Python-related texts and documentation." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## TL;DR" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Sequences** are an abstract idea that summarizes *four* behaviors an object may or may not exhibit: We describe them as **finite** and **ordered** **containers** that we may **loop over**. Examples are objects of type `list`, `tuple`, but also `str`. Objects that exhibit all behaviors *except* being ordered are referred to as **collections**. The objects inside a sequence are labeled with a unique *index*, an `int` object in the range $0 \\leq \\text{index} < \\lvert \\text{sequence} \\rvert$, and called its **elements**.\n", + "\n", + "`list` objects are **mutable**. That means we can change the references to other objects it contains, and, in particular, re-assign them. 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* mutate it.\n", + "\n", + "Often, the work we do with sequences follows 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* work with all the data **materialized** in memory. Instead, **iterators** allow us to process sequential data on a one-by-one basis." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "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": 2 +} From 8fd14a614de167abda7d92d5e450c258d5a220a0 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 6 Nov 2019 11:10:29 +0100 Subject: [PATCH 042/242] Streamline previous content --- 01_elements.ipynb | 402 ++++++++++++++++++++++-------------------- 02_functions.ipynb | 118 ++++++------- 03_conditionals.ipynb | 59 +++---- 04_iteration.ipynb | 184 +++++++++---------- 05_numbers.ipynb | 34 ++-- 06_text.ipynb | 20 +-- 6 files changed, 413 insertions(+), 404 deletions(-) diff --git a/01_elements.ipynb b/01_elements.ipynb index f9c6131..fed6458 100644 --- a/01_elements.ipynb +++ b/01_elements.ipynb @@ -35,7 +35,7 @@ } }, "source": [ - "## Example: Average of a Subset of Numbers" + "## Example: Averaging Even Numbers" ] }, { @@ -46,11 +46,11 @@ } }, "source": [ - "As our introductory example, we want to calculate the *average* of all *even* numbers from `1` through `10`.\n", + "As our introductory example, we want to calculate the *average* of all *evens* in a **list** of numbers: `[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]`.\n", "\n", - "While we could come up with an [analytical solution](https://math.stackexchange.com/questions/935405/what-s-the-difference-between-analytical-and-numerical-approaches-to-problems/935446#935446) (i.e., derive some equation with \"pen and paper\" from, e.g., one of [Faulhaber's formulas](https://en.wikipedia.org/wiki/Faulhaber%27s_formula)), we instead solve the task programmatically.\n", + "While we could try to develop an [analytical solution](https://math.stackexchange.com/questions/935405/what-s-the-difference-between-analytical-and-numerical-approaches-to-problems/935446#935446) (i.e., derive some equation with \"pen and paper\"), we solve the task programmatically instead.\n", "\n", - "We start by creating a **list** called `numbers` that holds all the individual numbers between **brackets** `[` and `]`." + "We start by creating a list called `numbers` that holds all the individual numbers between **brackets** `[` and `]`." ] }, { @@ -64,7 +64,7 @@ }, "outputs": [], "source": [ - "numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" + "numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" ] }, { @@ -90,7 +90,7 @@ { "data": { "text/plain": [ - "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" + "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" ] }, "execution_count": 2, @@ -116,7 +116,7 @@ "\n", "The `if number % 2 == 0` may look disturbing at first sight. Both the `%` and `==` must have an unintuitive meaning here. Luckily, the **comment** in the same line after the `#` symbol has the answer: The program only does something if the current `number` is even.\n", "\n", - "In particular, it increases `count` by $1$ and adds the current `number` onto the [running](https://en.wikipedia.org/wiki/Running_total) `total`. Both `count` and `number` are initially set to $0$ and the single `=` symbol reads as \"... is *set* equal to ...\". It could not indicate a mathematical equation as, for example, `count` is generally not equal to `count + 1`.\n", + "In particular, it increases `count` by `1` and adds the current `number` onto the [running](https://en.wikipedia.org/wiki/Running_total) `total`. Both `count` and `number` are initially set to `0` and the single `=` symbol reads as \"... is *set* equal to ...\". It could not indicate a mathematical equation as, for example, `count` is generally not equal to `count + 1`.\n", "\n", "Lastly, the `average` is calculated as the ratio of the final **values** of `total` and `count`. Overall, we divide the sum of all even numbers by their count: This is nothing but the definition of an average.\n", "\n", @@ -167,7 +167,7 @@ { "data": { "text/plain": [ - "6.0" + "7.0" ] }, "execution_count": 4, @@ -427,7 +427,7 @@ } }, "source": [ - "When we compare the output of the `*` and `/` operators for multiplication and division, we note the subtle *difference* between the $42$ and the $42.0$: They are the *same* number represented as a *different* **data type**." + "When we compare the output of the `*` and `/` operators for multiplication and division, we note the subtle *difference* between the `42` and the `42.0`: They are the *same* number represented as a *different* **data type**." ] }, { @@ -615,9 +615,9 @@ } }, "source": [ - "The remainder is $0$ *only if* a number is *divisible* by another.\n", + "The remainder is `0` *only if* a number is *divisible* by another.\n", "\n", - "A popular convention in both, computer science and mathematics, is to abbreviate \"only if\" as **iff**, which is short for \"**[if and only if](https://en.wikipedia.org/wiki/If_and_only_if)**.\" The iff means that a remainder of $0$ implies that a number is divisible by another but also that a number divisible by another implies a remainder of $0$. The implication goes in *both* directions!" + "A popular convention in both, computer science and mathematics, is to abbreviate \"only if\" as **iff**, which is short for \"**[if and only if](https://en.wikipedia.org/wiki/If_and_only_if)**.\" The iff means that a remainder of `0` implies that a number is divisible by another but also that a number divisible by another implies a remainder of `0`. The implication goes in *both* directions!" ] }, { @@ -988,7 +988,7 @@ { "data": { "text/plain": [ - "139878568414128" + "140706351197104" ] }, "execution_count": 28, @@ -1012,7 +1012,7 @@ { "data": { "text/plain": [ - "139878568593256" + "140706351380256" ] }, "execution_count": 29, @@ -1036,7 +1036,7 @@ { "data": { "text/plain": [ - "139878567760368" + "140706351003760" ] }, "execution_count": 30, @@ -1142,6 +1142,41 @@ "a is d" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we want to check the opposite case, we use the negated version of the `is` operator." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a is not d" + ] + }, { "cell_type": "markdown", "metadata": { @@ -1166,7 +1201,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 35, "metadata": { "slideshow": { "slide_type": "slide" @@ -1179,7 +1214,7 @@ "int" ] }, - "execution_count": 34, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } @@ -1190,7 +1225,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 36, "metadata": { "slideshow": { "slide_type": "-" @@ -1203,7 +1238,7 @@ "float" ] }, - "execution_count": 35, + "execution_count": 36, "metadata": {}, "output_type": "execute_result" } @@ -1227,7 +1262,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 37, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1240,7 +1275,7 @@ "True" ] }, - "execution_count": 36, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } @@ -1262,7 +1297,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 38, "metadata": { "slideshow": { "slide_type": "-" @@ -1276,7 +1311,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0ma\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mis_integer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0ma\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mis_integer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mAttributeError\u001b[0m: 'int' object has no attribute 'is_integer'" ] } @@ -1298,7 +1333,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 39, "metadata": { "slideshow": { "slide_type": "slide" @@ -1311,7 +1346,7 @@ "str" ] }, - "execution_count": 38, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } @@ -1322,7 +1357,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 40, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1335,7 +1370,7 @@ "'python rocks'" ] }, - "execution_count": 39, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } @@ -1346,7 +1381,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 41, "metadata": { "slideshow": { "slide_type": "-" @@ -1359,7 +1394,7 @@ "'PYTHON ROCKS'" ] }, - "execution_count": 40, + "execution_count": 41, "metadata": {}, "output_type": "execute_result" } @@ -1370,7 +1405,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 42, "metadata": { "slideshow": { "slide_type": "skip" @@ -1383,7 +1418,7 @@ "'Python Rocks'" ] }, - "execution_count": 41, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } @@ -1418,7 +1453,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 43, "metadata": { "slideshow": { "slide_type": "slide" @@ -1431,7 +1466,7 @@ "789" ] }, - "execution_count": 42, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } @@ -1442,7 +1477,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 44, "metadata": { "slideshow": { "slide_type": "-" @@ -1455,7 +1490,7 @@ "42.0" ] }, - "execution_count": 43, + "execution_count": 44, "metadata": {}, "output_type": "execute_result" } @@ -1477,7 +1512,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 45, "metadata": { "slideshow": { "slide_type": "-" @@ -1490,7 +1525,7 @@ "'Python rocks'" ] }, - "execution_count": 44, + "execution_count": 45, "metadata": {}, "output_type": "execute_result" } @@ -1549,7 +1584,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 46, "metadata": { "slideshow": { "slide_type": "slide" @@ -1558,10 +1593,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "invalid syntax (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 3.99 $ + 10.40 $\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 3.99 $ + 10.40 $\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], @@ -1582,7 +1617,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 47, "metadata": { "slideshow": { "slide_type": "slide" @@ -1591,10 +1626,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "invalid syntax (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m for number in numbers\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m for number in numbers\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], @@ -1616,7 +1651,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 48, "metadata": { "slideshow": { "slide_type": "slide" @@ -1625,10 +1660,10 @@ "outputs": [ { "ename": "IndentationError", - "evalue": "expected an indented block (, line 2)", + "evalue": "expected an indented block (, line 2)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m2\u001b[0m\n\u001b[0;31m print(number)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mIndentationError\u001b[0m\u001b[0;31m:\u001b[0m expected an indented block\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m2\u001b[0m\n\u001b[0;31m print(number)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mIndentationError\u001b[0m\u001b[0;31m:\u001b[0m expected an indented block\n" ] } ], @@ -1660,12 +1695,12 @@ "\n", "However, there are also so-called **runtime errors**, often called **exceptions**, that occur whenever otherwise (i.e., syntactically) correct code does not run because of invalid input.\n", "\n", - "This example does not work because just like in the \"real\" world, Python does not know how to divide by $0$. The syntactically correct code leads to a `ZeroDivisionError`." + "This example does not work because just like in the \"real\" world, Python does not know how to divide by `0`. The syntactically correct code leads to a `ZeroDivisionError`." ] }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 49, "metadata": { "slideshow": { "slide_type": "slide" @@ -1679,7 +1714,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m1\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m1\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" ] } @@ -1714,7 +1749,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 50, "metadata": { "code_folding": [], "slideshow": { @@ -1736,7 +1771,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 51, "metadata": { "slideshow": { "slide_type": "-" @@ -1746,10 +1781,10 @@ { "data": { "text/plain": [ - "3.0" + "3.5" ] }, - "execution_count": 50, + "execution_count": 51, "metadata": {}, "output_type": "execute_result" } @@ -1795,7 +1830,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 52, "metadata": { "slideshow": { "slide_type": "skip" @@ -1817,10 +1852,10 @@ " " ], "text/plain": [ - "" + "" ] }, - "execution_count": 51, + "execution_count": 52, "metadata": {}, "output_type": "execute_result" } @@ -1838,12 +1873,12 @@ } }, "source": [ - "For example, while the above code to calculate the average of the even numbers from 1 through 10 is correct, a Pythonista would re-write it in a more \"Pythonic\" way and use the [sum()](https://docs.python.org/3/library/functions.html#sum) and [len()](https://docs.python.org/3/library/functions.html#len) (= \"length\") [built-in functions](https://docs.python.org/3/library/functions.html) (cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb#Built-in-Functions)) as well as a so-called **list comprehension** (cf., [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#List-Comprehensions)). Pythonic code runs faster in many cases and is less error-prone." + "For example, while the above code to calculate the average of the even numbers from `1` through `12` is correct, a Pythonista would re-write it in a more \"Pythonic\" way and use the [sum()](https://docs.python.org/3/library/functions.html#sum) and [len()](https://docs.python.org/3/library/functions.html#len) (= \"length\") [built-in functions](https://docs.python.org/3/library/functions.html) (cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb#Built-in-Functions)) as well as a so-called **list comprehension** (cf., [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#List-Comprehensions)). Pythonic code runs faster in many cases and is less error-prone." ] }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 53, "metadata": { "slideshow": { "slide_type": "slide" @@ -1851,12 +1886,12 @@ }, "outputs": [], "source": [ - "numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" + "numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" ] }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 54, "metadata": { "slideshow": { "slide_type": "-" @@ -1869,7 +1904,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 55, "metadata": { "slideshow": { "slide_type": "-" @@ -1879,10 +1914,10 @@ { "data": { "text/plain": [ - "[2, 4, 6, 8, 10]" + "[8, 12, 2, 6, 10, 4]" ] }, - "execution_count": 54, + "execution_count": 55, "metadata": {}, "output_type": "execute_result" } @@ -1893,7 +1928,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 56, "metadata": { "slideshow": { "slide_type": "-" @@ -1906,7 +1941,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 57, "metadata": { "slideshow": { "slide_type": "-" @@ -1916,10 +1951,10 @@ { "data": { "text/plain": [ - "6.0" + "7.0" ] }, - "execution_count": 56, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" } @@ -1941,7 +1976,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 58, "metadata": { "slideshow": { "slide_type": "slide" @@ -2069,7 +2104,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 59, "metadata": { "slideshow": { "slide_type": "slide" @@ -2094,7 +2129,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 60, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2107,7 +2142,7 @@ "20.0" ] }, - "execution_count": 59, + "execution_count": 60, "metadata": {}, "output_type": "execute_result" } @@ -2129,7 +2164,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 61, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2142,7 +2177,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 62, "metadata": { "slideshow": { "slide_type": "-" @@ -2155,7 +2190,7 @@ "20" ] }, - "execution_count": 61, + "execution_count": 62, "metadata": {}, "output_type": "execute_result" } @@ -2177,7 +2212,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 63, "metadata": { "slideshow": { "slide_type": "slide" @@ -2190,7 +2225,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 64, "metadata": { "slideshow": { "slide_type": "-" @@ -2203,7 +2238,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 65, "metadata": { "slideshow": { "slide_type": "-" @@ -2216,7 +2251,7 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 66, "metadata": { "slideshow": { "slide_type": "-" @@ -2229,7 +2264,7 @@ "42" ] }, - "execution_count": 65, + "execution_count": 66, "metadata": {}, "output_type": "execute_result" } @@ -2251,7 +2286,7 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 67, "metadata": { "slideshow": { "slide_type": "slide" @@ -2264,7 +2299,7 @@ "789" ] }, - "execution_count": 66, + "execution_count": 67, "metadata": {}, "output_type": "execute_result" } @@ -2275,7 +2310,7 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 68, "metadata": { "slideshow": { "slide_type": "-" @@ -2299,7 +2334,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 69, "metadata": { "slideshow": { "slide_type": "-" @@ -2313,7 +2348,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mb\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mb\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'b' is not defined" ] } @@ -2335,7 +2370,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 70, "metadata": { "slideshow": { "slide_type": "skip" @@ -2348,7 +2383,7 @@ "'__main__'" ] }, - "execution_count": 69, + "execution_count": 70, "metadata": {}, "output_type": "execute_result" } @@ -2370,7 +2405,7 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 71, "metadata": { "slideshow": { "slide_type": "slide" @@ -2410,7 +2445,7 @@ " '_34',\n", " '_35',\n", " '_36',\n", - " '_38',\n", + " '_37',\n", " '_39',\n", " '_4',\n", " '_40',\n", @@ -2418,16 +2453,17 @@ " '_42',\n", " '_43',\n", " '_44',\n", + " '_45',\n", " '_5',\n", - " '_50',\n", " '_51',\n", - " '_54',\n", - " '_56',\n", - " '_59',\n", - " '_61',\n", - " '_65',\n", + " '_52',\n", + " '_55',\n", + " '_57',\n", + " '_60',\n", + " '_62',\n", " '_66',\n", - " '_69',\n", + " '_67',\n", + " '_70',\n", " '_9',\n", " '__',\n", " '___',\n", @@ -2508,6 +2544,7 @@ " '_i69',\n", " '_i7',\n", " '_i70',\n", + " '_i71',\n", " '_i8',\n", " '_i9',\n", " '_ih',\n", @@ -2529,7 +2566,7 @@ " 'total']" ] }, - "execution_count": 70, + "execution_count": 71, "metadata": {}, "output_type": "execute_result" } @@ -2564,7 +2601,7 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 72, "metadata": { "slideshow": { "slide_type": "slide" @@ -2575,30 +2612,6 @@ "b = a" ] }, - { - "cell_type": "code", - "execution_count": 72, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "42" - ] - }, - "execution_count": 72, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a" - ] - }, { "cell_type": "code", "execution_count": 73, @@ -2619,6 +2632,30 @@ "output_type": "execute_result" } ], + "source": [ + "a" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "b" ] @@ -2638,7 +2675,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 75, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2651,7 +2688,7 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 76, "metadata": { "slideshow": { "slide_type": "-" @@ -2664,7 +2701,7 @@ "123" ] }, - "execution_count": 75, + "execution_count": 76, "metadata": {}, "output_type": "execute_result" } @@ -2686,7 +2723,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 77, "metadata": { "slideshow": { "slide_type": "-" @@ -2699,7 +2736,7 @@ "42" ] }, - "execution_count": 76, + "execution_count": 77, "metadata": {}, "output_type": "execute_result" } @@ -2721,7 +2758,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 78, "metadata": { "slideshow": { "slide_type": "slide" @@ -2734,7 +2771,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 79, "metadata": { "slideshow": { "slide_type": "skip" @@ -2747,7 +2784,7 @@ "list" ] }, - "execution_count": 78, + "execution_count": 79, "metadata": {}, "output_type": "execute_result" } @@ -2758,7 +2795,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 80, "metadata": { "slideshow": { "slide_type": "-" @@ -2786,7 +2823,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 81, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2799,7 +2836,7 @@ "1" ] }, - "execution_count": 80, + "execution_count": 81, "metadata": {}, "output_type": "execute_result" } @@ -2821,7 +2858,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 82, "metadata": { "slideshow": { "slide_type": "slide" @@ -2834,7 +2871,7 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 83, "metadata": { "slideshow": { "slide_type": "-" @@ -2847,7 +2884,7 @@ "[99, 2, 3]" ] }, - "execution_count": 82, + "execution_count": 83, "metadata": {}, "output_type": "execute_result" } @@ -2869,7 +2906,7 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 84, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2882,7 +2919,7 @@ "[99, 2, 3]" ] }, - "execution_count": 83, + "execution_count": 84, "metadata": {}, "output_type": "execute_result" } @@ -2962,7 +2999,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 85, "metadata": { "slideshow": { "slide_type": "slide" @@ -2975,7 +3012,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 86, "metadata": { "slideshow": { "slide_type": "-" @@ -2988,7 +3025,7 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 87, "metadata": { "slideshow": { "slide_type": "-" @@ -3001,7 +3038,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 88, "metadata": { "slideshow": { "slide_type": "-" @@ -3025,7 +3062,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 89, "metadata": { "slideshow": { "slide_type": "skip" @@ -3038,7 +3075,7 @@ }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 90, "metadata": { "slideshow": { "slide_type": "skip" @@ -3051,7 +3088,7 @@ }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 91, "metadata": { "slideshow": { "slide_type": "skip" @@ -3064,7 +3101,7 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 92, "metadata": { "slideshow": { "slide_type": "skip" @@ -3073,10 +3110,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "can't assign to operator (, line 1)", + "evalue": "can't assign to operator (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m address@work = \"Burgplatz 2, Vallendar\"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m can't assign to operator\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m address@work = \"Burgplatz 2, Vallendar\"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m can't assign to operator\n" ] } ], @@ -3097,7 +3134,7 @@ }, { "cell_type": "code", - "execution_count": 92, + "execution_count": 93, "metadata": { "slideshow": { "slide_type": "skip" @@ -3121,7 +3158,7 @@ }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 94, "metadata": { "slideshow": { "slide_type": "skip" @@ -3134,7 +3171,7 @@ "'__main__'" ] }, - "execution_count": 93, + "execution_count": 94, "metadata": {}, "output_type": "execute_result" } @@ -3167,7 +3204,7 @@ }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 95, "metadata": { "slideshow": { "slide_type": "skip" @@ -3189,10 +3226,10 @@ " " ], "text/plain": [ - "" + "" ] }, - "execution_count": 94, + "execution_count": 95, "metadata": {}, "output_type": "execute_result" } @@ -3232,7 +3269,7 @@ }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 96, "metadata": { "slideshow": { "slide_type": "slide" @@ -3245,7 +3282,7 @@ "123" ] }, - "execution_count": 95, + "execution_count": 96, "metadata": {}, "output_type": "execute_result" } @@ -3256,7 +3293,7 @@ }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 97, "metadata": {}, "outputs": [ { @@ -3265,7 +3302,7 @@ "42" ] }, - "execution_count": 96, + "execution_count": 97, "metadata": {}, "output_type": "execute_result" } @@ -3283,7 +3320,7 @@ }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 98, "metadata": { "slideshow": { "slide_type": "-" @@ -3296,7 +3333,7 @@ "165" ] }, - "execution_count": 97, + "execution_count": 98, "metadata": {}, "output_type": "execute_result" } @@ -3318,7 +3355,7 @@ }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 99, "metadata": { "slideshow": { "slide_type": "-" @@ -3331,7 +3368,7 @@ "4492125" ] }, - "execution_count": 98, + "execution_count": 99, "metadata": {}, "output_type": "execute_result" } @@ -3353,7 +3390,7 @@ }, { "cell_type": "code", - "execution_count": 99, + "execution_count": 100, "metadata": { "slideshow": { "slide_type": "-" @@ -3366,7 +3403,7 @@ "3" ] }, - "execution_count": 99, + "execution_count": 100, "metadata": {}, "output_type": "execute_result" } @@ -3388,7 +3425,7 @@ }, { "cell_type": "code", - "execution_count": 100, + "execution_count": 101, "metadata": { "slideshow": { "slide_type": "-" @@ -3401,7 +3438,7 @@ "104" ] }, - "execution_count": 100, + "execution_count": 101, "metadata": {}, "output_type": "execute_result" } @@ -3434,7 +3471,7 @@ }, { "cell_type": "code", - "execution_count": 101, + "execution_count": 102, "metadata": { "slideshow": { "slide_type": "slide" @@ -3448,7 +3485,7 @@ }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 103, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3461,7 +3498,7 @@ "'Hi class'" ] }, - "execution_count": 102, + "execution_count": 103, "metadata": {}, "output_type": "execute_result" } @@ -3483,7 +3520,7 @@ }, { "cell_type": "code", - "execution_count": 103, + "execution_count": 104, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3496,7 +3533,7 @@ "'Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi '" ] }, - "execution_count": 103, + "execution_count": 104, "metadata": {}, "output_type": "execute_result" } @@ -3531,7 +3568,7 @@ }, { "cell_type": "code", - "execution_count": 104, + "execution_count": 105, "metadata": { "slideshow": { "slide_type": "slide" @@ -3544,7 +3581,7 @@ }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 106, "metadata": { "slideshow": { "slide_type": "-" @@ -3568,7 +3605,7 @@ }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 107, "metadata": { "slideshow": { "slide_type": "skip" @@ -3615,7 +3652,7 @@ }, { "cell_type": "code", - "execution_count": 107, + "execution_count": 108, "metadata": { "slideshow": { "slide_type": "slide" @@ -3643,7 +3680,7 @@ }, { "cell_type": "code", - "execution_count": 108, + "execution_count": 109, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3656,7 +3693,7 @@ }, { "cell_type": "code", - "execution_count": 109, + "execution_count": 110, "metadata": { "slideshow": { "slide_type": "-" @@ -3686,17 +3723,8 @@ } }, "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." - ] - }, - { - "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", @@ -3764,7 +3792,7 @@ "\n", "- flow control (cf., [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb))\n", " - expression of **business logic** or an **algorithm**\n", - " - conditional execution of a small **branch** within a program (e.g., `if` statements)\n", + " - conditional execution of parts of a program (e.g., `if` statements)\n", " - repetitive execution of parts of a program (e.g., `for`-loops)" ] } diff --git a/02_functions.ipynb b/02_functions.ipynb index 809982f..897f8a7 100644 --- a/02_functions.ipynb +++ b/02_functions.ipynb @@ -19,7 +19,7 @@ } }, "source": [ - "In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb), we typed the **business logic** of our little program to calculate the mean of a subset of a list of numbers right into the code cells. Then, we executed them one after another. We had no way of **re-using** the code except for either re-executing the cells or copying and pasting their contents into other cells. And whenever we find ourselves doing repetitive manual work, we can be sure that there must be a way of automating what we are doing.\n", + "In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Example:-Averaging-Even-Numbers), we typed the code to calculate the average of the even numbers in a list into several code cells. Then, we executed them one after another. We had no way of **re-using** the code except for either re-executing the cells or copying and pasting their contents into other cells. And, whenever we find ourselves doing repetitive manual work, we can be sure that there must be a way of automating what we are doing.\n", "\n", "At the same time, we executed built-in functions (e.g., [print()](https://docs.python.org/3/library/functions.html#print), [sum()](https://docs.python.org/3/library/functions.html#sum), [len()](https://docs.python.org/3/library/functions.html#len), [id()](https://docs.python.org/3/library/functions.html#id), or [type()](https://docs.python.org/3/library/functions.html#type)) that obviously must be re-using the same parts inside core Python every time we use them.\n", "\n", @@ -55,7 +55,7 @@ "\n", "Together, the name and the list of parameters are also referred to as the function's **[signature](https://en.wikipedia.org/wiki/Type_signature)** (i.e., `average_evens(numbers)` below).\n", "\n", - "A function may come with an *explicit* **[return value](https://docs.python.org/3/reference/simple_stmts.html#the-return-statement)** (i.e., \"result\" or \"output\") specified with the `return` statement: Functions that have one are considered **fruitful**; otherwise, they are **void**. Functions of the latter kind are still useful because of their **side effects** (e.g., the [print()](https://docs.python.org/3/library/functions.html#print) built-in). Strictly speaking, they also have an *implicit* return value of `None` that is different from the `False` we saw in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Identity-/-\"Memory-Location\").\n", + "A function may come with an *explicit* **return value** (i.e., \"result\" or \"output\") specified with the `return` statement (cf., [reference](https://docs.python.org/3/reference/simple_stmts.html#the-return-statement)): Functions that have one are considered **fruitful**; otherwise, they are **void**. Functions of the latter kind are still useful because of their **side effects** as, for example, the built-in [print()](https://docs.python.org/3/library/functions.html#print) function. Strictly speaking, they also have an *implicit* return value of `None`.\n", "\n", "A function should define a **docstring** that describes what it does in a short subject line, what parameters it expects (i.e., their types), and what it returns (if anything). A docstring is a syntactically valid multi-line string (i.e., type `str`) defined within **triple-double quotes** `\"\"\"`. Strings are covered in depth in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb#The-str-Type). Widely adopted standards as to how to format a docstring are [PEP 257](https://www.python.org/dev/peps/pep-0257/) and section 3.8 in [Google's Python Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md)." ] @@ -142,7 +142,7 @@ { "data": { "text/plain": [ - "139829511291360" + "139646582981088" ] }, "execution_count": 3, @@ -302,7 +302,7 @@ }, "outputs": [], "source": [ - "nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" + "nums = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" ] }, { @@ -317,7 +317,7 @@ { "data": { "text/plain": [ - "6.0" + "7.0" ] }, "execution_count": 9, @@ -337,7 +337,7 @@ } }, "source": [ - "The return value is commonly assigned to a new variable for later reference. Otherwise, we would lose access to it in memory right away." + "The return value is commonly assigned to a variable for later reference. Otherwise, we would lose access to it in memory right away." ] }, { @@ -365,7 +365,7 @@ { "data": { "text/plain": [ - "6.0" + "7.0" ] }, "execution_count": 11, @@ -493,7 +493,7 @@ } }, "source": [ - "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010%5D%0A%0Adef%20average_evens%28numbers%29%3A%0A%20%20%20%20evens%20%3D%20%5Bn%20for%20n%20in%20numbers%20if%20n%20%25%202%20%3D%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28evens%29%20/%20len%28evens%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_evens%28nums%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) visualizes what happens in memory: To be precise, in the exact moment when the function call is initiated and `nums` passed in as the `numbers` argument, there are *two* pointers to the *same* `list` object (cf., steps 4-5 in the visualization). We also see how Python creates a *new* **frame** that holds the function's local scope (i.e., \"internal names\") in addition to the **global** frame. Frames are nothing but [namespaces](https://en.wikipedia.org/wiki/Namespace) to *isolate* the names of different **scopes** from each other. The list comprehension `[n for n in numbers if n % 2 == 0]` constitutes yet another frame that is in scope as the `list` object assigned to `evens` is *being* created (cf., steps 6-18). When the function returns, only the global frame is left (cf., steps 21-22)." + "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010,%2011,%2012%5D%0A%0Adef%20average_evens%28numbers%29%3A%0A%20%20%20%20evens%20%3D%20%5Bn%20for%20n%20in%20numbers%20if%20n%20%25%202%20%3D%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28evens%29%20/%20len%28evens%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_evens%28nums%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) visualizes what happens in memory: To be precise, in the exact moment when the function call is initiated and `nums` passed in as the `numbers` argument, there are *two* pointers to the *same* `list` object (cf., steps 4-5 in the visualization). We also see how Python creates a *new* **frame** that holds the function's local scope (i.e., \"internal names\") in addition to the **global** frame. Frames are nothing but [namespaces](https://en.wikipedia.org/wiki/Namespace) to *isolate* the names of different **scopes** from each other. The list comprehension `[n for n in numbers if n % 2 == 0]` constitutes yet another frame that is in scope as the `list` object assigned to `evens` is *being* created (cf., steps 6-20). When the function returns, only the global frame is left (cf., last step)." ] }, { @@ -565,7 +565,7 @@ { "data": { "text/plain": [ - "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" + "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" ] }, "execution_count": 16, @@ -600,7 +600,7 @@ { "data": { "text/plain": [ - "6.0" + "7.0" ] }, "execution_count": 17, @@ -635,7 +635,7 @@ { "data": { "text/plain": [ - "6.0" + "7.0" ] }, "execution_count": 18, @@ -655,7 +655,7 @@ } }, "source": [ - "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010%5D%0A%0Adef%20average_wrong%28numbers%29%3A%0A%20%20%20%20evens%20%3D%20%5Bn%20for%20n%20in%20nums%20if%20n%20%25%202%20%3D%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28evens%29%20/%20len%28evens%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_wrong%28%5B123,%20456,%20789%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) is again helpful at visualizing the error interactively: Creating the `list` object `evens` eventually points to takes *12* computational steps, namely one for setting up an empty `list` object, *ten* for filling it with elements derived from `nums` in the global scope, and one to make `evens` point at it (cf., steps 6-18).\n", + "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010,%2011,%2012%5D%0A%0Adef%20average_wrong%28numbers%29%3A%0A%20%20%20%20evens%20%3D%20%5Bn%20for%20n%20in%20nums%20if%20n%20%25%202%20%3D%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28evens%29%20/%20len%28evens%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_wrong%28%5B123,%20456,%20789%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) is again helpful at visualizing the error interactively: Creating the `list` object `evens` eventually points to takes *16* computational steps, namely two for managing the list comprehension, one for setting up an empty `list` object, *twelve* for filling it with elements derived from `nums` in the global scope (i.e., that is the error), and one to make `evens` point at it (cf., steps 6-21).\n", "\n", "The frames logic shown by PythonTutor is the mechanism by which Python not only manages the names inside *one* function call but also for *many* potentially *simultaneous* calls, as revealed in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Trivial-Example:-Countdown). It is the reason why we may re-use the same names for the parameters and variables inside both `average_evens()` and `average_wrong()` without Python mixing them up. So, as we already read in the [Zen of Python](https://www.python.org/dev/peps/pep-0020/), \"namespaces are one honking great idea\" (cf., `import this`), and a frame is just a special kind of namespace." ] @@ -732,7 +732,7 @@ { "data": { "text/plain": [ - "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" + "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" ] }, "execution_count": 20, @@ -802,7 +802,7 @@ { "data": { "text/plain": [ - "5.0" + "6.0" ] }, "execution_count": 22, @@ -837,7 +837,7 @@ { "data": { "text/plain": [ - "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" + "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" ] }, "execution_count": 23, @@ -859,7 +859,7 @@ "source": [ "The reason why everything works is that *every time* we (re-)assign an object to a variable *inside* a function with the `=` statement, this is done in the *local* scope by default. There are ways to change variables existing in an outer scope from within a function, but this is a rather advanced topic on its own.\n", "\n", - "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010%5D%0A%0Adef%20average_odds%28numbers%29%3A%0A%20%20%20%20nums%20%3D%20%5Bint%28n%29%20for%20n%20in%20numbers%5D%0A%20%20%20%20odds%20%3D%20%5Bn%20for%20n%20in%20nums%20if%20n%20%25%202%20!%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28odds%29%20/%20len%28odds%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_odds%28%5B1.0,%2010.0,%203.0,%2010.0,%205.0%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how *two* `nums` variables exist in *different* scopes pointing to *different* objects (cf., steps 14-25) when we execute `average_odds([1.0, 10.0, 3.0, 10.0, 5.0])`.\n", + "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010,%2011,%2012%5D%0A%0Adef%20average_odds%28numbers%29%3A%0A%20%20%20%20nums%20%3D%20%5Bint%28n%29%20for%20n%20in%20numbers%5D%0A%20%20%20%20odds%20%3D%20%5Bn%20for%20n%20in%20nums%20if%20n%20%25%202%20!%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28odds%29%20/%20len%28odds%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_odds%28%5B1.0,%2010.0,%203.0,%2010.0,%205.0%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how *two* `nums` variables exist in *different* scopes pointing to *different* objects (cf., steps 14-25) when we execute `average_odds([1.0, 10.0, 3.0, 10.0, 5.0])`.\n", "\n", "Variables whose names collide with the ones of variables in enclosing scopes - and the global scope is just the most enclosing scope - are said to **shadow** them.\n", "\n", @@ -902,7 +902,7 @@ { "data": { "text/plain": [ - "10" + "12" ] }, "execution_count": 24, @@ -926,7 +926,7 @@ { "data": { "text/plain": [ - "55" + "78" ] }, "execution_count": 25, @@ -946,7 +946,7 @@ } }, "source": [ - "We may cast objects as a different type. For example, to \"convert\" a float or a text into an integer, we use the [int()](https://docs.python.org/3/library/functions.html#int) built-in. It creates a *new* object of type `int` from the provided `avg` or `\"6\"` objects that continue to exist in memory unchanged." + "We may cast objects as a different type. For example, to \"convert\" a float or a text into an integer, we use the [int()](https://docs.python.org/3/library/functions.html#int) built-in. It creates a *new* object of type `int` from the provided `avg` or `\"7\"` objects that continue to exist in memory unchanged." ] }, { @@ -961,7 +961,7 @@ { "data": { "text/plain": [ - "6.0" + "7.0" ] }, "execution_count": 26, @@ -985,7 +985,7 @@ { "data": { "text/plain": [ - "6" + "7" ] }, "execution_count": 27, @@ -1009,7 +1009,7 @@ { "data": { "text/plain": [ - "6" + "7" ] }, "execution_count": 28, @@ -1018,7 +1018,7 @@ } ], "source": [ - "int(\"6\")" + "int(\"7\")" ] }, { @@ -1029,7 +1029,7 @@ } }, "source": [ - "Observe that casting as an integer is different from rounding with the [round()](https://docs.python.org/3/library/functions.html#round) built-in function." + "Casting as an `int` object is different from rounding with the built-in [round()](https://docs.python.org/3/library/functions.html#round) function!" ] }, { @@ -1102,18 +1102,18 @@ "outputs": [ { "ename": "ValueError", - "evalue": "invalid literal for int() with base 10: 'six'", + "evalue": "invalid literal for int() with base 10: 'seven'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"six\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mValueError\u001b[0m: invalid literal for int() with base 10: 'six'" + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"seven\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: invalid literal for int() with base 10: 'seven'" ] } ], "source": [ - "int(\"six\")" + "int(\"seven\")" ] }, { @@ -1281,7 +1281,7 @@ { "data": { "text/plain": [ - "12.0" + "14.0" ] }, "execution_count": 36, @@ -1318,7 +1318,7 @@ { "data": { "text/plain": [ - "12.0" + "14.0" ] }, "execution_count": 37, @@ -1342,7 +1342,7 @@ { "data": { "text/plain": [ - "12.0" + "14.0" ] }, "execution_count": 38, @@ -1366,7 +1366,7 @@ { "data": { "text/plain": [ - "12.0" + "14.0" ] }, "execution_count": 39, @@ -1483,7 +1483,7 @@ } }, "source": [ - "The outcome of `average_evens(nums)` is, of course, still `6.0`." + "The outcome of `average_evens(nums)` is, of course, still `7.0`." ] }, { @@ -1498,7 +1498,7 @@ { "data": { "text/plain": [ - "6.0" + "7.0" ] }, "execution_count": 43, @@ -1555,7 +1555,7 @@ } }, "source": [ - "Now we call the function either with or without the `scalar` argument.\n", + "Now, we call the function either with or without the `scalar` argument.\n", "\n", "If `scalar` is to be passed in, this may be done as either a positional or a keyword argument. Which of the two versions where `scalar` is `2` is more \"natural\" and faster to comprehend in a larger program?" ] @@ -1572,7 +1572,7 @@ { "data": { "text/plain": [ - "6.0" + "7.0" ] }, "execution_count": 45, @@ -1596,7 +1596,7 @@ { "data": { "text/plain": [ - "12.0" + "14.0" ] }, "execution_count": 46, @@ -1620,7 +1620,7 @@ { "data": { "text/plain": [ - "12.0" + "14.0" ] }, "execution_count": 47, @@ -1705,7 +1705,7 @@ { "data": { "text/plain": [ - "6.0" + "7.0" ] }, "execution_count": 49, @@ -1729,7 +1729,7 @@ { "data": { "text/plain": [ - "12.0" + "14.0" ] }, "execution_count": 50, @@ -1749,7 +1749,7 @@ } }, "source": [ - "If we pass in `scalar` as a positional argument instead, we obtain a `TypeError`." + "If we pass in `scalar` as a positional argument instead, we get a `TypeError`." ] }, { @@ -1841,11 +1841,11 @@ } }, "source": [ - "If you think this is a rather pointless thing to do, you are absolutely correct!\n", + "If you think this is rather pointless to do, you are absolutely correct!\n", "\n", "We created a `function` object, dit *not* call it, and Python immediately forgot about it. So what's the point?\n", "\n", - "To prove that a `lambda` expression creates a callable `function` object, we use the simple `=` statement to assign it to the variable `add_three`, which is really `add_three()` as per our convention from above." + "To show that a `lambda` expression creates a callable `function` object, we use the simple `=` statement to assign it to the variable `add_three`, which is really `add_three()` as per our convention from above." ] }, { @@ -1928,7 +1928,7 @@ } ], "source": [ - "(lambda x: x + 3)(39) # this looks weird but will become very useful" + "(lambda x: x + 3)(39)" ] }, { @@ -1939,7 +1939,7 @@ } }, "source": [ - "The main point of having functions without a name is to use them in a situation where we know ahead of time that we use the function *once* only.\n", + "The main point of having functions without a name is to use them in a situation where we know ahead of time that we use the function only *once*.\n", "\n", "Popular applications of lambda expressions occur in combination with the **map-filter-reduce** paradigm or when we do \"number crunching\" with **arrays** and **data frames**. We look at both in detail in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb)." ] @@ -2021,7 +2021,7 @@ "source": [ "The [math](https://docs.python.org/3/library/math.html) module provides non-trivial mathematical functions like $sin(x)$ and constants like $\\pi$ or $\\text{e}$.\n", "\n", - "To make functions and variables defined \"somewhere else\" available in our current program, we must first **[import](https://docs.python.org/3/reference/simple_stmts.html#import)** them with the `import` statement. " + "To make functions and variables defined \"somewhere else\" available in our current program, we must first **import** them with the `import` statement (cf., [reference](https://docs.python.org/3/reference/simple_stmts.html#import)). " ] }, { @@ -2084,7 +2084,7 @@ { "data": { "text/plain": [ - "139829616976344" + "139646755983752" ] }, "execution_count": 58, @@ -2489,7 +2489,7 @@ } }, "source": [ - "Often, we need a random variable, for example, when we want to build a simulation program. The [random](https://docs.python.org/3/library/random.html) module in the [standard library](https://docs.python.org/3/library/index.html) often suffices for that." + "Often, we need a random variable, for example, when we want to build a simulation. The [random](https://docs.python.org/3/library/random.html) module in the [standard library](https://docs.python.org/3/library/index.html) often suffices for that." ] }, { @@ -2697,7 +2697,7 @@ { "data": { "text/plain": [ - "0.4165845384207939" + "0.21932911318720072" ] }, "execution_count": 75, @@ -2732,7 +2732,7 @@ { "data": { "text/plain": [ - ">" + ">" ] }, "execution_count": 76, @@ -2781,7 +2781,7 @@ { "data": { "text/plain": [ - "2" + "7" ] }, "execution_count": 78, @@ -2801,7 +2801,7 @@ } }, "source": [ - "To reproduce the same random numbers in a simulation each time we run it, we set the **[random seed](https://en.wikipedia.org/wiki/Random_seed)**. It is good practice to do this at the beginning of a program or notebook. Then every time we re-start the program, we get the *same* random numbers again. This becomes very important, for example, when we employ machine learning algorithms that rely on randomization, like the infamous [Random Forest](https://en.wikipedia.org/wiki/Random_forest), and want to obtain **reproducable** results.\n", + "To reproduce the *same* random numbers in a simulation each time we run it, we set the **[random seed](https://en.wikipedia.org/wiki/Random_seed)**. It is good practice to do that at the beginning of a program or notebook. It becomes essential when we employ randomized machine learning algorithms, like the [Random Forest](https://en.wikipedia.org/wiki/Random_forest), and want to obtain **reproducible** results for publication in academic journals.\n", "\n", "The [random](https://docs.python.org/3/library/random.html) module provides the [random.seed()](https://docs.python.org/3/library/random.html#random.seed) function to do that." ] @@ -3050,7 +3050,7 @@ { "data": { "text/plain": [ - "array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])" + "array([ 7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4])" ] }, "execution_count": 87, @@ -3111,7 +3111,7 @@ { "data": { "text/plain": [ - "array([ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20])" + "array([14, 22, 16, 10, 6, 24, 4, 12, 18, 20, 2, 8])" ] }, "execution_count": 89, @@ -3146,7 +3146,7 @@ { "data": { "text/plain": [ - "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" + "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4, 7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" ] }, "execution_count": 90, @@ -3181,7 +3181,7 @@ { "data": { "text/plain": [ - "55" + "78" ] }, "execution_count": 91, @@ -3205,7 +3205,7 @@ { "data": { "text/plain": [ - "1" + "7" ] }, "execution_count": 92, @@ -3389,7 +3389,7 @@ { "data": { "text/plain": [ - "6.0" + "7.0" ] }, "execution_count": 97, @@ -3413,7 +3413,7 @@ { "data": { "text/plain": [ - "12.0" + "14.0" ] }, "execution_count": 98, diff --git a/03_conditionals.ipynb b/03_conditionals.ipynb index 90da64f..072448a 100644 --- a/03_conditionals.ipynb +++ b/03_conditionals.ipynb @@ -189,7 +189,7 @@ { "data": { "text/plain": [ - "94731133531104" + "94711290794976" ] }, "execution_count": 5, @@ -213,7 +213,7 @@ { "data": { "text/plain": [ - "94731133531072" + "94711290794944" ] }, "execution_count": 6, @@ -313,7 +313,7 @@ { "data": { "text/plain": [ - "94731133518064" + "94711290781936" ] }, "execution_count": 10, @@ -1505,19 +1505,19 @@ } }, "source": [ - "To write useful programs, we need to control the flow of execution, for example, to react to user input. The logic by which a program does that is referred to as **business logic**.\n", + "To write useful programs, we need to control the flow of execution, for example, to react to user input. The logic by which a program follows the rules from the \"real world\" is referred to as **[business logic](https://en.wikipedia.org/wiki/Business_logic)**, even if \"real world\" refers to the domain of mathematics and not a business application.\n", "\n", - "One language construct to do so is the **[conditional statement](https://docs.python.org/3/reference/compound_stmts.html#the-if-statement)**, or `if` statement for short. It consists of:\n", + "One language feature to do so is the `if` statement (cf., [reference](https://docs.python.org/3/reference/compound_stmts.html#the-if-statement)). It consists of:\n", "\n", "- *one* mandatory `if`-clause,\n", - "- an *arbitrary* number of `elif`-clauses (i.e. \"else if\"), and\n", + "- an *arbitrary* number of `elif`-clauses (i.e., \"else if\"), and\n", "- an *optional* `else`-clause.\n", "\n", - "The `if`- and `elif`-clauses each specify one *boolean* expression, also called **condition**, while the `else`-clause serves as the \"catch everything else\" case.\n", + "The `if`- and `elif`-clauses each specify one *boolean* expression, also called **condition** in this context, while the `else`-clause serves as the \"catch everything else\" case.\n", "\n", - "In terms of syntax, the header lines end with a colon, and the code blocks are indented.\n", + "In contrast to our intuitive interpretation in natural languages, only the code in *one* of the alternatives, also called **branches**, is executed. To be precise, it is always the code in the first clause whose condition evaluates to `True`.\n", "\n", - "In contrast to our intuitive interpretation in natural languages, only the code in *one* of the alternatives, also called **branches**, is executed. To be precise, it is always the code in the first branch whose condition evaluates to `True`." + "In terms of syntax, the header lines end with a colon, and the code blocks are indented. Formally, any statement that is written across several lines is a **[compound statement](https://docs.python.org/3/reference/compound_stmts.html#compound-statements)**, the code blocks are called **suites** and belong to one header line, and the term **clause** refers to a header line and its suite as a whole. So far, we have seen three compound statements: `for`, `if`, and `def`. On the contrary, **[simple statements](https://docs.python.org/3/reference/simple_stmts.html#simple-statements)**, for example, `=`, `del`, or `return`, are written on *one* line." ] }, { @@ -1597,15 +1597,7 @@ "slide_type": "-" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "You read this just as often as you see heads when tossing a coin\n" - ] - } - ], + "outputs": [], "source": [ "if random.random() > 0.5:\n", " print(\"You read this just as often as you see heads when tossing a coin\")" @@ -1715,7 +1707,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "z is odd\n" + "z is positive\n" ] } ], @@ -1753,7 +1745,7 @@ } }, "source": [ - "When all we do with an `if` statement is to assign an object to a variable according to a single true-or-false condition (i.e., a binary choice), there is a shortcut: We assign the variable the result of a so-called **conditional expression**, or `if` expression for short, instead.\n", + "When an `if` statement assigns an object to a variable according to a true-or-false condition (i.e., a binary choice), there is a shortcut: We assign the variable the result of a so-called **[conditional expression](https://docs.python.org/3/reference/expressions.html#conditional-expressions)**, or `if` expression for short, instead.\n", "\n", "Think of a situation where we evaluate a piece-wise functional relationship $y = f(x)$ at a given $x$, for example:" ] @@ -1847,7 +1839,7 @@ } }, "source": [ - "On the contrary, the `if` expression fits into one line. The main downside here is a potential loss in readability, in particular, if the functional relationship is not that simple." + "On the contrary, the `if` expression fits into one line. The main downside is a potential loss in readability, in particular, if the functional relationship is not that simple. Also, some practitioners do *not* like that the condition is in the middle of the expression." ] }, { @@ -1895,7 +1887,7 @@ } }, "source": [ - "In this example, however, the most elegant solution would be to use the built-in [max()](https://docs.python.org/3/library/functions.html#max) function." + "In this example, however, the most elegant solution is to use the built-in [max()](https://docs.python.org/3/library/functions.html#max) function." ] }, { @@ -1935,17 +1927,6 @@ "y" ] }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "Conditional expressions may not only be used in the way described in this section. We already saw them as part of a *list comprehension* in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) and [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) and revisit this construct in greater detail in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#List-Comprehensions)." - ] - }, { "cell_type": "markdown", "metadata": { @@ -1967,9 +1948,9 @@ "source": [ "In the previous two chapters, we encountered a couple of *runtime* errors. A natural urge we might have after reading about conditional statements is to write code that somehow reacts to the occurrence of such exceptions. All we need is a way to formulate a condition for that.\n", "\n", - "For sure, this is such a common thing to do that Python provides a language construct for it, namely the `try` [statement](https://docs.python.org/3/reference/compound_stmts.html#the-try-statement).\n", + "For sure, this is such a common thing to do that Python provides a language construct for it, namely the compound `try` statement (cf., [reference](https://docs.python.org/3/reference/compound_stmts.html#the-try-statement)).\n", "\n", - "In its simplest form, it comes with just two branches: `try` and `except`. The following tells Python to execute the code in the `try`-branch, and if *anything* goes wrong, continue in the `except`-branch instead of **raising** an error to us. Of course, if nothing goes wrong, the `except`-branch is *not* executed." + "In its simplest form, it comes with just two clauses: `try` and `except`. The following tells Python to execute the code in the `try`-clause, and if *anything* goes wrong, continue in the `except`-clause instead of **raising** an error to us. Of course, if nothing goes wrong, the `except`-clause is *not* executed." ] }, { @@ -2017,9 +1998,9 @@ } }, "source": [ - "However, it is good practice *not* to **handle** *any* possible exception but only the ones we may *expect* from the code in the `try`-branch. The reasoning why this is done is a bit involved. We only remark that the codebase becomes easier to understand as we communicate to any human reader what could go wrong during execution in an *explicit* way. Python comes with a lot of [built-in exceptions](https://docs.python.org/3/library/exceptions.html#concrete-exceptions) that we should familiarize ourselves with.\n", + "However, it is good practice *not* to **handle** *any* possible exception but only the ones we may *expect* from the code in the `try`-clause. The reasoning why this is done is a bit involved. We only remark that the codebase becomes easier to understand as we communicate to any human reader what could go wrong during execution in an *explicit* way. Python comes with a lot of [built-in exceptions](https://docs.python.org/3/library/exceptions.html#concrete-exceptions) that we should familiarize ourselves with.\n", "\n", - "Another good practice is to always keep the code in the `try`-branch short to not *accidentally* handle an exception we do *not* want to handle.\n", + "Another good practice is to always keep the code in the `try`-clause short to not *accidentally* handle an exception we do *not* want to handle.\n", "\n", "In the example, we are dividing numbers and may expect a `ZeroDivisionError`." ] @@ -2056,9 +2037,9 @@ } }, "source": [ - "Often, we may have to run some code *independent* of an exception occurring, for example, to close a connection to a database. To achieve that, we add a `finally`-branch to the `try` statement.\n", + "Often, we may have to run some code *independent* of an exception occurring, for example, to close a connection to a database. To achieve that, we add a `finally`-clause to the `try` statement.\n", "\n", - "Similarly, we may have to run some code *only if* no exception occurs, but we do not want to put it in the `try`-branch as per the good practice mentioned above. To achieve that, we add an `else`-branch to the `try` statement.\n", + "Similarly, we may have to run some code *only if* no exception occurs, but we do not want to put it in the `try`-clause as per the good practice mentioned above. To achieve that, we add an `else`-clause to the `try` statement.\n", "\n", "To showcase everything together, we look at one last example. To spice it up a bit, we randomize the input. So run the cell several times and see for yourself." ] diff --git a/04_iteration.ipynb b/04_iteration.ipynb index 3a2e42d..2246da5 100644 --- a/04_iteration.ipynb +++ b/04_iteration.ipynb @@ -859,7 +859,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "69.6 µs ± 25.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + "36.5 µs ± 2.47 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], @@ -881,7 +881,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "1.55 ms ± 11.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + "1.58 ms ± 20.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], @@ -903,7 +903,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "189 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "194 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -925,7 +925,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "2.07 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "2.15 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -947,7 +947,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "5.41 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "5.59 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -4107,7 +4107,7 @@ "source": [ "We use the built-in [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function to make sure `factorial()` is called with an `int` object as the argument. We further **validate the input** by verifying that the integer is non-negative.\n", "\n", - "Meanwhile, we also see how we manually raise exceptions with the `raise` [statement](https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement), another way of controlling the flow of execution.\n", + "Meanwhile, we also see how we manually raise exceptions with the `raise` statement (cf., [reference](https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement)), another way of controlling the flow of execution.\n", "\n", "The first two branches in the revised `factorial()` function act as **guardians** ensuring that the code does not produce *unexpected* runtime errors: Errors may be expected when mentioned in the docstring.\n", "\n", @@ -4322,7 +4322,7 @@ } }, "source": [ - "Whereas functions combined with `if` statements suffice to model any repetitive logic, Python comes with a `while` [statement](https://docs.python.org/3/reference/compound_stmts.html#the-while-statement) that often makes it easier to implement iterative ideas.\n", + "Whereas functions combined with `if` statements suffice to model any repetitive logic, Python comes with a compound `while` statement (cf., [reference](https://docs.python.org/3/reference/compound_stmts.html#the-while-statement)) that often makes it easier to implement iterative ideas.\n", "\n", "It consists of a header line with a boolean expression followed by an indented code block. Before the first and after every execution of the code block, the boolean expression is evaluated, and if it is (still) equal to `True`, the code block runs (again). Eventually, some variable referenced in the boolean expression is changed in the code block such that the condition becomes `False`.\n", "\n", @@ -4565,7 +4565,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "4.9 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "4.69 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -4792,9 +4792,9 @@ "source": [ "Recursion and the `while` statement are two sides of the same coin. Disregarding that in the case of recursion Python internally faces some additional burden for managing the stack of frames in memory, both approaches lead to the *same* computational steps in memory. More importantly, we can re-formulate a recursive implementation in an iterative way and vice versa despite one of the two ways often \"feeling\" a lot more natural given a particular problem.\n", "\n", - "So how does the `for` [statement](https://docs.python.org/3/reference/compound_stmts.html#the-for-statement) we saw in the very first example in this book fit into this picture? It is a *redundant* language construct to provide a *shorter* and more *convenient* syntax for common applications of the `while` statement. In programming, such additions to a language are called **syntactic sugar**. A cup of tea tastes better with sugar, but we may drink tea without sugar too.\n", + "So how does the compound `for` statement (cf., [reference](https://docs.python.org/3/reference/compound_stmts.html#the-for-statement)) in this book's very first example fit into this picture? It is a *redundant* language construct to provide a *shorter* and more *convenient* syntax for common applications of the `while` statement. In programming, such additions to a language are called **syntactic sugar**. A cup of tea tastes better with sugar, but we may drink tea without sugar too.\n", "\n", - "Consider the following `numbers` list. Without the `for` statement, we have to manage a temporary **index variable** `i` to loop over all the elements and also obtain the individual elements with the `[]` operator in each iteration of the loop." + "Consider `elements` below. Without the `for` statement, we have to manage a temporary **index variable**, `index`, to loop over all the elements and also obtain the individual elements with the `[]` operator in each iteration of the loop." ] }, { @@ -4807,7 +4807,7 @@ }, "outputs": [], "source": [ - "numbers = [0, 1, 2, 3, 4]" + "elements = [0, 1, 2, 3, 4]" ] }, { @@ -4828,12 +4828,12 @@ } ], "source": [ - "i = 0\n", - "while i < len(numbers):\n", - " number = numbers[i]\n", - " print(number, end=\" \")\n", - " i += 1\n", - "del i" + "index = 0\n", + "while index < len(elements):\n", + " element = elements[index]\n", + " print(element, end=\" \")\n", + " index += 1\n", + "del index" ] }, { @@ -4865,8 +4865,8 @@ } ], "source": [ - "for number in numbers:\n", - " print(number, end=\" \")" + "for element in elements:\n", + " print(element, end=\" \")" ] }, { @@ -4877,7 +4877,7 @@ } }, "source": [ - "For sequences of integers, the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in makes the `for` statement even more convenient: It creates a list-like object of type `range` that generates integers \"on the fly,\" and we look closely at the underlying effects in memory in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Mapping)." + "For sequences of integers, the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in makes the `for` statement even more convenient: It creates a `list`-like object of type `range` that generates integers \"on the fly,\" and we look closely at the underlying effects in memory in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Mapping)." ] }, { @@ -4898,8 +4898,8 @@ } ], "source": [ - "for number in range(5):\n", - " print(number, end=\" \")" + "for element in range(5):\n", + " print(element, end=\" \")" ] }, { @@ -4955,8 +4955,8 @@ } ], "source": [ - "for number in [1, 3, 5, 7, 9]:\n", - " print(number, end=\" \")" + "for element in [1, 3, 5, 7, 9]:\n", + " print(element, end=\" \")" ] }, { @@ -4977,8 +4977,8 @@ } ], "source": [ - "for number in range(1, 10, 2):\n", - " print(number, end=\" \")" + "for element in range(1, 10, 2):\n", + " print(element, end=\" \")" ] }, { @@ -5097,7 +5097,7 @@ } }, "source": [ - "The cell below shows the *exact* workings of the `in` operator: Although `3.0` is *not* contained in `numbers`, it evaluates equal to the `3` that is, which is why the following expression evaluates to `True`. So, while we could colloquially say that `numbers` \"contains\" `3.0`, it actually does not." + "The cell below shows the *exact* workings of the `in` operator: Although `3.0` is *not* contained in `elements`, it evaluates equal to the `3` that is, which is why the following expression evaluates to `True`. So, while we could colloquially say that `elements` \"contains\" `3.0`, it actually does not." ] }, { @@ -5121,7 +5121,7 @@ } ], "source": [ - "3.0 in numbers" + "3.0 in elements" ] }, { @@ -5550,7 +5550,7 @@ } }, "source": [ - "Let's say we have a `numbers` list and want to check if the square of at least one of its elements is above a `threshold` of `100`." + "Let's say we have a `samples` list and want to check if the square of at least one of its elements is above a `threshold` of `100`." ] }, { @@ -5563,7 +5563,7 @@ }, "outputs": [], "source": [ - "numbers = [3, 7, 2, 9, 11, 4, 7, 9, 4, 5]" + "samples = [3, 7, 2, 9, 11, 4, 7, 9, 4, 5]" ] }, { @@ -5587,7 +5587,7 @@ } }, "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 search condition." + "A first naive implementation could look like this: We loop over *every* element in `samples` and set an **indicator variable** `is_above`, initialized as `False`, to `True` once we encounter an element satisfying the search condition." ] }, { @@ -5603,22 +5603,22 @@ "name": "stdout", "output_type": "stream", "text": [ - "3 7 2 9 11 4 7 9 4 5 => at least one number's square is above 100\n" + "3 7 2 9 11 4 7 9 4 5 => at least one sample's square is above 100\n" ] } ], "source": [ "is_above = False\n", "\n", - "for number in numbers:\n", - " print(number, end=\" \") # added for didactical purposes\n", - " if number ** 2 > threshold:\n", + "for sample in samples:\n", + " print(sample, end=\" \") # added for didactical purposes\n", + " if sample ** 2 > threshold:\n", " is_above = True\n", "\n", "if is_above:\n", - " print(\"=> at least one number's square is above\", threshold)\n", + " print(\"=> at least one sample's square is above\", threshold)\n", "else:\n", - " print(\"=> no number's square is above\", threshold)" + " print(\"=> no sample's square is above\", threshold)" ] }, { @@ -5629,11 +5629,11 @@ } }, "source": [ - "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", + "This implementation is *inefficient* as even if the *first* element in `samples` 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* clear right away.\n", "\n", - "Luckily, Python provides a `break` [statement](https://docs.python.org/3/reference/simple_stmts.html#the-break-statement) that lets us stop the `for`-loop in any iteration of the loop. Conceptually, it is yet another means of controlling the flow of execution." + "Luckily, Python provides the `break` statement (cf., [reference](https://docs.python.org/3/reference/simple_stmts.html#the-break-statement)) that lets us stop the `for`-loop in any iteration of the loop. Conceptually, it is yet another means of controlling the flow of execution." ] }, { @@ -5649,23 +5649,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "3 7 2 9 11 => at least one number's square is above 100\n" + "3 7 2 9 11 => at least one sample's square is above 100\n" ] } ], "source": [ "is_above = False\n", "\n", - "for number in numbers:\n", - " print(number, end=\" \") # added for didactical purposes\n", - " if number ** 2 > threshold:\n", + "for sample in samples:\n", + " print(sample, end=\" \") # added for didactical purposes\n", + " if sample ** 2 > threshold:\n", " is_above = True\n", " break\n", "\n", "if is_above:\n", - " print(\"=> at least one number's square is above\", threshold)\n", + " print(\"=> at least one sample's square is above\", threshold)\n", "else:\n", - " print(\"=> no number's square is above\", threshold)" + " print(\"=> no sample's square is above\", threshold)" ] }, { @@ -5687,7 +5687,7 @@ } }, "source": [ - "### The `for`-`else` Clause" + "### The `for`-`else`-Clause" ] }, { @@ -5698,7 +5698,7 @@ } }, "source": [ - "To express the logic in a prettier way, we add an `else`-clause at the end of the `for`-loop. The `else`-branch is executed *iff* 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 had better been named a `then`-clause. In most scenarios, however, the `else`-clause logically goes together with some `if` statement in the `for`-loop's body.\n", + "To express the logic in a prettier way, we add an `else`-clause at the end of the `for`-loop (cf., [reference](https://docs.python.org/3/reference/compound_stmts.html#the-for-statement)). The `else`-clause is executed *iff* 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 had better been named a `then`-clause. In most scenarios, however, the `else`-clause logically goes together with some `if` statement in the `for`-loop's body.\n", "\n", "Overall, the code's expressive power increases. Not many programming languages support a `for`-`else`-branching, which turns out to be very useful in practice." ] @@ -5727,23 +5727,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "3 7 2 9 11 => at least one number's square is above 100\n" + "3 7 2 9 11 => at least one sample's square is above 100\n" ] } ], "source": [ - "for number in numbers:\n", - " print(number, end=\" \") # added for didactical purposes\n", - " if number ** 2 > threshold:\n", + "for sample in samples:\n", + " print(sample, end=\" \") # added for didactical purposes\n", + " if sample ** 2 > threshold:\n", " is_above = True\n", " break\n", "else:\n", " is_above = False\n", "\n", "if is_above:\n", - " print(\"=> at least one number's square is above\", threshold)\n", + " print(\"=> at least one sample's square is above\", threshold)\n", "else:\n", - " print(\"=> no number's square is above\", threshold)" + " print(\"=> no sample's square is above\", threshold)" ] }, { @@ -5770,18 +5770,18 @@ "name": "stdout", "output_type": "stream", "text": [ - "3 7 2 9 11 => at least one number's square is above 100\n" + "3 7 2 9 11 => at least one sample's square is above 100\n" ] } ], "source": [ - "for number in numbers:\n", - " print(number, end=\" \") # added for didactical purposes\n", - " if number ** 2 > threshold:\n", - " print(\"=> at least one number's square is above\", threshold)\n", + "for sample in samples:\n", + " print(sample, end=\" \") # added for didactical purposes\n", + " if sample ** 2 > threshold:\n", + " print(\"=> at least one sample's square is above\", threshold)\n", " break\n", "else:\n", - " print(\"=> no number's square is above\", threshold)" + " print(\"=> no sample's square is above\", threshold)" ] }, { @@ -5792,7 +5792,7 @@ } }, "source": [ - "Of course, if we set the `threshold` an element's square has to pass higher, for example, to `200`, we have to loop through the entire `numbers` list. There is *no way* to optimize this **[linear search](https://en.wikipedia.org/wiki/Linear_search)** further, at least as long as we model `numbers` as a `list` object. More advanced data types, however, exist that mitigate that downside." + "Of course, if we set the `threshold` an element's square has to pass higher, for example, to `200`, we have to loop over all `samples`. There is *no way* to optimize this **[linear search](https://en.wikipedia.org/wiki/Linear_search)** further, at least as long as we model `samples` as a `list` object. More advanced data types, however, exist that mitigate that downside." ] }, { @@ -5808,20 +5808,20 @@ "name": "stdout", "output_type": "stream", "text": [ - "3 7 2 9 11 4 7 9 4 5 => no number's square is above 200\n" + "3 7 2 9 11 4 7 9 4 5 => no sample's square is above 200\n" ] } ], "source": [ "threshold = 200\n", "\n", - "for number in numbers:\n", - " print(number, end=\" \") # added for didactical purposes\n", - " if number ** 2 > threshold:\n", - " print(\"=> at least one number's square is above\", threshold)\n", + "for sample in samples:\n", + " print(sample, end=\" \") # added for didactical purposes\n", + " if sample ** 2 > threshold:\n", + " print(\"=> at least one sample's square is above\", threshold)\n", " break\n", "else:\n", - " print(\"=> no number's square is above\", threshold)" + " print(\"=> no sample's square is above\", threshold)" ] }, { @@ -5843,7 +5843,7 @@ } }, "source": [ - "Often, we process some iterable with numeric data, for example, a list of numbers as in the introductory example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Example:-Average-of-a-Subset-of-Numbers) or, more realistically, data from a CSV file with many rows and columns.\n", + "Often, we process some iterable with numeric data, for example, a list of `numbers` as in this book's introductory example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Example:-Averaging-Even-Numbers) 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", @@ -5875,7 +5875,7 @@ } }, "source": [ - "Calculate the sum of all even numbers from $1$ through $12$ after squaring them and adding $1$ to the squares:\n", + "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", @@ -5893,7 +5893,7 @@ }, "outputs": [], "source": [ - "numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" + "numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" ] }, { @@ -5909,7 +5909,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "2 > 5 4 > 17 6 > 37 8 > 65 10 > 101 12 > 145 " + "8 > 65 12 > 145 2 > 5 6 > 37 10 > 101 4 > 17 " ] }, { @@ -5926,11 +5926,11 @@ "source": [ "total = 0\n", "\n", - "for x in numbers:\n", - " if x % 2 == 0: # only keep even numbers\n", - " y = (x ** 2) + 1\n", - " print(x, y, sep=\" > \", end=\" \") # added for didactical purposes\n", - " total += y\n", + "for number in numbers:\n", + " if number % 2 == 0: # only keep even numbers\n", + " square = (number ** 2) + 1\n", + " print(number, square, sep=\" > \", end=\" \") # added for didactical purposes\n", + " total += square\n", "\n", "total" ] @@ -5969,7 +5969,7 @@ } }, "source": [ - "Calculate the sum of every third and even number from $1$ through $12$ after squaring them and adding $1$ to the squares:\n", + "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", @@ -5990,7 +5990,7 @@ { "data": { "text/plain": [ - "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" + "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" ] }, "execution_count": 78, @@ -6015,13 +6015,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "6 > 37 12 > 145 " + "8 > 65 12 > 145 4 > 17 " ] }, { "data": { "text/plain": [ - "182" + "227" ] }, "execution_count": 79, @@ -6032,12 +6032,12 @@ "source": [ "total = 0\n", "\n", - "for i, x in enumerate(numbers, start=1):\n", + "for i, number in enumerate(numbers, start=1):\n", " if i % 3 == 0: # only keep every third number\n", - " if x % 2 == 0: # only keep even numbers\n", - " y = (x ** 2) + 1\n", - " print(x, y, sep=\" > \", end=\" \") # added for didactical purposes \n", - " total += y\n", + " if number % 2 == 0: # only keep even numbers\n", + " square = (number ** 2) + 1\n", + " print(number, square, sep=\" > \", end=\" \") # added for didactical purposes \n", + " total += square\n", "\n", "total" ] @@ -6052,7 +6052,7 @@ "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](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb#The-if-Statement). Then, however, we trade off horizontal space against a more \"complex\" `if` logic, and this is *not* a real improvement.\n", "\n", - "A Pythonista would instead make use of the `continue` [statement](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", + "A Pythonista would instead make use of the `continue` statement (cf., [reference](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." ] @@ -6081,13 +6081,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "6 > 37 12 > 145 " + "8 > 65 12 > 145 4 > 17 " ] }, { "data": { "text/plain": [ - "182" + "227" ] }, "execution_count": 80, @@ -6098,15 +6098,15 @@ "source": [ "total = 0\n", "\n", - "for i, x in enumerate(numbers, start=1):\n", + "for i, number in enumerate(numbers, start=1):\n", " if i % 3 != 0: # only keep every third number\n", " continue\n", - " elif x % 2 != 0: # only keep even numbers\n", + " elif number % 2 != 0: # only keep even numbers\n", " continue\n", "\n", - " y = (x ** 2) + 1\n", - " print(x, y, sep=\" > \", end=\" \") # added for didactical purposes \n", - " total += y\n", + " square = (number ** 2) + 1\n", + " print(number, square, sep=\" > \", end=\" \") # added for didactical purposes \n", + " total += square\n", "\n", "total" ] diff --git a/05_numbers.ipynb b/05_numbers.ipynb index c545cde..a671e68 100644 --- a/05_numbers.ipynb +++ b/05_numbers.ipynb @@ -21,7 +21,7 @@ "source": [ "After learning about the basic building blocks of expressing and structuring the business logic in programs, we focus our attention on the **data types** Python offers us, both built-in and available via the [standard library](https://docs.python.org/3/library/index.html) or third-party packages.\n", "\n", - "We start with the \"simple\" ones: Numerical types in this chapter and textual data in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb). An important fact that holds for all objects of these types is that they are **immutable**. To re-use the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Objects-vs.-Types-vs.-Values), this means that the $0$s and $1$s making up an object's *value* cannot be changed once the bag is created in memory, implying that any operation with or method on the object creates a *new* object in a *different* memory location.\n", + "We start with the \"simple\" ones: Numeric types in this chapter and textual data in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb). An important fact that holds for all objects of these types is that they are **immutable**. To re-use the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Objects-vs.-Types-vs.-Values), this means that the $0$s and $1$s making up an object's *value* cannot be changed once the bag is created in memory, implying that any operation with or method on the object creates a *new* object in a *different* memory location.\n", "\n", "[Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb) and Chapter 8 then cover the more \"complex\" data types, including, for example, the `list` type. Finally, Chapter 9 completes the picture by introducing language constructs to create custom types.\n", "\n", @@ -31,7 +31,7 @@ "- [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb#Boolean-Expressions) raises questions regarding the **limited precision** of `float` numbers (e.g., `42 == 42.000000000000001` evaluates to `True`), and\n", "- [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Infinite-Recursion) shows that sometimes a `float` \"walks\" and \"quacks\" like an `int`, whereas the reverse is true in other cases.\n", "\n", - "This chapter introduces all the [built-in numerical types](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex): `int`, `float`, and `complex`. To mitigate the limited precision of floating-point numbers, we also look at two replacements for the `float` type in the [standard library](https://docs.python.org/3/library/index.html), namely the `Decimal` type in the [decimals](https://docs.python.org/3/library/decimal.html#decimal.Decimal) and the `Fraction` type in the [fractions](https://docs.python.org/3/library/fractions.html#fractions.Fraction) module." + "This chapter introduces all the [built-in numeric types](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex): `int`, `float`, and `complex`. To mitigate the limited precision of floating-point numbers, we also look at two replacements for the `float` type in the [standard library](https://docs.python.org/3/library/index.html), namely the `Decimal` type in the [decimals](https://docs.python.org/3/library/decimal.html#decimal.Decimal) and the `Fraction` type in the [fractions](https://docs.python.org/3/library/fractions.html#fractions.Fraction) module." ] }, { @@ -53,7 +53,7 @@ } }, "source": [ - "The simplest numerical type is the `int` type: It behaves like an [integer in ordinary math](https://en.wikipedia.org/wiki/Integer) (i.e., the set $\\mathbb{Z}$) and supports operators in the way we saw in the section on arithmetic operators in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#%28Arithmetic%29-Operators)." + "The simplest numeric type is the `int` type: It behaves like an [integer in ordinary math](https://en.wikipedia.org/wiki/Integer) (i.e., the set $\\mathbb{Z}$) and supports operators in the way we saw in the section on arithmetic operators in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#%28Arithmetic%29-Operators)." ] }, { @@ -92,7 +92,7 @@ { "data": { "text/plain": [ - "139630197982416" + "140087541220560" ] }, "execution_count": 2, @@ -160,7 +160,7 @@ } }, "source": [ - "A nice feature is using underscores `_` as (thousands) separator in numerical literals. For example, `1_000_000` evaluates to `1000000` in memory." + "A nice feature is using underscores `_` as (thousands) separator in numeric literals. For example, `1_000_000` evaluates to `1000000` in memory." ] }, { @@ -1165,7 +1165,7 @@ } }, "source": [ - "Whereas the boolean literals `True` and `False` are commonly *not* regarded as numerical types, they behave like `1` and `0` in an arithmetic context." + "Whereas the boolean literals `True` and `False` are commonly *not* regarded as numeric types, they behave like `1` and `0` in an arithmetic context." ] }, { @@ -2115,7 +2115,7 @@ { "data": { "text/plain": [ - "139630198164096" + "140087541402072" ] }, "execution_count": 63, @@ -2218,7 +2218,7 @@ } }, "source": [ - "In cases where the dot `.` is unnecessary from a mathematical point of view, we either need to end the number with it nevertheless or use the [float()](https://docs.python.org/3/library/functions.html#float) built-in to cast the number explicitly. [float()](https://docs.python.org/3/library/functions.html#float) can process any numerical object or a properly formatted `str` object." + "In cases where the dot `.` is unnecessary from a mathematical point of view, we either need to end the number with it nevertheless or use the [float()](https://docs.python.org/3/library/functions.html#float) built-in to cast the number explicitly. [float()](https://docs.python.org/3/library/functions.html#float) can process any numeric object or a properly formatted `str` object." ] }, { @@ -3517,7 +3517,7 @@ "source": [ "The [format()](https://docs.python.org/3/library/functions.html#format) function does *not* round a `float` object in the mathematical sense! It just allows us to show an arbitrary number of the digits as stored in memory, and it also does *not* change these.\n", "\n", - "On the contrary, the built-in [round()](https://docs.python.org/3/library/functions.html#round) function creates a *new* numerical object that is a rounded version of the one passed in as the argument. It adheres to the common rules of math.\n", + "On the contrary, the built-in [round()](https://docs.python.org/3/library/functions.html#round) function creates a *new* numeric object that is a rounded version of the one passed in as the argument. It adheres to the common rules of math.\n", "\n", "For example, let's round `1 / 3` to five decimals. The obtained value for `roughly_a_third` is also *imprecise* but different from the \"exact\" representation of `1 / 3` above." ] @@ -4617,7 +4617,7 @@ } }, "source": [ - "The [quantize()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize) method allows us to [quantize](https://www.dictionary.com/browse/quantize) (i.e., \"round\") a `Decimal` number at any precision that is *smaller* than the set precision. It looks at the number of decimals (i.e., to the right of the period) of the numerical argument we pass in.\n", + "The [quantize()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize) method allows us to [quantize](https://www.dictionary.com/browse/quantize) (i.e., \"round\") a `Decimal` number at any precision that is *smaller* than the set precision. It looks at the number of decimals (i.e., to the right of the period) of the numeric argument we pass in.\n", "\n", "For example, as the overall imprecise value of `two` still has an internal precision of `28` digits, we can correctly round it to *four* decimals (i.e., `Decimal(\"0.0001\")` has four decimals)." ] @@ -5498,7 +5498,7 @@ { "data": { "text/plain": [ - "139630197402224" + "140087540555856" ] }, "execution_count": 176, @@ -5636,7 +5636,7 @@ } }, "source": [ - "Alternatively, we may use the [complex()](https://docs.python.org/3/library/functions.html#complex) built-in: This takes two parameters where the second is optional and defaults to `0`. We may either call it with one or two arguments of any numerical type or a `str` object in the format of the previous code cell without any spaces." + "Alternatively, we may use the [complex()](https://docs.python.org/3/library/functions.html#complex) built-in: This takes two parameters where the second is optional and defaults to `0`. We may either call it with one or two arguments of any numeric type or a `str` object in the format of the previous code cell without any spaces." ] }, { @@ -5708,7 +5708,7 @@ } ], "source": [ - "complex(Decimal(\"2.0\"), Fraction(1, 2)) # the arguments may be any numerical type" + "complex(Decimal(\"2.0\"), Fraction(1, 2)) # the arguments may be any numeric type" ] }, { @@ -5743,7 +5743,7 @@ } }, "source": [ - "Arithmetic expressions work with `complex` numbers. They may be mixed with the other numerical types, and the result is always a `complex` number." + "Arithmetic expressions work with `complex` numbers. They may be mixed with the other numeric types, and the result is always a `complex` number." ] }, { @@ -6111,7 +6111,7 @@ } }, "source": [ - "Analogous to the discussion of containers and iterables in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Containers-vs.-Iterables), we contrast the *concrete* numerical data types in this chapter with the *abstract* ideas behind [numbers in mathematics](https://en.wikipedia.org/wiki/Number).\n", + "Analogous to the discussion of containers and iterables in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Containers-vs.-Iterables), we contrast the *concrete* numeric data types in this chapter with the *abstract* ideas behind [numbers in mathematics](https://en.wikipedia.org/wiki/Number).\n", "\n", "The figure below summarizes five *major* sets of [numbers in mathematics](https://en.wikipedia.org/wiki/Number) as we know them from high school:\n", "\n", @@ -6723,7 +6723,7 @@ } }, "source": [ - "Replacing *concrete* data types with *abstract* ones is particularly valuable in the context of input validation: The revised version of the `factorial()` function below allows its user to take advantage of *duck typing*: If a real but non-integer argument `n` is passed in, `factorial()` tries to cast `n` as an `int` object with the [trunc()](https://docs.python.org/3/library/math.html#math.trunc) function from the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html). [trunc()](https://docs.python.org/3/library/math.html#math.trunc) cuts off all decimals and any *concrete* numerical type implementing the *abstract* `numbers.Real` type supports it (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Real)).\n", + "Replacing *concrete* data types with *abstract* ones is particularly valuable in the context of input validation: The revised version of the `factorial()` function below allows its user to take advantage of *duck typing*: If a real but non-integer argument `n` is passed in, `factorial()` tries to cast `n` as an `int` object with the [trunc()](https://docs.python.org/3/library/math.html#math.trunc) function from the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html). [trunc()](https://docs.python.org/3/library/math.html#math.trunc) cuts off all decimals and any *concrete* numeric type implementing the *abstract* `numbers.Real` type supports it (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Real)).\n", "\n", "Two popular and distinguished Pythonistas, [Luciano Ramalho](https://github.com/ramalho) and [Alex Martelli](https://en.wikipedia.org/wiki/Alex_Martelli), coin the term **goose typing** to specifically mean using the built-in [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function with an *abstract base class* (cf., Chapter 11 in this [book](https://www.amazon.com/Fluent-Python-Concise-Effective-Programming/dp/1491946008) or this [summary](https://dgkim5360.github.io/blog/python/2017/07/duck-typing-vs-goose-typing-pythonic-interfaces/) thereof)." ] @@ -7015,7 +7015,7 @@ } }, "source": [ - "There exist three numerical types in core Python:\n", + "There exist three numeric types in core Python:\n", "- `int`: a near-perfect model for whole numbers (i.e., the set $\\mathbb{Z}$); inherently precise\n", "- `float`: the \"gold\" standard to approximate real numbers (i.e., the set $\\mathbb{R}$); inherently imprecise\n", "- `complex`: layer on top of the `float` type; therefore inherently imprecise\n", diff --git a/06_text.ipynb b/06_text.ipynb index 9092188..44eaa3c 100644 --- a/06_text.ipynb +++ b/06_text.ipynb @@ -19,7 +19,7 @@ } }, "source": [ - "In this chapter, we continue the study of the built-in data types. Building on our knowledge of numbers, the next layer consists of textual data that are modeled primarily with the `str` type in Python. `str` objects are naturally more \"complex\" than numerical objects as any text consists of an arbitrary and possibly large number of individual characters that may be chosen from any alphabet in the history of humankind. Luckily, Python abstracts away most of this complexity." + "In this chapter, we continue the study of the built-in data types. Building on our knowledge of numbers, the next layer consists of textual data that are modeled primarily with the `str` type in Python. `str` objects are naturally more \"complex\" than numeric objects as any text consists of an arbitrary and possibly large number of individual characters that may be chosen from any alphabet in the history of humankind. Luckily, Python abstracts away most of this complexity." ] }, { @@ -80,7 +80,7 @@ { "data": { "text/plain": [ - "140133793916176" + "140488988107952" ] }, "execution_count": 2, @@ -737,14 +737,14 @@ } }, "source": [ - "The idea of a **sequence** is yet another *abstract* concept.\n", + "A **sequence** is yet another *abstract* concept (cf., *containers* and *iterables* in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Containers-vs.-Iterables)).\n", "\n", - "It unifies *four* [orthogonal](https://en.wikipedia.org/wiki/Orthogonality) (i.e., \"independent\") *abstract* concepts into one: Any *concrete* data type, such as `str`, is considered a sequence if it simultaneously\n", + "It unifies *four* [orthogonal](https://en.wikipedia.org/wiki/Orthogonality) (i.e., \"independent\") behaviors into one idea: Any data type, such as `str`, is considered a sequence if it simultaneously\n", "\n", "1. **contains** other \"things,\"\n", "2. is **iterable**, and \n", "3. comes with a *predictable* **order** of its\n", - "4. **finite** number of elements.\n", + "4. **finite** number of \"things.\"\n", "\n", "[Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Collections-vs.-Sequences) formalizes sequences in great detail. Here, we keep our focus on the `str` type that historically received its name as it models a \"**[string of characters](https://en.wikipedia.org/wiki/String_%28computer_science%29)**,\" and a \"string\" is more formally called a sequence in the computer science literature.\n", "\n", @@ -1441,7 +1441,7 @@ } }, "source": [ - "Whereas elements of a `list` object *may* be *re-assigned*, as shortly hinted at in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?), this is *not* allowed for `str` objects. Once created, they *cannot* be *changed*. Formally, we say that they are **immutable**. In that regard, `str` objects and all the numerical types in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb) are alike.\n", + "Whereas elements of a `list` object *may* be *re-assigned*, as shortly hinted at in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?), this is *not* allowed for `str` objects. Once created, they *cannot* be *changed*. Formally, we say that they are **immutable**. In that regard, `str` objects and all the numeric types in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb) are alike.\n", "\n", "On the contrary, objects that may be changed after creation, are called **mutable**. We already saw in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?) how mutable objects are more difficult to reason about for a beginner, in particular, if more than *one* variable point to one. Yet, mutability does have its place in a programmer's toolbox, and we revisit this idea in the next chapters.\n", "\n", @@ -1533,7 +1533,7 @@ { "data": { "text/plain": [ - "140133784610832" + "140488987187120" ] }, "execution_count": 46, @@ -1557,7 +1557,7 @@ { "data": { "text/plain": [ - "140133793916176" + "140488988107952" ] }, "execution_count": 47, @@ -1972,7 +1972,7 @@ { "data": { "text/plain": [ - "140133891261864" + "140489068668384" ] }, "execution_count": 61, @@ -2009,7 +2009,7 @@ { "data": { "text/plain": [ - "140133784910848" + "140488987340848" ] }, "execution_count": 63, From 3cab7a329d379cac354abe901de011f018f4c722 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 6 Nov 2019 11:10:46 +0100 Subject: [PATCH 043/242] Add note why to use python -m pip ... --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3837323..d2f8d4b 100644 --- a/README.md +++ b/README.md @@ -126,8 +126,8 @@ are popular tools to automate the described management of virtual environments. After activation for the first time, you must install the project's **dependencies** (= the third-party packages needed to run the code), most notably [Jupyter](https://pypi.org/project/jupyter/) in this project (the -"python -m" is often left out; if you have poetry installed, you may just -type `poetry install` instead). +"python -m" is often left out [but should not be](https://snarky.ca/why-you-should-use-python-m-pip/); +if you have poetry installed, you may just type `poetry install` instead). - `python -m pip install -r requirements.txt` From 8c9af64b4e5fbfb787edf756bbc7494e1ed680ec Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 6 Nov 2019 16:19:16 +0100 Subject: [PATCH 044/242] Add review and exercises for notebook 07 --- 07_sequences_review_and_exercises.ipynb | 389 ++++++++++++++++++++++++ 1 file changed, 389 insertions(+) create mode 100644 07_sequences_review_and_exercises.ipynb diff --git a/07_sequences_review_and_exercises.ipynb b/07_sequences_review_and_exercises.ipynb new file mode 100644 index 0000000..17e1ffc --- /dev/null +++ b/07_sequences_review_and_exercises.ipynb @@ -0,0 +1,389 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "# Chapter 7: Sequential Data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Content Review" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb) of the book. Then work through the ten review questions." + ] + }, + { + "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**: We have seen **containers** and **iterables** before in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Containers-vs.-Iterables). How do they relate to **sequences**? " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: Explain the difference between a **mutable** and an **immutable** object in Python with an example!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4.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 **records**.\n", + "\n", + "Describe why `tuple` objects are a suitable replacement for `list` objects in general!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4.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?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: With the [map()](https://docs.python.org/3/library/functions.html#map) and [filter()](https://docs.python.org/3/library/functions.html#filter) built-ins and the [reduce()](https://docs.python.org/3/library/functools.html#functools.reduce) function from the [functools](https://docs.python.org/3/library/functools.html) module in the [standard library](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": [ + " " + ] + }, + { + "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": [ + "**Q6**: `sequence` objects are *not* part of core Python but may be imported from the [standard library](https://docs.python.org/3/library/index.html)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: The **map-filter-reduce** paradigm is an excellent mental concept to organize one's code with. Then, there is a good chance that a program can be **parallelized** if the data input grows." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: `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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Coding Exercises" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Working with Lists" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10.1**: 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():\n", + " ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "nested_sum(nested_numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10.2**: Provide a one-line expression to obtain the *same* result as `nested_sum()`!\n", + "\n", + "Hints: Use a *list comprehension*. You may want to use the built-in [sum()](https://docs.python.org/3/library/functions.html#sum) function several times." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10.3**: 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()](https://docs.python.org/3/library/functions.html#isinstance) function to check how an element is to be processed. Get extra credit for adhering to *goose typing*, as explained in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb#Goose-Typing)." + ] + }, + { + "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": [ + "def mixed_sum():\n", + " ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mixed_sum(nums)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10.4.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](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():\n", + " ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cum_sum(sum_up)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10.4.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? Describe everything in the docstring.\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([])" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "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": 2 +} From 16194990b94935ed7024207dfc66e42eafa39d8d Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 6 Nov 2019 16:27:13 +0100 Subject: [PATCH 045/242] Bump version number --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c2a9bdc..333c176 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "intro-to-python" -version = "0.4.0" +version = "0.5.0" description = "An introduction to Python and programming for wanna-be data scientists" authors = ["Alexander Hess "] license = "MIT" From 675dbc43b2fad74a6019493e65c5ba28680c0033 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 20 Nov 2019 10:58:01 +0100 Subject: [PATCH 046/242] Adjust content overview in README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d2f8d4b..4da6b2f 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ As such they can be viewed in a plain web browser: - [05 - Bits & Numbers](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb) - [06 - Bytes & Text](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb) - [07 - Sequential Data](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb) +- [08 - Mappings & Sets](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings.ipynb) However, it is recommended that students **install Python and Jupyter locally** and run the code in the notebooks on their own. From fe138176058c9fbcb6fe85040421f46ceb267867 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 20 Nov 2019 10:58:54 +0100 Subject: [PATCH 047/242] Add note for lecture in spring term 2020 --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4da6b2f..97c9120 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,7 @@ This opens a new tab in your web browser just as above. Alexander Hess is a PhD student at the Chair of Logistics Management at the [WHU - Otto Beisheim School of Management](https://www.whu.edu) where he conducts research on urban delivery platforms and teaches an introductory -course on Python (cf., [course listing](https://vlv.whu.edu/campus/all/event.asp?objgguid=0xE57C2715B01B441AAFD3E79AA05CACCF&from=vvz&gguid=0x6A2B0ED5B2B949E69957A2099E7DE2F1&mode=own&tguid=0x3980A9BBC3BF4A638E977F2DC163F44B&lang=en)). +course on Python (cf., [Fall Term 2019](https://vlv.whu.edu/campus/all/event.asp?objgguid=0xE57C2715B01B441AAFD3E79AA05CACCF&from=vvz&gguid=0x6A2B0ED5B2B949E69957A2099E7DE2F1&mode=own&tguid=0x3980A9BBC3BF4A638E977F2DC163F44B&lang=en), +[Spring Term 2020](https://vlv.whu.edu/campus/all/event.asp?objgguid=0x3354F4C108FF4E959CDD692A325D9AFE&from=vvz&gguid=0x262E29795DD742CFBDE72B12B69CEFD6&mode=own&lang=en&tguid=0x2E4A7D1FF3C34AD08FF07685461781C9)). Connect him on [LinkedIn](https://www.linkedin.com/in/webartifex). From ca8fefdce19c731cc25e6fe498394a7991076a81 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 20 Nov 2019 11:00:24 +0100 Subject: [PATCH 048/242] Streamline previous content --- 00_start_up.ipynb | 8 +- 01_elements.ipynb | 97 +- 01_elements_review_and_exercises.ipynb | 4 +- 02_functions.ipynb | 58 +- 02_functions_review_and_exercises.ipynb | 58 +- 03_conditionals.ipynb | 12 +- 03_conditionals_review_and_exercises.ipynb | 30 +- 04_iteration.ipynb | 160 +- 04_iteration_review_and_exercises.ipynb | 23 +- 05_numbers.ipynb | 456 ++- 05_numbers_review_and_exercises.ipynb | 22 +- 06_text.ipynb | 660 ++-- 07_sequences.ipynb | 3920 ++++++++++++++------ 13 files changed, 3595 insertions(+), 1913 deletions(-) diff --git a/00_start_up.ipynb b/00_start_up.ipynb index 97fe84b..9aed12d 100644 --- a/00_start_up.ipynb +++ b/00_start_up.ipynb @@ -585,7 +585,7 @@ " - replaced the infamous course on the [Scheme](https://groups.csail.mit.edu/mac/projects/scheme/) language (cf., [source](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](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 **re-written 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", + " - 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", " - Python and C++, Java, and Go are the only four server-side languages to be deployed to production\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", @@ -605,7 +605,7 @@ } }, "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](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](https://stackoverflow.blog/2017/09/14/python-growing-quickly/) and [DataCamp](https://www.datacamp.com/community/blog/python-scientific-computing-case)." + "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](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](https://stackoverflow.blog/2017/09/14/python-growing-quickly/) and [DataCamp](https://www.datacamp.com/community/blog/python-scientific-computing-case)." ] }, { @@ -715,7 +715,7 @@ "- **Managers**: People that need to organize things and command others (like a \"boss\"). Their schedule is usually organized by the hour or even 30-minute intervals.\n", "- **Makers**: People that create things (like 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", + "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." ] @@ -795,7 +795,7 @@ " 5. [Bits & Numbers](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb)\n", " 6. [Bytes & Text](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb)\n", " 7. [Sequential Data](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb)\n", - " 8. Mappings & Sets\n", + " 8. [Mappings & Sets](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings.ipynb)\n", "- How can we create custom data types?\n", " 9. Object-Orientation" ] diff --git a/01_elements.ipynb b/01_elements.ipynb index fed6458..07d6774 100644 --- a/01_elements.ipynb +++ b/01_elements.ipynb @@ -48,7 +48,7 @@ "source": [ "As our introductory example, we want to calculate the *average* of all *evens* in a **list** of numbers: `[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]`.\n", "\n", - "While we could try to develop an [analytical solution](https://math.stackexchange.com/questions/935405/what-s-the-difference-between-analytical-and-numerical-approaches-to-problems/935446#935446) (i.e., derive some equation with \"pen and paper\"), we solve the task programmatically instead.\n", + "While we are used to finding an [analytical solution](https://math.stackexchange.com/questions/935405/what-s-the-difference-between-analytical-and-numerical-approaches-to-problems/935446#935446) in math (i.e., derive some equation with \"pen and paper\"), we solve this task *programmatically* instead.\n", "\n", "We start by creating a list called `numbers` that holds all the individual numbers between **brackets** `[` and `]`." ] @@ -114,7 +114,7 @@ "\n", "Intuitively, the line `for number in numbers` describes a \"loop\" over all the numbers in the `numbers` list, one at a time.\n", "\n", - "The `if number % 2 == 0` may look disturbing at first sight. Both the `%` and `==` must have an unintuitive meaning here. Luckily, the **comment** in the same line after the `#` symbol has the answer: The program only does something if the current `number` is even.\n", + "The `if number % 2 == 0` may look confusing at first sight. Both `%` and `==` must have an unintuitive meaning here. Luckily, the **comment** in the same line after the `#` symbol has the answer: The program does something only for an even `number`.\n", "\n", "In particular, it increases `count` by `1` and adds the current `number` onto the [running](https://en.wikipedia.org/wiki/Running_total) `total`. Both `count` and `number` are initially set to `0` and the single `=` symbol reads as \"... is *set* equal to ...\". It could not indicate a mathematical equation as, for example, `count` is generally not equal to `count + 1`.\n", "\n", @@ -200,7 +200,7 @@ "source": [ "Note how only two of the previous four code cells generate an **output** while two remained \"silent\" (i.e., there is no \"**Out[...]**\" after running the cell).\n", "\n", - "By default, Jupyter notebooks show the value of a cell's last **expression**. This output can be suppressed by ending the last line with a semicolon `;`." + "By default, Jupyter notebooks show the value of the **expression** in the last line of a code cell only. This output can be suppressed by ending the last line with a semicolon `;`." ] }, { @@ -249,7 +249,7 @@ } }, "source": [ - "To visualize something before the end of the cell, we use the built-in [print()](https://docs.python.org/3/library/functions.html#print) **function**. Here, the parentheses `()` indicate that we execute code defined somewhere else." + "To visualize something before the end of the cell, we use the built-in [print()](https://docs.python.org/3/library/functions.html#print) **function**. Here, the parentheses `()` indicate that we execute code written somewhere else." ] }, { @@ -331,9 +331,9 @@ "\n", "The arithmetic operators either \"operate\" with the number immediately following them (= **unary** operators; e.g., negation) or \"process\" the two numbers \"around\" them (= **binary** operators; e.g., addition).\n", "\n", - "By definition, operators have **no** permanent **side effects** in the computer's memory. Although the code cells in this section do indeed create *new* numbers in memory (e.g., `77 + 13` creates `90`), they are immediately \"forgotten\" as they are not stored in a **variable** like `numbers` or `average` above. We continue this thought further below when we compare **expressions** with **statements**.\n", + "By definition, operators have **no** permanent **side effects** in the computer's memory. Although the code cells in this section do indeed create *new* numbers in memory (e.g., `77 + 13` creates `90`), they are immediately \"forgotten\" as they are not stored in a **variable** like `numbers` or `average` above. We develop this thought further at the end of this chapter when we compare **expressions** with **statements**.\n", "\n", - "Let's see some examples of operators. We start with the binary `+` and the `-` operators for addition and subtraction. Binary operators are designed to resemble what mathematicians call [infix notation](https://en.wikipedia.org/wiki/Infix_notation) and have the expected meaning." + "Let's see some examples of operators. We start with the binary `+` and the `-` operators for addition and subtraction. Binary operators mimic what mathematicians call [infix notation](https://en.wikipedia.org/wiki/Infix_notation) and have the expected meaning." ] }, { @@ -392,7 +392,7 @@ } }, "source": [ - "The `-` operator is used as a unary operator as well. Then it just flips the sign of a number." + "The `-` operator may be used as a unary operator as well. Then it just flips the sign of a number." ] }, { @@ -617,7 +617,9 @@ "source": [ "The remainder is `0` *only if* a number is *divisible* by another.\n", "\n", - "A popular convention in both, computer science and mathematics, is to abbreviate \"only if\" as **iff**, which is short for \"**[if and only if](https://en.wikipedia.org/wiki/If_and_only_if)**.\" The iff means that a remainder of `0` implies that a number is divisible by another but also that a number divisible by another implies a remainder of `0`. The implication goes in *both* directions!" + "A popular convention in both, computer science and mathematics, is to abbreviate \"only if\" as **iff**, which is short for \"**[if and only if](https://en.wikipedia.org/wiki/If_and_only_if)**.\" The iff means that a remainder of `0` implies that a number is divisible by another but also that a number divisible by another implies a remainder of `0`. The implication goes in *both* directions!\n", + "\n", + "So, `49` is divisible by `7`." ] }, { @@ -652,7 +654,7 @@ } }, "source": [ - "Modulo division is also useful if we, for example, need to get the last couple of digits of a large integer." + "Modulo division is also useful if we want to extract the last couple of digits in a large integer." ] }, { @@ -711,7 +713,7 @@ } }, "source": [ - "The [divmod()](https://docs.python.org/3/library/functions.html#divmod) built-in function combines the integer and modulo divisions into one operation. However, this is not an operator but a function. Also, [divmod()](https://docs.python.org/3/library/functions.html#divmod) returns a \"pair\" of integers and not just one object." + "The built-in [divmod()](https://docs.python.org/3/library/functions.html#divmod) function combines the integer and modulo divisions into one operation. However, this is not an operator but a function. Also, [divmod()](https://docs.python.org/3/library/functions.html#divmod) returns a \"pair\" of integers and not just one." ] }, { @@ -781,7 +783,7 @@ } }, "source": [ - "The standard [order of precedence](https://docs.python.org/3/reference/expressions.html#operator-precedence) from mathematics applies (i.e., \"PEMDAS\" rule)." + "The standard [order of precedence](https://docs.python.org/3/reference/expressions.html#operator-precedence) from mathematics applies (i.e., [PEMDAS](http://mathworld.wolfram.com/PEMDAS.html) rule) when several operators are combined." ] }, { @@ -988,7 +990,7 @@ { "data": { "text/plain": [ - "140706351197104" + "140398796190992" ] }, "execution_count": 28, @@ -1012,7 +1014,7 @@ { "data": { "text/plain": [ - "140706351380256" + "140398796375888" ] }, "execution_count": 29, @@ -1036,7 +1038,7 @@ { "data": { "text/plain": [ - "140706351003760" + "140398796056176" ] }, "execution_count": 30, @@ -1056,7 +1058,7 @@ } }, "source": [ - "These addresses are *not* meaningful for anything other than checking if two variables **point** at the same object. Let's create a second variable `d` and also set it to `789`." + "These addresses are *not* meaningful for anything other than checking if two variables reference the *same* object. Let's create a second variable `d` and also set it to `789`." ] }, { @@ -1507,7 +1509,7 @@ } }, "source": [ - "In this book, we follow the convention of creating strings with **double quotes** `\"` instead of the **single quotes** `'` to which Python defaults in its literal output for `str` objects. Both types of quotes may be used interchangeably." + "In this book, we follow the convention of creating strings with **double quotes** `\"` instead of the **single quotes** `'` to which Python defaults in its *literal* notation for `str` objects. Both types of quotes may be used interchangeably." ] }, { @@ -1825,7 +1827,7 @@ "source": [ "Thus, adhering to just syntax rules is *never* enough. Over time, **best practices** and **style guides** were created to make it less likely for a developer to mess up a program and also to allow \"onboarding\" him as a contributor to an established code base, often called **legacy code**, faster. These rules are not enforced by Python itself: Badly styled code still runs. At the very least, Python programs should be styled according to [PEP 8](https://www.python.org/dev/peps/pep-0008/) and documented \"inline\" (i.e., in the code itself) according to [PEP 257](https://www.python.org/dev/peps/pep-0257/).\n", "\n", - "An easier to read version of PEP 8 is [here](https://pep8.org/). The video below features a well known \"[Pythonista](https://en.wiktionary.org/wiki/Pythonista)\" talking about the importance of code style." + "An easier to read version of PEP 8 is [here](https://pep8.org/). The video below features a well known **[Pythonista](https://en.wiktionary.org/wiki/Pythonista)** talking about the importance of code style." ] }, { @@ -1852,7 +1854,7 @@ " " ], "text/plain": [ - "" + "" ] }, "execution_count": 52, @@ -1873,7 +1875,7 @@ } }, "source": [ - "For example, while the above code to calculate the average of the even numbers from `1` through `12` is correct, a Pythonista would re-write it in a more \"Pythonic\" way and use the [sum()](https://docs.python.org/3/library/functions.html#sum) and [len()](https://docs.python.org/3/library/functions.html#len) (= \"length\") [built-in functions](https://docs.python.org/3/library/functions.html) (cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb#Built-in-Functions)) as well as a so-called **list comprehension** (cf., [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#List-Comprehensions)). Pythonic code runs faster in many cases and is less error-prone." + "For example, while the above code to calculate the average of the even numbers in `[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]` is correct, a Pythonista would rewrite it in a more \"Pythonic\" way and use the [sum()](https://docs.python.org/3/library/functions.html#sum) and [len()](https://docs.python.org/3/library/functions.html#len) (= \"length\") [built-in functions](https://docs.python.org/3/library/functions.html) (cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb#Built-in-Functions)) as well as a so-called **list comprehension** (cf., [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#List-Comprehensions)). Pythonic code runs faster in many cases and is less error-prone." ] }, { @@ -2075,7 +2077,7 @@ "\n", "At the same time, for a beginner's course, it is often easier to code linearly.\n", "\n", - "In real data science projects, one would probably employ a mixed approach and put re-usable code into so-called Python modules (i.e., *.py* files; cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb#Local-Modules-and-Packages)) and then use Jupyter notebooks to build up a linear report or storyline for a business argument to be made." + "In real data science projects, one would probably employ a mixed approach and put reusable code into so-called Python modules (i.e., *.py* files; cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb#Local-Modules-and-Packages)) and then use Jupyter notebooks to build up a linear report or storyline for a business argument to be made." ] }, { @@ -2097,9 +2099,9 @@ } }, "source": [ - "**Variables** are created with the **[assignment statement](https://docs.python.org/3/reference/simple_stmts.html#assignment-statements)** `=`, which is *not* an operator, mainly because of its side effect of making a **[name](https://docs.python.org/3/reference/lexical_analysis.html#identifiers)** point to an object in memory.\n", + "**Variables** are created with the **[assignment statement](https://docs.python.org/3/reference/simple_stmts.html#assignment-statements)** `=`, which is *not* an operator, mainly because of its side effect of making a **[name](https://docs.python.org/3/reference/lexical_analysis.html#identifiers)** reference an object in memory.\n", "\n", - "We read the terms **variable**, **name**, and **identifier** used interchangebly in many Python-related texts. In this book, we adopt the following convention: First, we treat *name* and *identifier* as perfect synonyms but only use the term *name* in the text for clarity. Second, whereas *name* only refers to a string of letters, numbers, and some other symbols, a *variable* refers to the combination of a *name* and a *pointer* to some object in memory." + "We read the terms **variable**, **name**, and **identifier** used interchangebly in many Python-related texts. In this book, we adopt the following convention: First, we treat *name* and *identifier* as perfect synonyms but only use the term *name* in the text for clarity. Second, whereas *name* only refers to a string of letters, numbers, and some other symbols, a *variable* means the combination of a *name* and a *reference* to an object in memory." ] }, { @@ -2124,7 +2126,7 @@ } }, "source": [ - "When referenced, a variable evaluates to the value of the object it points to. Colloquially, we could say that `a` evaluates to `20.0`, but this would not be an accurate description of what is going on in memory. We see some more colloquialisms in this section but should always relate this to what Python actually does in memory." + "When used as a *literal*, a variable evaluates to the value of the object it references. Colloquially, we could say that `a` evaluates to `20.0`, but this would not be an accurate description of what is going on in memory. We see some more colloquialisms in this section but should always relate this to what Python actually does in memory." ] }, { @@ -2172,7 +2174,7 @@ }, "outputs": [], "source": [ - "a = 20 # this makes a point to an object of a different type" + "a = 20 # this makes a reference an object of a different type" ] }, { @@ -2281,7 +2283,7 @@ } }, "source": [ - "Variables are **[de-referenced](https://docs.python.org/3/reference/simple_stmts.html#the-del-statement)** (i.e., \"deleted\") with the `del` statement. It does *not* delete the object to which a variable points to but merely removes the variable's name from the \"global list of all names.\"" + "Variables are **[dereferenced](https://docs.python.org/3/reference/simple_stmts.html#the-del-statement)** (i.e., \"deleted\") with the `del` statement. It does *not* delete the object a variable references but merely removes the variable's name from the \"global list of all names.\"" ] }, { @@ -2594,9 +2596,9 @@ } }, "source": [ - "It is *crucial* to understand that *several* variables may point to the *same* object in memory. Not having this in mind may lead to many hard to track down bugs.\n", + "It is *crucial* to understand that *several* variables may reference the *same* object in memory. Not having this in mind may lead to many hard to track down bugs.\n", "\n", - "Make `b` point to whatever object `a` is pointing to." + "Make `b` reference whatever object `a` is referencing." ] }, { @@ -2670,7 +2672,7 @@ "source": [ "For \"simple\" types like `int` or `float` this never causes troubles.\n", "\n", - "Let's \"change the value\" of `a`. To be precise, let's create a *new* `123` object and make `a` point to it." + "Let's \"change the value\" of `a`. To be precise, let's create a *new* `123` object and make `a` reference it." ] }, { @@ -2718,7 +2720,7 @@ } }, "source": [ - "`b` \"is still the same\" as before. To be precise, `b` still points to the *same object* as before." + "`b` \"is still the same\" as before. To be precise, `b` still references the *same object* as before." ] }, { @@ -2753,7 +2755,7 @@ } }, "source": [ - "However, if a variable points to an object of a more \"complex\" type (e.g., `list`), \"weird\" things happen." + "However, if a variable references an object of a more \"complex\" type (e.g., `list`), \"weird\" things happen." ] }, { @@ -2816,7 +2818,7 @@ "source": [ "Let's change the first element of `x`.\n", "\n", - "[Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#The-list-Type) discusses lists in more depth. For now, let's view a `list` object as some sort of **container** that holds an arbitrary number of pointers to other objects and treat the brackets `[]` attached to it as just another operator, called the **indexing operator**. `x[0]` instructs Python to first follow the pointer from the global list of all names to the `x` object. Then, it follows the first pointer it finds there to the `1` object. The indexing operator must be an operator as we merely read the first element and do not change anything in memory.\n", + "[Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#The-list-Type) discusses lists in more depth. For now, let's view a `list` object as some sort of **container** that holds an arbitrary number of references to other objects and treat the brackets `[]` attached to it as just another operator, called the **indexing operator**. `x[0]` instructs Python to first follow the reference from the global list of all names to the `x` object. Then, it follows the first reference it finds there to the `1` object. The indexing operator must be an operator as we merely read the first element and do not change anything in memory.\n", "\n", "Note how Python **begins counting at 0**. This is not the case for many other languages, for example, [MATLAB](https://en.wikipedia.org/wiki/MATLAB), [R](https://en.wikipedia.org/wiki/R_%28programming_language%29), or [Stata](https://en.wikipedia.org/wiki/Stata). To understand why this makes sense, see this short [note](https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html) by one of the all-time greats in computer science, the late [Edsger Dijkstra](https://en.wikipedia.org/wiki/Edsger_W._Dijkstra)." ] @@ -2853,7 +2855,7 @@ } }, "source": [ - "To change the first entry in the list, we use the assignment statement `=` again. Here, this does *not* create a *new* variable (or overwrite an existing one) but only changes the object to which the first pointer in the `x` list points to. As we only change parts of the `x` object, we say that we **mutate** (i.e., \"change\") its **state**. To use the bag analogy from above, we keep the same bag but \"flip\" some of the $0$s into $1$s and some of the $1$s into $0$s." + "To change the first entry in the list, we use the assignment statement `=` again. Here, this does *not* create a *new* variable, or overwrite an existing one, but only changes the object which the first element in `x` referenced. As we only change parts of the `x` object, we say that we **mutate** (i.e., \"change\") its **state**. To use the bag analogy from above, we keep the same bag but \"flip\" some of the $0$s into $1$s and some of the $1$s into $0$s." ] }, { @@ -2901,7 +2903,7 @@ } }, "source": [ - "The changes made to the object `x` is pointing to are also seen through the `y` variable!" + "The changes made to the object `x` is referencing can also be seen through the `y` variable!" ] }, { @@ -2938,11 +2940,11 @@ "source": [ "The illustrated difference in behavior has to do with the fact that integers and floats are **immutable** types while lists are **mutable**.\n", "\n", - "In the first case, an object cannot be changed \"in place\" once it is created in memory. When we assigned `123` to the already existing `a`, we did not change the $0$s and $1$s in the object `a` pointed to before the assignment but created a new integer object and made `a` point to it while the `b` variable is *not* affected.\n", + "In the first case, an object cannot be changed \"in place\" once it is created in memory. When we assigned `123` to the already existing `a`, we did not change the $0$s and $1$s in the object `a` referenced before the assignment but created a new integer object and made `a` reference it while the `b` variable is *not* affected.\n", "\n", - "In the second case, `x[0] = 99` creates a *new* integer object `99` and merely changes the first pointer in the `x` list.\n", + "In the second case, `x[0] = 99` creates a *new* integer object `99` and merely changes the first reference in the `x` list.\n", "\n", - "In general, the assignment statement creates a new name and makes it point to whatever object is on the right-hand side *iff* the left-hand side is a *pure* name (i.e., it contains no operators like the indexing operator in the example). Otherwise, it *mutates* an already existing object. And, we always must expect that the latter might have more than one variable pointing to it.\n", + "In general, the assignment statement creates a new name and makes it reference whatever object is on the right-hand side *iff* the left-hand side is a *pure* name (i.e., it contains no operators like the indexing operator in the example). Otherwise, it *mutates* an already existing object. And, we always must expect that the latter might have more than one variable referencing it.\n", "\n", "Visualizing what is going on in the memory with a tool like [PythonTutor](http://pythontutor.com/visualize.html#code=x%20%3D%20%5B1,%202,%203%5D%0Ay%20%3D%20x%0Ax%5B0%5D%20%3D%2099%0Aprint%28y%5B0%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) might be helpful for a beginner." ] @@ -3226,7 +3228,7 @@ " " ], "text/plain": [ - "" + "" ] }, "execution_count": 95, @@ -3258,7 +3260,7 @@ } }, "source": [ - "An **[expression](https://docs.python.org/3/reference/expressions.html)** is any syntactically correct **combination** of **variables** and **literals** with **operators**.\n", + "An **[expression](https://docs.python.org/3/reference/expressions.html)** is any syntactically correct *combination* of *variables* and *literals* with *operators*.\n", "\n", "In simple words, anything that may be used on the right-hand side of an assignment statement without creating a `SyntaxError` is an expression.\n", "\n", @@ -3645,7 +3647,7 @@ "source": [ "We use the `#` symbol to write comments in plain English right into the code.\n", "\n", - "As a good practice, comments should not describe *what* happens (this should be evident by reading the code; otherwise, it is most likely badly written code) but *why* something happens.\n", + "As a good practice, comments should *not* describe *what* happens (this should be evident by reading the code; otherwise, it is most likely badly written code) but *why* something happens.\n", "\n", "Comments may be added either at the end of a line of code, by convention separated with two spaces, or on a line on their own." ] @@ -3732,9 +3734,9 @@ "\n", "\n", "- input (examples)\n", - " - (numeric) data from a CSV file\n", + " - data from a CSV file\n", " - text entered on a command line\n", - " - (relational) data obtained from a database\n", + " - data obtained from a database\n", " - etc.\n", "\n", "\n", @@ -3749,20 +3751,20 @@ " - 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 8)\n", + " - built-in objects (incl. **literals**) vs. user-defined objects (cf., Chapter 9)\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", - " - **names** that point to **objects** in **memory**\n", - " - e.g., `x = 1` creates the variable `x` that points to the object `1`\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", + " - e.g., addition `+`, subtraction `-`, multiplication `*`, and division `/` all take two objects, whereas the negation `-` only takes one\n", "\n", "\n", "- expressions\n", @@ -3774,9 +3776,9 @@ "\n", "- statements\n", " - instructions that **\"do\" something** and **have side effects** in memory\n", - " - re-map names to different objects and *change* the state of the program\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 point to an object\n", + " - e.g., the assignment statement `=` makes a name reference an object\n", "\n", "\n", "- comments\n", @@ -3788,6 +3790,7 @@ " - 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](https://docs.python.org/3/library/functions.html) like [print()](https://docs.python.org/3/library/functions.html#print), [sum()](https://docs.python.org/3/library/functions.html#sum), or [len()](https://docs.python.org/3/library/functions.html#len)\n", "\n", "\n", "- flow control (cf., [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb))\n", diff --git a/01_elements_review_and_exercises.ipynb b/01_elements_review_and_exercises.ipynb index 965b8be..21ba4c5 100644 --- a/01_elements_review_and_exercises.ipynb +++ b/01_elements_review_and_exercises.ipynb @@ -138,7 +138,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q7**: \"**dunder**\" refers to a group of Australian (\"down under\") geeks that work on core Python." + "**Q7**: \"**dunder**\" refers to a group of Australian (i.e., \"down under\") geeks that work on core Python." ] }, { @@ -166,7 +166,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q9**: When NASA famously converted some measurements to the wrong unit and lost a Mars satellite in 1999 ([source](https://www.wired.com/2010/11/1110mars-climate-observer-report/)), this is an example of a so-called **runtime error**." + "**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**." ] }, { diff --git a/02_functions.ipynb b/02_functions.ipynb index 897f8a7..39d7084 100644 --- a/02_functions.ipynb +++ b/02_functions.ipynb @@ -19,9 +19,9 @@ } }, "source": [ - "In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Example:-Averaging-Even-Numbers), we typed the code to calculate the average of the even numbers in a list into several code cells. Then, we executed them one after another. We had no way of **re-using** the code except for either re-executing the cells or copying and pasting their contents into other cells. And, whenever we find ourselves doing repetitive manual work, we can be sure that there must be a way of automating what we are doing.\n", + "In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Example:-Averaging-Even-Numbers), we typed the code to calculate the average of the even numbers in a list into several code cells. Then, we executed them one after another. We had no way of **reusing** the code except for either executing cells multiple times or copying and pasting their contents into other cells. And, whenever we find ourselves doing repetitive manual work, we can be sure that there must be a way of automating what we are doing.\n", "\n", - "At the same time, we executed built-in functions (e.g., [print()](https://docs.python.org/3/library/functions.html#print), [sum()](https://docs.python.org/3/library/functions.html#sum), [len()](https://docs.python.org/3/library/functions.html#len), [id()](https://docs.python.org/3/library/functions.html#id), or [type()](https://docs.python.org/3/library/functions.html#type)) that obviously must be re-using the same parts inside core Python every time we use them.\n", + "At the same time, we executed built-in functions (e.g., [print()](https://docs.python.org/3/library/functions.html#print), [sum()](https://docs.python.org/3/library/functions.html#sum), [len()](https://docs.python.org/3/library/functions.html#len), [id()](https://docs.python.org/3/library/functions.html#id), or [type()](https://docs.python.org/3/library/functions.html#type)) that obviously must be reusing the same parts inside core Python every time we use them.\n", "\n", "This chapter shows how Python offers language constructs that let us **define** functions ourselves that we may then **call** just like the built-in ones." ] @@ -45,7 +45,7 @@ } }, "source": [ - "So-called **[user-defined functions](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)** may be created with the `def` statement. To extend an already familiar example, we re-use the introductory example from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Best-Practices) in its final Pythonic version and transform it into the function `average_evens()` below. \n", + "So-called **[user-defined functions](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)** may be created with the `def` statement. To extend an already familiar example, we reuse the introductory example from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Best-Practices) in its final Pythonic version and transform it into the function `average_evens()` below. \n", "\n", "A function's **name** must be chosen according to the same naming rules as ordinary variables as Python manages function names like variables. In this book, we further adopt the convention of ending function names with parentheses `()` in text cells for faster comprehension when reading (i.e., `average_evens()` vs. `average_evens`). These are *not* part of the name but must always be written out in the `def` statement for syntactic reasons.\n", "\n", @@ -142,7 +142,7 @@ { "data": { "text/plain": [ - "139646582981088" + "140384699277792" ] }, "execution_count": 3, @@ -407,7 +407,7 @@ } }, "source": [ - "Notice how the parameters listed in a function's definition (i.e., `numbers`) and variables created inside it during execution (i.e., `evens` and `average`) are **local** to that function. That means they only point to an object in memory *while* the function is being executed and de-referenced immediately when the function returns. We say they **go out of scope** once the function terminates." + "Notice how the parameters listed in a function's definition (i.e., `numbers`) and variables created inside it during execution (i.e., `evens` and `average`) are **local** to that function. That means they only reference an object in memory *while* the function is being executed and de-referenced immediately when the function returns. We say they **go out of scope** once the function terminates." ] }, { @@ -493,7 +493,7 @@ } }, "source": [ - "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010,%2011,%2012%5D%0A%0Adef%20average_evens%28numbers%29%3A%0A%20%20%20%20evens%20%3D%20%5Bn%20for%20n%20in%20numbers%20if%20n%20%25%202%20%3D%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28evens%29%20/%20len%28evens%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_evens%28nums%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) visualizes what happens in memory: To be precise, in the exact moment when the function call is initiated and `nums` passed in as the `numbers` argument, there are *two* pointers to the *same* `list` object (cf., steps 4-5 in the visualization). We also see how Python creates a *new* **frame** that holds the function's local scope (i.e., \"internal names\") in addition to the **global** frame. Frames are nothing but [namespaces](https://en.wikipedia.org/wiki/Namespace) to *isolate* the names of different **scopes** from each other. The list comprehension `[n for n in numbers if n % 2 == 0]` constitutes yet another frame that is in scope as the `list` object assigned to `evens` is *being* created (cf., steps 6-20). When the function returns, only the global frame is left (cf., last step)." + "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010,%2011,%2012%5D%0A%0Adef%20average_evens%28numbers%29%3A%0A%20%20%20%20evens%20%3D%20%5Bn%20for%20n%20in%20numbers%20if%20n%20%25%202%20%3D%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28evens%29%20/%20len%28evens%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_evens%28nums%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) visualizes what happens in memory: To be precise, in the exact moment when the function call is initiated and `nums` passed in as the `numbers` argument, there are *two* references to the *same* `list` object (cf., steps 4-5 in the visualization). We also see how Python creates a *new* **frame** that holds the function's local scope (i.e., \"internal names\") in addition to the **global** frame. Frames are nothing but [namespaces](https://en.wikipedia.org/wiki/Namespace) to *isolate* the names of different **scopes** from each other. The list comprehension `[n for n in numbers if n % 2 == 0]` constitutes yet another frame that is in scope as the `list` object assigned to `evens` is *being* created (cf., steps 6-20). When the function returns, only the global frame is left (cf., last step)." ] }, { @@ -655,9 +655,9 @@ } }, "source": [ - "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010,%2011,%2012%5D%0A%0Adef%20average_wrong%28numbers%29%3A%0A%20%20%20%20evens%20%3D%20%5Bn%20for%20n%20in%20nums%20if%20n%20%25%202%20%3D%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28evens%29%20/%20len%28evens%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_wrong%28%5B123,%20456,%20789%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) is again helpful at visualizing the error interactively: Creating the `list` object `evens` eventually points to takes *16* computational steps, namely two for managing the list comprehension, one for setting up an empty `list` object, *twelve* for filling it with elements derived from `nums` in the global scope (i.e., that is the error), and one to make `evens` point at it (cf., steps 6-21).\n", + "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010,%2011,%2012%5D%0A%0Adef%20average_wrong%28numbers%29%3A%0A%20%20%20%20evens%20%3D%20%5Bn%20for%20n%20in%20nums%20if%20n%20%25%202%20%3D%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28evens%29%20/%20len%28evens%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_wrong%28%5B123,%20456,%20789%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) is again helpful at visualizing the error interactively: Creating the `list` object `evens` eventually references takes *16* computational steps, namely two for managing the list comprehension, one for setting up an empty `list` object, *twelve* for filling it with elements derived from `nums` in the global scope (i.e., that is the error), and one to make `evens` reference it (cf., steps 6-21).\n", "\n", - "The frames logic shown by PythonTutor is the mechanism by which Python not only manages the names inside *one* function call but also for *many* potentially *simultaneous* calls, as revealed in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Trivial-Example:-Countdown). It is the reason why we may re-use the same names for the parameters and variables inside both `average_evens()` and `average_wrong()` without Python mixing them up. So, as we already read in the [Zen of Python](https://www.python.org/dev/peps/pep-0020/), \"namespaces are one honking great idea\" (cf., `import this`), and a frame is just a special kind of namespace." + "The frames logic shown by PythonTutor is the mechanism by which Python not only manages the names inside *one* function call but also for *many* potentially *simultaneous* calls, as revealed in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Trivial-Example:-Countdown). It is the reason why we may reuse the same names for the parameters and variables inside both `average_evens()` and `average_wrong()` without Python mixing them up. So, as we already read in the [Zen of Python](https://www.python.org/dev/peps/pep-0020/), \"namespaces are one honking great idea\" (cf., `import this`), and a frame is just a special kind of namespace." ] }, { @@ -822,7 +822,7 @@ } }, "source": [ - "Python, however, is again smart enough to keep all the involved `nums` variables apart. So the global `nums` is still pointing to the very same `list` object as before." + "Python, however, is again smart enough to keep all the involved `nums` variables apart. So, the global `nums` is still referencing the *same* `list` object as before." ] }, { @@ -859,7 +859,7 @@ "source": [ "The reason why everything works is that *every time* we (re-)assign an object to a variable *inside* a function with the `=` statement, this is done in the *local* scope by default. There are ways to change variables existing in an outer scope from within a function, but this is a rather advanced topic on its own.\n", "\n", - "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010,%2011,%2012%5D%0A%0Adef%20average_odds%28numbers%29%3A%0A%20%20%20%20nums%20%3D%20%5Bint%28n%29%20for%20n%20in%20numbers%5D%0A%20%20%20%20odds%20%3D%20%5Bn%20for%20n%20in%20nums%20if%20n%20%25%202%20!%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28odds%29%20/%20len%28odds%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_odds%28%5B1.0,%2010.0,%203.0,%2010.0,%205.0%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how *two* `nums` variables exist in *different* scopes pointing to *different* objects (cf., steps 14-25) when we execute `average_odds([1.0, 10.0, 3.0, 10.0, 5.0])`.\n", + "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010,%2011,%2012%5D%0A%0Adef%20average_odds%28numbers%29%3A%0A%20%20%20%20nums%20%3D%20%5Bint%28n%29%20for%20n%20in%20numbers%5D%0A%20%20%20%20odds%20%3D%20%5Bn%20for%20n%20in%20nums%20if%20n%20%25%202%20!%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28odds%29%20/%20len%28odds%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_odds%28%5B1.0,%2010.0,%203.0,%2010.0,%205.0%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how *two* `nums` variables exist in *different* scopes referencing *different* objects (cf., steps 14-25) when we execute `average_odds([1.0, 10.0, 3.0, 10.0, 5.0])`.\n", "\n", "Variables whose names collide with the ones of variables in enclosing scopes - and the global scope is just the most enclosing scope - are said to **shadow** them.\n", "\n", @@ -1124,7 +1124,7 @@ } }, "source": [ - "We may also go in the other direction with the [float()](https://docs.python.org/3/library/functions.html#float) built-in function." + "We may also go in the other direction with the [float()](https://docs.python.org/3/library/functions.html#float) built-in." ] }, { @@ -1432,9 +1432,9 @@ "source": [ "Defining both `average_evens()` and `scaled_average_evens()` is also kind of repetitive as most of their code is the same. Such a redundancy makes a codebase harder to maintain in the long run as whenever we change the logic in one function, we must *not* forget to do so for the other function as well.\n", "\n", - "A better way is to design related functions in a **modular** fashion such that they re-use each other's logic.\n", + "A better way is to design related functions in a **modular** fashion such that they reuse each other's logic.\n", "\n", - "For example, as not scaling an average is just a special case of scaling it with `1`, we could re-define the two functions like below: In this setting, the function resembling the *special* case (i.e., `average_evens()`) **forwards** the call to the more *general* function (i.e., `scaled_average_evens()`) using a `scalar=1` argument." + "For example, as not scaling an average is just a special case of scaling it with `1`, we could redefine the two functions like below: In this setting, the function resembling the *special* case (i.e., `average_evens()`) **forwards** the call to the more *general* function (i.e., `scaled_average_evens()`) using a `scalar=1` argument." ] }, { @@ -1796,17 +1796,17 @@ } }, "source": [ - "The `def` statement is a statement because of its side effect of creating a *new* name that points to a *new* `function` object in memory.\n", + "The `def` statement is a statement because of its side effect of creating a *new* name that references a *new* `function` object in memory.\n", "\n", - "We can thus think of it as doing *two* things at once (i.e., either both of them happen or none). First, a `function` object is created that contains the concrete $0$s and $1$s that resemble the instructions we put into the function's body. In the context of a function, these $0$s and $1$s are also called **[byte code](https://en.wikipedia.org/wiki/Bytecode)**. Then, a name pointing at the new `function` object is created.\n", + "We can thus think of it as doing *two* things at once (i.e., either both of them happen or none). First, a `function` object is created that contains the concrete $0$s and $1$s that resemble the instructions we put into the function's body. In the context of a function, these $0$s and $1$s are also called **[byte code](https://en.wikipedia.org/wiki/Bytecode)**. Then, a name referencing the new `function` object is created.\n", "\n", "Only this second aspect makes `def` a statement: Merely creating a new object in memory without making it accessible for later reference does *not* constitute a side effect because the state the program is *not* changed. After all, if we cannot reference an object, how do we know it exists in the first place?\n", "\n", - "Python provides a so-called **[lambda expression](https://docs.python.org/3/reference/expressions.html#lambda)** syntax that allows us to *only* create a `function` object in memory *without* making a name point to it.\n", + "Python provides a so-called **[lambda expression](https://docs.python.org/3/reference/expressions.html#lambda)** syntax that allows us to *only* create a `function` object in memory *without* making a name reference it.\n", "\n", "It starts with the keyword `lambda` followed by an optional comma separated enumeration of parameters, a mandatory colon, and *one* expression that also is the resulting `function` object's return value.\n", "\n", - "Because it does not create a name pointing to the object, we effectively create \"anonymous\" functions with it. In the example, we create a `function` object that adds `3` to the only argument passed in as the parameter `x` and returns that sum." + "Because it does not create a name referencing the object, we effectively create \"anonymous\" functions with it. In the example, we create a `function` object that adds `3` to the only argument passed in as the parameter `x` and returns that sum." ] }, { @@ -1965,9 +1965,9 @@ "source": [ "So far, we have only used what we refer to as **core** Python in this book. By this, we mean all the syntactical rules as specified in the [language reference](https://docs.python.org/3/reference/) and a minimal set of about 50 built-in [functions](https://docs.python.org/3/library/functions.html). With this, we could already implement any algorithm or business logic we can think of!\n", "\n", - "However, after our first couple of programs, we would already start seeing recurring patterns in the code we write. In other words, we would constantly be \"re-inventing the wheel\" in each new project.\n", + "However, after our first couple of programs, we would already start seeing recurring patterns in the code we write. In other words, we would constantly be \"reinventing the wheel\" in each new project.\n", "\n", - "Would it not be smarter to pull out the re-usable components from our programs and put them into some project independent **library** of generically useful functionalities? Then we would only need a way of including these **utilities** in our projects.\n", + "Would it not be smarter to pull out the reusable components from our programs and put them into some project independent **library** of generically useful functionalities? Then we would only need a way of including these **utilities** in our projects.\n", "\n", "As all programmers across all languages face this very same issue, most programming languages come with a so-called **[standard library](https://en.wikipedia.org/wiki/Standard_library)** that provides utilities to accomplish everyday tasks without much code. Examples are making an HTTP request to some website, open and read popular file types (e.g., CSV or Excel files), do something on a computer's file system, and many more." ] @@ -1993,7 +1993,7 @@ "source": [ "Python also comes with a [standard library](https://docs.python.org/3/library/index.html) that is structured into coherent modules and packages for given topics: A **module** is just a plain text file with the file extension *.py* that contains Python code while a **package** is a folder that groups several related modules.\n", "\n", - "The code in the [standard library](https://docs.python.org/3/library/index.html) is contributed and maintained by many volunteers around the world. In contrast to so-called \"third-party\" packages (cf., the next section below), the Python core development team closely monitors and tests the code in the [standard library](https://docs.python.org/3/library/index.html). Consequently, we can be reasonably sure that anything provided by it works correctly independent of our computer's operating system and will most likely also be there in the next Python versions. Parts in the [standard library](https://docs.python.org/3/library/index.html) that are computationally expensive are often re-written in C and, therefore, much faster than anything we could write in Python ourselves. So, whenever we can solve a problem with the help of the [standard library](https://docs.python.org/3/library/index.html), it is almost always the best way to do so as well.\n", + "The code in the [standard library](https://docs.python.org/3/library/index.html) is contributed and maintained by many volunteers around the world. In contrast to so-called \"third-party\" packages (cf., the next section below), the Python core development team closely monitors and tests the code in the [standard library](https://docs.python.org/3/library/index.html). Consequently, we can be reasonably sure that anything provided by it works correctly independent of our computer's operating system and will most likely also be there in the next Python versions. Parts in the [standard library](https://docs.python.org/3/library/index.html) that are computationally expensive are often rewritten in C and, therefore, much faster than anything we could write in Python ourselves. So, whenever we can solve a problem with the help of the [standard library](https://docs.python.org/3/library/index.html), it is almost always the best way to do so as well.\n", "\n", "The [standard library](https://docs.python.org/3/library/index.html) has grown very big over the years, and we refer to the website [PYMOTW](https://pymotw.com/3/index.html) (i.e., \"Python Module of the Week\") that features well written introductory tutorials and how-to guides to most parts of the library. The same author also published a [book](https://www.amazon.com/Python-Standard-Library-Example-Developers/dp/0134291050/ref=as_li_ss_tl?ie=UTF8&qid=1493563121&sr=8-1&keywords=python+3+standard+library+by+example) that many Pythonistas keep on their shelf for reference. Knowing what is in the [standard library](https://docs.python.org/3/library/index.html) is quite valuable for solving real-world tasks quickly.\n", "\n", @@ -2045,7 +2045,7 @@ } }, "source": [ - "This creates the variable `math` that points to a **[module object](https://docs.python.org/3/glossary.html#term-module)** (i.e., type `module`) in memory." + "This creates the variable `math` that references a **[module object](https://docs.python.org/3/glossary.html#term-module)** (i.e., type `module`) in memory." ] }, { @@ -2084,7 +2084,7 @@ { "data": { "text/plain": [ - "139646755983752" + "140384754610488" ] }, "execution_count": 58, @@ -2430,7 +2430,7 @@ "source": [ "If we only need one particular function from a module, we may also use the alternative `from ... import ...` syntax.\n", "\n", - "This does *not* create a module object but only makes a variable in our current location point to an object defined inside a module directly." + "This does *not* create a module object but only makes a variable in our current location reference an object defined inside a module directly." ] }, { @@ -2697,7 +2697,7 @@ { "data": { "text/plain": [ - "0.21932911318720072" + "0.17184403163109208" ] }, "execution_count": 75, @@ -2732,7 +2732,7 @@ { "data": { "text/plain": [ - ">" + ">" ] }, "execution_count": 76, @@ -2781,7 +2781,7 @@ { "data": { "text/plain": [ - "7" + "2" ] }, "execution_count": 78, @@ -3242,7 +3242,7 @@ "\n", "The *name* to be imported is the file's name except for the *.py* part. For this to work, the file's name *must* adhere to the *same* rules as hold for [variable names](https://docs.python.org/3/reference/lexical_analysis.html#identifiers) in general.\n", "\n", - "What happens during an import is as follows. When Python sees the `import sample_module` part, it first creates a *new* object of type `module` in memory. This is effectively an *empty* namespace. Then, it executes the imported file's code from top to bottom. Whatever variables are still defined at the end of this, are put into the module's namespace. Only if the file's code does *not* raise an error, will Python make a variable in our current location (i.e., `mod` here) point to the created `module` object. Otherwise, it is discarded. In essence, it is as if we copied and pasted the file's code in place of the import statement. If we import an already imported module again, Python is smart enough to avoid doing all this work all over and does nothing." + "What happens during an import is as follows. When Python sees the `import sample_module` part, it first creates a *new* object of type `module` in memory. This is effectively an *empty* namespace. Then, it executes the imported file's code from top to bottom. Whatever variables are still defined at the end of this, are put into the module's namespace. Only if the file's code does *not* raise an error, will Python make a variable in our current location (i.e., `mod` here) reference the created `module` object. Otherwise, it is discarded. In essence, it is as if we copied and pasted the file's code in place of the import statement. If we import an already imported module again, Python is smart enough to avoid doing all this work all over and does nothing." ] }, { @@ -3341,7 +3341,7 @@ } }, "source": [ - "We use the imported `mod.average_evens()` just like `average_evens()` defined above. The advantage we get from **modularization** with *.py* files is that we can now easily re-use functions across different Jupyter notebooks without re-defining them again and again. Also, we can \"source out\" code that distracts from the storyline told in a notebook." + "We use the imported `mod.average_evens()` just like `average_evens()` defined above. The advantage we get from **modularization** with *.py* files is that we can now easily reuse functions across different Jupyter notebooks without redefining them again and again. Also, we can \"source out\" code that distracts from the storyline told in a notebook." ] }, { @@ -3462,7 +3462,7 @@ "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 codebase), and\n", - "- eliminate redundancies by allowing **re-use of code**.\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", diff --git a/02_functions_review_and_exercises.ipynb b/02_functions_review_and_exercises.ipynb index d0935c6..ca631a7 100644 --- a/02_functions_review_and_exercises.ipynb +++ b/02_functions_review_and_exercises.ipynb @@ -215,21 +215,27 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "import ..." + ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "r = ..." + ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "..." + ] }, { "cell_type": "markdown", @@ -244,17 +250,8 @@ "metadata": {}, "outputs": [], "source": [ - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n" + "def sphere_volume(...):\n", + " ..." ] }, { @@ -269,42 +266,54 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "radius = ..." + ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "sphere_volume(...)" + ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "sphere_volume(...)" + ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "sphere_volume(...)" + ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "sphere_volume(...)" + ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "sphere_volume(...)" + ] }, { "cell_type": "markdown", @@ -334,7 +343,9 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "radius = ..." + ] }, { "cell_type": "code", @@ -342,14 +353,15 @@ "metadata": {}, "outputs": [], "source": [ - "\n" + "for ... in ...:\n", + " ..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "**Q11.6**: What lesson did you learn about the `float` type?" + "**Q11.6**: What lesson do you learn about the `float` type?" ] }, { diff --git a/03_conditionals.ipynb b/03_conditionals.ipynb index 072448a..9ffe09e 100644 --- a/03_conditionals.ipynb +++ b/03_conditionals.ipynb @@ -19,7 +19,7 @@ } }, "source": [ - "We analyzed every aspect of the `average_evens()` function in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) except for the `if` related parts. While it seems to do what we expect it to, there is a whole lot more we learn from taking it apart. In particular, the `if` may occur within both a **statement** or an **expression**, analogous as to how a noun in a natural language is *either* the subject of *or* an object in a sentence. What is common to both usages is that it leads to code being executed for *parts* of the input only. It is our first way of **controlling** the **flow of execution** in a program.\n", + "We analyzed every aspect of the `average_evens()` function in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) except for the `if`-related parts. While it seems to do what we expect it to, there is a whole lot more we learn from taking it apart. In particular, the `if` may occur within both a **statement** or an **expression**, analogous as to how a noun in a natural language is *either* the subject of *or* an object in a sentence. What is common to both usages is that it leads to code being executed for *parts* of the input only. It is our first way of **controlling** the **flow of execution** in a program.\n", "\n", "After deconstructing `if` in the first part of this chapter, we take a close look at a similar concept, namely handling **exceptions**." ] @@ -189,7 +189,7 @@ { "data": { "text/plain": [ - "94711290794976" + "93918328484832" ] }, "execution_count": 5, @@ -213,7 +213,7 @@ { "data": { "text/plain": [ - "94711290794944" + "93918328484800" ] }, "execution_count": 6, @@ -313,7 +313,7 @@ { "data": { "text/plain": [ - "94711290781936" + "93918328471792" ] }, "execution_count": 10, @@ -357,7 +357,7 @@ } }, "source": [ - "`True`, `False`, and `None` have the property that they each exist in memory only *once*. Objects designed this way are so-called **singletons**. This **[design pattern](https://en.wikipedia.org/wiki/Design_Patterns)** was originally developed to keep a program's memory usage at a minimum. It may only be employed in situations where we know that an object does *not* mutate its value (i.e., to re-use the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Objects-vs.-Types-vs.-Values), no flipping of $0$s and $1$s in the bag is allowed). In languages \"closer\" to the memory like C, we would have to code this singleton logic ourselves, but Python has this built in for *some* types.\n", + "`True`, `False`, and `None` have the property that they each exist in memory only *once*. Objects designed this way are so-called **singletons**. This **[design pattern](https://en.wikipedia.org/wiki/Design_Patterns)** was originally developed to keep a program's memory usage at a minimum. It may only be employed in situations where we know that an object does *not* mutate its value (i.e., to reuse the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Objects-vs.-Types-vs.-Values), no flipping of $0$s and $1$s in the bag is allowed). In languages \"closer\" to the memory like C, we would have to code this singleton logic ourselves, but Python has this built in for *some* types.\n", "\n", "We verify this with either the `is` operator or by comparing memory addresses." ] @@ -418,7 +418,7 @@ } }, "source": [ - "So the following expression regards *four* objects in memory: *One* `list` object holding ten pointers to *three* other objects." + "So the following expression regards *four* objects in memory: *One* `list` object holding ten references to *three* other objects." ] }, { diff --git a/03_conditionals_review_and_exercises.ipynb b/03_conditionals_review_and_exercises.ipynb index 89d7009..90b4ff9 100644 --- a/03_conditionals_review_and_exercises.ipynb +++ b/03_conditionals_review_and_exercises.ipynb @@ -110,7 +110,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q6**: What does the `finally`-branch enforce in this code snippet? How can a `try` statement be useful *without* an `except`-branch?" + "**Q6**: What does the `finally`-clause enforce in this code snippet? How can a `try` statement be useful *without* an `except`-clause?" ] }, { @@ -210,22 +210,8 @@ "metadata": {}, "outputs": [], "source": [ - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n" + "def discounted_price(...):\n", + " ..." ] }, { @@ -279,7 +265,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q9.3**: Re-calculate the last two line items with order quantities of $20$ and $15$. What do you observe?" + "**Q9.3**: Calculate the last two line items with order quantities of $20$ and $15$. What do you observe?" ] }, { @@ -304,7 +290,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - " (your observation)" + " " ] }, { @@ -373,12 +359,6 @@ "outputs": [], "source": [ "for number in numbers:\n", - " ...\n", - " ...\n", - " ...\n", - " ...\n", - " ...\n", - " ...\n", " ..." ] }, diff --git a/04_iteration.ipynb b/04_iteration.ipynb index 2246da5..aabaaef 100644 --- a/04_iteration.ipynb +++ b/04_iteration.ipynb @@ -91,7 +91,7 @@ " Args:\n", " n (int): seconds until the party begins\n", " \"\"\"\n", - " if n == 0: # base case\n", + " if n == 0: # = base case\n", " print(\"Happy New Year!\")\n", " else:\n", " print(n)\n", @@ -222,7 +222,7 @@ " Returns:\n", " factorial (int)\n", " \"\"\"\n", - " if n == 0:\n", + " if n == 0: # = base case\n", " return 1\n", " else:\n", " recurse = factorial(n - 1)\n", @@ -323,7 +323,7 @@ " Returns:\n", " factorial (int)\n", " \"\"\"\n", - " if n == 0:\n", + " if n == 0: # = base case\n", " return 1\n", " return n * factorial(n - 1)" ] @@ -517,7 +517,7 @@ " Returns:\n", " gcd (int)\n", " \"\"\"\n", - " if b == 0:\n", + " if b == 0: # = base case\n", " return a \n", " return gcd(b, a % b)" ] @@ -787,9 +787,9 @@ " Returns:\n", " ith_fibonacci (int)\n", " \"\"\"\n", - " if i == 0:\n", + " if i == 0: # = first base case\n", " return 0\n", - " elif i == 1:\n", + " elif i == 1: # = second base case\n", " return 1\n", " return fibonacci(i - 1) + fibonacci(i - 2)" ] @@ -841,7 +841,7 @@ "\n", "To understand this in detail, we would have to study algorithms and data structures (e.g., with [this book](https://www.amazon.de/Introduction-Algorithms-Press-Thomas-Cormen/dp/0262033844/ref=sr_1_1?__mk_de_DE=%C3%85M%C3%85%C5%BD%C3%95%C3%91&crid=1JNE8U0VZGU0O&qid=1569837169&s=gateway&sprefix=algorithms+an%2Caps%2C180&sr=8-1)), a discipline within computer science, and dive into the analysis of **[time complexity of algorithms](https://en.wikipedia.org/wiki/Time_complexity)**.\n", "\n", - "Luckily, in the Fibonacci case, the inefficiency can be resolved with a **caching** (i.e., \"re-use\") strategy from the field of **[dynamic programming](https://en.wikipedia.org/wiki/Dynamic_programming)**, namely **[memoization](https://en.wikipedia.org/wiki/Memoization)**. We do so in Chapter 8, after introducing the [dictionaries](https://docs.python.org/3/library/stdtypes.html#dict) data type.\n", + "Luckily, in the Fibonacci case, the inefficiency can be resolved with a **caching** (i.e., \"reuse\") strategy from the field of **[dynamic programming](https://en.wikipedia.org/wiki/Dynamic_programming)**, namely **[memoization](https://en.wikipedia.org/wiki/Memoization)**. We do so in [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings.ipynb#Memoization), after introducing the [dictionaries](https://docs.python.org/3/library/stdtypes.html#dict) data type.\n", "\n", "Let's measure the average run times for `fibonacci()` and varying `i` arguments with the `%%timeit` [cell magic](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-timeit) that comes with Jupyter." ] @@ -859,7 +859,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "36.5 µs ± 2.47 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + "37.5 µs ± 4.26 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], @@ -881,7 +881,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "1.58 ms ± 20.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + "1.49 ms ± 22.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], @@ -903,7 +903,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "194 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "199 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -925,7 +925,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "2.15 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "2.04 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -947,7 +947,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "5.59 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "5.33 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -4011,9 +4011,9 @@ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mRecursionError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mcountdown\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m\u001b[0m in \u001b[0;36mcountdown\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 11\u001b[0;31m \u001b[0mcountdown\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mcountdown\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 11\u001b[0;31m \u001b[0mcountdown\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "... last 1 frames repeated, from the frame below ...\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36mcountdown\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 11\u001b[0;31m \u001b[0mcountdown\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mcountdown\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 11\u001b[0;31m \u001b[0mcountdown\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mRecursionError\u001b[0m: maximum recursion depth exceeded while calling a Python object" ] } @@ -4050,9 +4050,9 @@ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mRecursionError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 12\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# = base case\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 12\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "... last 1 frames repeated, from the frame below ...\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 12\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# = base case\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 12\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mRecursionError\u001b[0m: maximum recursion depth exceeded in comparison" ] } @@ -4143,7 +4143,7 @@ " raise TypeError(\"Factorial is only defined for integers\")\n", " elif n < 0:\n", " raise ValueError(\"Factorial is not defined for negative integers\")\n", - " elif n == 0:\n", + " elif n == 0: # = base case\n", " return 1\n", " return n * factorial(n - 1)" ] @@ -4235,7 +4235,7 @@ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 13\u001b[0m \"\"\"\n\u001b[1;32m 14\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 15\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 16\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is not defined for negative integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 13\u001b[0m \"\"\"\n\u001b[1;32m 14\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 15\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 16\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is not defined for negative integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mTypeError\u001b[0m: Factorial is only defined for integers" ] } @@ -4261,7 +4261,7 @@ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m42\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 17\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is not defined for negative integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 18\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 17\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is not defined for negative integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 18\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# = base case\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mValueError\u001b[0m: Factorial is not defined for negative integers" ] } @@ -4565,7 +4565,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "4.69 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "4.6 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -4790,11 +4790,11 @@ } }, "source": [ - "Recursion and the `while` statement are two sides of the same coin. Disregarding that in the case of recursion Python internally faces some additional burden for managing the stack of frames in memory, both approaches lead to the *same* computational steps in memory. More importantly, we can re-formulate a recursive implementation in an iterative way and vice versa despite one of the two ways often \"feeling\" a lot more natural given a particular problem.\n", + "Recursion and the `while` statement are two sides of the same coin. Disregarding that in the case of recursion Python internally faces some additional burden for managing the stack of frames in memory, both approaches lead to the *same* computational steps in memory. More importantly, we can formulate any recursive implementation in an iterative way and vice versa despite one of the two ways often \"feeling\" a lot more natural given a particular problem.\n", "\n", "So how does the compound `for` statement (cf., [reference](https://docs.python.org/3/reference/compound_stmts.html#the-for-statement)) in this book's very first example fit into this picture? It is a *redundant* language construct to provide a *shorter* and more *convenient* syntax for common applications of the `while` statement. In programming, such additions to a language are called **syntactic sugar**. A cup of tea tastes better with sugar, but we may drink tea without sugar too.\n", "\n", - "Consider `elements` below. Without the `for` statement, we have to manage a temporary **index variable**, `index`, to loop over all the elements and also obtain the individual elements with the `[]` operator in each iteration of the loop." + "Consider `elements` below. Without the `for` statement, we must manage a temporary **index variable**, `index`, to loop over all the elements and also obtain the individual elements with the `[]` operator in each iteration of the loop." ] }, { @@ -5000,21 +5000,21 @@ } }, "source": [ - "The essential difference between the above `list` objects, `[0, 1, 2, 3, 4]` and `[1, 3, 5, 7, 9]`, and the `range` objects, `range(5)` and `range(1, 10, 2)`, is that in the former case *six* objects are created in memory *before* the `for` statement starts running, *one* `list` holding pointers to *five* `int` objects, whereas in the latter case only *one* `range` object is created that **generates** `int` objects one at a time *while* the `for`-loop runs.\n", + "The essential difference between the above `list` objects, `[0, 1, 2, 3, 4]` and `[1, 3, 5, 7, 9]`, and the `range` objects, `range(5)` and `range(1, 10, 2)`, is that in the former case *six* objects are created in memory *before* the `for` statement starts running, *one* `list` holding references to *five* `int` objects, whereas in the latter case only *one* `range` object is created that **generates** `int` objects one at a time *while* the `for`-loop runs.\n", "\n", "However, we can loop over both of them. So a natural question to ask is why Python treats objects of *different* types in the *same* way when used with a `for` statement.\n", "\n", - "So far, the overarching storyline in this book goes like this: In Python, *everything* is an object. Besides its *identity* and *value*, every object is characterized by belonging to *one data type* that determines how the object behaves and what we may do with it.\n", + "So far, the overarching storyline in this book goes like this: In Python, *everything* is an object. Besides its *identity* and *value*, every object is characterized by \"belonging\" to *one* data type that determines how the object behaves and what we may do with it.\n", "\n", - "Now, just as we classify objects by their types, we also classify these **concrete data types** (e.g., `int`, `float`, `str`, or `list`) into **abstract concepts**.\n", + "Now, just as we classify objects by data type, we also classify these data types (e.g., `int`, `float`, `str`, or `list`) into **abstract concepts**.\n", "\n", - "We did this already in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?) when we described a `list` object as \"some sort of container that holds [...] pointers to other objects\". So, abstractly speaking, **containers** are any objects that are \"composed\" of other objects and also \"manage\" how these objects are organized. `list` objects, for example, have the property that they model an internal order associated with its elements. There exist, however, other container types, many of which do *not* come with an order. So, containers primarily \"contain\" other objects and have *nothing* to do with looping.\n", + "We did this already in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?) when we described a `list` object as \"some sort of container that holds [...] references to other objects\". So, abstractly speaking, **containers** are any objects that are \"composed\" of other objects and also \"manage\" how these objects are organized. `list` objects, for example, have the property that they model an order associated with their elements. There exist, however, other container types, many of which do *not* come with an order. So, containers primarily \"contain\" other objects and have *nothing* to do with looping.\n", "\n", - "On the contrary, the abstract concept of **iterables** is all about looping: Any object that we can loop over is, by definition, an iterable. So, `range` objects, for example, are iterables that do *not* contain other objects. Moreover, looping does *not* have to occur in a *predictable* order, although this is the case for both `list` and `range` objects.\n", + "On the contrary, the abstract concept of **iterables** is all about looping: Any object that we can loop over is, by definition, an iterable. So, `range` objects, for example, are iterables, even though they hold no references to other objects. Moreover, looping does *not* have to occur in a *predictable* order, although this is the case for both `list` and `range` objects.\n", "\n", - "Typically, containers are iterables, and iterables are containers. Yet, only because these two concepts coincide often, we must not think of them as the same. Chapter 9 finally gives an explanation as to how abstract concepts are implemented and play together.\n", + "Typically, containers are iterables, and iterables are containers. Yet, only because these two concepts coincide often, we must not think of them as the same. In [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Collections-vs.-Sequences), we formalize these two concepts and introduce many more. Finally, Chapter 9 gives an explanation how abstract concepts are implemented and play together.\n", "\n", - "So, `list` objects like `first_names` below are iterable containers. They implement even more abstract concepts, as [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Collections-vs.-Sequences) reveals." + "Let's continue with `first_names` below as an example an illustrate what iterable containers are." ] }, { @@ -5038,7 +5038,7 @@ } }, "source": [ - "The characteristic operator associated with a container type is the `in` operator: It checks if a given object evaluates equal to at least one of the objects in the container. Colloquially, it checks if an object is \"contained\" in the container. Formally, this operation is called **membership testing**." + "The characteristic operator associated with container types is the `in` operator: It checks if a given object evaluates equal to at least one of the objects in the container. Colloquially, it checks if an object is \"contained\" in the container. Formally, this operation is called **membership testing**." ] }, { @@ -5112,7 +5112,7 @@ { "data": { "text/plain": [ - "True" + "[0, 1, 2, 3, 4]" ] }, "execution_count": 58, @@ -5120,6 +5120,30 @@ "output_type": "execute_result" } ], + "source": [ + "elements" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "3.0 in elements" ] @@ -5137,7 +5161,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 60, "metadata": { "slideshow": { "slide_type": "slide" @@ -5170,7 +5194,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 61, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5203,7 +5227,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 62, "metadata": { "slideshow": { "slide_type": "skip" @@ -5236,7 +5260,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 63, "metadata": { "slideshow": { "slide_type": "skip" @@ -5249,7 +5273,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 64, "metadata": { "slideshow": { "slide_type": "skip" @@ -5297,7 +5321,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 65, "metadata": { "code_folding": [], "slideshow": { @@ -5338,7 +5362,7 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 66, "metadata": { "slideshow": { "slide_type": "slide" @@ -5358,7 +5382,7 @@ "144" ] }, - "execution_count": 65, + "execution_count": 66, "metadata": {}, "output_type": "execute_result" } @@ -5391,7 +5415,7 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 67, "metadata": { "slideshow": { "slide_type": "slide" @@ -5411,7 +5435,7 @@ "218922995834555169026" ] }, - "execution_count": 66, + "execution_count": 67, "metadata": {}, "output_type": "execute_result" } @@ -5444,7 +5468,7 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 68, "metadata": { "slideshow": { "slide_type": "slide" @@ -5480,7 +5504,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 69, "metadata": { "slideshow": { "slide_type": "slide" @@ -5500,7 +5524,7 @@ "3628800" ] }, - "execution_count": 68, + "execution_count": 69, "metadata": {}, "output_type": "execute_result" } @@ -5555,7 +5579,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 70, "metadata": { "slideshow": { "slide_type": "slide" @@ -5568,7 +5592,7 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 71, "metadata": { "slideshow": { "slide_type": "-" @@ -5592,7 +5616,7 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 72, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5638,7 +5662,7 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 73, "metadata": { "slideshow": { "slide_type": "slide" @@ -5716,7 +5740,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 74, "metadata": { "slideshow": { "slide_type": "slide" @@ -5759,7 +5783,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 75, "metadata": { "slideshow": { "slide_type": "slide" @@ -5797,7 +5821,7 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 76, "metadata": { "slideshow": { "slide_type": "skip" @@ -5885,7 +5909,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 77, "metadata": { "slideshow": { "slide_type": "slide" @@ -5898,7 +5922,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 78, "metadata": { "slideshow": { "slide_type": "-" @@ -5918,7 +5942,7 @@ "370" ] }, - "execution_count": 77, + "execution_count": 78, "metadata": {}, "output_type": "execute_result" } @@ -5980,7 +6004,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 79, "metadata": { "slideshow": { "slide_type": "slide" @@ -5993,7 +6017,7 @@ "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" ] }, - "execution_count": 78, + "execution_count": 79, "metadata": {}, "output_type": "execute_result" } @@ -6004,7 +6028,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 80, "metadata": { "slideshow": { "slide_type": "-" @@ -6024,7 +6048,7 @@ "227" ] }, - "execution_count": 79, + "execution_count": 80, "metadata": {}, "output_type": "execute_result" } @@ -6070,7 +6094,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 81, "metadata": { "slideshow": { "slide_type": "slide" @@ -6090,7 +6114,7 @@ "227" ] }, - "execution_count": 80, + "execution_count": 81, "metadata": {}, "output_type": "execute_result" } @@ -6178,7 +6202,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 82, "metadata": { "slideshow": { "slide_type": "slide" @@ -6191,7 +6215,7 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 83, "metadata": { "slideshow": { "slide_type": "-" @@ -6204,7 +6228,7 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 84, "metadata": { "code_folding": [], "slideshow": { @@ -6283,7 +6307,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 85, "metadata": { "slideshow": { "slide_type": "slide" @@ -6320,7 +6344,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 86, "metadata": { "slideshow": { "slide_type": "slide" @@ -6358,7 +6382,7 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 87, "metadata": { "slideshow": { "slide_type": "skip" @@ -6371,7 +6395,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 88, "metadata": { "slideshow": { "slide_type": "slide" @@ -6441,7 +6465,7 @@ "\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\" pointers to other objects in memory. On the contrary, the `range` type does not point to any other objects but instead creates *new* `int` objects \"on the fly\" (i.e., when being looped over)." + "**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)." ] } ], diff --git a/04_iteration_review_and_exercises.ipynb b/04_iteration_review_and_exercises.ipynb index 64d6903..9e2b28f 100644 --- a/04_iteration_review_and_exercises.ipynb +++ b/04_iteration_review_and_exercises.ipynb @@ -455,19 +455,8 @@ "\n", " # answer to Q15.6\n", " # ...\n", - " # ...\n", - " # ...\n", - " # ...\n", - " # ...\n", - " # ...\n", - " # ...\n", - " # ...\n", - " # ...\n", - " # ...\n", - " # ...\n", "\n", " # answer to Q15.7\n", - " # ...\n", " # ..." ] }, @@ -543,7 +532,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Pythonic Re-Factoring" + "#### Pythonic Refactoring" ] }, { @@ -569,7 +558,6 @@ " # ...\n", "\n", " # answer to Q15.10\n", - " # ...\n", " # ..." ] }, @@ -726,7 +714,8 @@ "metadata": {}, "outputs": [], "source": [ - "\n" + "for ... in ...:\n", + " ..." ] }, { @@ -811,9 +800,9 @@ " if offset is not None:\n", " count += offset\n", "\n", - " hanoi_ordered(..., offset=offset) # <- answer to Q15.18 between the ()\n", - " # answer to Q15.18\n", - " hanoi_ordered(..., offset=count) # <- answer to Q15.18 between the ()" + " hanoi_ordered(..., offset=offset) # <- answer to Q15.18\n", + " # ... <- answer to Q15.18\n", + " hanoi_ordered(..., offset=count) # <- answer to Q15.18" ] }, { diff --git a/05_numbers.ipynb b/05_numbers.ipynb index a671e68..48d8623 100644 --- a/05_numbers.ipynb +++ b/05_numbers.ipynb @@ -21,9 +21,9 @@ "source": [ "After learning about the basic building blocks of expressing and structuring the business logic in programs, we focus our attention on the **data types** Python offers us, both built-in and available via the [standard library](https://docs.python.org/3/library/index.html) or third-party packages.\n", "\n", - "We start with the \"simple\" ones: Numeric types in this chapter and textual data in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb). An important fact that holds for all objects of these types is that they are **immutable**. To re-use the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Objects-vs.-Types-vs.-Values), this means that the $0$s and $1$s making up an object's *value* cannot be changed once the bag is created in memory, implying that any operation with or method on the object creates a *new* object in a *different* memory location.\n", + "We start with the \"simple\" ones: Numeric types in this chapter and textual data in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb). An important fact that holds for all objects of these types is that they are **immutable**. To reuse the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Objects-vs.-Types-vs.-Values), this means that the $0$s and $1$s making up an object's *value* cannot be changed once the bag is created in memory, implying that any operation with or method on the object creates a *new* object in a *different* memory location.\n", "\n", - "[Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb) and Chapter 8 then cover the more \"complex\" data types, including, for example, the `list` type. Finally, Chapter 9 completes the picture by introducing language constructs to create custom types.\n", + "[Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb) and [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings.ipynb) then cover the more \"complex\" data types, including, for example, the `list` type. Finally, Chapter 9 completes the picture by introducing language constructs to create custom types.\n", "\n", "We have already seen many hints indicating that numbers are not as trivial to work with as it seems at first sight:\n", "\n", @@ -92,7 +92,7 @@ { "data": { "text/plain": [ - "140087541220560" + "140673805309168" ] }, "execution_count": 2, @@ -307,7 +307,7 @@ } }, "source": [ - "We may pass a `str` object formatted this way as the argument to the [int()](https://docs.python.org/3/library/functions.html#int) built-in, together with `base=2`, to (re-)create an `int` object, for example, with the value of `3`." + "We may pass a `str` object formatted this way as the argument to the [int()](https://docs.python.org/3/library/functions.html#int) built-in, together with `base=2`, to create an `int` object, for example, with the value of `3`." ] }, { @@ -883,7 +883,7 @@ } }, "source": [ - "To (re-)create an `int` object with the value `177`, we call the [int()](https://docs.python.org/3/library/functions.html#int) built-in with a properly formatted `str` object and `base=16` as arguments." + "To obtain a *new* `int` object with the value `177`, we call the [int()](https://docs.python.org/3/library/functions.html#int) built-in with a properly formatted `str` object and `base=16` as arguments." ] }, { @@ -2115,7 +2115,7 @@ { "data": { "text/plain": [ - "140087541402072" + "140673805486768" ] }, "execution_count": 63, @@ -5498,7 +5498,7 @@ { "data": { "text/plain": [ - "140087540555856" + "140673804641712" ] }, "execution_count": 176, @@ -6054,7 +6054,7 @@ } }, "source": [ - "Also, a conjugate() method is bound to every `complex` object. The [complex conjugate](https://en.wikipedia.org/wiki/Complex_conjugate) is defined to be the complex number with identical real part but an imaginary part reversed in sign." + "Also, a `conjugate()` method is bound to every `complex` object. The [complex conjugate](https://en.wikipedia.org/wiki/Complex_conjugate) is defined to be the complex number with identical real part but an imaginary part reversed in sign." ] }, { @@ -6111,17 +6111,17 @@ } }, "source": [ - "Analogous to the discussion of containers and iterables in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Containers-vs.-Iterables), we contrast the *concrete* numeric data types in this chapter with the *abstract* ideas behind [numbers in mathematics](https://en.wikipedia.org/wiki/Number).\n", + "Analogous to the discussion of *containers* and *iterables* in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Containers-vs.-Iterables), we contrast the *concrete* numeric data types in this chapter with the *abstract* ideas behind [numbers in mathematics](https://en.wikipedia.org/wiki/Number).\n", "\n", "The figure below summarizes five *major* sets of [numbers in mathematics](https://en.wikipedia.org/wiki/Number) as we know them from high school:\n", "\n", "- $\\mathbb{N}$: [Natural numbers](https://en.wikipedia.org/wiki/Natural_number) are all non-negative count numbers, e.g., $0, 1, 2, ...$\n", "- $\\mathbb{Z}$: [Integers](https://en.wikipedia.org/wiki/Integer) are all numbers *without* a fractional component, e.g., $-1, 0, 1, ...$\n", "- $\\mathbb{Q}$: [Rational numbers](https://en.wikipedia.org/wiki/Rational_number) are all numbers that can be expressed as a quotient of two integers, e.g., $-\\frac{1}{2}, 0, \\frac{1}{2}, ...$\n", - "- $\\mathbb{R}$: [Real numbers](https://en.wikipedia.org/wiki/Real_number) are all numbers that can be represented as a distance along a line (negative means \"reversed\"), e.g., $\\sqrt{2}, \\pi, \\text{e}, ...$\n", + "- $\\mathbb{R}$: [Real numbers](https://en.wikipedia.org/wiki/Real_number) are all numbers that can be represented as a distance along a line, and negative means \"reversed,\" e.g., $\\sqrt{2}, \\pi, \\text{e}, ...$\n", "- $\\mathbb{C}$: [Complex numbers](https://en.wikipedia.org/wiki/Complex_number) are all numbers of the form $a + b\\textbf{i}$ where $a$ and $b$ are real numbers and $\\textbf{i}$ is the [imaginary number](https://en.wikipedia.org/wiki/Imaginary_number), e.g., $0, \\textbf{i}, 1 + \\textbf{i}, ...$\n", "\n", - "In the listed order, the five sets are perfect subsets and $\\mathbb{C}$ is the largest set (to be precise, all sets are infinite, but they still have a different number of elements)." + "In the listed order, the five sets are perfect subsets of the respective following sets, and $\\mathbb{C}$ is the largest set (cf., the figure below illustrates that observation as well). To be precise, all sets are infinite, but they still have a different number of elements." ] }, { @@ -6143,13 +6143,13 @@ } }, "source": [ - "The *concrete* data types introduced in this chapter are all *imperfect* models of *abstract* mathematical ideas.\n", + "The data types introduced in this chapter are all *imperfect* models of *abstract* mathematical ideas.\n", "\n", - "The `int` and `Fraction` types are the models \"closest\" to the idea they implement: Whereas $\\mathbb{Z}$ and $\\mathbb{Q}$ are, by definition, infinite, every computer runs out of bits when representing sufficiently large integers or fractions with a sufficiently large number of decimals. However, within a system-dependent minimum and maximum integer range, we can model an integer or fraction without any loss in precision.\n", + "The `int` and `Fraction` types are the models \"closest\" to the idea they implement: Whereas $\\mathbb{Z}$ and $\\mathbb{Q}$ are, by definition, infinite, every computer runs out of bits when representing sufficiently large integers or fractions with a sufficiently large number of decimals. However, within a system-dependent range, we can model an integer or fraction without any loss in precision.\n", "\n", "For the other types, in particular, the `float` type, the implications of their imprecision are discussed in detail above.\n", "\n", - "The abstract concepts behind the four outer-most mathematical sets are part of Python since [PEP 3141](https://www.python.org/dev/peps/pep-3141/) in 2007. The [numbers](https://docs.python.org/3/library/numbers.html) module in the [standard library](https://docs.python.org/3/library/index.html) defines what programmers call the **[numerical tower](https://en.wikipedia.org/wiki/Numerical_tower)**, a collection of five **[abstract data types](https://en.wikipedia.org/wiki/Abstract_data_type)**, or **abstract base classes** as they are called in Python jargon:\n", + "The abstract concepts behind the four outer-most mathematical sets are formalized in Python since [PEP 3141](https://www.python.org/dev/peps/pep-3141/) in 2007. The [numbers](https://docs.python.org/3/library/numbers.html) module in the [standard library](https://docs.python.org/3/library/index.html) defines what programmers call the **[numerical tower](https://en.wikipedia.org/wiki/Numerical_tower)**, a collection of five **[abstract data types](https://en.wikipedia.org/wiki/Abstract_data_type)**, or **abstract base classes** (ABCs) as they are called in Python jargon:\n", "\n", "- `numbers.Number`: \"any number\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Number))\n", "- `numbers.Complex`: \"all complex numbers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Complex))\n", @@ -6220,9 +6220,9 @@ "source": [ "As a reminder, the built-in [help()](https://docs.python.org/3/library/functions.html#help) function is always our friend.\n", "\n", - "The abstract types' docstrings are unsurprisingly similar to the corresponding concrete types' docstrings (for now, let's not worry about the dunder-style names in the docstrings).\n", + "The ABCs' docstrings are unsurprisingly similar to the corresponding data types' docstrings. For now, let's not worry about the dunder-style names in the docstrings.\n", "\n", - "For example, both `numbers.Complex` and `complex` list the `imag` and `real` attributes." + "For example, both `numbers.Complex` and `complex` list the `imag` and `real` attributes shown above." ] }, { @@ -6231,7 +6231,7 @@ "metadata": { "scrolled": true, "slideshow": { - "slide_type": "slide" + "slide_type": "skip" } }, "outputs": [ @@ -6336,17 +6336,6 @@ "help(numbers.Complex)" ] }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "For sure, Python understands the built-in types as literals." - ] - }, { "cell_type": "code", "execution_count": 201, @@ -6509,9 +6498,9 @@ } }, "source": [ - "One way to use *abstract* data types is to use them in place of a *concrete* data type.\n", + "The primary purpose of ABCs is to classify the *concrete* data types and standardize how they behave.\n", "\n", - "For example, we may pass them as arguments to the built-in [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function and check in which of the five mathematical sets the object `1 / 10` is." + "For, example, as all numeric data types are `Complex` numbers in the abstract sense, they all work with the built-in [abs()](https://docs.python.org/3/library/functions.html#abs) function (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Complex)). While it is intuitively clear what the [absolute value](https://en.wikipedia.org/wiki/Absolute_value) (i.e., \"distance\" to $0$) of an integer, a fraction, or any real number is, [abs()](https://docs.python.org/3/library/functions.html#abs) calculates the equivalent of that for complex numbers. That concept is called the [magnitude](https://en.wikipedia.org/wiki/Magnitude_%28mathematics%29) of a number, and is really a *generalization* of the absolute value." ] }, { @@ -6526,7 +6515,7 @@ { "data": { "text/plain": [ - "True" + "42" ] }, "execution_count": 202, @@ -6535,7 +6524,7 @@ } ], "source": [ - "isinstance(1 / 10, float)" + "abs(-42)" ] }, { @@ -6543,14 +6532,14 @@ "execution_count": 203, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ - "True" + "Decimal('0.1')" ] }, "execution_count": 203, @@ -6559,7 +6548,7 @@ } ], "source": [ - "isinstance(1 / 10, numbers.Number)" + "abs(Decimal(\"-0.1\"))" ] }, { @@ -6574,7 +6563,7 @@ { "data": { "text/plain": [ - "True" + "5.0" ] }, "execution_count": 204, @@ -6583,12 +6572,263 @@ } ], "source": [ - "isinstance(1 / 10, numbers.Complex)" + "abs(4 - 3j)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "On the contrary, only `Real` numbers in the abstract sense may be rounded with the built-in [round()](https://docs.python.org/3/library/functions.html#round) function." ] }, { "cell_type": "code", "execution_count": 205, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 205, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "round(42.1)" + ] + }, + { + "cell_type": "code", + "execution_count": 206, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 206, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "round(Decimal(\"0.1\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`Complex` numbers are two-dimensional. So, rounding makes no sense and raises a `TypeError`." + ] + }, + { + "cell_type": "code", + "execution_count": 207, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "type complex doesn't define __round__ method", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mround\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m3j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: type complex doesn't define __round__ method" + ] + } + ], + "source": [ + "round(4 + 3j)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Knowing what ABCs a numeric type adheres to, is not only important in the context of built-ins. The [trunc()](https://docs.python.org/3/library/math.html#math.trunc) function from the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html), for example, only works with `Real` types (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Real))." + ] + }, + { + "cell_type": "code", + "execution_count": 208, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import math" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[trunc()](https://docs.python.org/3/library/math.html#math.trunc) cuts off a number's decimals." + ] + }, + { + "cell_type": "code", + "execution_count": 209, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 209, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.trunc(9 / 10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A `Complex` number leads to a `TypeError`." + ] + }, + { + "cell_type": "code", + "execution_count": 210, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "type complex doesn't define __trunc__ method", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0.9\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: type complex doesn't define __trunc__ method" + ] + } + ], + "source": [ + "math.trunc(0.9 + 1j)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Another way to use ABCs is in place of a *concrete* data type.\n", + "\n", + "For example, we may pass them as arguments to the built-in [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function and check in which of the five mathematical sets the object `1 / 10` is." + ] + }, + { + "cell_type": "code", + "execution_count": 211, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 211, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(1 / 10, float) # we know that from before" + ] + }, + { + "cell_type": "code", + "execution_count": 212, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 212, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(1 / 10, numbers.Number) # a float object is a Number in the abstract sense" + ] + }, + { + "cell_type": "code", + "execution_count": 213, "metadata": { "slideshow": { "slide_type": "-" @@ -6601,13 +6841,37 @@ "True" ] }, - "execution_count": 205, + "execution_count": 213, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "isinstance(1 / 10, numbers.Real)" + "isinstance(1 / 10, numbers.Complex) # float objects are always also Complex numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 214, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 214, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(1 / 10, numbers.Real) # a float object's purpose is to model a Real number" ] }, { @@ -6623,7 +6887,7 @@ }, { "cell_type": "code", - "execution_count": 206, + "execution_count": 215, "metadata": { "slideshow": { "slide_type": "slide" @@ -6636,13 +6900,13 @@ "False" ] }, - "execution_count": 206, + "execution_count": 215, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "isinstance(1 / 10, numbers.Rational)" + "isinstance(1 / 10, numbers.Rational) # the type of `1 / 10` is what is important, not its value" ] }, { @@ -6658,7 +6922,7 @@ }, { "cell_type": "code", - "execution_count": 207, + "execution_count": 216, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6671,7 +6935,7 @@ "True" ] }, - "execution_count": 207, + "execution_count": 216, "metadata": {}, "output_type": "execute_result" } @@ -6682,7 +6946,7 @@ }, { "cell_type": "code", - "execution_count": 208, + "execution_count": 217, "metadata": { "slideshow": { "slide_type": "-" @@ -6695,7 +6959,7 @@ "False" ] }, - "execution_count": 208, + "execution_count": 217, "metadata": {}, "output_type": "execute_result" } @@ -6723,46 +6987,9 @@ } }, "source": [ - "Replacing *concrete* data types with *abstract* ones is particularly valuable in the context of input validation: The revised version of the `factorial()` function below allows its user to take advantage of *duck typing*: If a real but non-integer argument `n` is passed in, `factorial()` tries to cast `n` as an `int` object with the [trunc()](https://docs.python.org/3/library/math.html#math.trunc) function from the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html). [trunc()](https://docs.python.org/3/library/math.html#math.trunc) cuts off all decimals and any *concrete* numeric type implementing the *abstract* `numbers.Real` type supports it (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Real)).\n", + "Replacing *concrete* data types with ABCs is particularly valuable in the context of input validation: The revised version of the `factorial()` function below allows its user to take advantage of *duck typing*: If a real but non-integer argument `n` is passed in, `factorial()` tries to cast `n` as an `int` object with [math.trunc()](https://docs.python.org/3/library/math.html#math.trunc).\n", "\n", - "Two popular and distinguished Pythonistas, [Luciano Ramalho](https://github.com/ramalho) and [Alex Martelli](https://en.wikipedia.org/wiki/Alex_Martelli), coin the term **goose typing** to specifically mean using the built-in [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function with an *abstract base class* (cf., Chapter 11 in this [book](https://www.amazon.com/Fluent-Python-Concise-Effective-Programming/dp/1491946008) or this [summary](https://dgkim5360.github.io/blog/python/2017/07/duck-typing-vs-goose-typing-pythonic-interfaces/) thereof)." - ] - }, - { - "cell_type": "code", - "execution_count": 209, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [], - "source": [ - "import math" - ] - }, - { - "cell_type": "code", - "execution_count": 210, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 210, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "math.trunc(1 / 10)" + "Two popular and distinguished Pythonistas, [Luciano Ramalho](https://github.com/ramalho) and [Alex Martelli](https://en.wikipedia.org/wiki/Alex_Martelli), coin the term **goose typing** to specifically mean using the built-in [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function with an ABC (cf., Chapter 11 in this [book](https://www.amazon.com/Fluent-Python-Concise-Effective-Programming/dp/1491946008) or this [summary](https://dgkim5360.github.io/blog/python/2017/07/duck-typing-vs-goose-typing-pythonic-interfaces/) thereof)." ] }, { @@ -6778,7 +7005,7 @@ }, { "cell_type": "code", - "execution_count": 211, + "execution_count": 218, "metadata": { "slideshow": { "slide_type": "slide" @@ -6810,7 +7037,7 @@ "\n", " if n < 0:\n", " raise ValueError(\"Factorial is not defined for negative integers\")\n", - " elif n == 0:\n", + " elif n == 0: # = base case\n", " return 1\n", " return n * factorial(n - 1)" ] @@ -6828,7 +7055,7 @@ }, { "cell_type": "code", - "execution_count": 212, + "execution_count": 219, "metadata": { "slideshow": { "slide_type": "slide" @@ -6841,7 +7068,7 @@ "1" ] }, - "execution_count": 212, + "execution_count": 219, "metadata": {}, "output_type": "execute_result" } @@ -6852,7 +7079,7 @@ }, { "cell_type": "code", - "execution_count": 213, + "execution_count": 220, "metadata": { "slideshow": { "slide_type": "-" @@ -6865,7 +7092,7 @@ "6" ] }, - "execution_count": 213, + "execution_count": 220, "metadata": {}, "output_type": "execute_result" } @@ -6876,7 +7103,7 @@ }, { "cell_type": "code", - "execution_count": 214, + "execution_count": 221, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6889,7 +7116,7 @@ "6" ] }, - "execution_count": 214, + "execution_count": 221, "metadata": {}, "output_type": "execute_result" } @@ -6906,12 +7133,12 @@ } }, "source": [ - "With the keyword-only argument `strict`, we can control whether or not a passed in `float` object may be rounded. By default, this is not allowed and results in a `TypeError`." + "With the keyword-only argument `strict`, we can control whether or not a passed in `float` object may come with decimals that are then truncated. By default, this is not allowed and results in a `TypeError`." ] }, { "cell_type": "code", - "execution_count": 215, + "execution_count": 222, "metadata": { "slideshow": { "slide_type": "slide" @@ -6925,8 +7152,8 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mReal\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mstrict\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 18\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"n is not an integer-like value; it has decimals\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 19\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mReal\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mstrict\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 18\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"n is not an integer-like value; it has decimals\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 19\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mTypeError\u001b[0m: n is not an integer-like value; it has decimals" ] } @@ -6935,9 +7162,20 @@ "factorial(3.1)" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In non-strict mode, the passed in `3.1` is truncated into `3` resulting in a factorial of `6`." + ] + }, { "cell_type": "code", - "execution_count": 216, + "execution_count": 223, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6950,7 +7188,7 @@ "6" ] }, - "execution_count": 216, + "execution_count": 223, "metadata": {}, "output_type": "execute_result" } @@ -6967,12 +7205,12 @@ } }, "source": [ - "For `complex` numbers, `factorial()` still raises a `TypeError`." + "For `complex` numbers, `factorial()` still raises a `TypeError` because they are neither an `Integral` nor a `Real` number." ] }, { "cell_type": "code", - "execution_count": 217, + "execution_count": 224, "metadata": { "slideshow": { "slide_type": "slide" @@ -6986,8 +7224,8 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m2j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 21\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 22\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m2j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 21\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 22\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mTypeError\u001b[0m: Factorial is only defined for integers" ] } @@ -7016,20 +7254,20 @@ }, "source": [ "There exist three numeric types in core Python:\n", - "- `int`: a near-perfect model for whole numbers (i.e., the set $\\mathbb{Z}$); inherently precise\n", - "- `float`: the \"gold\" standard to approximate real numbers (i.e., the set $\\mathbb{R}$); inherently imprecise\n", - "- `complex`: layer on top of the `float` type; therefore inherently imprecise\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](https://docs.python.org/3/library/index.html) adds two more types that can be used as substitutes for `float` objects:\n", + "Furthermore, the [standard library](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., the set $\\mathbb{Q}$); built on top of the `int` type and therefore inherently precise\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 the various **abstract** ideas of what numbers are in mathematics." + "The **numerical tower** is Python's way of implementing various **abstract** ideas of what numbers are in mathematics." ] } ], diff --git a/05_numbers_review_and_exercises.ipynb b/05_numbers_review_and_exercises.ipynb index 90983d4..2ae8301 100644 --- a/05_numbers_review_and_exercises.ipynb +++ b/05_numbers_review_and_exercises.ipynb @@ -156,7 +156,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q9**: How can **abstract data types**, for example, as defined in the **numerical tower**, be helpful in enabling **duck typing**?" + "**Q9**: How can **abstract base classes**, for example, as defined in the **numerical tower**, be helpful in enabling **duck typing**?" ] }, { @@ -519,7 +519,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q19.7**: Re-write the function `discounted_price()` from [Chapter 3's Review & Exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_review_and_exercises.ipynb#Discounting-Customer-Orders) section!\n", + "**Q19.7**: Rewrite the function `discounted_price()` from [Chapter 3's Review & Exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_review_and_exercises.ipynb#Discounting-Customer-Orders) section!\n", "\n", "It takes the *positional* arguments `unit_price` and `quantity` and implements a discount scheme for a line item in a customer order as follows:\n", "\n", @@ -530,7 +530,7 @@ "\n", "The function then returns the overall price for the line item as a `Decimal` number with a precision of *two* decimals.\n", "\n", - "Enable **duck typing** by allowing the function to be called with various numeric types as the arguments, in particular, `quantity` may be a non-integer as well: Use an appropriate **abstract data type** from the [numbers](https://docs.python.org/3/library/numbers.html) module in the [standard library](https://docs.python.org/3/library/index.html) to verify the arguments' types and also that they are both positive!\n", + "Enable **duck typing** by allowing the function to be called with various numeric types as the arguments, in particular, `quantity` may be a non-integer as well: Use an appropriate **abstract base class** from the [numbers](https://docs.python.org/3/library/numbers.html) module in the [standard library](https://docs.python.org/3/library/index.html) to verify the arguments' types and also that they are both positive!\n", "\n", "It is considered a *best practice* to only round towards the *end* of the calculations." ] @@ -551,22 +551,6 @@ "outputs": [], "source": [ "def discounted_price(...):\n", - " ...\n", - " ...\n", - " ...\n", - " ...\n", - " ...\n", - " ...\n", - " ...\n", - " ...\n", - " ...\n", - " ...\n", - " ...\n", - " ...\n", - " ...\n", - " ...\n", - " ...\n", - " ...\n", " ..." ] }, diff --git a/06_text.ipynb b/06_text.ipynb index 44eaa3c..3fbbcb5 100644 --- a/06_text.ipynb +++ b/06_text.ipynb @@ -54,7 +54,7 @@ }, "outputs": [], "source": [ - "school = \"WHU - Otto Beisheim School of Management\"" + "text = \"Lorem ipsum dolor sit amet, consectetur ...\"" ] }, { @@ -65,7 +65,7 @@ } }, "source": [ - "Like everything in Python, `school` is an object." + "Like everything in Python, `text` is an object." ] }, { @@ -80,7 +80,7 @@ { "data": { "text/plain": [ - "140488988107952" + "139674889049360" ] }, "execution_count": 2, @@ -89,7 +89,7 @@ } ], "source": [ - "id(school)" + "id(text)" ] }, { @@ -113,7 +113,7 @@ } ], "source": [ - "type(school)" + "type(text)" ] }, { @@ -141,7 +141,7 @@ { "data": { "text/plain": [ - "'WHU - Otto Beisheim School of Management'" + "'Lorem ipsum dolor sit amet, consectetur ...'" ] }, "execution_count": 4, @@ -150,7 +150,7 @@ } ], "source": [ - "school" + "text" ] }, { @@ -182,7 +182,7 @@ { "data": { "text/plain": [ - "\"It's cool that strings are so versatile in Python!\"" + "\"It's cool that strings are so versatile!\"" ] }, "execution_count": 5, @@ -191,7 +191,7 @@ } ], "source": [ - "\"It's cool that strings are so versatile in Python!\"" + "\"It's cool that strings are so versatile!\"" ] }, { @@ -206,7 +206,7 @@ { "data": { "text/plain": [ - "\"It's cool that strings are so versatile in Python!\"" + "\"It's cool that strings are so versatile!\"" ] }, "execution_count": 6, @@ -215,7 +215,7 @@ } ], "source": [ - "'It\\'s cool that strings are so versatile in Python!'" + "'It\\'s cool that strings are so versatile!'" ] }, { @@ -623,7 +623,7 @@ "execution_count": 20, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "skip" } }, "outputs": [ @@ -660,7 +660,7 @@ "execution_count": 21, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "skip" } }, "outputs": [ @@ -737,7 +737,7 @@ } }, "source": [ - "A **sequence** is yet another *abstract* concept (cf., *containers* and *iterables* in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Containers-vs.-Iterables)).\n", + "A **sequence** is yet another *abstract* concept (cf., the \"*Containers vs. Iterables*\" section in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Containers-vs.-Iterables)).\n", "\n", "It unifies *four* [orthogonal](https://en.wikipedia.org/wiki/Orthogonality) (i.e., \"independent\") behaviors into one idea: Any data type, such as `str`, is considered a sequence if it simultaneously\n", "\n", @@ -748,7 +748,7 @@ "\n", "[Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Collections-vs.-Sequences) formalizes sequences in great detail. Here, we keep our focus on the `str` type that historically received its name as it models a \"**[string of characters](https://en.wikipedia.org/wiki/String_%28computer_science%29)**,\" and a \"string\" is more formally called a sequence in the computer science literature.\n", "\n", - "Behaving like a sequence, `str` objects may be treated like `list` objects in many cases. For example, the built-in [len()](https://docs.python.org/3/library/functions.html#len) function tells us how many elements (i.e., characters) make up `school`. [len()](https://docs.python.org/3/library/functions.html#len) would not work on an \"*infinite*\" object: As anything modeled in a program must fit into a computer's finite memory at runtime, there cannot exist objects containing a truly infinite number of elements; however, [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Collections-vs.-Sequences#Mapping) introduces *concrete* iterable data types that can be used to model an *infinite* series of elements and that, consequently, have no concept of \"length.\"" + "Behaving like a sequence, `str` objects may be treated like `list` objects in many cases. For example, the built-in [len()](https://docs.python.org/3/library/functions.html#len) function tells us how many elements (i.e., characters) make up `text`. [len()](https://docs.python.org/3/library/functions.html#len) would not work with an *infinite* object: As anything modeled in a program must fit into a computer's finite memory at runtime, there cannot exist objects containing a truly infinite number of elements; however, [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Iterators-vs.-Iterables) introduces iterable data types that can be used to model an *infinite* series of elements and that, consequently, have no concept of \"length.\"" ] }, { @@ -763,7 +763,7 @@ { "data": { "text/plain": [ - "40" + "43" ] }, "execution_count": 23, @@ -772,7 +772,7 @@ } ], "source": [ - "len(school)" + "len(text)" ] }, { @@ -783,9 +783,7 @@ } }, "source": [ - "Being iterable, we can iterate over a `str` object, for example, with a `for`-loop, and do something with the individual characters, for example, print them out with extra space in between them.\n", - "\n", - "[Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Containers-vs.-Iterables) already shows that we can loop over *different* concrete types: The example there first loops over the `list` object `[0, 1, 2, 3, 4]` and then the `range` object `range(5)`, with the same outcome. Now, we add the `str` type to the list of *concrete* types we can loop over. *Abstractly* speaking, all three are *iterable*, and there are many more iterable types in Python." + "Being iterable, we may loop over `text` and do something with the individual characters, for example, print them out with extra space in between them." ] }, { @@ -801,12 +799,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "W H U - O t t o B e i s h e i m S c h o o l o f M a n a g e m e n t " + "L o r e m i p s u m d o l o r s i t a m e t , c o n s e c t e t u r . . . " ] } ], "source": [ - "for letter in school:\n", + "for letter in text:\n", " print(letter, end=\" \")" ] }, @@ -818,7 +816,9 @@ } }, "source": [ - "Being a container, we can check if a given object is a member of a sequence with the `in` operator. In the context of `str` objects, the `in` operator has *two* usages: First, it checks if a *single* character is contained in a `str` object. Second, it may also check if a shorter `str` object, then called a **substring**, is contained in a longer one." + "Being a container, we may check if a given `str` object is contained in `text` with the `in` operator.\n", + "\n", + "The `in` operator has *two* usages: First, it checks if a *single* character is contained in a `str` object. Second, it may also check if a shorter `str` object, then called a **substring**, is contained in a longer one." ] }, { @@ -842,7 +842,7 @@ } ], "source": [ - "\"O\" in school" + "\"L\" in text" ] }, { @@ -866,7 +866,7 @@ } ], "source": [ - "\"WHU\" in school" + "\"ipsum\" in text" ] }, { @@ -890,7 +890,7 @@ } ], "source": [ - "\"EBS\" in school" + "\"veni, vidi, vici\" in text" ] }, { @@ -927,7 +927,7 @@ { "data": { "text/plain": [ - "'W'" + "'L'" ] }, "execution_count": 28, @@ -936,7 +936,7 @@ } ], "source": [ - "school[0]" + "text[0]" ] }, { @@ -951,7 +951,7 @@ { "data": { "text/plain": [ - "'H'" + "'o'" ] }, "execution_count": 29, @@ -960,7 +960,7 @@ } ], "source": [ - "school[1]" + "text[1]" ] }, { @@ -971,7 +971,7 @@ } }, "source": [ - "The index must be of type `int`." + "The index must be of type `int` or we get a `TypeError`." ] }, { @@ -979,7 +979,7 @@ "execution_count": 30, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "skip" } }, "outputs": [ @@ -990,13 +990,13 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mschool\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: string indices must be integers" ] } ], "source": [ - "school[1.0]" + "text[1.0]" ] }, { @@ -1015,14 +1015,14 @@ "execution_count": 31, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ - "'t'" + "'.'" ] }, "execution_count": 31, @@ -1031,7 +1031,7 @@ } ], "source": [ - "school[39]" + "text[42] # = len(text) - 1" ] }, { @@ -1061,13 +1061,13 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mschool\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m40\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m43\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;31m# = len(text)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mIndexError\u001b[0m: string index out of range" ] } ], "source": [ - "school[40]" + "text[43] # = len(text)" ] }, { @@ -1078,7 +1078,7 @@ } }, "source": [ - "We may use *negative* indexes to start counting from the end of the `str` object." + "We may use *negative* indexes to start counting from the end of the `str` object. That only works because sequences are *finite*." ] }, { @@ -1093,7 +1093,7 @@ { "data": { "text/plain": [ - "'t'" + "'.'" ] }, "execution_count": 33, @@ -1102,7 +1102,7 @@ } ], "source": [ - "school[-1]" + "text[-1]" ] }, { @@ -1128,7 +1128,7 @@ { "data": { "text/plain": [ - "'O'" + "'i'" ] }, "execution_count": 34, @@ -1137,7 +1137,7 @@ } ], "source": [ - "school[6]" + "text[6]" ] }, { @@ -1152,7 +1152,7 @@ { "data": { "text/plain": [ - "'O'" + "'i'" ] }, "execution_count": 35, @@ -1161,7 +1161,7 @@ } ], "source": [ - "school[-34]" + "text[-37]" ] }, { @@ -1185,7 +1185,9 @@ "source": [ "A **slice** is a substring of a `str` object.\n", "\n", - "The **slicing operator** is a generalization of the indexing operator: We can put one, two, or three integers within the brackets, separated by colons `:`. The three integers are then referred to as the *start*, *end*, and *step* values." + "The **slicing operator** is a generalization of the indexing operator: We can put one, two, or three integers within the brackets, separated by colons `:`. The three integers are then referred to as the *start*, *end*, and *step* values.\n", + "\n", + "Let's start with two integers, *start* and *end*." ] }, { @@ -1200,7 +1202,7 @@ { "data": { "text/plain": [ - "'WHU'" + "'Lorem'" ] }, "execution_count": 36, @@ -1209,7 +1211,7 @@ } ], "source": [ - "school[0:3]" + "text[0:5]" ] }, { @@ -1220,7 +1222,9 @@ } }, "source": [ - "Whereas the *start* is always included in the result, the *end* is not. Counter-intuitive at first, this makes working with individual slices easier as they add up to the original `str` object again. As the *end* is not included, we must end the second slice with `len(school)` or `40` below." + "Whereas the *start* is always included in the result, the *end* is not. Counter-intuitive at first, this makes working with individual slices easier as they add up to the original `str` object again.\n", + "\n", + "So, as the *end* is *not* included, we must end the second slice with `len(text)` or `43` below." ] }, { @@ -1235,7 +1239,7 @@ { "data": { "text/plain": [ - "'WHU - Otto Beisheim School of Management'" + "'Lorem ipsum dolor sit amet, consectetur ...'" ] }, "execution_count": 37, @@ -1244,7 +1248,7 @@ } ], "source": [ - "school[0:3] + school[3:40]" + "text[0:5] + text[5:len(text)]" ] }, { @@ -1255,7 +1259,7 @@ } }, "source": [ - "For convenience, the indexes do not need to lie in the range from 0 to the `str` object's \"length\" when slicing. This is *not* the case for indexing as the `IndexError` above shows." + "For convenience, the indexes do not need to lie within the range from `0` to `len(text)` when slicing." ] }, { @@ -1270,7 +1274,7 @@ { "data": { "text/plain": [ - "'WHU - Otto Beisheim School of Management'" + "'Lorem ipsum dolor sit amet, consectetur ...'" ] }, "execution_count": 38, @@ -1279,7 +1283,7 @@ } ], "source": [ - "school[0:999]" + "text[0:999]" ] }, { @@ -1305,7 +1309,7 @@ { "data": { "text/plain": [ - "'WHU - Otto Beisheim School of Management'" + "'Lorem ipsum dolor sit amet, consectetur ...'" ] }, "execution_count": 39, @@ -1314,7 +1318,7 @@ } ], "source": [ - "school[:3] + school[3:]" + "text[:5] + text[5:]" ] }, { @@ -1340,7 +1344,7 @@ { "data": { "text/plain": [ - "'WHU Otto Beisheim School'" + "'Lorem ipsum dolor sit amet.'" ] }, "execution_count": 40, @@ -1349,7 +1353,7 @@ } ], "source": [ - "school[:3] + school[5:26]" + "text[:26] + text[42]" ] }, { @@ -1375,7 +1379,7 @@ { "data": { "text/plain": [ - "'WU-Ot esemSho fMngmn'" + "'Lrmismdlrstae,cnettr..'" ] }, "execution_count": 41, @@ -1384,7 +1388,7 @@ } ], "source": [ - "school[::2]" + "text[::2]" ] }, { @@ -1410,7 +1414,7 @@ { "data": { "text/plain": [ - "'tnemeganaM fo loohcS miehsieB ottO - UHW'" + "'... rutetcesnoc ,tema tis rolod muspi meroL'" ] }, "execution_count": 42, @@ -1419,7 +1423,7 @@ } ], "source": [ - "school[::-1]" + "text[::-1]" ] }, { @@ -1443,9 +1447,9 @@ "source": [ "Whereas elements of a `list` object *may* be *re-assigned*, as shortly hinted at in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?), this is *not* allowed for `str` objects. Once created, they *cannot* be *changed*. Formally, we say that they are **immutable**. In that regard, `str` objects and all the numeric types in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb) are alike.\n", "\n", - "On the contrary, objects that may be changed after creation, are called **mutable**. We already saw in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?) how mutable objects are more difficult to reason about for a beginner, in particular, if more than *one* variable point to one. Yet, mutability does have its place in a programmer's toolbox, and we revisit this idea in the next chapters.\n", + "On the contrary, objects that may be changed after creation, are called **mutable**. We already saw in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?) how mutable objects are more difficult to reason about for a beginner, in particular, if more than *one* variable references it. Yet, mutability does have its place in a programmer's toolbox, and we revisit this idea in the next chapters.\n", "\n", - "`str` objects are *immutable* as the `TypeError` indicates: Assignment to an index is *not* supported by this type." + "The `TypeError` indicates that `str` objects are *immutable*: Assignment to an index or a slice are *not* supported." ] }, { @@ -1464,66 +1468,18 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mschool\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"E\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"Z\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: 'str' object does not support item assignment" ] } ], "source": [ - "school[0] = \"E\"" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "The only thing we can do is to create a *new* `str` object in memory." + "text[0] = \"Z\"" ] }, { "cell_type": "code", "execution_count": 44, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [], - "source": [ - "new_school = \"EBS\" + school[3:]" - ] - }, - { - "cell_type": "code", - "execution_count": 45, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "'EBS - Otto Beisheim School of Management'" - ] - }, - "execution_count": 45, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "new_school" - ] - }, - { - "cell_type": "code", - "execution_count": 46, "metadata": { "slideshow": { "slide_type": "-" @@ -1531,125 +1487,19 @@ }, "outputs": [ { - "data": { - "text/plain": [ - "140488987187120" - ] - }, - "execution_count": 46, - "metadata": {}, - "output_type": "execute_result" + "ename": "TypeError", + "evalue": "'str' object does not support item assignment", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"random\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: 'str' object does not support item assignment" + ] } ], "source": [ - "id(new_school)" - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "140488988107952" - ] - }, - "execution_count": 47, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "id(school)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "## String Operations" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "As mentioned before, the `+` and `*` operators are *overloaded* and used for **string concatenation**." - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [], - "source": [ - "greeting = \"Hello \"" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "'Hello WHU'" - ] - }, - "execution_count": 49, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "greeting + school[:3]" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "'WHU WHU WHU WHU WHU WHU WHU WHU WHU WHU '" - ] - }, - "execution_count": 50, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "10 * school[:4]" + "text[:5] = \"random\"" ] }, { @@ -1678,7 +1528,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 45, "metadata": { "slideshow": { "slide_type": "slide" @@ -1688,21 +1538,45 @@ { "data": { "text/plain": [ - "6" + "'Lorem ipsum dolor sit amet, consectetur ...'" ] }, - "execution_count": 51, + "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "school.find(\"O\")" + "text" ] }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "22" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.find(\"a\")" + ] + }, + { + "cell_type": "code", + "execution_count": 47, "metadata": { "slideshow": { "slide_type": "-" @@ -1715,18 +1589,18 @@ "-1" ] }, - "execution_count": 52, + "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "school.find(\"Z\")" + "text.find(\"z\")" ] }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 48, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1736,16 +1610,16 @@ { "data": { "text/plain": [ - "11" + "12" ] }, - "execution_count": 53, + "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "school.find(\"Beisheim\")" + "text.find(\"dolor\")" ] }, { @@ -1761,7 +1635,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 49, "metadata": { "slideshow": { "slide_type": "slide" @@ -1771,21 +1645,21 @@ { "data": { "text/plain": [ - "12" + "1" ] }, - "execution_count": 54, + "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "school.find(\"e\")" + "text.find(\"o\")" ] }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 50, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1795,24 +1669,24 @@ { "data": { "text/plain": [ - "16" + "13" ] }, - "execution_count": 55, + "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "school.find(\"e\", 13) # 13 not 12 as otherwise the same character is found" + "text.find(\"o\", 2) # 2 not 1 as otherwise the same \"o\" is found again" ] }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 51, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ @@ -1822,13 +1696,13 @@ "-1" ] }, - "execution_count": 56, + "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "school.find(\"e\", 13, 15) # \"e\" does not occur in the specified slice" + "text.find(\"o\", 2, 12) # \"o\" does not occur in the specified slice" ] }, { @@ -1844,7 +1718,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 52, "metadata": { "slideshow": { "slide_type": "slide" @@ -1854,16 +1728,16 @@ { "data": { "text/plain": [ - "4" + "1" ] }, - "execution_count": 57, + "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "school.count(\"o\")" + "text.count(\"l\")" ] }, { @@ -1874,12 +1748,12 @@ } }, "source": [ - "As [count()](https://docs.python.org/3/library/stdtypes.html#str.count) is *case-sensitive*, we must **chain** it with the [lower()](https://docs.python.org/3/library/stdtypes.html#str.lower) method to get the count of all \"O\"s and \"o\"s." + "As [count()](https://docs.python.org/3/library/stdtypes.html#str.count) is *case-sensitive*, we must **chain** it with the [lower()](https://docs.python.org/3/library/stdtypes.html#str.lower) method to get the count of all `\"L\"`s and `\"l\"`s." ] }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 53, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1889,16 +1763,16 @@ { "data": { "text/plain": [ - "5" + "2" ] }, - "execution_count": 58, + "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "school.lower().count(\"o\")" + "text.lower().count(\"l\")" ] }, { @@ -1909,12 +1783,12 @@ } }, "source": [ - "Alternatively, we may use the [upper()](https://docs.python.org/3/library/stdtypes.html#str.upper) method and search for \"O\"s." + "Alternatively, we may use the [upper()](https://docs.python.org/3/library/stdtypes.html#str.upper) method and search for `\"O\"`s." ] }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 54, "metadata": { "slideshow": { "slide_type": "skip" @@ -1924,16 +1798,16 @@ { "data": { "text/plain": [ - "5" + "2" ] }, - "execution_count": 59, + "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "school.upper().count(\"O\")" + "text.upper().count(\"L\")" ] }, { @@ -1944,12 +1818,12 @@ } }, "source": [ - "Because `str` objects are *immutable*, the methods always return *new* objects, even if a method does *not* change the value of the `str` object at all." + "Because `str` objects are *immutable*, [upper()](https://docs.python.org/3/library/stdtypes.html#str.upper) and [lower()](https://docs.python.org/3/library/stdtypes.html#str.lower) return *new* `str` objects, even if they do *not* change the value of the original `str` object." ] }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 55, "metadata": { "slideshow": { "slide_type": "slide" @@ -1957,12 +1831,12 @@ }, "outputs": [], "source": [ - "example = \"test\"" + "example = \"random\"" ] }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 56, "metadata": { "slideshow": { "slide_type": "-" @@ -1972,10 +1846,10 @@ { "data": { "text/plain": [ - "140489068668384" + "139674947440512" ] }, - "execution_count": 61, + "execution_count": 56, "metadata": {}, "output_type": "execute_result" } @@ -1986,7 +1860,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 57, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1999,7 +1873,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 58, "metadata": { "slideshow": { "slide_type": "-" @@ -2009,10 +1883,10 @@ { "data": { "text/plain": [ - "140488987340848" + "139674887826488" ] }, - "execution_count": 63, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } @@ -2034,7 +1908,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 59, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2047,7 +1921,7 @@ "False" ] }, - "execution_count": 64, + "execution_count": 59, "metadata": {}, "output_type": "execute_result" } @@ -2058,7 +1932,7 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 60, "metadata": { "slideshow": { "slide_type": "-" @@ -2071,7 +1945,7 @@ "True" ] }, - "execution_count": 65, + "execution_count": 60, "metadata": {}, "output_type": "execute_result" } @@ -2088,12 +1962,14 @@ } }, "source": [ - "Another popular string method is [split()](https://docs.python.org/3/library/stdtypes.html#str.split): It separates a longer `str` object into a list of smaller ones. By default, groups of whitespace are used as the *separator*." + "Another popular string method is [split()](https://docs.python.org/3/library/stdtypes.html#str.split): It separates a longer `str` object into smaller ones contained in a `list` object. By default, groups of contiguous whitespace are used as the *separator*.\n", + "\n", + "As an example, we use [split()](https://docs.python.org/3/library/stdtypes.html#str.split) to print out the individual words in `text` on separate lines." ] }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 61, "metadata": { "slideshow": { "slide_type": "slide" @@ -2104,18 +1980,18 @@ "name": "stdout", "output_type": "stream", "text": [ - "WHU\n", - "-\n", - "Otto\n", - "Beisheim\n", - "School\n", - "of\n", - "Management\n" + "Lorem\n", + "ipsum\n", + "dolor\n", + "sit\n", + "amet,\n", + "consectetur\n", + "...\n" ] } ], "source": [ - "for word in school.split():\n", + "for word in text.split():\n", " print(word)" ] }, @@ -2132,7 +2008,7 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 62, "metadata": { "slideshow": { "slide_type": "slide" @@ -2145,7 +2021,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 63, "metadata": { "slideshow": { "slide_type": "-" @@ -2158,7 +2034,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 64, "metadata": { "slideshow": { "slide_type": "-" @@ -2171,7 +2047,7 @@ "'This will become a sentence'" ] }, - "execution_count": 69, + "execution_count": 64, "metadata": {}, "output_type": "execute_result" } @@ -2193,7 +2069,7 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 65, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2206,7 +2082,7 @@ "'a b c d e'" ] }, - "execution_count": 70, + "execution_count": 65, "metadata": {}, "output_type": "execute_result" } @@ -2223,12 +2099,12 @@ } }, "source": [ - "The [replace()](https://docs.python.org/3/library/stdtypes.html#str.replace) method creates a *new* `str` object with parts of the text replaced." + "The [replace()](https://docs.python.org/3/library/stdtypes.html#str.replace) method creates a *new* `str` object with parts of the original `str` object potentially replaced." ] }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 66, "metadata": { "slideshow": { "slide_type": "slide" @@ -2241,7 +2117,7 @@ "'This is a sentence'" ] }, - "execution_count": 71, + "execution_count": 66, "metadata": {}, "output_type": "execute_result" } @@ -2258,7 +2134,7 @@ } }, "source": [ - "## String Comparison" + "## String Operations" ] }, { @@ -2269,12 +2145,82 @@ } }, "source": [ - "The *relational* operators also work with `str` objects, another example of operator overloading. Comparison is done one character at a time until the first pair differs or one operand ends. However, `str` objects are sorted in a \"weird\" way. The reason for this is that computers store characters internally as numbers (i.e., $0$s and $1$s). Depending on the character encoding, these numbers vary. Commonly, characters and symbols used in the American language are encoded with the numbers 0 through 127, the so-called [ASCII standard](https://en.wikipedia.org/wiki/ASCII). However, Python works with the more general [Unicode/UTF-8 standard](https://en.wikipedia.org/wiki/UTF-8) that understands every language ever used by humans, even emojis." + "As mentioned in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Operator-Overloading), the `+` and `*` operators are *overloaded* and used for **string concatenation**. They always create *new* `str` objects. That has nothing to do with the `str` type's immutability, but is the default behavior of operators." ] }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 67, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Hello Lore'" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Hello \" + text[:4]" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum '" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "5 * text[:12]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### String Comparison" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The *relational* operators also work with `str` objects, another example of operator overloading. Comparison is done one character at a time until the first pair differs or one operand ends. However, `str` objects are sorted in a \"weird\" way. The reason for this is that computers store characters internally as numbers (i.e., $0$s and $1$s). Depending on the character encoding, these numbers vary. Commonly, characters and symbols used in American English are encoded with the numbers 0 through 127, the so-called [ASCII standard](https://en.wikipedia.org/wiki/ASCII). However, Python works with the more general [Unicode/UTF-8 standard](https://en.wikipedia.org/wiki/UTF-8) that understands every language ever used by humans, even emojis." + ] + }, + { + "cell_type": "code", + "execution_count": 69, "metadata": { "slideshow": { "slide_type": "slide" @@ -2289,7 +2235,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 70, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2302,7 +2248,7 @@ "True" ] }, - "execution_count": 73, + "execution_count": 70, "metadata": {}, "output_type": "execute_result" } @@ -2313,7 +2259,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 71, "metadata": { "slideshow": { "slide_type": "-" @@ -2326,7 +2272,7 @@ "False" ] }, - "execution_count": 74, + "execution_count": 71, "metadata": {}, "output_type": "execute_result" } @@ -2348,7 +2294,7 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 72, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2361,7 +2307,7 @@ "True" ] }, - "execution_count": 75, + "execution_count": 72, "metadata": {}, "output_type": "execute_result" } @@ -2378,12 +2324,12 @@ } }, "source": [ - "To provide a simple intuition for the \"weird\" sorting above, let's think of the American alphabet as being represented by the numbers as listed below. Then `\"Banana\"` is clearly \"smaller\" than `\"apple\"`." + "To provide a simple intuition for the \"weird\" sorting above, let's think of the alphabet as being represented by the numbers as listed below. Then `\"Banana\"` is clearly \"smaller\" than `\"apple\"`. In general, all the upper case letters are \"smaller\" than all the lower case letters." ] }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 73, "metadata": { "slideshow": { "slide_type": "slide" @@ -2479,7 +2425,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 74, "metadata": { "slideshow": { "slide_type": "slide" @@ -2493,7 +2439,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 75, "metadata": { "slideshow": { "slide_type": "-" @@ -2506,7 +2452,7 @@ "'Hello Alexander! Good morning.'" ] }, - "execution_count": 78, + "execution_count": 75, "metadata": {}, "output_type": "execute_result" } @@ -2528,7 +2474,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 76, "metadata": { "slideshow": { "slide_type": "slide" @@ -2541,7 +2487,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 77, "metadata": { "slideshow": { "slide_type": "-" @@ -2554,7 +2500,7 @@ "'Pi is 3.14'" ] }, - "execution_count": 80, + "execution_count": 77, "metadata": {}, "output_type": "execute_result" } @@ -2565,7 +2511,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 78, "metadata": { "slideshow": { "slide_type": "-" @@ -2578,7 +2524,7 @@ "'Pi is 3.142'" ] }, - "execution_count": 81, + "execution_count": 78, "metadata": {}, "output_type": "execute_result" } @@ -2611,7 +2557,7 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 79, "metadata": { "slideshow": { "slide_type": "slide" @@ -2624,7 +2570,7 @@ "'Hello Alexander! Good morning.'" ] }, - "execution_count": 82, + "execution_count": 79, "metadata": {}, "output_type": "execute_result" } @@ -2646,7 +2592,7 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 80, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2659,7 +2605,7 @@ "'Good morning, Alexander'" ] }, - "execution_count": 83, + "execution_count": 80, "metadata": {}, "output_type": "execute_result" } @@ -2681,7 +2627,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 81, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2694,7 +2640,7 @@ "'Hello Alexander! Good morning.'" ] }, - "execution_count": 84, + "execution_count": 81, "metadata": {}, "output_type": "execute_result" } @@ -2716,7 +2662,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 82, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2729,7 +2675,7 @@ "'Pi is 3.14'" ] }, - "execution_count": 85, + "execution_count": 82, "metadata": {}, "output_type": "execute_result" } @@ -2764,7 +2710,7 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 83, "metadata": { "slideshow": { "slide_type": "skip" @@ -2777,7 +2723,7 @@ "'Pi is 3.14'" ] }, - "execution_count": 86, + "execution_count": 83, "metadata": {}, "output_type": "execute_result" } @@ -2799,7 +2745,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 84, "metadata": { "slideshow": { "slide_type": "skip" @@ -2812,7 +2758,7 @@ "'Hello Alexander! Good morning.'" ] }, - "execution_count": 87, + "execution_count": 84, "metadata": {}, "output_type": "execute_result" } @@ -2847,7 +2793,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 85, "metadata": { "slideshow": { "slide_type": "slide" @@ -2870,7 +2816,7 @@ }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 86, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2902,7 +2848,7 @@ }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 87, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2934,7 +2880,7 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 88, "metadata": { "slideshow": { "slide_type": "skip" @@ -2947,7 +2893,7 @@ "'This is a sentence\\nthat is printed\\non three lines.'" ] }, - "execution_count": 91, + "execution_count": 88, "metadata": {}, "output_type": "execute_result" } @@ -2982,7 +2928,7 @@ }, { "cell_type": "code", - "execution_count": 92, + "execution_count": 89, "metadata": { "slideshow": { "slide_type": "slide" @@ -3015,7 +2961,7 @@ }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 90, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3024,10 +2970,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "(unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape (, line 1)", + "evalue": "(unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print(\"C:\\Users\\Administrator\\Desktop\\Project_Folder\")\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print(\"C:\\Users\\Administrator\\Desktop\\Project_Folder\")\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape\n" ] } ], @@ -3048,7 +2994,7 @@ }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 91, "metadata": { "slideshow": { "slide_type": "slide" @@ -3069,7 +3015,7 @@ }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 92, "metadata": { "slideshow": { "slide_type": "-" @@ -3101,7 +3047,7 @@ }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 93, "metadata": { "slideshow": { "slide_type": "slide" @@ -3122,7 +3068,7 @@ }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 94, "metadata": { "slideshow": { "slide_type": "-" @@ -3165,7 +3111,7 @@ }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 95, "metadata": { "slideshow": { "slide_type": "slide" @@ -3174,10 +3120,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "EOL while scanning string literal (, line 1)", + "evalue": "EOL while scanning string literal (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m \"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m EOL while scanning string literal\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m \"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m EOL while scanning string literal\n" ] } ], @@ -3200,7 +3146,7 @@ }, { "cell_type": "code", - "execution_count": 99, + "execution_count": 96, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3227,7 +3173,7 @@ }, { "cell_type": "code", - "execution_count": 100, + "execution_count": 97, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3240,7 +3186,7 @@ "'\\nI am a multi-line string\\nconsisting of 4 lines.\\n'" ] }, - "execution_count": 100, + "execution_count": 97, "metadata": {}, "output_type": "execute_result" } @@ -3262,7 +3208,7 @@ }, { "cell_type": "code", - "execution_count": 101, + "execution_count": 98, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3297,7 +3243,7 @@ }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 99, "metadata": { "slideshow": { "slide_type": "slide" @@ -3333,7 +3279,7 @@ }, { "cell_type": "code", - "execution_count": 103, + "execution_count": 100, "metadata": { "slideshow": { "slide_type": "slide" @@ -3347,7 +3293,7 @@ }, { "cell_type": "code", - "execution_count": 104, + "execution_count": 101, "metadata": { "slideshow": { "slide_type": "-" @@ -3360,7 +3306,7 @@ "\"Lorem Ipsum is simply dummy text of the printing and typesetting industry.\\nLorem Ipsum has been the industry's standard dummy text ever since the 1500s\\nwhen an unknown printer took a galley of type and scrambled it to make a type\\nspecimen book. It has survived not only five centuries but also the leap into\\nelectronic typesetting, remaining essentially unchanged. It was popularised in\\nthe 1960s with the release of Letraset sheets.\\n\"" ] }, - "execution_count": 104, + "execution_count": 101, "metadata": {}, "output_type": "execute_result" } @@ -3371,7 +3317,7 @@ }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 102, "metadata": { "slideshow": { "slide_type": "-" diff --git a/07_sequences.ipynb b/07_sequences.ipynb index 11add0a..f20b670 100644 --- a/07_sequences.ipynb +++ b/07_sequences.ipynb @@ -23,7 +23,7 @@ "\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 and Chapter 8 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. Unsurprisingly, computer scientists refer to the ideas and theories behind these data types as **[data structures](https://en.wikipedia.org/wiki/Data_structure)**.\n", + "This chapter and [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings.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. Unsurprisingly, computer scientists refer to the ideas and theories behind these data types as **[data structures](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](https://en.wikipedia.org/wiki/Spreadsheet) or [matrices](https://en.wikipedia.org/wiki/Matrix_%28mathematics%29)/[vectors](https://en.wikipedia.org/wiki/Vector_%28mathematics_and_physics%29). Such 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." ] @@ -49,9 +49,9 @@ "source": [ "[Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb#A-\"String\"-of-Characters) already describes the *sequence* properties of `str` objects. Here, we take a step back and study these properties on their own before looking at bigger ideas.\n", "\n", - "The [collections.abc](https://docs.python.org/3/library/collections.abc.html) module in the [standard library](https://docs.python.org/3/library/index.html) defines a variety of **abstract base classes** (ABCs). We saw ABCs already in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb#The-Numerical-Tower), where we use the ones from the [numbers](https://docs.python.org/3/library/numbers.html) module in the [standard library](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](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", + "The [collections.abc](https://docs.python.org/3/library/collections.abc.html) module in the [standard library](https://docs.python.org/3/library/index.html) defines a variety of **abstract base classes** (ABCs). We saw ABCs already in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb#The-Numerical-Tower), where we use the ones from the [numbers](https://docs.python.org/3/library/numbers.html) module in the [standard library](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](https://docs.python.org/3/library/collections.abc.html) module to classify the data types in this chapter and [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings.ipynb) according to their behavior in various contexts.\n", "\n", - "As an illustration, consider `numbers` and `word` below, two objects of *different* types." + "As an illustration, consider `numbers` and `text` below, two objects of *different* types." ] }, { @@ -65,7 +65,7 @@ "outputs": [], "source": [ "numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]\n", - "word = \"random\"" + "text = \"Lorem ipsum dolor sit amet, consectetur ...\"" ] }, { @@ -76,7 +76,7 @@ } }, "source": [ - "They have in common that we may loop over them with the `for` statement. So, in the context of iteration, both exhibit the *same* behavior." + "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." ] }, { @@ -114,12 +114,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "r a n d o m " + "L o r e m i p s u m d o l o r s i t a m e t , c o n s e c t e t u r . . . " ] } ], "source": [ - "for character in word:\n", + "for character in text:\n", " print(character, end=\" \")" ] }, @@ -133,7 +133,7 @@ "source": [ "In [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.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](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](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](https://docs.python.org/3/library/functions.html) include, among others, [enumerate()](https://docs.python.org/3/library/functions.html#enumerate), [sum()](https://docs.python.org/3/library/functions.html#sum), or [zip()](https://docs.python.org/3/library/functions.html#zip). So, they do *not* require the argument to be of a certain concrete data type (e.g., `list`); instead, any *iterable* type works." + "For the data science practitioner, it is worthwhile to know such terms as, for example, the documentation on the [built-ins](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](https://docs.python.org/3/library/functions.html) include [enumerate()](https://docs.python.org/3/library/functions.html#enumerate), [sum()](https://docs.python.org/3/library/functions.html#sum), or [zip()](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." ] }, { @@ -183,7 +183,7 @@ "source": [ "As in the context of *goose typing* in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb#Goose-Typing), we can use ABCs with the built-in [isinstance()](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 `word`." + "So, let's \"ask\" Python if it can loop over `numbers` or `text`." ] }, { @@ -231,7 +231,7 @@ } ], "source": [ - "isinstance(word, abc.Iterable)" + "isinstance(text, abc.Iterable)" ] }, { @@ -315,7 +315,7 @@ }, "source": [ "Most of the data types in this and the next chapter exhibit three [orthogonal](https://en.wikipedia.org/wiki/Orthogonality) (i.e., \"independent\") behaviors, formalized by ABCs in the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module as:\n", - "- `Iterable`: An object supports being looped over.\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", @@ -367,7 +367,7 @@ } ], "source": [ - "\"r\" in word" + "\"l\" in text" ] }, { @@ -378,7 +378,7 @@ } }, "source": [ - "Alternatively, we could also check if `numbers` and `word` are `Container` types with the [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function." + "Alternatively, we could also check if `numbers` and `text` are `Container` types with [isinstance()](https://docs.python.org/3/library/functions.html#isinstance)." ] }, { @@ -426,7 +426,7 @@ } ], "source": [ - "isinstance(word, abc.Container)" + "isinstance(text, abc.Container)" ] }, { @@ -497,7 +497,7 @@ } }, "source": [ - "Analogously, being `Sized` types, we can pass `numbers` and `word` as the argument to the built-in [len()](https://docs.python.org/3/library/functions.html#len) function and obtain \"meaningful\" results. The exact meaning depends on the *concrete* data type: For `numbers`, [len()](https://docs.python.org/3/library/functions.html#len) tells us how many elements are in the `list` object; for `word`, it tells us how many [Unicode characters](https://en.wikipedia.org/wiki/Unicode) make up the `str` object. But, *abstractly* speaking, both data types exhibit the *same* behavior of *finiteness*." + "Analogously, being `Sized` types, we can pass `numbers` and `text` as the argument to the built-in [len()](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()](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](https://en.wikipedia.org/wiki/Unicode) make up the `str` object. *Abstractly* speaking, both data types exhibit the *same* behavior of *finiteness*." ] }, { @@ -536,7 +536,7 @@ { "data": { "text/plain": [ - "6" + "43" ] }, "execution_count": 17, @@ -545,7 +545,7 @@ } ], "source": [ - "len(word)" + "len(text)" ] }, { @@ -593,7 +593,7 @@ } ], "source": [ - "isinstance(word, abc.Sized)" + "isinstance(text, abc.Sized)" ] }, { @@ -666,7 +666,9 @@ "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](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", - "So, both `numbers` and `word` are collections." + "Without going into too much detail, one way to read the summary table at the beginning of the [collections.abc](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." ] }, { @@ -714,7 +716,7 @@ } ], "source": [ - "isinstance(word, abc.Collection)" + "isinstance(text, abc.Collection)" ] }, { @@ -727,7 +729,7 @@ "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](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", - "We add the [reversed()](https://docs.python.org/3/library/functions.html#reversed) built-in to the `for`-loop from above to iterate over the elements or characters in reverse order." + "The [reversed()](https://docs.python.org/3/library/functions.html#reversed) built-in allows us to loop over the elements or characters in reverse order." ] }, { @@ -765,12 +767,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "m o d n a r " + ". . . r u t e t c e s n o c , 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(word):\n", + "for character in reversed(text):\n", " print(character, end=\" \")" ] }, @@ -819,7 +821,7 @@ } ], "source": [ - "isinstance(word, abc.Reversible)" + "isinstance(text, abc.Reversible)" ] }, { @@ -878,7 +880,7 @@ } ], "source": [ - "isinstance(word, abc.Sequence)" + "isinstance(text, abc.Sequence)" ] }, { @@ -976,7 +978,7 @@ } }, "source": [ - "[PythonTutor](http://www.pythontutor.com/visualize.html#code=empty%20%3D%20%5B%5D%0Asimple%20%3D%20%5B40,%2050%5D%0Anested%20%3D%20%5Bempty,%2010,%2020.0,%20%22Thirty%22,%20simple%5D&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how `nested` holds references to the `empty` and `simple` objects. Technically, it holds three more references pointing to the `10`, `20.0`, and `\"Thirty\"` objects as well. However, to simplify the visualization, these three objects are shown right inside the `nested` object as they are immutable and of \"flat\" data types. In general, the $0$s and $1$s inside a `list` object in memory always constitute pointers to other objects only." + "[PythonTutor](http://www.pythontutor.com/visualize.html#code=empty%20%3D%20%5B%5D%0Asimple%20%3D%20%5B40,%2050%5D%0Anested%20%3D%20%5Bempty,%2010,%2020.0,%20%22Thirty%22,%20simple%5D&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how `nested` holds references to the `empty` and `simple` objects. Technically, it holds three more references to the `10`, `20.0`, and `\"Thirty\"` objects as well. However, to simplify the visualization, these three objects are shown right inside the `nested` object. That may be done because they are immutable and \"flat\" data types. In general, the $0$s and $1$s inside a `list` object in memory always constitute references to other objects only." ] }, { @@ -1026,7 +1028,7 @@ { "data": { "text/plain": [ - "140322034424136" + "140157873498184" ] }, "execution_count": 34, @@ -1070,7 +1072,7 @@ } }, "source": [ - "Alternatively, we may use the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in to create a `list` object out of an iterable we pass to it as the argument.\n", + "Alternatively, we use the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in to create a `list` object out of an iterable we pass to it as the argument.\n", "\n", "For example, we can wrap the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in with [list()](https://docs.python.org/3/library/functions.html#func-list): As described in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Containers-vs.-Iterables), `range` objects, like `range(1, 13)` below, are iterable and generate `int` objects \"on the fly\" (i.e., one by one). The [list()](https://docs.python.org/3/library/functions.html#func-list) around it acts like a `for`-loop and **materializes** twelve `int` objects in memory that then become the elements of the newly created `list` object. [PythonTutor](http://www.pythontutor.com/visualize.html#code=r%20%3D%20range%281,%2013%29%0Al%20%3D%20list%28range%281,%2013%29%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows this difference visually." ] @@ -1167,7 +1169,7 @@ } }, "source": [ - "As another example, we may also create a `list` object from a `str` object as the latter is iterable, as well. Then, the individual characters become the elements of the new `list` object!" + "As another example, we create a `list` object from a `str` object, which is iterable, as well. Then, the individual characters become the elements of the new `list` object!" ] }, { @@ -1182,7 +1184,7 @@ { "data": { "text/plain": [ - "['W', 'H', 'U']" + "['i', 't', 'e', 'r', 'a', 'b', 'l', 'e']" ] }, "execution_count": 39, @@ -1191,7 +1193,7 @@ } ], "source": [ - "list(\"WHU\")" + "list(\"iterable\")" ] }, { @@ -1213,7 +1215,7 @@ } }, "source": [ - "`list` objects are *sequences*. To reiterate that concept from above *without* the formal ABCs from the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module, we briefly summarize the *four* behaviors of a sequence and provide some more `list`-specific details below:" + "`list` objects are *sequences*. To reiterate that, we briefly summarize the *four* behaviors of a sequence and provide some more `list`-specific details below:" ] }, { @@ -1224,16 +1226,16 @@ } }, "source": [ - "- **Container**:\n", + "- `Container`:\n", " - holds references to other objects in memory (with their own *identity* and *type*)\n", " - implements membership testing via the `in` operator\n", - "- **Iterable**:\n", + "- `Iterable`:\n", " - supports being looped over\n", " - works with the `for` or `while` statements\n", - "- **Reversible**:\n", + "- `Reversible`:\n", " - the elements come in a *predictable* order that we may traverse in a forward or backward fashion\n", " - works with the [reversed()](https://docs.python.org/3/library/functions.html#reversed) built-in\n", - "- **Sized**:\n", + "- `Sized`:\n", " - the number of elements is finite *and* known in advance\n", " - works with the built-in [len()](https://docs.python.org/3/library/functions.html#len) function" ] @@ -1246,7 +1248,7 @@ } }, "source": [ - "The \"length\" of `nested` is *five* because `simple` counts as only *one* element. In other words, `nested` holds five references to other objects." + "The \"length\" of `nested` is *five* because `empty` and `simple` count as *one* element each. In other words, `nested` holds five references to other objects, two of which are `list` objects." ] }, { @@ -1281,7 +1283,7 @@ } }, "source": [ - "With a `for`-loop, we can traverse all elements in a *predictable* order, forward or backward. As `list` objects only hold references to other objects, these have a *indentity* and may be of *different* types; however, this is rarely, if ever, useful in practice." + "With a `for`-loop, we can iterate over all elements in a *predictable* order, forward or backward. As `list` objects hold *references* to other *objects*, these have an *indentity* and may even be of *different* types; however, the latter observation is rarely, if ever, useful in practice." ] }, { @@ -1297,11 +1299,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "[] \t140322025703432 \t\n", - "10 \t94360180081984 \t\n", - "20.0 \t140322034534104 \t\n", - "Thirty \t140322025251816 \t\n", - "[40, 50] \t140322034424072 \t\n" + "[] \t140157882223304 \t\n", + "10 \t93987402609984 \t\n", + "20.0 \t140157882328912 \t\n", + "Thirty \t140157873509520 \t\n", + "[40, 50] \t140157873031176 \t\n" ] } ], @@ -1340,7 +1342,7 @@ } }, "source": [ - "The `in` operator checks if a given object is \"contained\" in a `list` object. It uses the `==` operator behind the scenes (i.e., *not* the `is` operator) conducting a so-called **[linear search](https://en.wikipedia.org/wiki/Linear_search)**: So, Python implicitly loops over *all* elements and only stops prematurely if an element evaluates equal to the given object. A linear search may, therefore, be relatively *slow* for big `list` objects." + "The `in` operator checks if a given object is \"contained\" in a `list` object. It uses the `==` operator behind the scenes (i.e., *not* the `is` operator) conducting a **[linear search](https://en.wikipedia.org/wiki/Linear_search)**: So, Python implicitly loops over *all* elements and only stops prematurely if an element evaluates equal to the searched object. A linear search may, therefore, be relatively *slow* for big `list` objects." ] }, { @@ -1445,9 +1447,9 @@ } }, "source": [ - "Because of the *predictable* order and the *finiteness*, each element in a sequence can be labeled with a unique *index* (i.e., an `int` object in the range $0 \\leq \\text{index} < \\lvert \\text{sequence} \\rvert$).\n", + "Because of the *predictable* order and the *finiteness*, each element in a sequence can be labeled with a unique *index*, an `int` object in the range $0 \\leq \\text{index} < \\lvert \\text{sequence} \\rvert$.\n", "\n", - "Brackets, `[` and `]`, are the literal syntax for accessing individual elements of any sequence type. In this book, we also call them the *indexing operator*." + "Brackets, `[` and `]`, are the literal syntax for accessing individual elements of any sequence type. In this book, we also call them the *indexing operator* in this context." ] }, { @@ -1482,7 +1484,7 @@ } }, "source": [ - "The last index is one less than `len(nested)` above, and Python raises an `IndexError` if we look up an index that is not in the implied range." + "The last index is one less than `len(nested)`, and Python raises an `IndexError` if we look up an index that is not in the range." ] }, { @@ -1533,7 +1535,7 @@ { "data": { "text/plain": [ - "50" + "[40, 50]" ] }, "execution_count": 48, @@ -1541,6 +1543,30 @@ "output_type": "execute_result" } ], + "source": [ + "nested[-1]" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "50" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "nested[-1][1]" ] @@ -1571,7 +1597,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 50, "metadata": { "slideshow": { "slide_type": "slide" @@ -1584,7 +1610,7 @@ "[10, 20.0, 'Thirty']" ] }, - "execution_count": 49, + "execution_count": 50, "metadata": {}, "output_type": "execute_result" } @@ -1601,12 +1627,12 @@ } }, "source": [ - "To obtain \"every other\" element, we slice from beginning to end, defaulting to `0` and `len(nested)`, in steps of `2`." + "To obtain \"every other\" element, we slice from beginning to end, defaulting to `0` and `len(nested)` when omitted, in steps of `2`." ] }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 51, "metadata": { "slideshow": { "slide_type": "-" @@ -1619,7 +1645,7 @@ "[[], 20.0, [40, 50]]" ] }, - "execution_count": 50, + "execution_count": 51, "metadata": {}, "output_type": "execute_result" } @@ -1636,20 +1662,9 @@ } }, "source": [ - "The literal notation with the colons `:` is *syntactic sugar*, and Python provides the [slice()](https://docs.python.org/3/library/functions.html#slice) built-in to slice with `slice` objects. [slice()](https://docs.python.org/3/library/functions.html#slice) takes *start*, *stop*, and *step* arguments in the same way as the already familiar [range()](https://docs.python.org/3/library/functions.html#func-range) built-in." - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [], - "source": [ - "middle = slice(1, 4)" + "The literal notation with the colons `:` is *syntactic sugar*. It saves us from using the [slice()](https://docs.python.org/3/library/functions.html#slice) built-in to create `slice` objects. [slice()](https://docs.python.org/3/library/functions.html#slice) takes *start*, *stop*, and *step* arguments in the same way as the familiar [range()](https://docs.python.org/3/library/functions.html#func-range), and the `slice` objects it creates are used just as *indexes* above.\n", + "\n", + "In most cases, the literal notation is more convenient to use; however, with `slice` objects, we can give names to slices and reuse them across several sequences." ] }, { @@ -1660,31 +1675,9 @@ "slide_type": "skip" } }, - "outputs": [ - { - "data": { - "text/plain": [ - "slice" - ] - }, - "execution_count": 52, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "type(middle)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "In most cases, the literal notation is more convenient to use; however, with `slice` objects, we may give names to slices and re-use them across several sequences." + "middle = slice(1, 4)" ] }, { @@ -1699,7 +1692,7 @@ { "data": { "text/plain": [ - "[10, 20.0, 'Thirty']" + "slice" ] }, "execution_count": 53, @@ -1708,7 +1701,7 @@ } ], "source": [ - "nested[middle]" + "type(middle)" ] }, { @@ -1723,7 +1716,7 @@ { "data": { "text/plain": [ - "[11, 8, 5]" + "[10, 20.0, 'Thirty']" ] }, "execution_count": 54, @@ -1732,7 +1725,7 @@ } ], "source": [ - "numbers[middle]" + "nested[middle]" ] }, { @@ -1747,7 +1740,7 @@ { "data": { "text/plain": [ - "'and'" + "[11, 8, 5]" ] }, "execution_count": 55, @@ -1756,7 +1749,31 @@ } ], "source": [ - "word[middle]" + "numbers[middle]" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'ore'" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[middle]" ] }, { @@ -1772,7 +1789,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 57, "metadata": { "slideshow": { "slide_type": "skip" @@ -1785,7 +1802,7 @@ "1" ] }, - "execution_count": 56, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" } @@ -1796,7 +1813,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 58, "metadata": { "slideshow": { "slide_type": "skip" @@ -1809,7 +1826,7 @@ "4" ] }, - "execution_count": 57, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } @@ -1831,7 +1848,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 59, "metadata": { "slideshow": { "slide_type": "skip" @@ -1855,7 +1872,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 60, "metadata": { "slideshow": { "slide_type": "slide" @@ -1868,7 +1885,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 61, "metadata": { "slideshow": { "slide_type": "-" @@ -1881,7 +1898,7 @@ "[[], 10, 20.0, 'Thirty', [40, 50]]" ] }, - "execution_count": 60, + "execution_count": 61, "metadata": {}, "output_type": "execute_result" } @@ -1898,14 +1915,14 @@ } }, "source": [ - "At first glance, `nested` and `nested_copy` seem to cause no pain. For `list` objects, the comparison operator `==` goes over the elements in both operands in a pairwise fashion and checks if they all evaluate equal.\n", + "At first glance, `nested` and `nested_copy` seem to cause no pain. For `list` objects, the comparison operator `==` goes over the elements in both operands in a pairwise fashion and checks if they all evaluate equal (cf., the \"*List Comparison*\" section below for more details).\n", "\n", - "We confirm that `nested` and `nested_copy` compare equal as expected but also that they are *different* objects." + "We confirm that `nested` and `nested_copy` compare equal as could be expected but also that they are *different* objects." ] }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 62, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1918,7 +1935,7 @@ "True" ] }, - "execution_count": 61, + "execution_count": 62, "metadata": {}, "output_type": "execute_result" } @@ -1929,7 +1946,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 63, "metadata": { "slideshow": { "slide_type": "-" @@ -1942,7 +1959,7 @@ "False" ] }, - "execution_count": 62, + "execution_count": 63, "metadata": {}, "output_type": "execute_result" } @@ -1959,14 +1976,14 @@ } }, "source": [ - "However, as [PythonTutor](http://pythontutor.com/visualize.html#code=nested%20%3D%20%5B%5B%5D,%2010,%2020.0,%20%22Thirty%22,%20%5B40,%2050%5D%5D%0Anested_copy%20%3D%20nested%5B%3A%5D&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) reveals, only the *pointers* to the elements are copied! That concept is called a **[shallow copy](https://en.wikipedia.org/wiki/Object_copying#Shallow_copy)**.\n", + "However, as [PythonTutor](http://pythontutor.com/visualize.html#code=nested%20%3D%20%5B%5B%5D,%2010,%2020.0,%20%22Thirty%22,%20%5B40,%2050%5D%5D%0Anested_copy%20%3D%20nested%5B%3A%5D&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) reveals, only the *references* to the elements are copied, and not the objects in `nested` themselves! Because of that, `nested_copy` is a so-called **[shallow copy](https://en.wikipedia.org/wiki/Object_copying#Shallow_copy)** of `nested`.\n", "\n", - "We could also see this with the [id()](https://docs.python.org/3/library/functions.html#id) function: The respective first elements in both `nested` and `nested_copy` are the *same* `empty` object." + "We could also see this with the [id()](https://docs.python.org/3/library/functions.html#id) function: The respective first elements in both `nested` and `nested_copy` are the *same* object, namely `empty`. So, we have three ways of accessing the *same* address in memory. Also, we say that `nested` and `nested_copy` partially share the *same* state." ] }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 64, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1979,7 +1996,7 @@ "True" ] }, - "execution_count": 63, + "execution_count": 64, "metadata": {}, "output_type": "execute_result" } @@ -1990,7 +2007,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 65, "metadata": { "slideshow": { "slide_type": "skip" @@ -2000,44 +2017,16 @@ { "data": { "text/plain": [ - "[]" + "140157882223304" ] }, - "execution_count": 64, + "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "nested[0]" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "Knowing this becomes critical if the elements in a `list` object are mutable objects. Then, because of the original `list` object and its copy both pointing at the *same* objects in memory, if some of them are mutated, these changes are visible to both! We already saw a similar kind of confusion in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?) in a \"simpler\" setting and look into this in detail in the next section.\n", - "\n", - "Instead of a shallow copy, we could also create a so-called **[deep copy](https://en.wikipedia.org/wiki/Object_copying#Deep_copy)** of `nested`: That concept recursively follows every pointer in a possible nested data structure and creates copies of *every* involved object.\n", - "\n", - "To explicitly create shallow or deep copies, the [copy](https://docs.python.org/3/library/copy.html) module in the [standard library](https://docs.python.org/3/library/index.html) provides two functions, [copy()](https://docs.python.org/3/library/copy.html#copy.copy) and [deepcopy()](https://docs.python.org/3/library/copy.html#copy.deepcopy). We must always remember that slicing creates *shallow* copies only." - ] - }, - { - "cell_type": "code", - "execution_count": 65, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [], - "source": [ - "import copy" + "id(nested[0])" ] }, { @@ -2048,9 +2037,20 @@ "slide_type": "skip" } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "140157882223304" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "nested_deep_copy = copy.deepcopy(nested)" + "id(nested_copy[0])" ] }, { @@ -2065,7 +2065,7 @@ { "data": { "text/plain": [ - "True" + "140157882223304" ] }, "execution_count": 67, @@ -2073,6 +2073,73 @@ "output_type": "execute_result" } ], + "source": [ + "id(empty)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Knowing this becomes critical if the elements in a `list` object are mutable objects (i.e., we can change them *in place*), and this is the case with `nested` and `nested_copy`, as we see in the next section on \"*Mutability*\".\n", + "\n", + "As both the original `nested` object and its copy reference the *same* `list` objects in memory, any changes made to them are visible to both! Because of that, working with shallow copies can easily become confusing.\n", + "\n", + "Instead of a shallow copy, we could also create a so-called **[deep copy](https://en.wikipedia.org/wiki/Object_copying#Deep_copy)** of `nested`: Then, the copying process recursively follows every reference in a nested data structure and creates copies of *every* object found.\n", + "\n", + "To explicitly create shallow or deep copies, the [copy](https://docs.python.org/3/library/copy.html) module in the [standard library](https://docs.python.org/3/library/index.html) provides two functions, [copy()](https://docs.python.org/3/library/copy.html#copy.copy) and [deepcopy()](https://docs.python.org/3/library/copy.html#copy.deepcopy). We must always remember that slicing creates *shallow* copies only." + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import copy" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "nested_deep_copy = copy.deepcopy(nested)" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "nested == nested_deep_copy" ] @@ -2090,7 +2157,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 71, "metadata": { "slideshow": { "slide_type": "skip" @@ -2103,7 +2170,7 @@ "False" ] }, - "execution_count": 68, + "execution_count": 71, "metadata": {}, "output_type": "execute_result" } @@ -2112,6 +2179,54 @@ "nested[0] is nested_deep_copy[0]" ] }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140157882223304" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(nested[0])" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140157873029448" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(nested_deep_copy[0])" + ] + }, { "cell_type": "markdown", "metadata": { @@ -2142,12 +2257,12 @@ } }, "source": [ - "In contrast to `str` objects, `list` objects are *mutable*: We may assign new elements to indices or slices and also remove elements. That changes *parts* of a `list` object in memory." + "In contrast to `str` objects, `list` objects are *mutable*: We may assign new elements to indices or slices and also remove elements. That changes the *references* in a `list` object. In general, if an object is *mutable*, we say that it may be changed *in place*." ] }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 74, "metadata": { "slideshow": { "slide_type": "slide" @@ -2160,7 +2275,7 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 75, "metadata": { "slideshow": { "slide_type": "-" @@ -2173,7 +2288,7 @@ "[0, 10, 20.0, 'Thirty', [40, 50]]" ] }, - "execution_count": 70, + "execution_count": 75, "metadata": {}, "output_type": "execute_result" } @@ -2195,7 +2310,7 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 76, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2208,7 +2323,7 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 77, "metadata": { "slideshow": { "slide_type": "-" @@ -2221,7 +2336,7 @@ "[100, 100, 100, [40, 50]]" ] }, - "execution_count": 72, + "execution_count": 77, "metadata": {}, "output_type": "execute_result" } @@ -2232,7 +2347,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 78, "metadata": { "slideshow": { "slide_type": "-" @@ -2245,7 +2360,7 @@ "4" ] }, - "execution_count": 73, + "execution_count": 78, "metadata": {}, "output_type": "execute_result" } @@ -2262,12 +2377,12 @@ } }, "source": [ - "The `list` object's identity does *not* change. That is the whole point behind mutable objects." + "The `list` object's identity does *not* change. That is the main point behind mutable objects." ] }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 79, "metadata": { "slideshow": { "slide_type": "skip" @@ -2277,10 +2392,10 @@ { "data": { "text/plain": [ - "140322034424136" + "140157873498184" ] }, - "execution_count": 74, + "execution_count": 79, "metadata": {}, "output_type": "execute_result" } @@ -2297,12 +2412,12 @@ } }, "source": [ - "`nested_copy` is still unchanged!" + "`nested_copy` is unchanged!" ] }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 80, "metadata": { "slideshow": { "slide_type": "slide" @@ -2315,7 +2430,7 @@ "[[], 10, 20.0, 'Thirty', [40, 50]]" ] }, - "execution_count": 75, + "execution_count": 80, "metadata": {}, "output_type": "execute_result" } @@ -2337,7 +2452,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 81, "metadata": { "slideshow": { "slide_type": "-" @@ -2350,7 +2465,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 82, "metadata": { "slideshow": { "slide_type": "-" @@ -2363,7 +2478,7 @@ "[[], 10, 20.0, 'Thirty', [1, 2, 3]]" ] }, - "execution_count": 77, + "execution_count": 82, "metadata": {}, "output_type": "execute_result" } @@ -2380,12 +2495,12 @@ } }, "source": [ - "This has a surprising side effect on `nested`!" + "That has a surprising side effect on `nested`." ] }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 83, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2398,7 +2513,7 @@ "[100, 100, 100, [1, 2, 3]]" ] }, - "execution_count": 78, + "execution_count": 83, "metadata": {}, "output_type": "execute_result" } @@ -2415,14 +2530,14 @@ } }, "source": [ - "That is because `nested_copy` is a shallow copy of `nested`. [PythonTutor](http://pythontutor.com/visualize.html#code=nested%20%3D%20%5B%5B%5D,%2010,%2020.0,%20%22Thirty%22,%20%5B40,%2050%5D%5D%0Anested_copy%20%3D%20nested%5B%3A%5D%0Anested%5B%3A4%5D%20%3D%20%5B100,%20100,%20100%5D%0Anested_copy%5B-1%5D%5B%3A%5D%20%3D%20%5B1,%202,%203%5D&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how both point to the *same* nested `list` object.\n", + "That is precisely the confusion we talked about above when we said that `nested_copy` is a *shallow* copy of `nested`. [PythonTutor](http://pythontutor.com/visualize.html#code=nested%20%3D%20%5B%5B%5D,%2010,%2020.0,%20%22Thirty%22,%20%5B40,%2050%5D%5D%0Anested_copy%20%3D%20nested%5B%3A%5D%0Anested%5B%3A4%5D%20%3D%20%5B100,%20100,%20100%5D%0Anested_copy%5B-1%5D%5B%3A%5D%20%3D%20%5B1,%202,%203%5D&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how both reference the *same* nested `list` object that is changed *in place* from `[40, 50]` into `[1, 2, 3]`.\n", "\n", "Lastly, we use the `del` statement to remove an element." ] }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 84, "metadata": { "slideshow": { "slide_type": "slide" @@ -2433,138 +2548,6 @@ "del nested[-1]" ] }, - { - "cell_type": "code", - "execution_count": 80, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[100, 100, 100]" - ] - }, - "execution_count": 80, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "nested" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "The `del` statement, of course, also works for slices." - ] - }, - { - "cell_type": "code", - "execution_count": 81, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [], - "source": [ - "del nested[:2]" - ] - }, - { - "cell_type": "code", - "execution_count": 82, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[100]" - ] - }, - "execution_count": 82, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "nested" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "### List Operations" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "As with `str` objects, the `+` and `*` operators are overloaded for concatenation and always return *new* `list` objects." - ] - }, - { - "cell_type": "code", - "execution_count": 83, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [], - "source": [ - "first = [10, 20, 30]\n", - "second = [40, 50, 60]" - ] - }, - { - "cell_type": "code", - "execution_count": 84, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[10, 20, 30, 40, 50, 60]" - ] - }, - "execution_count": 84, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "first + second" - ] - }, { "cell_type": "code", "execution_count": 85, @@ -2577,7 +2560,7 @@ { "data": { "text/plain": [ - "[10, 20, 30, 10, 20, 30]" + "[100, 100, 100]" ] }, "execution_count": 85, @@ -2586,31 +2569,7 @@ } ], "source": [ - "2 * first" - ] - }, - { - "cell_type": "code", - "execution_count": 86, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "[40, 50, 60, 40, 50, 60, 40, 50, 60]" - ] - }, - "execution_count": 86, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "second * 3" + "nested" ] }, { @@ -2621,9 +2580,20 @@ } }, "source": [ - "Besides being an operator, the `*` symbol has a second syntactical use, as explained in [PEP 3132](https://www.python.org/dev/peps/pep-3132/) and [PEP 448](https://www.python.org/dev/peps/pep-0448/): It implements what is called **iterable unpacking**. It is *not* an operator syntactically but a notation that Python processes as a literal.\n", - "\n", - "In the example, Python interprets the expression as if the elements of the iterable `second` were placed between `30` and `70` one by one. So, we do not obtain a nested but a *flat* list." + "The `del` statement also works for slices. Here, we remove all references `nested` holds." + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "del nested[:]" ] }, { @@ -2631,14 +2601,14 @@ "execution_count": 87, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ - "[30, 40, 50, 60, 70]" + "[]" ] }, "execution_count": 87, @@ -2647,7 +2617,44 @@ } ], "source": [ - "[30, *second, 70]" + "nested" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Mutability for sequences is formalized by the `MutableSequence` ABC in the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module.\n", + "\n", + "So, we can als \"ask\" Python if `nested` is mutable." + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(nested, abc.MutableSequence)" ] }, { @@ -2669,16 +2676,16 @@ } }, "source": [ - "The `list` type is an essential data structure in any real-world Python application, and many typical `list` related algorithms from computer science theory are already built into it at the C level (cf., the [documentation](https://docs.python.org/3/tutorial/datastructures.html#more-on-lists) for a full overview). So, understanding and applying the built-in methods of the `list` type not only speeds up the development process but also makes programs significantly faster.\n", + "The `list` type is an essential data structure in any real-world Python application, and many typical `list`-related algorithms from computer science theory are already built into it at the C level (cf., the [documentation](https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types) or the [tutorial](https://docs.python.org/3/tutorial/datastructures.html#more-on-lists) for a full overview; unfortunately, not all methods have direct links). So, understanding and applying the built-in methods of the `list` type not only speeds up the development process but also makes programs significantly faster.\n", "\n", - "In contrast to the `str` type's methods, the `list` type's methods *always* mutate (i.e., \"change\") an object *in place*. They do *not* create a *new* `list` object and return `None` to indicate that. So, we must *never* assign the return value of `list` methods to the variable holding the list!\n", + "In contrast to the `str` type's methods in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb#String-Methods) (e.g., [upper()](https://docs.python.org/3/library/stdtypes.html#str.upper) or [lower()](https://docs.python.org/3/library/stdtypes.html#str.lower)), the `list` type's methods that mutate an object do so *in place*. That means they *never* create *new* `list` objects and return `None` to indicate that. So, we must *never* assign the return value of `list` methods to the variable holding the list!\n", "\n", "Let's look at the following `names` example." ] }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 89, "metadata": { "slideshow": { "slide_type": "slide" @@ -2686,7 +2693,7 @@ }, "outputs": [], "source": [ - "names = [\"Carl\", \"Berthold\", \"Achim\", \"Xavier\", \"Peter\"]" + "names = [\"Carl\", \"Peter\"]" ] }, { @@ -2697,12 +2704,12 @@ } }, "source": [ - "To add an object to the end of `names`, we use the append() method. The code cell shows no output indicating that `None` must have been returned." + "To add an object to the end of `names`, we use the `append()` method. The code cell shows no output indicating that `None` must be the return value." ] }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 90, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2715,7 +2722,7 @@ }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 91, "metadata": { "slideshow": { "slide_type": "-" @@ -2725,10 +2732,10 @@ { "data": { "text/plain": [ - "['Carl', 'Berthold', 'Achim', 'Xavier', 'Peter', 'Eckardt']" + "['Carl', 'Peter', 'Eckardt']" ] }, - "execution_count": 90, + "execution_count": 91, "metadata": {}, "output_type": "execute_result" } @@ -2745,12 +2752,12 @@ } }, "source": [ - "With the extend() method, we may also append multiple elements provided by an iterable at once. Here, the iterable is a `list` object itself holding two `str` objects." + "With the `extend()` method, we may also append multiple elements provided by an iterable. Here, the iterable is a `list` object itself holding two `str` objects." ] }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 92, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2763,7 +2770,7 @@ }, { "cell_type": "code", - "execution_count": 92, + "execution_count": 93, "metadata": { "slideshow": { "slide_type": "-" @@ -2773,10 +2780,10 @@ { "data": { "text/plain": [ - "['Carl', 'Berthold', 'Achim', 'Xavier', 'Peter', 'Eckardt', 'Karl', 'Oliver']" + "['Carl', 'Peter', 'Eckardt', 'Karl', 'Oliver']" ] }, - "execution_count": 92, + "execution_count": 93, "metadata": {}, "output_type": "execute_result" } @@ -2793,36 +2800,25 @@ } }, "source": [ - "`list` objects may be sorted *in place* with the [sort()](https://docs.python.org/3/library/stdtypes.html#list.sort) method. That is different from the built-in [sorted()](https://docs.python.org/3/library/functions.html#sorted) function that takes any *finite* and *iterable* object and returns a *new* `list` object with the iterable's elements sorted." - ] - }, - { - "cell_type": "code", - "execution_count": 93, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "['Achim', 'Berthold', 'Carl', 'Eckardt', 'Karl', 'Oliver', 'Peter', 'Xavier']" - ] - }, - "execution_count": 93, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "sorted(names)" + "Similar to `append()`, we may add a new element at an arbitrary position with the `insert()` method. `insert()` takes two arguments, an *index* and the element to be inserted." ] }, { "cell_type": "code", "execution_count": 94, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "names.insert(1, \"Berthold\")" + ] + }, + { + "cell_type": "code", + "execution_count": 95, "metadata": { "slideshow": { "slide_type": "-" @@ -2832,10 +2828,10 @@ { "data": { "text/plain": [ - "['Carl', 'Berthold', 'Achim', 'Xavier', 'Peter', 'Eckardt', 'Karl', 'Oliver']" + "['Carl', 'Berthold', 'Peter', 'Eckardt', 'Karl', 'Oliver']" ] }, - "execution_count": 94, + "execution_count": 95, "metadata": {}, "output_type": "execute_result" } @@ -2844,9 +2840,90 @@ "names" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`list` objects may be sorted *in place* with the [sort()](https://docs.python.org/3/library/stdtypes.html#list.sort) method. That is different from the built-in [sorted()](https://docs.python.org/3/library/functions.html#sorted) function that takes any *finite* and *iterable* object and returns a *new* `list` object with the iterable's elements sorted!" + ] + }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 96, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Berthold', 'Carl', 'Eckardt', 'Karl', 'Oliver', 'Peter']" + ] + }, + "execution_count": 96, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sorted(names)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As the previous code cell created a *new* `list` object, `names` is still unsorted." + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Carl', 'Berthold', 'Peter', 'Eckardt', 'Karl', 'Oliver']" + ] + }, + "execution_count": 97, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's sort the elements in `names` instead." + ] + }, + { + "cell_type": "code", + "execution_count": 98, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2859,7 +2936,7 @@ }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 99, "metadata": { "slideshow": { "slide_type": "-" @@ -2869,10 +2946,10 @@ { "data": { "text/plain": [ - "['Achim', 'Berthold', 'Carl', 'Eckardt', 'Karl', 'Oliver', 'Peter', 'Xavier']" + "['Berthold', 'Carl', 'Eckardt', 'Karl', 'Oliver', 'Peter']" ] }, - "execution_count": 96, + "execution_count": 99, "metadata": {}, "output_type": "execute_result" } @@ -2889,12 +2966,12 @@ } }, "source": [ - "To sort in reverse order, we pass a keyword-only `reverse=True` argument to either the [sort()](https://docs.python.org/3/library/stdtypes.html#list.sort) method or the [sorted()](https://docs.python.org/3/library/functions.html#sorted) function. In the latter case, we could also use the [reversed()](https://docs.python.org/3/library/functions.html#reversed) built-in instead; however, that *neither* returns a new `list` object *nor* changes the existing one in place. We revisit it at the end of this chapter." + "To sort in reverse order, we pass a keyword-only `reverse=True` argument to either the [sort()](https://docs.python.org/3/library/stdtypes.html#list.sort) method or the [sorted()](https://docs.python.org/3/library/functions.html#sorted) function." ] }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 100, "metadata": { "slideshow": { "slide_type": "slide" @@ -2907,7 +2984,7 @@ }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 101, "metadata": { "slideshow": { "slide_type": "-" @@ -2917,10 +2994,10 @@ { "data": { "text/plain": [ - "['Xavier', 'Peter', 'Oliver', 'Karl', 'Eckardt', 'Carl', 'Berthold', 'Achim']" + "['Peter', 'Oliver', 'Karl', 'Eckardt', 'Carl', 'Berthold']" ] }, - "execution_count": 98, + "execution_count": 101, "metadata": {}, "output_type": "execute_result" } @@ -2937,14 +3014,18 @@ } }, "source": [ - "Both, the [sort()](https://docs.python.org/3/library/stdtypes.html#list.sort) method and the [sorted()](https://docs.python.org/3/library/functions.html#sorted) function, also accept a keyword-only `key` argument that must be a reference to a `function` object accepting one positional argument. Then, the elements in the `list` object are passed to that on a one-by-one basis, and the return values are used as the **sort keys**.\n", + "The [sort()](https://docs.python.org/3/library/stdtypes.html#list.sort) method and the [sorted()](https://docs.python.org/3/library/functions.html#sorted) function sort the elements in `names` in alphabetical order, forward or backward. However, that does *not* hold in general.\n", + "\n", + "We mention above that `list` objects may contain objects of *any* type and even of *mixed* types. Because of that, the sorting is **[delegated](https://en.wikipedia.org/wiki/Delegation_(object-oriented_programming))** to the elements in a `list` object. In a way, Python \"asks\" the elements in a `list` object to sort themselves. As `names` contains only `str` objects, they are sorted according the the comparison rules explained in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb#String-Comparison).\n", + "\n", + "To customize the sorting, we pass a keyword-only `key` argument to [sort()](https://docs.python.org/3/library/stdtypes.html#list.sort) or [sorted()](https://docs.python.org/3/library/functions.html#sorted), which must be a `function` object accepting *one* positional argument. Then, the elements in the `list` object are passed to that one by one, and the return values are used as the **sort keys**. The `key` argument is also a popular use case for `lambda` expressions.\n", "\n", "For example, to sort `names` not by alphabet but by the names' lengths, we pass in a reference to the built-in [len()](https://docs.python.org/3/library/functions.html#len) function as `key=len`. Note that there are *no* parentheses after `len`!" ] }, { "cell_type": "code", - "execution_count": 99, + "execution_count": 102, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2963,103 +3044,9 @@ } }, "source": [ - "If two names have the same length, their relative order is kept as is. A [sorting algorithm](https://en.wikipedia.org/wiki/Sorting_algorithm) is called **[stable](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability)** if it has that property. That is why `\"Karl\"` comes before `\"Carl\" ` below.\n", + "If two names have the same length, their relative order is kept as is. That is why `\"Karl\"` comes before `\"Carl\" ` below. A [sorting algorithm](https://en.wikipedia.org/wiki/Sorting_algorithm) with that property is called **[stable](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability)**.\n", "\n", - "Sorting is an important topic in programming and we refer to the official [HOWTO](https://docs.python.org/3/howto/sorting.html) for a more comprehensive introduction." - ] - }, - { - "cell_type": "code", - "execution_count": 100, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "['Karl', 'Carl', 'Peter', 'Achim', 'Xavier', 'Oliver', 'Eckardt', 'Berthold']" - ] - }, - "execution_count": 100, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "names" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "The pop() method removes the last element from a `list` object *and* returns it." - ] - }, - { - "cell_type": "code", - "execution_count": 101, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "'Berthold'" - ] - }, - "execution_count": 101, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "names.pop()" - ] - }, - { - "cell_type": "code", - "execution_count": 102, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "['Karl', 'Carl', 'Peter', 'Achim', 'Xavier', 'Oliver', 'Eckardt']" - ] - }, - "execution_count": 102, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "names" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "It takes an optional index argument and removes that instead." + "Sorting is an important topic in programming, and we refer to the official [HOWTO](https://docs.python.org/3/howto/sorting.html) for a more comprehensive introduction." ] }, { @@ -3067,14 +3054,14 @@ "execution_count": 103, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "-" } }, "outputs": [ { "data": { "text/plain": [ - "'Carl'" + "['Karl', 'Carl', 'Peter', 'Oliver', 'Eckardt', 'Berthold']" ] }, "execution_count": 103, @@ -3083,12 +3070,36 @@ } ], "source": [ - "names.pop(1)" + "names" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`sort(reverse=True)` is different from the `reverse()` method. Whereas the former applies some sorting rule in reverse order, the latter simply reverses the elements in a `list` object." ] }, { "cell_type": "code", "execution_count": 104, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "names.reverse()" + ] + }, + { + "cell_type": "code", + "execution_count": 105, "metadata": { "slideshow": { "slide_type": "-" @@ -3098,10 +3109,10 @@ { "data": { "text/plain": [ - "['Karl', 'Peter', 'Achim', 'Xavier', 'Oliver', 'Eckardt']" + "['Berthold', 'Eckardt', 'Oliver', 'Peter', 'Carl', 'Karl']" ] }, - "execution_count": 104, + "execution_count": 105, "metadata": {}, "output_type": "execute_result" } @@ -3118,12 +3129,158 @@ } }, "source": [ - "Instead of removing an element by its index, we can remove it by its value with the remove() method. Behind the scenes, Python then compares the object passed as its argument, `\"Peter\"` in the example, sequentially to each element with the `==` operator and removes the first one that evaluates equal." + "The `pop()` method removes the *last* element from a `list` object *and* returns it. Below we **capture** the `removed` element to show that the return value is not `None` as with all the methods introduced so far." ] }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 106, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "removed = names.pop()" + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Karl'" + ] + }, + "execution_count": 107, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "removed" + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Berthold', 'Eckardt', 'Oliver', 'Peter', 'Carl']" + ] + }, + "execution_count": 108, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`pop()` takes an optional *index* argument and removes that instead.\n", + "\n", + "So, to remove the second element, `\"Eckhardt\"`, from `names`, we write this." + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "removed = names.pop(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Eckardt'" + ] + }, + "execution_count": 110, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "removed" + ] + }, + { + "cell_type": "code", + "execution_count": 111, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Berthold', 'Oliver', 'Peter', 'Carl']" + ] + }, + "execution_count": 111, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Instead of removing an element by its index, we can also remove it by its value with the `remove()` method. Behind the scenes, Python then compares the object to be removed, `\"Peter\"` in the example, sequentially to each element with the `==` operator and removes the *first* one that evaluates equal to it. `remove()` does *not* return the removed element." + ] + }, + { + "cell_type": "code", + "execution_count": 112, "metadata": { "slideshow": { "slide_type": "slide" @@ -3136,7 +3293,7 @@ }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 113, "metadata": { "slideshow": { "slide_type": "-" @@ -3146,10 +3303,10 @@ { "data": { "text/plain": [ - "['Karl', 'Achim', 'Xavier', 'Oliver', 'Eckardt']" + "['Berthold', 'Oliver', 'Carl']" ] }, - "execution_count": 106, + "execution_count": 113, "metadata": {}, "output_type": "execute_result" } @@ -3166,12 +3323,12 @@ } }, "source": [ - "remove() raises a `ValueError` if the value is not found." + "Also, `remove()` raises a `ValueError` if the value is not found." ] }, { "cell_type": "code", - "execution_count": 107, + "execution_count": 114, "metadata": { "slideshow": { "slide_type": "skip" @@ -3185,7 +3342,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnames\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mremove\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Peter\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnames\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mremove\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Peter\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mValueError\u001b[0m: list.remove(x): x not in list" ] } @@ -3202,211 +3359,7 @@ } }, "source": [ - "`list` objects implement an index() method that returns the position of the first occurrence of an element. It fails loudly with a `ValueError` if the element cannot be found by value." - ] - }, - { - "cell_type": "code", - "execution_count": 108, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "['Karl', 'Achim', 'Xavier', 'Oliver', 'Eckardt']" - ] - }, - "execution_count": 108, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "names" - ] - }, - { - "cell_type": "code", - "execution_count": 109, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "3" - ] - }, - "execution_count": 109, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "names.index(\"Oliver\")" - ] - }, - { - "cell_type": "code", - "execution_count": 110, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "ename": "ValueError", - "evalue": "'Carl' is not in list", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnames\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Carl\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mValueError\u001b[0m: 'Carl' is not in list" - ] - } - ], - "source": [ - "names.index(\"Carl\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "The count() method returns the number of occurrences of a value." - ] - }, - { - "cell_type": "code", - "execution_count": 111, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "1" - ] - }, - "execution_count": 111, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "names.count(\"Xavier\")" - ] - }, - { - "cell_type": "code", - "execution_count": 112, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 112, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "names.count(\"Yves\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "### List Comparison" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "The relational operators also work with `list` objects; yet another example of operator overloading.\n", - "\n", - "Comparison is made in a pairwise fashion until the first pair of elements does not evaluate equal or one of the `list` objects ends. The exact comparison rules depend on the elements and not the `list` object. We say that comparison is **[delegated](https://en.wikipedia.org/wiki/Delegation_(object-oriented_programming))** to the objects to be compared. Usually, all elements are of the *same* type. Then, the comparison is straightforward and conceptually the same as for string comparison in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb#String-Comparison)." - ] - }, - { - "cell_type": "code", - "execution_count": 113, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "['Karl', 'Achim', 'Xavier', 'Oliver', 'Eckardt']" - ] - }, - "execution_count": 113, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "names" - ] - }, - { - "cell_type": "code", - "execution_count": 114, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 114, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "names < [\"Karl\", \"Achim\", \"Oliver\", \"Xavier\", \"Eckardt\"]" + "`list` objects implement an `index()` method that returns the position of the first element that compares equal to its argument. It fails *loudly* with a `ValueError` if no element compares equal." ] }, { @@ -3414,14 +3367,14 @@ "execution_count": 115, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ - "True" + "['Berthold', 'Oliver', 'Carl']" ] }, "execution_count": 115, @@ -3430,18 +3383,7 @@ } ], "source": [ - "names < [\"Karl\", \"Xavier\", \"Achim\", \"Oliver\", \"Eckardt\"]" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "The shorter `list` object is considered \"smaller,\" and vice versa." + "names" ] }, { @@ -3456,7 +3398,7 @@ { "data": { "text/plain": [ - "False" + "1" ] }, "execution_count": 116, @@ -3465,7 +3407,7 @@ } ], "source": [ - "names < [\"Karl\", \"Achim\", \"Xavier\", \"Oliver\"]" + "names.index(\"Oliver\")" ] }, { @@ -3476,20 +3418,597 @@ "slide_type": "-" } }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "'Karl' is not in list", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnames\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Karl\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: 'Karl' is not in list" + ] + } + ], + "source": [ + "names.index(\"Karl\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `count()` method returns the number of elements that compare equal to its argument." + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { "text/plain": [ - "True" + "1" ] }, - "execution_count": 117, + "execution_count": 118, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "names < [\"Karl\", \"Achim\", \"Xavier\", \"Oliver\", \"Eckardt\", \"Peter\"]" + "names.count(\"Carl\")" + ] + }, + { + "cell_type": "code", + "execution_count": 119, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 119, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names.count(\"Karl\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Two more methods, `copy()` and `clear()`, are *syntactic sugar* and replace working with slices.\n", + "\n", + "`copy()` creates a *shallow* copy. So, `names.copy()` below does the same as taking a full slice with `names[:]`, and the caveats from above apply, too." + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "names_copy = names.copy()" + ] + }, + { + "cell_type": "code", + "execution_count": 121, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Berthold', 'Oliver', 'Carl']" + ] + }, + "execution_count": 121, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names_copy" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`clear()` removes all references from a `list` object. So, `names_copy.clear()` is the same as `del names_copy[:]`." + ] + }, + { + "cell_type": "code", + "execution_count": 122, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "names_copy.clear()" + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 123, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names_copy" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Many methods introduced in this section are mentioned in the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module's documentation as well: While the `index()` and `count()` methods come with any data type that is a `Sequence`, the `append()`, `extend()`, `insert()`, `reverse()`, `pop()`, and `remove()` methods are part of any `MutableSequence` type. The `sort()`, `copy()`, and `clear()` methods are `list`-specific.\n", + "\n", + "So, being a sequence does not only imply the four *behaviors* specified above, but also means that a data type comes with certain standardized methods." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### List Operations" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As with `str` objects, the `+` and `*` operators are overloaded for concatenation and always return a *new* `list` object. The references in this newly created `list` object reference the *same* objects as the two original `list` objects. So, the same caveat as with *shallow* copies from above applies!" + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Berthold', 'Oliver', 'Carl']" + ] + }, + "execution_count": 124, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "code", + "execution_count": 125, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "more_names = [\"Diedrich\", \"Yves\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 126, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Berthold', 'Oliver', 'Carl', 'Diedrich', 'Yves']" + ] + }, + "execution_count": 126, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names + more_names" + ] + }, + { + "cell_type": "code", + "execution_count": 127, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Berthold', 'Oliver', 'Carl', 'Berthold', 'Oliver', 'Carl']" + ] + }, + "execution_count": 127, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "2 * names" + ] + }, + { + "cell_type": "code", + "execution_count": 128, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Diedrich', 'Yves', 'Diedrich', 'Yves', 'Diedrich', 'Yves']" + ] + }, + "execution_count": 128, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "more_names * 3" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Besides being an operator, the `*` symbol has a second syntactical use, as explained in [PEP 3132](https://www.python.org/dev/peps/pep-3132/) and [PEP 448](https://www.python.org/dev/peps/pep-0448/): It implements what is called **iterable unpacking**. It is *not* an operator syntactically but a notation that Python reads as a literal.\n", + "\n", + "In the example, Python interprets the expression as if the elements of the iterable `names` were placed between `\"Achim\"` and `\"Xavier\"` one by one. So, we do not obtain a nested but a *flat* list." + ] + }, + { + "cell_type": "code", + "execution_count": 129, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Achim', 'Berthold', 'Oliver', 'Carl', 'Xavier']" + ] + }, + "execution_count": 129, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\"Achim\", *names, \"Xavier\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Effectively, Python reads that as if we wrote the following." + ] + }, + { + "cell_type": "code", + "execution_count": 130, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Achim', 'Berthold', 'Oliver', 'Carl', 'Xavier']" + ] + }, + "execution_count": 130, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\"Achim\", names[0], names[1], names[2], \"Xavier\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### List Comparison" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The relational operators also work with `list` objects; yet another example of operator overloading.\n", + "\n", + "Comparison is made in a pairwise fashion until the first pair of elements does not evaluate equal or one of the `list` objects ends. The exact comparison rules depend on the elements and not the `list` objects. As with [sort()](https://docs.python.org/3/library/stdtypes.html#list.sort) or [sorted()](https://docs.python.org/3/library/functions.html#sorted) above, comparison is *delegated* to the objects to be compared, and Python \"asks\" the elements in the two `list` objects to compare themselves. Usually, all elements are of the *same* type, and comparison is straightforward." + ] + }, + { + "cell_type": "code", + "execution_count": 131, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Berthold', 'Oliver', 'Carl']" + ] + }, + "execution_count": 131, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "code", + "execution_count": 132, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 132, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names == [\"Berthold\", \"Oliver\", \"Carl\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 133, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 133, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names != [\"Berthold\", \"Oliver\", \"Karl\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 134, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 134, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names < [\"Berthold\", \"Oliver\", \"Karl\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 135, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 135, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\"Achim\", \"Oliver\", \"Carl\"] < names" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If two `list` objects have a different number of elements and all overlapping elements compare equal, the shorter `list` object is considered \"smaller.\" That rule is a common cause for *semantic* errors in a program." + ] + }, + { + "cell_type": "code", + "execution_count": 136, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 136, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\"Berthold\", \"Oliver\"] < names" + ] + }, + { + "cell_type": "code", + "execution_count": 137, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 137, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names < [\"Berthold\", \"Oliver\", \"Carl\", \"Xavier\"]" ] }, { @@ -3518,7 +4037,7 @@ }, { "cell_type": "code", - "execution_count": 118, + "execution_count": 138, "metadata": { "slideshow": { "slide_type": "slide" @@ -3531,7 +4050,7 @@ }, { "cell_type": "code", - "execution_count": 119, + "execution_count": 139, "metadata": { "slideshow": { "slide_type": "-" @@ -3553,14 +4072,14 @@ } }, "source": [ - "While this function is being executed, two variables, namely `letters` in the global scope and `arg` inside the function's local scope, point to the *same* `list` object in memory. Furthermore, the passed in `arg` is also the return value.\n", + "While this function is being executed, two variables, namely `letters` in the global scope and `arg` inside the function's local scope, reference the *same* `list` object in memory. Furthermore, the passed in `arg` is also the return value.\n", "\n", - "So, after the function call, `letters_with_xyz` and `letters` are **aliases** as well, pointing to the *same* object." + "So, after the function call, `letters_with_xyz` and `letters` are **aliases** as well, referencing the *same* object." ] }, { "cell_type": "code", - "execution_count": 120, + "execution_count": 140, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3573,7 +4092,7 @@ }, { "cell_type": "code", - "execution_count": 121, + "execution_count": 141, "metadata": { "slideshow": { "slide_type": "-" @@ -3586,7 +4105,7 @@ "['a', 'b', 'c', 'x', 'y', 'z']" ] }, - "execution_count": 121, + "execution_count": 141, "metadata": {}, "output_type": "execute_result" } @@ -3597,7 +4116,7 @@ }, { "cell_type": "code", - "execution_count": 122, + "execution_count": 142, "metadata": { "slideshow": { "slide_type": "-" @@ -3610,7 +4129,7 @@ "['a', 'b', 'c', 'x', 'y', 'z']" ] }, - "execution_count": 122, + "execution_count": 142, "metadata": {}, "output_type": "execute_result" } @@ -3636,7 +4155,7 @@ }, { "cell_type": "code", - "execution_count": 123, + "execution_count": 143, "metadata": { "slideshow": { "slide_type": "slide" @@ -3649,7 +4168,7 @@ }, { "cell_type": "code", - "execution_count": 124, + "execution_count": 144, "metadata": { "slideshow": { "slide_type": "-" @@ -3666,7 +4185,7 @@ }, { "cell_type": "code", - "execution_count": 125, + "execution_count": 145, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3679,7 +4198,7 @@ }, { "cell_type": "code", - "execution_count": 126, + "execution_count": 146, "metadata": { "scrolled": true, "slideshow": { @@ -3693,7 +4212,7 @@ "['a', 'b', 'c', 'x', 'y', 'z']" ] }, - "execution_count": 126, + "execution_count": 146, "metadata": {}, "output_type": "execute_result" } @@ -3704,7 +4223,7 @@ }, { "cell_type": "code", - "execution_count": 127, + "execution_count": 147, "metadata": { "slideshow": { "slide_type": "-" @@ -3717,7 +4236,7 @@ "['a', 'b', 'c']" ] }, - "execution_count": 127, + "execution_count": 147, "metadata": {}, "output_type": "execute_result" } @@ -3739,7 +4258,7 @@ }, { "cell_type": "code", - "execution_count": 128, + "execution_count": 148, "metadata": { "slideshow": { "slide_type": "slide" @@ -3752,7 +4271,7 @@ }, { "cell_type": "code", - "execution_count": 129, + "execution_count": 149, "metadata": { "slideshow": { "slide_type": "-" @@ -3768,7 +4287,7 @@ }, { "cell_type": "code", - "execution_count": 130, + "execution_count": 150, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3781,7 +4300,7 @@ }, { "cell_type": "code", - "execution_count": 131, + "execution_count": 151, "metadata": { "slideshow": { "slide_type": "-" @@ -3794,7 +4313,7 @@ "['a', 'b', 'c', 'x', 'y', 'z']" ] }, - "execution_count": 131, + "execution_count": 151, "metadata": {}, "output_type": "execute_result" } @@ -3816,7 +4335,7 @@ }, { "cell_type": "code", - "execution_count": 132, + "execution_count": 152, "metadata": { "slideshow": { "slide_type": "skip" @@ -3829,7 +4348,7 @@ }, { "cell_type": "code", - "execution_count": 133, + "execution_count": 153, "metadata": { "slideshow": { "slide_type": "skip" @@ -3842,7 +4361,7 @@ "['a', 'b', 'c', 'x', 'y', 'z', 'x', 'y', 'z']" ] }, - "execution_count": 133, + "execution_count": 153, "metadata": {}, "output_type": "execute_result" } @@ -3886,7 +4405,7 @@ }, { "cell_type": "code", - "execution_count": 134, + "execution_count": 154, "metadata": { "slideshow": { "slide_type": "slide" @@ -3899,7 +4418,7 @@ }, { "cell_type": "code", - "execution_count": 135, + "execution_count": 155, "metadata": { "slideshow": { "slide_type": "-" @@ -3912,7 +4431,7 @@ "(7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4)" ] }, - "execution_count": 135, + "execution_count": 155, "metadata": {}, "output_type": "execute_result" } @@ -3934,7 +4453,7 @@ }, { "cell_type": "code", - "execution_count": 136, + "execution_count": 156, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3947,7 +4466,7 @@ }, { "cell_type": "code", - "execution_count": 137, + "execution_count": 157, "metadata": { "slideshow": { "slide_type": "-" @@ -3960,7 +4479,7 @@ "(7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4)" ] }, - "execution_count": 137, + "execution_count": 157, "metadata": {}, "output_type": "execute_result" } @@ -3982,20 +4501,20 @@ }, { "cell_type": "code", - "execution_count": 138, + "execution_count": 158, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ - "140322025210648" + "140157873009144" ] }, - "execution_count": 138, + "execution_count": 158, "metadata": {}, "output_type": "execute_result" } @@ -4006,10 +4525,10 @@ }, { "cell_type": "code", - "execution_count": 139, + "execution_count": 159, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ @@ -4019,7 +4538,7 @@ "tuple" ] }, - "execution_count": 139, + "execution_count": 159, "metadata": {}, "output_type": "execute_result" } @@ -4041,10 +4560,10 @@ }, { "cell_type": "code", - "execution_count": 140, + "execution_count": 160, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "skip" } }, "outputs": [], @@ -4054,10 +4573,10 @@ }, { "cell_type": "code", - "execution_count": 141, + "execution_count": 161, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ @@ -4067,7 +4586,7 @@ "()" ] }, - "execution_count": 141, + "execution_count": 161, "metadata": {}, "output_type": "execute_result" } @@ -4078,10 +4597,10 @@ }, { "cell_type": "code", - "execution_count": 142, + "execution_count": 162, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ @@ -4091,7 +4610,7 @@ "tuple" ] }, - "execution_count": 142, + "execution_count": 162, "metadata": {}, "output_type": "execute_result" } @@ -4113,10 +4632,10 @@ }, { "cell_type": "code", - "execution_count": 143, + "execution_count": 163, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "skip" } }, "outputs": [], @@ -4126,10 +4645,10 @@ }, { "cell_type": "code", - "execution_count": 144, + "execution_count": 164, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ @@ -4139,7 +4658,7 @@ "(1,)" ] }, - "execution_count": 144, + "execution_count": 164, "metadata": {}, "output_type": "execute_result" } @@ -4150,10 +4669,10 @@ }, { "cell_type": "code", - "execution_count": 145, + "execution_count": 165, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ @@ -4163,7 +4682,7 @@ "tuple" ] }, - "execution_count": 145, + "execution_count": 165, "metadata": {}, "output_type": "execute_result" } @@ -4174,7 +4693,7 @@ }, { "cell_type": "code", - "execution_count": 146, + "execution_count": 166, "metadata": { "slideshow": { "slide_type": "skip" @@ -4187,7 +4706,7 @@ }, { "cell_type": "code", - "execution_count": 147, + "execution_count": 167, "metadata": { "slideshow": { "slide_type": "skip" @@ -4200,7 +4719,7 @@ "1" ] }, - "execution_count": 147, + "execution_count": 167, "metadata": {}, "output_type": "execute_result" } @@ -4211,7 +4730,7 @@ }, { "cell_type": "code", - "execution_count": 148, + "execution_count": 168, "metadata": { "slideshow": { "slide_type": "skip" @@ -4224,7 +4743,7 @@ "int" ] }, - "execution_count": 148, + "execution_count": 168, "metadata": {}, "output_type": "execute_result" } @@ -4246,10 +4765,10 @@ }, { "cell_type": "code", - "execution_count": 149, + "execution_count": 169, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "skip" } }, "outputs": [ @@ -4259,7 +4778,7 @@ "(1,)" ] }, - "execution_count": 149, + "execution_count": 169, "metadata": {}, "output_type": "execute_result" } @@ -4268,6 +4787,30 @@ "tuple([1])" ] }, + { + "cell_type": "code", + "execution_count": 170, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "('i', 't', 'e', 'r', 'a', 'b', 'l', 'e')" + ] + }, + "execution_count": 170, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tuple(\"iterable\")" + ] + }, { "cell_type": "markdown", "metadata": { @@ -4294,10 +4837,10 @@ }, { "cell_type": "code", - "execution_count": 150, + "execution_count": 171, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "skip" } }, "outputs": [ @@ -4307,7 +4850,7 @@ "True" ] }, - "execution_count": 150, + "execution_count": 171, "metadata": {}, "output_type": "execute_result" } @@ -4329,10 +4872,10 @@ }, { "cell_type": "code", - "execution_count": 151, + "execution_count": 172, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "slide" } }, "outputs": [ @@ -4342,7 +4885,7 @@ "12" ] }, - "execution_count": 151, + "execution_count": 172, "metadata": {}, "output_type": "execute_result" } @@ -4364,7 +4907,7 @@ }, { "cell_type": "code", - "execution_count": 152, + "execution_count": 173, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4386,7 +4929,7 @@ }, { "cell_type": "code", - "execution_count": 153, + "execution_count": 174, "metadata": { "slideshow": { "slide_type": "-" @@ -4419,7 +4962,7 @@ }, { "cell_type": "code", - "execution_count": 154, + "execution_count": 175, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4432,7 +4975,7 @@ "False" ] }, - "execution_count": 154, + "execution_count": 175, "metadata": {}, "output_type": "execute_result" } @@ -4443,7 +4986,7 @@ }, { "cell_type": "code", - "execution_count": 155, + "execution_count": 176, "metadata": { "slideshow": { "slide_type": "-" @@ -4456,7 +4999,7 @@ "True" ] }, - "execution_count": 155, + "execution_count": 176, "metadata": {}, "output_type": "execute_result" } @@ -4467,7 +5010,7 @@ }, { "cell_type": "code", - "execution_count": 156, + "execution_count": 177, "metadata": { "slideshow": { "slide_type": "skip" @@ -4480,7 +5023,7 @@ "True" ] }, - "execution_count": 156, + "execution_count": 177, "metadata": {}, "output_type": "execute_result" } @@ -4502,7 +5045,7 @@ }, { "cell_type": "code", - "execution_count": 157, + "execution_count": 178, "metadata": { "slideshow": { "slide_type": "slide" @@ -4515,7 +5058,7 @@ "7" ] }, - "execution_count": 157, + "execution_count": 178, "metadata": {}, "output_type": "execute_result" } @@ -4526,7 +5069,7 @@ }, { "cell_type": "code", - "execution_count": 158, + "execution_count": 179, "metadata": { "slideshow": { "slide_type": "-" @@ -4539,7 +5082,7 @@ "4" ] }, - "execution_count": 158, + "execution_count": 179, "metadata": {}, "output_type": "execute_result" } @@ -4550,7 +5093,7 @@ }, { "cell_type": "code", - "execution_count": 159, + "execution_count": 180, "metadata": { "slideshow": { "slide_type": "-" @@ -4563,7 +5106,7 @@ "(2, 6, 9, 10, 1, 4)" ] }, - "execution_count": 159, + "execution_count": 180, "metadata": {}, "output_type": "execute_result" } @@ -4585,7 +5128,7 @@ }, { "cell_type": "code", - "execution_count": 160, + "execution_count": 181, "metadata": { "slideshow": { "slide_type": "slide" @@ -4599,7 +5142,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnumbers\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m99\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnumbers\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m99\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: 'tuple' object does not support item assignment" ] } @@ -4616,44 +5159,31 @@ } }, "source": [ - "If we need to \"modify\" the `tuple` object, we must create a *new* `tuple` object, for example, like so: We take a slice of the elements we want to keep and use the overloaded `+` operator to concatenate the slice with another `tuple` object." + "We can verify the immutability with the `MutableSequence` ABC from the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module: [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) returns `False`. So, a data type that is a `Sequence` may be mutable or not. If it is a `MutableSequence`, it is mutable. If it is *not* a `MutableSequence`, it is *immutable*. There is *no* `ImmutableSequence` ABC." ] }, { "cell_type": "code", - "execution_count": 161, + "execution_count": 182, "metadata": { "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [], - "source": [ - "new_numbers = numbers[:-1] + (99,)" - ] - }, - { - "cell_type": "code", - "execution_count": 162, - "metadata": { - "slideshow": { - "slide_type": "-" + "slide_type": "skip" } }, "outputs": [ { "data": { "text/plain": [ - "(7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 99)" + "False" ] }, - "execution_count": 162, + "execution_count": 182, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "new_numbers" + "isinstance(numbers, abc.MutableSequence)" ] }, { @@ -4664,17 +5194,44 @@ } }, "source": [ - "The `*` operator works as well." + "The `+` and `*` operators work with `tuple` objects as well. However, we should *not* do that as the whole point of immutability is to *not* mutate an object.\n", + "\n", + "So, instead of writing something like below, we should use a `list` object and call its `append()` method." ] }, { "cell_type": "code", - "execution_count": 163, + "execution_count": 183, "metadata": { "slideshow": { "slide_type": "skip" } }, + "outputs": [ + { + "data": { + "text/plain": [ + "(7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4, 99)" + ] + }, + "execution_count": 183, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers + (99,) " + ] + }, + { + "cell_type": "code", + "execution_count": 184, + "metadata": { + "scrolled": false, + "slideshow": { + "slide_type": "skip" + } + }, "outputs": [ { "data": { @@ -4682,7 +5239,7 @@ "(7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4, 7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4)" ] }, - "execution_count": 163, + "execution_count": 184, "metadata": {}, "output_type": "execute_result" } @@ -4699,12 +5256,12 @@ } }, "source": [ - "Being immutable, `tuple` objects only provide the count() and index() methods." + "Being immutable, `tuple` objects only provide the `count()` and `index()` methods of `Sequence` types. The `append()`, `extend()`, `insert()`, `reverse()`, `pop()`, and `remove()` methods of `MutableSequence` types are *not* available. The same holds for the `list`-specific methods `sort()`, `copy()`, and `clear()`." ] }, { "cell_type": "code", - "execution_count": 164, + "execution_count": 185, "metadata": { "slideshow": { "slide_type": "skip" @@ -4717,7 +5274,7 @@ "0" ] }, - "execution_count": 164, + "execution_count": 185, "metadata": {}, "output_type": "execute_result" } @@ -4728,7 +5285,7 @@ }, { "cell_type": "code", - "execution_count": 165, + "execution_count": 186, "metadata": { "slideshow": { "slide_type": "skip" @@ -4741,7 +5298,7 @@ "10" ] }, - "execution_count": 165, + "execution_count": 186, "metadata": {}, "output_type": "execute_result" } @@ -4758,15 +5315,39 @@ } }, "source": [ - "The relational operators compare the elements of two `tuple` objects in a pairwise fashion as above." + "The relational operators work in the *same* way as for `list` objects." ] }, { "cell_type": "code", - "execution_count": 166, + "execution_count": 187, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4)" + ] + }, + "execution_count": 187, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 188, + "metadata": { + "slideshow": { + "slide_type": "skip" } }, "outputs": [ @@ -4776,13 +5357,85 @@ "True" ] }, - "execution_count": 166, + "execution_count": 188, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "numbers < new_numbers" + "numbers == (7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4)" + ] + }, + { + "cell_type": "code", + "execution_count": 189, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 189, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers != (99, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4)" + ] + }, + { + "cell_type": "code", + "execution_count": 190, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 190, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers < (99, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4)" + ] + }, + { + "cell_type": "code", + "execution_count": 191, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 191, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(0, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4) < numbers" ] }, { @@ -4795,15 +5448,15 @@ "source": [ "While `tuple` objects are immutable, this only relates to the references they hold. If a `tuple` object contains mutable objects, the entire nested structure is *not* immutable as a whole.\n", "\n", - "Consider the following stylized example `not_immutable`: It contains *three* elements, `1`, `[2, ..., 11]`, and `12`, and the elements of the nested `list` object may be changed. While it is not practical to mix data types in a `tuple` object that is used as an \"immutable list,\" we want to make the point that the mere usage of the `tuple` type does *not* guarantee a nested object to be immutable." + "Consider the following stylized example `not_immutable`: It contains *three* elements, `1`, `[2, ..., 11]`, and `12`, and the elements of the nested `list` object may be changed. While it is not practical to mix data types in a `tuple` object that is used as an \"immutable list,\" we want to make the point that the mere usage of the `tuple` type does *not* guarantee a nested object to be immutable as a whole." ] }, { "cell_type": "code", - "execution_count": 167, + "execution_count": 192, "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "slide" } }, "outputs": [], @@ -4813,10 +5466,34 @@ }, { "cell_type": "code", - "execution_count": 168, + "execution_count": 193, "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, [2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 12)" + ] + }, + "execution_count": 193, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "not_immutable" + ] + }, + { + "cell_type": "code", + "execution_count": 194, + "metadata": { + "slideshow": { + "slide_type": "fragment" } }, "outputs": [], @@ -4826,10 +5503,10 @@ }, { "cell_type": "code", - "execution_count": 169, + "execution_count": 195, "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "-" } }, "outputs": [ @@ -4839,7 +5516,7 @@ "(1, [99, 99, 99], 12)" ] }, - "execution_count": 169, + "execution_count": 195, "metadata": {}, "output_type": "execute_result" } @@ -4874,7 +5551,7 @@ }, { "cell_type": "code", - "execution_count": 170, + "execution_count": 196, "metadata": { "slideshow": { "slide_type": "slide" @@ -4887,7 +5564,7 @@ }, { "cell_type": "code", - "execution_count": 171, + "execution_count": 197, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4900,7 +5577,7 @@ "7" ] }, - "execution_count": 171, + "execution_count": 197, "metadata": {}, "output_type": "execute_result" } @@ -4911,7 +5588,7 @@ }, { "cell_type": "code", - "execution_count": 172, + "execution_count": 198, "metadata": { "slideshow": { "slide_type": "-" @@ -4924,7 +5601,7 @@ "11" ] }, - "execution_count": 172, + "execution_count": 198, "metadata": {}, "output_type": "execute_result" } @@ -4935,7 +5612,7 @@ }, { "cell_type": "code", - "execution_count": 173, + "execution_count": 199, "metadata": { "slideshow": { "slide_type": "-" @@ -4948,7 +5625,7 @@ "8" ] }, - "execution_count": 173, + "execution_count": 199, "metadata": {}, "output_type": "execute_result" } @@ -4970,7 +5647,7 @@ }, { "cell_type": "code", - "execution_count": 174, + "execution_count": 200, "metadata": { "slideshow": { "slide_type": "skip" @@ -4984,7 +5661,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mn1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn5\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn6\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn7\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn8\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn9\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn10\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn11\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mn1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn5\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn6\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn7\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn8\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn9\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn10\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn11\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mValueError\u001b[0m: too many values to unpack (expected 11)" ] } @@ -4995,7 +5672,7 @@ }, { "cell_type": "code", - "execution_count": 175, + "execution_count": 201, "metadata": { "slideshow": { "slide_type": "skip" @@ -5009,7 +5686,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mn1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn5\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn6\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn7\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn8\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn9\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn10\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn11\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn12\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn13\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mn1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn5\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn6\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn7\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn8\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn9\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn10\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn11\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn12\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn13\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mValueError\u001b[0m: not enough values to unpack (expected 13, got 12)" ] } @@ -5033,7 +5710,7 @@ }, { "cell_type": "code", - "execution_count": 176, + "execution_count": 202, "metadata": { "slideshow": { "slide_type": "slide" @@ -5046,7 +5723,7 @@ }, { "cell_type": "code", - "execution_count": 177, + "execution_count": 203, "metadata": { "slideshow": { "slide_type": "-" @@ -5059,7 +5736,7 @@ "7" ] }, - "execution_count": 177, + "execution_count": 203, "metadata": {}, "output_type": "execute_result" } @@ -5070,7 +5747,7 @@ }, { "cell_type": "code", - "execution_count": 178, + "execution_count": 204, "metadata": { "slideshow": { "slide_type": "-" @@ -5083,7 +5760,7 @@ "[11, 8, 5, 3, 12, 2, 6, 9, 10, 1]" ] }, - "execution_count": 178, + "execution_count": 204, "metadata": {}, "output_type": "execute_result" } @@ -5094,7 +5771,7 @@ }, { "cell_type": "code", - "execution_count": 179, + "execution_count": 205, "metadata": { "slideshow": { "slide_type": "-" @@ -5107,7 +5784,7 @@ "4" ] }, - "execution_count": 179, + "execution_count": 205, "metadata": {}, "output_type": "execute_result" } @@ -5129,7 +5806,7 @@ }, { "cell_type": "code", - "execution_count": 180, + "execution_count": 206, "metadata": { "slideshow": { "slide_type": "skip" @@ -5142,7 +5819,7 @@ }, { "cell_type": "code", - "execution_count": 181, + "execution_count": 207, "metadata": { "slideshow": { "slide_type": "skip" @@ -5155,7 +5832,7 @@ "7" ] }, - "execution_count": 181, + "execution_count": 207, "metadata": {}, "output_type": "execute_result" } @@ -5166,7 +5843,7 @@ }, { "cell_type": "code", - "execution_count": 182, + "execution_count": 208, "metadata": { "slideshow": { "slide_type": "skip" @@ -5179,7 +5856,7 @@ "4" ] }, - "execution_count": 182, + "execution_count": 208, "metadata": {}, "output_type": "execute_result" } @@ -5203,7 +5880,7 @@ }, { "cell_type": "code", - "execution_count": 183, + "execution_count": 209, "metadata": { "slideshow": { "slide_type": "slide" @@ -5216,7 +5893,7 @@ }, { "cell_type": "code", - "execution_count": 184, + "execution_count": 210, "metadata": { "slideshow": { "slide_type": "-" @@ -5227,11 +5904,9 @@ "name": "stdout", "output_type": "stream", "text": [ - "Karl is a goalkeeper\n", - "Achim is a defender\n", - "Xavier is a midfielder\n", - "Oliver is a striker\n", - "Eckardt is a coach\n" + "Berthold is a goalkeeper\n", + "Oliver is a defender\n", + "Carl is a midfielder\n" ] } ], @@ -5253,7 +5928,7 @@ }, { "cell_type": "code", - "execution_count": 185, + "execution_count": 211, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5264,11 +5939,9 @@ "name": "stdout", "output_type": "stream", "text": [ - " ('Karl', 'goalkeeper')\n", - " ('Achim', 'defender')\n", - " ('Xavier', 'midfielder')\n", - " ('Oliver', 'striker')\n", - " ('Eckardt', 'coach')\n" + " ('Berthold', 'goalkeeper')\n", + " ('Oliver', 'defender')\n", + " ('Carl', 'midfielder')\n" ] } ], @@ -5290,7 +5963,7 @@ }, { "cell_type": "code", - "execution_count": 186, + "execution_count": 212, "metadata": { "slideshow": { "slide_type": "skip" @@ -5301,11 +5974,9 @@ "name": "stdout", "output_type": "stream", "text": [ - "0 -> Karl is a goalkeeper\n", - "1 -> Achim is a defender\n", - "2 -> Xavier is a midfielder\n", - "3 -> Oliver is a striker\n", - "4 -> Eckardt is a coach\n" + "0 -> Berthold is a goalkeeper\n", + "1 -> Oliver is a defender\n", + "2 -> Carl is a midfielder\n" ] } ], @@ -5340,7 +6011,7 @@ }, { "cell_type": "code", - "execution_count": 187, + "execution_count": 213, "metadata": { "slideshow": { "slide_type": "slide" @@ -5365,7 +6036,7 @@ }, { "cell_type": "code", - "execution_count": 188, + "execution_count": 214, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5380,7 +6051,7 @@ }, { "cell_type": "code", - "execution_count": 189, + "execution_count": 215, "metadata": { "slideshow": { "slide_type": "-" @@ -5393,7 +6064,7 @@ "(1, 0)" ] }, - "execution_count": 189, + "execution_count": 215, "metadata": {}, "output_type": "execute_result" } @@ -5415,7 +6086,7 @@ }, { "cell_type": "code", - "execution_count": 190, + "execution_count": 216, "metadata": { "slideshow": { "slide_type": "slide" @@ -5429,7 +6100,7 @@ }, { "cell_type": "code", - "execution_count": 191, + "execution_count": 217, "metadata": { "slideshow": { "slide_type": "-" @@ -5442,7 +6113,7 @@ }, { "cell_type": "code", - "execution_count": 192, + "execution_count": 218, "metadata": { "slideshow": { "slide_type": "-" @@ -5455,7 +6126,7 @@ "(1, 0)" ] }, - "execution_count": 192, + "execution_count": 218, "metadata": {}, "output_type": "execute_result" } @@ -5488,7 +6159,7 @@ }, { "cell_type": "code", - "execution_count": 193, + "execution_count": 219, "metadata": { "slideshow": { "slide_type": "skip" @@ -5501,7 +6172,7 @@ }, { "cell_type": "code", - "execution_count": 194, + "execution_count": 220, "metadata": { "slideshow": { "slide_type": "skip" @@ -5519,12 +6190,17 @@ " ith_fibonacci (int)\n", "\n", " Raises:\n", - " TypeError: if i is not an integer\n", + " TypeError: if i is not an integer or not integer-like\n", " ValueError: if i is not positive\n", " \"\"\"\n", " if not isinstance(i, numbers.Integral):\n", - " raise TypeError(\"i must be an integer\")\n", - " elif i < 0:\n", + " if isinstance(i, numbers.Real):\n", + " if i != int(i):\n", + " raise TypeError(\"i is not an integer-like value; it has decimals\")\n", + " i = int(i)\n", + " else:\n", + " raise TypeError(\"i must be an integer\")\n", + " if i < 0:\n", " raise ValueError(\"i must be non-negative\")\n", "\n", " a, b = 0, 1\n", @@ -5537,7 +6213,7 @@ }, { "cell_type": "code", - "execution_count": 195, + "execution_count": 221, "metadata": { "slideshow": { "slide_type": "skip" @@ -5550,7 +6226,7 @@ "144" ] }, - "execution_count": 195, + "execution_count": 221, "metadata": {}, "output_type": "execute_result" } @@ -5559,6 +6235,67 @@ "fibonacci(12)" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Because of the *goose typing*, we may pass `float` objects to `fibonacci()` as long as they contain no decimals." + ] + }, + { + "cell_type": "code", + "execution_count": 222, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "144" + ] + }, + "execution_count": 222, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fibonacci(12.0)" + ] + }, + { + "cell_type": "code", + "execution_count": 223, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "i is not an integer-like value; it has decimals", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfibonacci\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m12.3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfibonacci\u001b[0;34m(i)\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mReal\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mi\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 17\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"i is not an integer-like value; it has decimals\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 18\u001b[0m \u001b[0mi\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: i is not an integer-like value; it has decimals" + ] + } + ], + "source": [ + "fibonacci(12.3)" + ] + }, { "cell_type": "markdown", "metadata": { @@ -5587,7 +6324,7 @@ }, { "cell_type": "code", - "execution_count": 196, + "execution_count": 224, "metadata": { "slideshow": { "slide_type": "slide" @@ -5620,7 +6357,7 @@ }, { "cell_type": "code", - "execution_count": 197, + "execution_count": 225, "metadata": { "slideshow": { "slide_type": "-" @@ -5633,7 +6370,7 @@ "42" ] }, - "execution_count": 197, + "execution_count": 225, "metadata": {}, "output_type": "execute_result" } @@ -5655,7 +6392,7 @@ }, { "cell_type": "code", - "execution_count": 198, + "execution_count": 226, "metadata": { "slideshow": { "slide_type": "-" @@ -5668,7 +6405,7 @@ "100" ] }, - "execution_count": 198, + "execution_count": 226, "metadata": {}, "output_type": "execute_result" } @@ -5690,7 +6427,7 @@ }, { "cell_type": "code", - "execution_count": 199, + "execution_count": 227, "metadata": { "slideshow": { "slide_type": "slide" @@ -5704,8 +6441,8 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mproduct\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m\u001b[0m in \u001b[0;36mproduct\u001b[0;34m(*args)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mproduct\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\"\"\"Multiply all arguments.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0marg\u001b[0m \u001b[0;32min\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mproduct\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mproduct\u001b[0;34m(*args)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mproduct\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\"\"\"Multiply all arguments.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0marg\u001b[0m \u001b[0;32min\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mIndexError\u001b[0m: tuple index out of range" ] } @@ -5727,7 +6464,7 @@ }, { "cell_type": "code", - "execution_count": 200, + "execution_count": 228, "metadata": { "slideshow": { "slide_type": "slide" @@ -5740,7 +6477,7 @@ }, { "cell_type": "code", - "execution_count": 201, + "execution_count": 229, "metadata": { "slideshow": { "slide_type": "-" @@ -5753,7 +6490,7 @@ "[2, 5, 10]" ] }, - "execution_count": 201, + "execution_count": 229, "metadata": {}, "output_type": "execute_result" } @@ -5775,7 +6512,7 @@ }, { "cell_type": "code", - "execution_count": 202, + "execution_count": 230, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5788,7 +6525,7 @@ "100" ] }, - "execution_count": 202, + "execution_count": 230, "metadata": {}, "output_type": "execute_result" } @@ -5810,7 +6547,7 @@ }, { "cell_type": "code", - "execution_count": 203, + "execution_count": 231, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5823,7 +6560,7 @@ "100" ] }, - "execution_count": 203, + "execution_count": 231, "metadata": {}, "output_type": "execute_result" } @@ -5840,6 +6577,8 @@ } }, "source": [ + "In the \"*Packing & Unpacking with Functions*\" [exercise](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_review_and_exercises.ipynb#Packing-&-Unpacking-with-Functions) at the end of this chapter, we look at `product()` in more detail.\n", + "\n", "While we needed to unpack `one_hundred` above to avoid the semantic error, unpacking an argument in a function call may also be a convenience in general.\n", "\n", "For example, to print the elements of `one_hundred` in one line, we need to use a `for` statement, until now. With unpacking, we get away *without* a loop." @@ -5847,7 +6586,7 @@ }, { "cell_type": "code", - "execution_count": 204, + "execution_count": 232, "metadata": { "slideshow": { "slide_type": "skip" @@ -5868,7 +6607,7 @@ }, { "cell_type": "code", - "execution_count": 205, + "execution_count": 233, "metadata": { "slideshow": { "slide_type": "skip" @@ -5890,7 +6629,7 @@ }, { "cell_type": "code", - "execution_count": 206, + "execution_count": 234, "metadata": { "slideshow": { "slide_type": "skip" @@ -5906,7 +6645,7 @@ } ], "source": [ - "print(*one_hundred)" + "print(*one_hundred) # replaces the for-loop" ] }, { @@ -5917,7 +6656,7 @@ } }, "source": [ - "## The `namedtuple` Type" + "### The `namedtuple` Type" ] }, { @@ -5939,7 +6678,7 @@ }, { "cell_type": "code", - "execution_count": 207, + "execution_count": 235, "metadata": { "slideshow": { "slide_type": "slide" @@ -5965,7 +6704,7 @@ }, { "cell_type": "code", - "execution_count": 208, + "execution_count": 236, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5989,7 +6728,7 @@ }, { "cell_type": "code", - "execution_count": 209, + "execution_count": 237, "metadata": { "slideshow": { "slide_type": "-" @@ -6013,7 +6752,7 @@ }, { "cell_type": "code", - "execution_count": 210, + "execution_count": 238, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6023,10 +6762,10 @@ { "data": { "text/plain": [ - "140321614839000" + "140157466523464" ] }, - "execution_count": 210, + "execution_count": 238, "metadata": {}, "output_type": "execute_result" } @@ -6037,7 +6776,7 @@ }, { "cell_type": "code", - "execution_count": 211, + "execution_count": 239, "metadata": { "slideshow": { "slide_type": "-" @@ -6050,7 +6789,7 @@ "type" ] }, - "execution_count": 211, + "execution_count": 239, "metadata": {}, "output_type": "execute_result" } @@ -6067,12 +6806,47 @@ } }, "source": [ - "To create a `Point` object, we use the same *literal syntax* as for `current_position` above and prepend it with `Point`." + "The value of `Point` is just itself in a *literal notation*." ] }, { "cell_type": "code", - "execution_count": 212, + "execution_count": 240, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "__main__.Point" + ] + }, + "execution_count": 240, + "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": 241, "metadata": { "slideshow": { "slide_type": "slide" @@ -6096,7 +6870,7 @@ }, { "cell_type": "code", - "execution_count": 213, + "execution_count": 242, "metadata": { "slideshow": { "slide_type": "-" @@ -6109,7 +6883,7 @@ "Point(x=4, y=2)" ] }, - "execution_count": 213, + "execution_count": 242, "metadata": {}, "output_type": "execute_result" } @@ -6131,7 +6905,7 @@ }, { "cell_type": "code", - "execution_count": 214, + "execution_count": 243, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6141,10 +6915,10 @@ { "data": { "text/plain": [ - "140322025282656" + "140157872590424" ] }, - "execution_count": 214, + "execution_count": 243, "metadata": {}, "output_type": "execute_result" } @@ -6155,7 +6929,7 @@ }, { "cell_type": "code", - "execution_count": 215, + "execution_count": 244, "metadata": { "slideshow": { "slide_type": "-" @@ -6168,7 +6942,7 @@ "__main__.Point" ] }, - "execution_count": 215, + "execution_count": 244, "metadata": {}, "output_type": "execute_result" } @@ -6190,7 +6964,7 @@ }, { "cell_type": "code", - "execution_count": 216, + "execution_count": 245, "metadata": { "slideshow": { "slide_type": "slide" @@ -6203,7 +6977,7 @@ "4" ] }, - "execution_count": 216, + "execution_count": 245, "metadata": {}, "output_type": "execute_result" } @@ -6214,7 +6988,7 @@ }, { "cell_type": "code", - "execution_count": 217, + "execution_count": 246, "metadata": { "slideshow": { "slide_type": "-" @@ -6227,7 +7001,7 @@ "2" ] }, - "execution_count": 217, + "execution_count": 246, "metadata": {}, "output_type": "execute_result" } @@ -6249,7 +7023,7 @@ }, { "cell_type": "code", - "execution_count": 218, + "execution_count": 247, "metadata": { "slideshow": { "slide_type": "skip" @@ -6263,7 +7037,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mcurrent_position\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mz\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mcurrent_position\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mz\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mAttributeError\u001b[0m: 'Point' object has no attribute 'z'" ] } @@ -6287,7 +7061,7 @@ }, { "cell_type": "code", - "execution_count": 219, + "execution_count": 248, "metadata": { "slideshow": { "slide_type": "slide" @@ -6300,7 +7074,7 @@ "True" ] }, - "execution_count": 219, + "execution_count": 248, "metadata": {}, "output_type": "execute_result" } @@ -6311,7 +7085,7 @@ }, { "cell_type": "code", - "execution_count": 220, + "execution_count": 249, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6324,7 +7098,7 @@ "4" ] }, - "execution_count": 220, + "execution_count": 249, "metadata": {}, "output_type": "execute_result" } @@ -6335,7 +7109,7 @@ }, { "cell_type": "code", - "execution_count": 221, + "execution_count": 250, "metadata": { "slideshow": { "slide_type": "-" @@ -6348,7 +7122,7 @@ "2" ] }, - "execution_count": 221, + "execution_count": 250, "metadata": {}, "output_type": "execute_result" } @@ -6359,7 +7133,7 @@ }, { "cell_type": "code", - "execution_count": 222, + "execution_count": 251, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6382,7 +7156,7 @@ }, { "cell_type": "code", - "execution_count": 223, + "execution_count": 252, "metadata": { "slideshow": { "slide_type": "skip" @@ -6429,7 +7203,7 @@ }, { "cell_type": "code", - "execution_count": 224, + "execution_count": 253, "metadata": { "slideshow": { "slide_type": "slide" @@ -6466,7 +7240,7 @@ }, { "cell_type": "code", - "execution_count": 225, + "execution_count": 254, "metadata": { "slideshow": { "slide_type": "slide" @@ -6492,7 +7266,7 @@ }, { "cell_type": "code", - "execution_count": 226, + "execution_count": 255, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6509,7 +7283,7 @@ }, { "cell_type": "code", - "execution_count": 227, + "execution_count": 256, "metadata": { "slideshow": { "slide_type": "-" @@ -6522,7 +7296,7 @@ "[50, 122, 65, 26, 10, 145, 5, 37, 82, 101, 2, 17]" ] }, - "execution_count": 227, + "execution_count": 256, "metadata": {}, "output_type": "execute_result" } @@ -6546,7 +7320,7 @@ }, { "cell_type": "code", - "execution_count": 228, + "execution_count": 257, "metadata": { "slideshow": { "slide_type": "slide" @@ -6570,7 +7344,7 @@ }, { "cell_type": "code", - "execution_count": 229, + "execution_count": 258, "metadata": { "slideshow": { "slide_type": "-" @@ -6580,10 +7354,10 @@ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 229, + "execution_count": 258, "metadata": {}, "output_type": "execute_result" } @@ -6594,7 +7368,7 @@ }, { "cell_type": "code", - "execution_count": 230, + "execution_count": 259, "metadata": { "slideshow": { "slide_type": "-" @@ -6607,7 +7381,7 @@ "map" ] }, - "execution_count": 230, + "execution_count": 259, "metadata": {}, "output_type": "execute_result" } @@ -6631,7 +7405,7 @@ }, { "cell_type": "code", - "execution_count": 231, + "execution_count": 260, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6644,7 +7418,7 @@ "50" ] }, - "execution_count": 231, + "execution_count": 260, "metadata": {}, "output_type": "execute_result" } @@ -6655,7 +7429,7 @@ }, { "cell_type": "code", - "execution_count": 232, + "execution_count": 261, "metadata": { "slideshow": { "slide_type": "skip" @@ -6668,7 +7442,7 @@ "122" ] }, - "execution_count": 232, + "execution_count": 261, "metadata": {}, "output_type": "execute_result" } @@ -6679,7 +7453,7 @@ }, { "cell_type": "code", - "execution_count": 233, + "execution_count": 262, "metadata": { "slideshow": { "slide_type": "skip" @@ -6692,7 +7466,7 @@ "65" ] }, - "execution_count": 233, + "execution_count": 262, "metadata": {}, "output_type": "execute_result" } @@ -6716,7 +7490,7 @@ }, { "cell_type": "code", - "execution_count": 234, + "execution_count": 263, "metadata": { "slideshow": { "slide_type": "slide" @@ -6729,7 +7503,7 @@ }, { "cell_type": "code", - "execution_count": 235, + "execution_count": 264, "metadata": { "slideshow": { "slide_type": "-" @@ -6742,7 +7516,7 @@ "[50, 122, 65, 26, 10, 145, 5, 37, 82, 101, 2, 17]" ] }, - "execution_count": 235, + "execution_count": 264, "metadata": {}, "output_type": "execute_result" } @@ -6777,7 +7551,7 @@ }, { "cell_type": "code", - "execution_count": 236, + "execution_count": 265, "metadata": { "slideshow": { "slide_type": "slide" @@ -6805,7 +7579,7 @@ }, { "cell_type": "code", - "execution_count": 237, + "execution_count": 266, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6822,7 +7596,7 @@ }, { "cell_type": "code", - "execution_count": 238, + "execution_count": 267, "metadata": { "slideshow": { "slide_type": "-" @@ -6835,7 +7609,7 @@ "[50, 122, 26, 10, 82, 2]" ] }, - "execution_count": 238, + "execution_count": 267, "metadata": {}, "output_type": "execute_result" } @@ -6857,7 +7631,7 @@ }, { "cell_type": "code", - "execution_count": 239, + "execution_count": 268, "metadata": { "slideshow": { "slide_type": "slide" @@ -6870,7 +7644,7 @@ }, { "cell_type": "code", - "execution_count": 240, + "execution_count": 269, "metadata": { "slideshow": { "slide_type": "-" @@ -6880,10 +7654,10 @@ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 240, + "execution_count": 269, "metadata": {}, "output_type": "execute_result" } @@ -6894,7 +7668,7 @@ }, { "cell_type": "code", - "execution_count": 241, + "execution_count": 270, "metadata": { "slideshow": { "slide_type": "-" @@ -6907,7 +7681,7 @@ "filter" ] }, - "execution_count": 241, + "execution_count": 270, "metadata": {}, "output_type": "execute_result" } @@ -6929,7 +7703,7 @@ }, { "cell_type": "code", - "execution_count": 242, + "execution_count": 271, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6942,7 +7716,7 @@ "[50, 122, 65, 26, 10, 145, 5, 37, 82, 101, 2, 17]" ] }, - "execution_count": 242, + "execution_count": 271, "metadata": {}, "output_type": "execute_result" } @@ -6953,7 +7727,7 @@ }, { "cell_type": "code", - "execution_count": 243, + "execution_count": 272, "metadata": { "slideshow": { "slide_type": "-" @@ -6966,7 +7740,7 @@ "50" ] }, - "execution_count": 243, + "execution_count": 272, "metadata": {}, "output_type": "execute_result" } @@ -6977,7 +7751,7 @@ }, { "cell_type": "code", - "execution_count": 244, + "execution_count": 273, "metadata": { "slideshow": { "slide_type": "skip" @@ -6990,7 +7764,7 @@ "122" ] }, - "execution_count": 244, + "execution_count": 273, "metadata": {}, "output_type": "execute_result" } @@ -7001,7 +7775,7 @@ }, { "cell_type": "code", - "execution_count": 245, + "execution_count": 274, "metadata": { "slideshow": { "slide_type": "skip" @@ -7014,7 +7788,7 @@ "26" ] }, - "execution_count": 245, + "execution_count": 274, "metadata": {}, "output_type": "execute_result" } @@ -7036,7 +7810,7 @@ }, { "cell_type": "code", - "execution_count": 246, + "execution_count": 275, "metadata": { "slideshow": { "slide_type": "slide" @@ -7049,7 +7823,7 @@ "[50, 122, 26, 10, 82, 2]" ] }, - "execution_count": 246, + "execution_count": 275, "metadata": {}, "output_type": "execute_result" } @@ -7071,7 +7845,7 @@ }, { "cell_type": "code", - "execution_count": 247, + "execution_count": 276, "metadata": { "slideshow": { "slide_type": "fragment" @@ -7084,7 +7858,7 @@ "[50, 122, 26, 10, 82, 2]" ] }, - "execution_count": 247, + "execution_count": 276, "metadata": {}, "output_type": "execute_result" } @@ -7106,7 +7880,7 @@ }, { "cell_type": "code", - "execution_count": 248, + "execution_count": 277, "metadata": { "slideshow": { "slide_type": "fragment" @@ -7119,7 +7893,7 @@ "[65, 145, 5, 37, 101, 17]" ] }, - "execution_count": 248, + "execution_count": 277, "metadata": {}, "output_type": "execute_result" } @@ -7154,7 +7928,7 @@ }, { "cell_type": "code", - "execution_count": 249, + "execution_count": 278, "metadata": { "slideshow": { "slide_type": "slide" @@ -7167,7 +7941,7 @@ "370" ] }, - "execution_count": 249, + "execution_count": 278, "metadata": {}, "output_type": "execute_result" } @@ -7189,7 +7963,7 @@ }, { "cell_type": "code", - "execution_count": 250, + "execution_count": 279, "metadata": { "slideshow": { "slide_type": "fragment" @@ -7202,7 +7976,7 @@ "5" ] }, - "execution_count": 250, + "execution_count": 279, "metadata": {}, "output_type": "execute_result" } @@ -7213,7 +7987,7 @@ }, { "cell_type": "code", - "execution_count": 251, + "execution_count": 280, "metadata": { "slideshow": { "slide_type": "-" @@ -7226,7 +8000,7 @@ "145" ] }, - "execution_count": 251, + "execution_count": 280, "metadata": {}, "output_type": "execute_result" } @@ -7252,7 +8026,7 @@ }, { "cell_type": "code", - "execution_count": 252, + "execution_count": 281, "metadata": { "slideshow": { "slide_type": "slide" @@ -7278,7 +8052,7 @@ }, { "cell_type": "code", - "execution_count": 253, + "execution_count": 282, "metadata": { "slideshow": { "slide_type": "fragment" @@ -7304,7 +8078,7 @@ }, { "cell_type": "code", - "execution_count": 254, + "execution_count": 283, "metadata": { "slideshow": { "slide_type": "fragment" @@ -7340,7 +8114,7 @@ }, { "cell_type": "code", - "execution_count": 255, + "execution_count": 284, "metadata": { "slideshow": { "slide_type": "-" @@ -7353,7 +8127,7 @@ "370" ] }, - "execution_count": 255, + "execution_count": 284, "metadata": {}, "output_type": "execute_result" } @@ -7377,7 +8151,7 @@ }, { "cell_type": "code", - "execution_count": 256, + "execution_count": 285, "metadata": { "slideshow": { "slide_type": "slide" @@ -7390,7 +8164,7 @@ }, { "cell_type": "code", - "execution_count": 257, + "execution_count": 286, "metadata": { "slideshow": { "slide_type": "-" @@ -7403,7 +8177,7 @@ "370" ] }, - "execution_count": 257, + "execution_count": 286, "metadata": {}, "output_type": "execute_result" } @@ -7433,16 +8207,16 @@ "source": [ "[map()](https://docs.python.org/3/library/functions.html#map), [filter()](https://docs.python.org/3/library/functions.html#filter), and [reduce()](https://docs.python.org/3/library/functools.html#functools.reduce) take a `function` object as their first argument, and we defined `transform()`, `is_even()`, and `add()` to be used precisely for that.\n", "\n", - "Often, such functions are used *only once* in a program. However, the primary purpose of functions is to *re-use* them. In such cases, it makes more sense to define them \"anonymously\" right at the position where the first argument goes.\n", + "Often, such functions are used *only once* in a program. However, the primary purpose of functions is to *reuse* them. In such cases, it makes more sense to define them \"anonymously\" right at the position where the first argument goes.\n", "\n", - "As mentioned in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb#Anonymous-Functions), Python provides `lambda` expressions to create `function` objects *without* a variable pointing to them.\n", + "As mentioned in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb#Anonymous-Functions), Python provides `lambda` expressions to create `function` objects *without* a name referencing them.\n", "\n", "So, the above `add()` function could be rewritten as a `lambda` expression like so ..." ] }, { "cell_type": "code", - "execution_count": 258, + "execution_count": 287, "metadata": { "slideshow": { "slide_type": "slide" @@ -7455,7 +8229,7 @@ "(sum_so_far, next_number)>" ] }, - "execution_count": 258, + "execution_count": 287, "metadata": {}, "output_type": "execute_result" } @@ -7477,7 +8251,7 @@ }, { "cell_type": "code", - "execution_count": 259, + "execution_count": 288, "metadata": { "slideshow": { "slide_type": "fragment" @@ -7490,7 +8264,7 @@ "(x, y)>" ] }, - "execution_count": 259, + "execution_count": 288, "metadata": {}, "output_type": "execute_result" } @@ -7512,7 +8286,7 @@ }, { "cell_type": "code", - "execution_count": 260, + "execution_count": 289, "metadata": { "slideshow": { "slide_type": "slide" @@ -7525,7 +8299,7 @@ "370" ] }, - "execution_count": 260, + "execution_count": 289, "metadata": {}, "output_type": "execute_result" } @@ -7550,7 +8324,7 @@ }, { "cell_type": "code", - "execution_count": 261, + "execution_count": 290, "metadata": { "slideshow": { "slide_type": "fragment" @@ -7563,7 +8337,7 @@ "370" ] }, - "execution_count": 261, + "execution_count": 290, "metadata": {}, "output_type": "execute_result" } @@ -7588,7 +8362,7 @@ }, { "cell_type": "code", - "execution_count": 262, + "execution_count": 291, "metadata": { "slideshow": { "slide_type": "fragment" @@ -7601,7 +8375,7 @@ "370" ] }, - "execution_count": 262, + "execution_count": 291, "metadata": {}, "output_type": "execute_result" } @@ -7654,7 +8428,7 @@ }, { "cell_type": "code", - "execution_count": 263, + "execution_count": 292, "metadata": { "slideshow": { "slide_type": "slide" @@ -7667,7 +8441,7 @@ }, { "cell_type": "code", - "execution_count": 264, + "execution_count": 293, "metadata": { "slideshow": { "slide_type": "-" @@ -7684,7 +8458,7 @@ }, { "cell_type": "code", - "execution_count": 265, + "execution_count": 294, "metadata": { "slideshow": { "slide_type": "-" @@ -7697,7 +8471,7 @@ "[65, 145, 5, 37, 101, 17]" ] }, - "execution_count": 265, + "execution_count": 294, "metadata": {}, "output_type": "execute_result" } @@ -7719,7 +8493,7 @@ }, { "cell_type": "code", - "execution_count": 266, + "execution_count": 295, "metadata": { "slideshow": { "slide_type": "slide" @@ -7732,7 +8506,7 @@ "[65, 145, 5, 37, 101, 17]" ] }, - "execution_count": 266, + "execution_count": 295, "metadata": {}, "output_type": "execute_result" } @@ -7756,7 +8530,7 @@ }, { "cell_type": "code", - "execution_count": 267, + "execution_count": 296, "metadata": { "slideshow": { "slide_type": "fragment" @@ -7769,7 +8543,7 @@ "370" ] }, - "execution_count": 267, + "execution_count": 296, "metadata": {}, "output_type": "execute_result" } @@ -7804,7 +8578,7 @@ }, { "cell_type": "code", - "execution_count": 268, + "execution_count": 297, "metadata": { "slideshow": { "slide_type": "skip" @@ -7817,7 +8591,7 @@ }, { "cell_type": "code", - "execution_count": 269, + "execution_count": 298, "metadata": { "slideshow": { "slide_type": "skip" @@ -7830,7 +8604,7 @@ "[[1, 2, 3, 4, 5, 6, 7], [2, 3, 4, 5, 6, 7, 8], [3, 4, 5, 6, 7, 8, 9]]" ] }, - "execution_count": 269, + "execution_count": 298, "metadata": {}, "output_type": "execute_result" } @@ -7854,7 +8628,7 @@ }, { "cell_type": "code", - "execution_count": 270, + "execution_count": 299, "metadata": { "slideshow": { "slide_type": "skip" @@ -7871,7 +8645,7 @@ }, { "cell_type": "code", - "execution_count": 271, + "execution_count": 300, "metadata": { "slideshow": { "slide_type": "skip" @@ -7884,7 +8658,7 @@ "[1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 8, 9]" ] }, - "execution_count": 271, + "execution_count": 300, "metadata": {}, "output_type": "execute_result" } @@ -7906,7 +8680,7 @@ }, { "cell_type": "code", - "execution_count": 272, + "execution_count": 301, "metadata": { "slideshow": { "slide_type": "skip" @@ -7919,7 +8693,7 @@ "[1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 8, 3, 4, 5, 6, 7, 8, 9]" ] }, - "execution_count": 272, + "execution_count": 301, "metadata": {}, "output_type": "execute_result" } @@ -7943,7 +8717,7 @@ }, { "cell_type": "code", - "execution_count": 273, + "execution_count": 302, "metadata": { "slideshow": { "slide_type": "skip" @@ -7956,7 +8730,7 @@ "105" ] }, - "execution_count": 273, + "execution_count": 302, "metadata": {}, "output_type": "execute_result" } @@ -7978,7 +8752,7 @@ }, { "cell_type": "code", - "execution_count": 274, + "execution_count": 303, "metadata": { "slideshow": { "slide_type": "skip" @@ -7991,7 +8765,7 @@ "105" ] }, - "execution_count": 274, + "execution_count": 303, "metadata": {}, "output_type": "execute_result" } @@ -8034,7 +8808,7 @@ }, { "cell_type": "code", - "execution_count": 275, + "execution_count": 304, "metadata": { "slideshow": { "slide_type": "skip" @@ -8059,7 +8833,7 @@ }, { "cell_type": "code", - "execution_count": 276, + "execution_count": 305, "metadata": { "slideshow": { "slide_type": "skip" @@ -8072,7 +8846,7 @@ "[1.25, 1.2, 1.1666666666666667, 1.5, 1.4, 1.3333333333333333, 1.75, 1.6, 1.5]" ] }, - "execution_count": 276, + "execution_count": 305, "metadata": {}, "output_type": "execute_result" } @@ -8101,7 +8875,7 @@ }, { "cell_type": "code", - "execution_count": 277, + "execution_count": 306, "metadata": { "slideshow": { "slide_type": "skip" @@ -8114,7 +8888,7 @@ "[1.25, 1.2, 1.1666666666666667, 1.5, 1.4, 1.3333333333333333, 1.75, 1.6, 1.5]" ] }, - "execution_count": 277, + "execution_count": 306, "metadata": {}, "output_type": "execute_result" } @@ -8136,7 +8910,7 @@ }, { "cell_type": "code", - "execution_count": 278, + "execution_count": 307, "metadata": { "slideshow": { "slide_type": "skip" @@ -8149,7 +8923,7 @@ "[5.0, 3.0, 2.333333333333333, 6.0, 3.5, 2.666666666666667, 7.0, 4.0, 3.0]" ] }, - "execution_count": 278, + "execution_count": 307, "metadata": {}, "output_type": "execute_result" } @@ -8171,7 +8945,7 @@ }, { "cell_type": "code", - "execution_count": 279, + "execution_count": 308, "metadata": { "slideshow": { "slide_type": "skip" @@ -8184,7 +8958,7 @@ "20.58" ] }, - "execution_count": 279, + "execution_count": 308, "metadata": {}, "output_type": "execute_result" } @@ -8206,7 +8980,7 @@ }, { "cell_type": "code", - "execution_count": 280, + "execution_count": 309, "metadata": { "slideshow": { "slide_type": "skip" @@ -8219,7 +8993,7 @@ "20.58" ] }, - "execution_count": 280, + "execution_count": 309, "metadata": {}, "output_type": "execute_result" } @@ -8265,7 +9039,7 @@ }, { "cell_type": "code", - "execution_count": 281, + "execution_count": 310, "metadata": { "slideshow": { "slide_type": "slide" @@ -8289,7 +9063,7 @@ }, { "cell_type": "code", - "execution_count": 282, + "execution_count": 311, "metadata": { "slideshow": { "slide_type": "-" @@ -8302,7 +9076,7 @@ "[65, 145, 5, 37, 101, 17]" ] }, - "execution_count": 282, + "execution_count": 311, "metadata": {}, "output_type": "execute_result" } @@ -8324,7 +9098,7 @@ }, { "cell_type": "code", - "execution_count": 283, + "execution_count": 312, "metadata": { "slideshow": { "slide_type": "fragment" @@ -8334,10 +9108,10 @@ { "data": { "text/plain": [ - " at 0x7f9f447618b8>" + " at 0x7f790c3a55e8>" ] }, - "execution_count": 283, + "execution_count": 312, "metadata": {}, "output_type": "execute_result" } @@ -8361,7 +9135,7 @@ }, { "cell_type": "code", - "execution_count": 284, + "execution_count": 313, "metadata": { "slideshow": { "slide_type": "fragment" @@ -8374,7 +9148,7 @@ "[65, 145, 5, 37, 101, 17]" ] }, - "execution_count": 284, + "execution_count": 313, "metadata": {}, "output_type": "execute_result" } @@ -8396,7 +9170,7 @@ }, { "cell_type": "code", - "execution_count": 285, + "execution_count": 314, "metadata": { "slideshow": { "slide_type": "fragment" @@ -8409,7 +9183,7 @@ "[65, 145, 5, 37, 101, 17]" ] }, - "execution_count": 285, + "execution_count": 314, "metadata": {}, "output_type": "execute_result" } @@ -8431,7 +9205,7 @@ }, { "cell_type": "code", - "execution_count": 286, + "execution_count": 315, "metadata": { "slideshow": { "slide_type": "fragment" @@ -8444,7 +9218,7 @@ "370" ] }, - "execution_count": 286, + "execution_count": 315, "metadata": {}, "output_type": "execute_result" } @@ -8466,7 +9240,7 @@ }, { "cell_type": "code", - "execution_count": 287, + "execution_count": 316, "metadata": { "slideshow": { "slide_type": "slide" @@ -8479,7 +9253,7 @@ }, { "cell_type": "code", - "execution_count": 288, + "execution_count": 317, "metadata": { "slideshow": { "slide_type": "-" @@ -8489,10 +9263,10 @@ { "data": { "text/plain": [ - " at 0x7f9f44761d68>" + " at 0x7f790c3a5a20>" ] }, - "execution_count": 288, + "execution_count": 317, "metadata": {}, "output_type": "execute_result" } @@ -8514,7 +9288,7 @@ }, { "cell_type": "code", - "execution_count": 289, + "execution_count": 318, "metadata": { "slideshow": { "slide_type": "-" @@ -8527,7 +9301,7 @@ "generator" ] }, - "execution_count": 289, + "execution_count": 318, "metadata": {}, "output_type": "execute_result" } @@ -8549,7 +9323,7 @@ }, { "cell_type": "code", - "execution_count": 290, + "execution_count": 319, "metadata": { "slideshow": { "slide_type": "fragment" @@ -8562,7 +9336,7 @@ "65" ] }, - "execution_count": 290, + "execution_count": 319, "metadata": {}, "output_type": "execute_result" } @@ -8573,7 +9347,7 @@ }, { "cell_type": "code", - "execution_count": 291, + "execution_count": 320, "metadata": { "slideshow": { "slide_type": "skip" @@ -8586,7 +9360,7 @@ "145" ] }, - "execution_count": 291, + "execution_count": 320, "metadata": {}, "output_type": "execute_result" } @@ -8597,7 +9371,7 @@ }, { "cell_type": "code", - "execution_count": 292, + "execution_count": 321, "metadata": { "slideshow": { "slide_type": "skip" @@ -8610,7 +9384,7 @@ "5" ] }, - "execution_count": 292, + "execution_count": 321, "metadata": {}, "output_type": "execute_result" } @@ -8621,7 +9395,7 @@ }, { "cell_type": "code", - "execution_count": 293, + "execution_count": 322, "metadata": { "slideshow": { "slide_type": "skip" @@ -8634,7 +9408,7 @@ "37" ] }, - "execution_count": 293, + "execution_count": 322, "metadata": {}, "output_type": "execute_result" } @@ -8645,7 +9419,7 @@ }, { "cell_type": "code", - "execution_count": 294, + "execution_count": 323, "metadata": { "slideshow": { "slide_type": "skip" @@ -8658,7 +9432,7 @@ "101" ] }, - "execution_count": 294, + "execution_count": 323, "metadata": {}, "output_type": "execute_result" } @@ -8669,7 +9443,7 @@ }, { "cell_type": "code", - "execution_count": 295, + "execution_count": 324, "metadata": { "slideshow": { "slide_type": "skip" @@ -8682,7 +9456,7 @@ "17" ] }, - "execution_count": 295, + "execution_count": 324, "metadata": {}, "output_type": "execute_result" } @@ -8704,7 +9478,7 @@ }, { "cell_type": "code", - "execution_count": 296, + "execution_count": 325, "metadata": { "slideshow": { "slide_type": "skip" @@ -8718,7 +9492,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mStopIteration\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgen\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgen\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mStopIteration\u001b[0m: " ] } @@ -8729,7 +9503,7 @@ }, { "cell_type": "code", - "execution_count": 297, + "execution_count": 326, "metadata": { "slideshow": { "slide_type": "skip" @@ -8743,7 +9517,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mStopIteration\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgen\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgen\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mStopIteration\u001b[0m: " ] } @@ -8765,7 +9539,7 @@ }, { "cell_type": "code", - "execution_count": 298, + "execution_count": 327, "metadata": { "slideshow": { "slide_type": "skip" @@ -8813,7 +9587,7 @@ }, { "cell_type": "code", - "execution_count": 299, + "execution_count": 328, "metadata": { "slideshow": { "slide_type": "skip" @@ -8826,7 +9600,7 @@ }, { "cell_type": "code", - "execution_count": 300, + "execution_count": 329, "metadata": { "slideshow": { "slide_type": "skip" @@ -8839,7 +9613,7 @@ "[[1, 2, 3, 4, 5, 6, 7], [2, 3, 4, 5, 6, 7, 8], [3, 4, 5, 6, 7, 8, 9]]" ] }, - "execution_count": 300, + "execution_count": 329, "metadata": {}, "output_type": "execute_result" } @@ -8861,7 +9635,7 @@ }, { "cell_type": "code", - "execution_count": 301, + "execution_count": 330, "metadata": { "slideshow": { "slide_type": "skip" @@ -8874,7 +9648,7 @@ "105" ] }, - "execution_count": 301, + "execution_count": 330, "metadata": {}, "output_type": "execute_result" } @@ -8896,7 +9670,7 @@ }, { "cell_type": "code", - "execution_count": 302, + "execution_count": 331, "metadata": { "slideshow": { "slide_type": "skip" @@ -8909,7 +9683,7 @@ "105" ] }, - "execution_count": 302, + "execution_count": 331, "metadata": {}, "output_type": "execute_result" } @@ -8931,7 +9705,7 @@ }, { "cell_type": "code", - "execution_count": 303, + "execution_count": 332, "metadata": { "slideshow": { "slide_type": "skip" @@ -8944,7 +9718,7 @@ }, { "cell_type": "code", - "execution_count": 304, + "execution_count": 333, "metadata": { "slideshow": { "slide_type": "skip" @@ -8954,10 +9728,10 @@ { "data": { "text/plain": [ - " at 0x7f9f44761e58>" + " at 0x7f790c3a5b10>" ] }, - "execution_count": 304, + "execution_count": 333, "metadata": {}, "output_type": "execute_result" } @@ -8968,7 +9742,7 @@ }, { "cell_type": "code", - "execution_count": 305, + "execution_count": 334, "metadata": { "slideshow": { "slide_type": "skip" @@ -8981,7 +9755,7 @@ "105" ] }, - "execution_count": 305, + "execution_count": 334, "metadata": {}, "output_type": "execute_result" } @@ -9003,7 +9777,7 @@ }, { "cell_type": "code", - "execution_count": 306, + "execution_count": 335, "metadata": { "slideshow": { "slide_type": "skip" @@ -9016,7 +9790,7 @@ "0" ] }, - "execution_count": 306, + "execution_count": 335, "metadata": {}, "output_type": "execute_result" } @@ -9051,7 +9825,7 @@ }, { "cell_type": "code", - "execution_count": 307, + "execution_count": 336, "metadata": { "slideshow": { "slide_type": "skip" @@ -9078,7 +9852,7 @@ }, { "cell_type": "code", - "execution_count": 308, + "execution_count": 337, "metadata": { "slideshow": { "slide_type": "skip" @@ -9091,7 +9865,7 @@ "20.58" ] }, - "execution_count": 308, + "execution_count": 337, "metadata": {}, "output_type": "execute_result" } @@ -9113,7 +9887,7 @@ }, { "cell_type": "code", - "execution_count": 309, + "execution_count": 338, "metadata": { "slideshow": { "slide_type": "skip" @@ -9126,7 +9900,7 @@ "20.58" ] }, - "execution_count": 309, + "execution_count": 338, "metadata": {}, "output_type": "execute_result" } @@ -9143,7 +9917,7 @@ } }, "source": [ - "In summary, we learn from this example that unpacking generator expressions may be a *bad* idea." + "In summary, we learn from this example that unpacking generator expressions *may* be a *bad* idea." ] }, { @@ -9172,7 +9946,7 @@ }, { "cell_type": "code", - "execution_count": 310, + "execution_count": 339, "metadata": { "slideshow": { "slide_type": "skip" @@ -9185,7 +9959,7 @@ "(65, 145, 5, 37, 101, 17)" ] }, - "execution_count": 310, + "execution_count": 339, "metadata": {}, "output_type": "execute_result" } @@ -9220,7 +9994,7 @@ }, { "cell_type": "code", - "execution_count": 311, + "execution_count": 340, "metadata": { "slideshow": { "slide_type": "slide" @@ -9246,7 +10020,7 @@ }, { "cell_type": "code", - "execution_count": 312, + "execution_count": 341, "metadata": { "slideshow": { "slide_type": "fragment" @@ -9259,7 +10033,7 @@ "False" ] }, - "execution_count": 312, + "execution_count": 341, "metadata": {}, "output_type": "execute_result" } @@ -9270,7 +10044,7 @@ }, { "cell_type": "code", - "execution_count": 313, + "execution_count": 342, "metadata": { "slideshow": { "slide_type": "-" @@ -9283,7 +10057,7 @@ "True" ] }, - "execution_count": 313, + "execution_count": 342, "metadata": {}, "output_type": "execute_result" } @@ -9305,7 +10079,7 @@ }, { "cell_type": "code", - "execution_count": 314, + "execution_count": 343, "metadata": { "slideshow": { "slide_type": "skip" @@ -9318,7 +10092,7 @@ "False" ] }, - "execution_count": 314, + "execution_count": 343, "metadata": {}, "output_type": "execute_result" } @@ -9349,7 +10123,7 @@ }, { "cell_type": "code", - "execution_count": 315, + "execution_count": 344, "metadata": { "slideshow": { "slide_type": "skip" @@ -9367,7 +10141,7 @@ }, { "cell_type": "code", - "execution_count": 316, + "execution_count": 345, "metadata": { "slideshow": { "slide_type": "skip" @@ -9380,7 +10154,7 @@ "False" ] }, - "execution_count": 316, + "execution_count": 345, "metadata": {}, "output_type": "execute_result" } @@ -9391,7 +10165,7 @@ }, { "cell_type": "code", - "execution_count": 317, + "execution_count": 346, "metadata": { "slideshow": { "slide_type": "skip" @@ -9404,7 +10178,7 @@ "True" ] }, - "execution_count": 317, + "execution_count": 346, "metadata": {}, "output_type": "execute_result" } @@ -9428,7 +10202,7 @@ }, { "cell_type": "code", - "execution_count": 318, + "execution_count": 347, "metadata": { "slideshow": { "slide_type": "fragment" @@ -9441,7 +10215,7 @@ "True" ] }, - "execution_count": 318, + "execution_count": 347, "metadata": {}, "output_type": "execute_result" } @@ -9452,7 +10226,7 @@ }, { "cell_type": "code", - "execution_count": 319, + "execution_count": 348, "metadata": { "slideshow": { "slide_type": "-" @@ -9465,7 +10239,7 @@ "False" ] }, - "execution_count": 319, + "execution_count": 348, "metadata": {}, "output_type": "execute_result" } @@ -9487,7 +10261,7 @@ }, { "cell_type": "code", - "execution_count": 320, + "execution_count": 349, "metadata": { "slideshow": { "slide_type": "skip" @@ -9500,7 +10274,7 @@ "True" ] }, - "execution_count": 320, + "execution_count": 349, "metadata": {}, "output_type": "execute_result" } @@ -9531,7 +10305,7 @@ }, { "cell_type": "code", - "execution_count": 321, + "execution_count": 350, "metadata": { "slideshow": { "slide_type": "skip" @@ -9549,7 +10323,7 @@ }, { "cell_type": "code", - "execution_count": 322, + "execution_count": 351, "metadata": { "slideshow": { "slide_type": "skip" @@ -9562,7 +10336,7 @@ "True" ] }, - "execution_count": 322, + "execution_count": 351, "metadata": {}, "output_type": "execute_result" } @@ -9573,7 +10347,7 @@ }, { "cell_type": "code", - "execution_count": 323, + "execution_count": 352, "metadata": { "slideshow": { "slide_type": "skip" @@ -9586,7 +10360,7 @@ "False" ] }, - "execution_count": 323, + "execution_count": 352, "metadata": {}, "output_type": "execute_result" } @@ -9625,7 +10399,7 @@ }, { "cell_type": "code", - "execution_count": 324, + "execution_count": 353, "metadata": { "slideshow": { "slide_type": "skip" @@ -9653,7 +10427,7 @@ }, { "cell_type": "code", - "execution_count": 325, + "execution_count": 354, "metadata": { "slideshow": { "slide_type": "skip" @@ -9666,7 +10440,7 @@ "7.0" ] }, - "execution_count": 325, + "execution_count": 354, "metadata": {}, "output_type": "execute_result" } @@ -9688,7 +10462,7 @@ }, { "cell_type": "code", - "execution_count": 326, + "execution_count": 355, "metadata": { "slideshow": { "slide_type": "skip" @@ -9701,7 +10475,7 @@ "7.0" ] }, - "execution_count": 326, + "execution_count": 355, "metadata": {}, "output_type": "execute_result" } @@ -9723,7 +10497,7 @@ }, { "cell_type": "code", - "execution_count": 327, + "execution_count": 356, "metadata": { "slideshow": { "slide_type": "skip" @@ -9736,7 +10510,7 @@ }, { "cell_type": "code", - "execution_count": 328, + "execution_count": 357, "metadata": { "slideshow": { "slide_type": "skip" @@ -9749,7 +10523,7 @@ }, { "cell_type": "code", - "execution_count": 329, + "execution_count": 358, "metadata": { "slideshow": { "slide_type": "skip" @@ -9762,7 +10536,7 @@ "49.994081434519636" ] }, - "execution_count": 329, + "execution_count": 358, "metadata": {}, "output_type": "execute_result" } @@ -9784,7 +10558,7 @@ }, { "cell_type": "code", - "execution_count": 330, + "execution_count": 359, "metadata": { "slideshow": { "slide_type": "skip" @@ -9798,8 +10572,8 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0maverage_evens\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m2\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mrandom\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrandint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m49\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0m_\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m10_000_000\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m\u001b[0m in \u001b[0;36maverage_evens\u001b[0;34m(numbers)\u001b[0m\n\u001b[1;32m 12\u001b[0m total, count = reduce(\n\u001b[1;32m 13\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 14\u001b[0;31m \u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mn\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mintegers\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0;36m2\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 15\u001b[0m )\n\u001b[1;32m 16\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mtotal\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mcount\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0maverage_evens\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m2\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mrandom\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrandint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m49\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0m_\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m10_000_000\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36maverage_evens\u001b[0;34m(numbers)\u001b[0m\n\u001b[1;32m 12\u001b[0m total, count = reduce(\n\u001b[1;32m 13\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 14\u001b[0;31m \u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mn\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mintegers\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0;36m2\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 15\u001b[0m )\n\u001b[1;32m 16\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mtotal\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mcount\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mTypeError\u001b[0m: reduce() of empty sequence with no initial value" ] } @@ -9831,12 +10605,12 @@ "\n", "Similarly, we have introduced data types in this chapter that all share the \"behavior\" of modeling some \"rule\" in memory to generate objects \"on the fly:\" They are the `map`, `filter`, and `generator` types. Their main commonality is supporting the built-in [next()](https://docs.python.org/3/library/functions.html#next) function. In computer science terminology, such data types are called **[iterators](https://en.wikipedia.org/wiki/Iterator)**, and the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module formalizes them with the `Iterator` ABC.\n", "\n", - "So, an example of an iterator is `evens_transformed` below, an object of type `map`." + "So, an example of an iterator is `evens_transformed` below, an object of type `generator`." ] }, { "cell_type": "code", - "execution_count": 331, + "execution_count": 360, "metadata": { "slideshow": { "slide_type": "slide" @@ -9849,7 +10623,7 @@ }, { "cell_type": "code", - "execution_count": 332, + "execution_count": 361, "metadata": { "slideshow": { "slide_type": "-" @@ -9857,7 +10631,7 @@ }, "outputs": [], "source": [ - "evens_transformed = map(lambda x: (x ** 2) + 1, filter(lambda x: x % 2 == 0, numbers))" + "evens_transformed = ((x ** 2) + 1 for x in numbers if x % 2 == 0)" ] }, { @@ -9873,7 +10647,7 @@ }, { "cell_type": "code", - "execution_count": 333, + "execution_count": 362, "metadata": { "slideshow": { "slide_type": "fragment" @@ -9886,7 +10660,7 @@ "True" ] }, - "execution_count": 333, + "execution_count": 362, "metadata": {}, "output_type": "execute_result" } @@ -9908,7 +10682,7 @@ }, { "cell_type": "code", - "execution_count": 334, + "execution_count": 363, "metadata": { "slideshow": { "slide_type": "fragment" @@ -9921,7 +10695,7 @@ "True" ] }, - "execution_count": 334, + "execution_count": 363, "metadata": {}, "output_type": "execute_result" } @@ -9949,7 +10723,7 @@ }, { "cell_type": "code", - "execution_count": 335, + "execution_count": 364, "metadata": { "slideshow": { "slide_type": "slide" @@ -9962,7 +10736,7 @@ }, { "cell_type": "code", - "execution_count": 336, + "execution_count": 365, "metadata": { "slideshow": { "slide_type": "-" @@ -9986,7 +10760,7 @@ }, { "cell_type": "code", - "execution_count": 337, + "execution_count": 366, "metadata": { "slideshow": { "slide_type": "fragment" @@ -9999,7 +10773,7 @@ "list_iterator" ] }, - "execution_count": 337, + "execution_count": 366, "metadata": {}, "output_type": "execute_result" } @@ -10023,7 +10797,7 @@ }, { "cell_type": "code", - "execution_count": 338, + "execution_count": 367, "metadata": { "slideshow": { "slide_type": "fragment" @@ -10036,7 +10810,7 @@ "(7, 11, 8)" ] }, - "execution_count": 338, + "execution_count": 367, "metadata": {}, "output_type": "execute_result" } @@ -10058,7 +10832,7 @@ }, { "cell_type": "code", - "execution_count": 339, + "execution_count": 368, "metadata": { "slideshow": { "slide_type": "fragment" @@ -10071,7 +10845,7 @@ "(5, 7)" ] }, - "execution_count": 339, + "execution_count": 368, "metadata": {}, "output_type": "execute_result" } @@ -10093,7 +10867,7 @@ }, { "cell_type": "code", - "execution_count": 340, + "execution_count": 369, "metadata": { "slideshow": { "slide_type": "slide" @@ -10106,7 +10880,7 @@ }, { "cell_type": "code", - "execution_count": 341, + "execution_count": 370, "metadata": { "slideshow": { "slide_type": "-" @@ -10119,7 +10893,7 @@ "(99, 99)" ] }, - "execution_count": 341, + "execution_count": 370, "metadata": {}, "output_type": "execute_result" } @@ -10141,7 +10915,7 @@ }, { "cell_type": "code", - "execution_count": 342, + "execution_count": 371, "metadata": { "slideshow": { "slide_type": "fragment" @@ -10154,7 +10928,7 @@ }, { "cell_type": "code", - "execution_count": 343, + "execution_count": 372, "metadata": { "slideshow": { "slide_type": "-" @@ -10167,7 +10941,7 @@ "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" ] }, - "execution_count": 343, + "execution_count": 372, "metadata": {}, "output_type": "execute_result" } @@ -10178,7 +10952,7 @@ }, { "cell_type": "code", - "execution_count": 344, + "execution_count": 373, "metadata": { "slideshow": { "slide_type": "fragment" @@ -10191,7 +10965,7 @@ "(6, 3)" ] }, - "execution_count": 344, + "execution_count": 373, "metadata": {}, "output_type": "execute_result" } @@ -10217,7 +10991,7 @@ }, { "cell_type": "code", - "execution_count": 345, + "execution_count": 374, "metadata": { "slideshow": { "slide_type": "slide" @@ -10230,7 +11004,7 @@ }, { "cell_type": "code", - "execution_count": 346, + "execution_count": 375, "metadata": { "slideshow": { "slide_type": "-" @@ -10240,10 +11014,10 @@ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 346, + "execution_count": 375, "metadata": {}, "output_type": "execute_result" } @@ -10254,7 +11028,7 @@ }, { "cell_type": "code", - "execution_count": 347, + "execution_count": 376, "metadata": { "slideshow": { "slide_type": "-" @@ -10267,7 +11041,7 @@ "zip" ] }, - "execution_count": 347, + "execution_count": 376, "metadata": {}, "output_type": "execute_result" } @@ -10278,7 +11052,7 @@ }, { "cell_type": "code", - "execution_count": 348, + "execution_count": 377, "metadata": { "slideshow": { "slide_type": "-" @@ -10291,7 +11065,7 @@ "True" ] }, - "execution_count": 348, + "execution_count": 377, "metadata": {}, "output_type": "execute_result" } @@ -10313,7 +11087,7 @@ }, { "cell_type": "code", - "execution_count": 349, + "execution_count": 378, "metadata": { "slideshow": { "slide_type": "slide" @@ -10326,7 +11100,7 @@ }, { "cell_type": "code", - "execution_count": 350, + "execution_count": 379, "metadata": { "slideshow": { "slide_type": "-" @@ -10336,10 +11110,10 @@ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 350, + "execution_count": 379, "metadata": {}, "output_type": "execute_result" } @@ -10356,12 +11130,12 @@ } }, "source": [ - "`zipper_iterator` points to the *same* object as `zipper`! That holds for *iterators* in general: Any *iterator* created from an existing *iterator* with [iter()](https://docs.python.org/3/library/functions.html#iter) is the *iterator* itself." + "`zipper_iterator` references the *same* object as `zipper`! That holds for *iterators* in general: Any *iterator* created from an existing *iterator* with [iter()](https://docs.python.org/3/library/functions.html#iter) is the *iterator* itself." ] }, { "cell_type": "code", - "execution_count": 351, + "execution_count": 380, "metadata": { "slideshow": { "slide_type": "-" @@ -10374,7 +11148,7 @@ "True" ] }, - "execution_count": 351, + "execution_count": 380, "metadata": {}, "output_type": "execute_result" } @@ -10393,12 +11167,12 @@ "source": [ "The Python core developers made that design decision so that *iterators* may also be looped over.\n", "\n", - "The `for`-loop below prints out *six* more `tuple` objects derived from the re-ordered `numbers` because the `iterator1` object hidden inside `zipper` already returned the first *six* elements. So, the respective first elements of the `tuple` objects printed range from `7` to `12`. Similarly, as `iterator2` already provided *three* elements from `numbers`, we see the respective second elements in the range from `4` to `9`." + "The `for`-loop below prints out *six* more `tuple` objects derived from the now ordered `numbers` because the `iterator1` object hidden inside `zipper` already returned the first *six* elements. So, the respective first elements of the `tuple` objects printed range from `7` to `12`. Similarly, as `iterator2` already provided *three* elements from `numbers`, we see the respective second elements in the range from `4` to `9`." ] }, { "cell_type": "code", - "execution_count": 352, + "execution_count": 381, "metadata": { "slideshow": { "slide_type": "fragment" @@ -10431,7 +11205,7 @@ }, { "cell_type": "code", - "execution_count": 353, + "execution_count": 382, "metadata": { "slideshow": { "slide_type": "skip" @@ -10456,7 +11230,7 @@ }, { "cell_type": "code", - "execution_count": 354, + "execution_count": 383, "metadata": { "slideshow": { "slide_type": "fragment" @@ -10470,7 +11244,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mStopIteration\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0miterator1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0miterator1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mStopIteration\u001b[0m: " ] } @@ -10492,7 +11266,7 @@ }, { "cell_type": "code", - "execution_count": 355, + "execution_count": 384, "metadata": { "slideshow": { "slide_type": "skip" @@ -10505,7 +11279,7 @@ "10" ] }, - "execution_count": 355, + "execution_count": 384, "metadata": {}, "output_type": "execute_result" } @@ -10514,6 +11288,17 @@ "next(iterator2)" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Understanding *iterators* and *iterables* is helpful for any data science practitioner that deals with large amounts of data. Even without that, these two terms occur everywhere in Python-related texts and documentation." + ] + }, { "cell_type": "markdown", "metadata": { @@ -10540,7 +11325,7 @@ }, { "cell_type": "code", - "execution_count": 356, + "execution_count": 385, "metadata": { "slideshow": { "slide_type": "slide" @@ -10553,7 +11338,7 @@ }, { "cell_type": "code", - "execution_count": 357, + "execution_count": 386, "metadata": { "slideshow": { "slide_type": "-" @@ -10586,10 +11371,10 @@ }, { "cell_type": "code", - "execution_count": 358, + "execution_count": 387, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "skip" } }, "outputs": [ @@ -10627,10 +11412,10 @@ }, { "cell_type": "code", - "execution_count": 359, + "execution_count": 388, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "fragment" } }, "outputs": [ @@ -10654,6 +11439,17 @@ " print(element, end=\" \")" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### sorted() vs. reversed()" + ] + }, { "cell_type": "markdown", "metadata": { @@ -10662,7 +11458,703 @@ } }, "source": [ - "Understanding *iterators* and *iterables* is helpful for any data science practitioner that deals with large amounts of data. Even without that, these two terms occur everywhere in Python-related texts and documentation." + "Now that we know the concept of an *iterator*, let's compare some of the built-ins introduced in this chapter in detail and make sure we understand what is going on in memory. This sub-section is thus a great summary of this chapter as well.\n", + "\n", + "We use two simple examples, `numbers` and `memoryless`, to guide us through the discussion. `numbers` creates *thirteen* objects in memory and `memoryless` only *one* (cf., [PythonTutor](http://www.pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%5D%0Amemoryless%20%3D%20range%281,%2013%29&cumulative=false&curInstr=2&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false))" + ] + }, + { + "cell_type": "code", + "execution_count": 389, + "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": 390, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "memoryless = range(1, 13)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [sorted()](https://docs.python.org/3/library/functions.html#sorted) function takes a *finite* and *iterable* object as its argument and *materializes* its elements into a *new* `list` object that is returned.\n", + "\n", + "The argument may already be materialized, as is the case with `numbers`, but could also be an *iterator* that generates *new* objects, such as `memoryless`. In both cases, we end up with materialized `list` objects with the elements sorted in *forward* order (cf., [PythonTutor](http://www.pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%5D%0Amemoryless%20%3D%20range%281,%2013%29%0Aresult1%20%3D%20sorted%28numbers%29%0Aresult2%20%3D%20sorted%28memoryless%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false))." + ] + }, + { + "cell_type": "code", + "execution_count": 391, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" + ] + }, + "execution_count": 391, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sorted(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 392, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" + ] + }, + "execution_count": 392, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sorted(memoryless)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "By adding a keyword-only argument `reverse=True`, the materialized `list` objects are sorted in *reverse* order (cf., [PythonTutor](http://www.pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%5D%0Amemoryless%20%3D%20range%281,%2013%29%0Aresult1%20%3D%20sorted%28numbers,%20reverse%3DTrue%29%0Aresult2%20%3D%20sorted%28memoryless,%20reverse%3DTrue%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false))." + ] + }, + { + "cell_type": "code", + "execution_count": 393, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]" + ] + }, + "execution_count": 393, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sorted(numbers, reverse=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 394, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]" + ] + }, + "execution_count": 394, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sorted(memoryless, reverse=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The order in `numbers` remains *unchanged*, and `memoryless` is still *not* materialized." + ] + }, + { + "cell_type": "code", + "execution_count": 395, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + "execution_count": 395, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 396, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "range(1, 13)" + ] + }, + "execution_count": 396, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "memoryless" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [reversed()](https://docs.python.org/3/library/functions.html#reversed) built-in takes a *sequence* object as its argument and returns an *iterator*. The argument must be *finite* and *reversible* (i.e., *iterable* in *reverse* order) as otherwise [reversed()](https://docs.python.org/3/library/functions.html#reversed) could neither determine the last element that becomes the first nor loop in a *predictable* backward fashion. [PythonTutor](http://www.pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%5D%0Amemoryless%20%3D%20range%281,%2013%29%0Aiterator1%20%3D%20reversed%28numbers%29%0Aiterator2%20%3D%20reversed%28memoryless%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) confirms that [reversed()](https://docs.python.org/3/library/functions.html#reversed) does *not* materialize any elements but only returns an *iterator*.\n", + "\n", + "**Side Note**: Even though `range` objects, like `memoryless` here, do *not* \"contain\" references to other objects, they count as *sequence* types, and as such, they are also *container* types. The `in` operator works with `range` objects because we can always cast the object to be checked as an `int` and check if that lies within the `range` object's *start* and *stop* values, taking a potential *step* value into account (cf., this [blog post](https://treyhunner.com/2018/02/python-range-is-not-an-iterator/) for more details on the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in)." + ] + }, + { + "cell_type": "code", + "execution_count": 397, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 397, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reversed(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 398, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 398, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reversed(memoryless)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To materialize the elements, we can pass the returned *iterators* to, for example, the [list()](https://docs.python.org/3/library/functions.html#func-list) or [tuple()](https://docs.python.org/3/library/functions.html#func-tuple) built-ins. That creates *new* `list` and `tuple` objects (cf., [PythonTutor](http://www.pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%5D%0Amemoryless%20%3D%20range%281,%2013%29%0Aresult1%20%3D%20list%28reversed%28numbers%29%29%0Aresult2%20%3D%20tuple%28reversed%28memoryless%29%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false)).\n", + "\n", + "To reiterate some more new terminology from this chapter, we describe [reversed()](https://docs.python.org/3/library/functions.html#reversed) as *lazy*, whereas [list()](https://docs.python.org/3/library/functions.html#func-list) and [tuple()](https://docs.python.org/3/library/functions.html#func-tuple) are *eager*. The former has no significant side effect in memory, while the latter may require a lot of memory." + ] + }, + { + "cell_type": "code", + "execution_count": 399, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[4, 1, 10, 9, 6, 2, 12, 3, 5, 8, 11, 7]" + ] + }, + "execution_count": 399, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(reversed(numbers))" + ] + }, + { + "cell_type": "code", + "execution_count": 400, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)" + ] + }, + "execution_count": 400, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tuple(reversed(memoryless))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Of course, we can also loop over the returned *iterators* instead.\n", + "\n", + "That works because *iterators* are always *iterables*; in particular, as the previous \"*The for Statement (revisited)*\" sub-section explains, the `for`-loops below call `iter(reversed(numbers))` and `iter(reversed(memoryless))` behind the scenes. However, the *iterators* returned by [iter()](https://docs.python.org/3/library/functions.html#iter) are the *same* as the `reversed(numbers)` and `reversed(memoryless)` iterators passed in! In summary, the `for`-loops below involve many subtleties that together make Python the expressive language it is." + ] + }, + { + "cell_type": "code", + "execution_count": 401, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": 402, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "12 11 10 9 8 7 6 5 4 3 2 1 " + ] + } + ], + "source": [ + "for element in reversed(memoryless):\n", + " print(element, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As with [sorted()](https://docs.python.org/3/library/functions.html#sorted), the [reversed()](https://docs.python.org/3/library/functions.html#reversed) built-in does *not* mutate its argument." + ] + }, + { + "cell_type": "code", + "execution_count": 403, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + "execution_count": 403, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 404, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "range(1, 13)" + ] + }, + "execution_count": 404, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "memoryless" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To point out the potentially obvious, we compare the results of *sorting* `numbers` in *reverse* order with *reversing* it: These are *different* concepts!" + ] + }, + { + "cell_type": "code", + "execution_count": 405, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + "execution_count": 405, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 406, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]" + ] + }, + "execution_count": 406, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sorted(numbers, reverse=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 407, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[4, 1, 10, 9, 6, 2, 12, 3, 5, 8, 11, 7]" + ] + }, + "execution_count": 407, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(reversed(numbers))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Whereas both [sorted()](https://docs.python.org/3/library/functions.html#sorted) and [reversed()](https://docs.python.org/3/library/functions.html#reversed) do *not* mutate their arguments, the *mutable* `list` type comes with two methods, [sort()](https://docs.python.org/3/library/stdtypes.html#list.sort) and `reverse()`, that implement the same logic but mutate an object, like `numbers` below, *in place*. To indicate that all changes occur *in place*, the [sort()](https://docs.python.org/3/library/stdtypes.html#list.sort) and `reverse()` methods always return `None`, which is not shown." + ] + }, + { + "cell_type": "code", + "execution_count": 408, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + "execution_count": 408, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `reverse()` method is *eager*, as opposed to the *lazy* [reversed()](https://docs.python.org/3/library/functions.html#reversed) built-in. That means the mutations causes by the `reverse()` method are written into memory right away." + ] + }, + { + "cell_type": "code", + "execution_count": 409, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "numbers.reverse()" + ] + }, + { + "cell_type": "code", + "execution_count": 410, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[4, 1, 10, 9, 6, 2, 12, 3, 5, 8, 11, 7]" + ] + }, + "execution_count": 410, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "*Sorting* `numbers` in *reverse* order below is of course still *different* from simply *reversing* it above." + ] + }, + { + "cell_type": "code", + "execution_count": 411, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "numbers.sort(reverse=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 412, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]" + ] + }, + "execution_count": 412, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 413, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "numbers.sort()" + ] + }, + { + "cell_type": "code", + "execution_count": 414, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" + ] + }, + "execution_count": 414, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" ] }, { @@ -10684,13 +12176,27 @@ } }, "source": [ - "**Sequences** are an abstract idea that summarizes *four* behaviors an object may or may not exhibit: We describe them as **finite** and **ordered** **containers** that we may **loop over**. Examples are objects of type `list`, `tuple`, but also `str`. Objects that exhibit all behaviors *except* being ordered are referred to as **collections**. The objects inside a sequence are labeled with a unique *index*, an `int` object in the range $0 \\leq \\text{index} < \\lvert \\text{sequence} \\rvert$, and called its **elements**.\n", + "**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", - "`list` objects are **mutable**. That means we can change the references to other objects it contains, and, in particular, re-assign them. 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* mutate it.\n", + "Examples are the `list`, `tuple`, but also the `str` types.\n", "\n", - "Often, the work we do with sequences follows 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", + "Objects that exhibit all behaviors *except* being ordered are referred to as **collections**.\n", "\n", - "An essential idea in this chapter is that, in many situations, we need *not* work with all the data **materialized** in memory. Instead, **iterators** allow us to process sequential data on a one-by-one basis." + "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**.\n", + "\n", + "The tasks we do with sequential data 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." ] } ], From c69687f463c3ead4d373545aee87fca281fcae04 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 20 Nov 2019 11:01:19 +0100 Subject: [PATCH 049/242] Add exercise on packing/unpacking with functions --- 07_sequences_review_and_exercises.ipynb | 1810 ++++++++++++++++++++++- stream.py | 51 + 2 files changed, 1840 insertions(+), 21 deletions(-) create mode 100644 stream.py diff --git a/07_sequences_review_and_exercises.ipynb b/07_sequences_review_and_exercises.ipynb index 17e1ffc..68582b2 100644 --- a/07_sequences_review_and_exercises.ipynb +++ b/07_sequences_review_and_exercises.ipynb @@ -19,7 +19,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Read [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb) of the book. Then work through the ten review questions." + "Read [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb) of the book. Then work through the twenty review questions." ] }, { @@ -54,7 +54,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q2**: Explain the difference between a **mutable** and an **immutable** object in Python with an example!" + "**Q2**: What are **abstract base classes**? How can we make use of the ones from the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module in the [standard library](https://docs.python.org/3/library/index.html)?" ] }, { @@ -68,7 +68,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q3**: What is the difference between a **shallow** and a **deep** copy of an object? How can one of them become a problem?" + "**Q3**: How are the *abstract behaviors* of **reversibility** and **finiteness** essential for *indexing* and *slicing* sequences?" ] }, { @@ -82,7 +82,49 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q4.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 **records**.\n", + "**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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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!" ] @@ -98,7 +140,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q4.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?" + "**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?" ] }, { @@ -112,7 +154,104 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q5**: With the [map()](https://docs.python.org/3/library/functions.html#map) and [filter()](https://docs.python.org/3/library/functions.html#filter) built-ins and the [reduce()](https://docs.python.org/3/library/functools.html#functools.reduce) function from the [functools](https://docs.python.org/3/library/functools.html) module in the [standard library](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?" + "**Q8**: How is (iterable) **packing** and **unpacking** useful in the context of **function definitions** and **calls**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: With the [map()](https://docs.python.org/3/library/functions.html#map) and [filter()](https://docs.python.org/3/library/functions.html#filter) built-ins and the [reduce()](https://docs.python.org/3/library/functools.html#functools.reduce) function from the [functools](https://docs.python.org/3/library/functools.html) module in the [standard library](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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: Looking at the `lambda` expression inside [reduce()](https://docs.python.org/3/library/functools.html#functools.reduce) below, what \"simple\" [built-in function](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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: What is the primary use case of **list comprehensions**? Why do we describe them as **eager**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q12**: **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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q13**: What does it mean for the **boolean reducers**, the built-in [all()](https://docs.python.org/3/library/functions.html#all) and [any()](https://docs.python.org/3/library/functions.html#any) functions, to follow the **short-circuiting** strategy?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q14**: What is an **iterator**? How does it relate to an **iterable**?" ] }, { @@ -140,7 +279,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q6**: `sequence` objects are *not* part of core Python but may be imported from the [standard library](https://docs.python.org/3/library/index.html)." + "**Q15**: `sequence` objects are *not* part of core Python but may be imported from the [standard library](https://docs.python.org/3/library/index.html)." ] }, { @@ -154,7 +293,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q7**: Passing **mutable** objects as arguments to functions is not problematic because functions operate in a **local** scope without affecting the **global** scope." + "**Q16**: The built-in [sort()](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()](https://docs.python.org/3/library/functions.html#sorted) method on `list` objects *mutates* them *in place*." ] }, { @@ -168,7 +307,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q8**: The **map-filter-reduce** paradigm is an excellent mental concept to organize one's code with. Then, there is a good chance that a program can be **parallelized** if the data input grows." + "**Q17**: Passing **mutable** objects as arguments to functions is not problematic because functions operate in a **local** scope without affecting the **global** scope." ] }, { @@ -182,7 +321,35 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q9**: `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." + "**Q18**: `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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q19**: 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q20**: Just as **list comprehensions** create `list` objects, **tuple comprehensions** create `tuple` objects." ] }, { @@ -210,7 +377,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q10.1**: 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", + "**Q21.1**: 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." ] @@ -230,7 +397,7 @@ "metadata": {}, "outputs": [], "source": [ - "def nested_sum():\n", + "def nested_sum(list_of_lists):\n", " ..." ] }, @@ -247,9 +414,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q10.2**: Provide a one-line expression to obtain the *same* result as `nested_sum()`!\n", + "**Q21.2**: Provide a one-line expression to obtain the *same* result as `nested_sum()`!\n", "\n", - "Hints: Use a *list comprehension*. You may want to use the built-in [sum()](https://docs.python.org/3/library/functions.html#sum) function several times." + "Hints: Use a *list comprehension*, or maybe even a *generator expression*. You may want to use the built-in [sum()](https://docs.python.org/3/library/functions.html#sum) function several times." ] }, { @@ -257,13 +424,15 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "..." + ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "**Q10.3**: 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", + "**Q21.3**: 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()](https://docs.python.org/3/library/functions.html#isinstance) function to check how an element is to be processed. Get extra credit for adhering to *goose typing*, as explained in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb#Goose-Typing)." ] @@ -283,7 +452,16 @@ "metadata": {}, "outputs": [], "source": [ - "def mixed_sum():\n", + "import collections.abc as abc" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def mixed_sum(list_of_lists):\n", " ..." ] }, @@ -293,14 +471,14 @@ "metadata": {}, "outputs": [], "source": [ - "mixed_sum(nums)" + "mixed_sum(mixed_numbers)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "**Q10.4.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", + "**Q21.4.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](https://en.wikipedia.org/wiki/Cumulative_distribution_function) from statistics." ] @@ -320,7 +498,7 @@ "metadata": {}, "outputs": [], "source": [ - "def cum_sum():\n", + "def cum_sum(numbers):\n", " ..." ] }, @@ -337,7 +515,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q10.4.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? Describe everything in the docstring.\n", + "**Q21.4.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? Describe everything in the docstring.\n", "\n", "Hint: It is possible to write this without any extra input validation." ] @@ -350,6 +528,1596 @@ "source": [ "cum_sum([])" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Packing & Unpacking with Functions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the \"*Function Definitions & Calls*\" section in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.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", + "**Q22.1**: What is the data type of `args` within the function's body?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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": [ + "**Q22.2**: What line in the body of `product()` causes this exception? What is the exact problem?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.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": [ + "**Q22.3**: What is wrong with that? What *kind* of error (cf., [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.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": [ + " " + ] + }, + { + "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": [ + "**Q22.4**: Describe why no error occurs by going over every line in `product()`!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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": [ + "**Q22.5**: What line causes troubles now? What is the exact problem?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q22.6**: 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()](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": [ + "**Q22.7**: 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](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb#Keyword-only-Arguments))." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def product(*args, ...):\n", + " \"\"\"Multiply all arguments.\"\"\"\n", + " ..." + ] + }, + { + "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": [ + "**Q22.8**: What is a **collection**? How is that different from a **sequence**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q22.9**: 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()](https://docs.python.org/3/library/functions.html#len) and [isinstance()](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](https://docs.python.org/3/library/collections.abc.html) module in the [standard library](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", + " ..." + ] + }, + { + "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 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings.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 special collection type, namely 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()](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": [ + "**Q22.10**: 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", + " ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, we have an implementation of `product()` that is convenient to use for the caller of our function. In particular, we can pass it a *collection* with or without *unpacking* it.\n", + "\n", + "However, this version of `product()` suffers from one more flaw: We cannot pass it a *stream* of data, as modeled, for example, with an *iterator* object that produces elements on a one-by-one basis.\n", + "\n", + "Let's look at an example. The [*stream.py*](https://github.com/webartifex/intro-to-python/blob/master/stream.py) module in the 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": [ + "stream = make_finite_stream()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "stream" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "type(stream)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Being a `generator`, `stream` is also an `Iterator` in the abstract sense." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "isinstance(stream, abc.Iterator)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Iterators* are good for only *one* thing: Giving us the \"next\" element in a line of many." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "next(stream)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "They themselves have *no* idea of how many elements they produce eventually: The built-in [len()](https://docs.python.org/3/library/functions.html#len) function raises a `TypeError`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "len(stream)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can use the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in to *materialize* the elements. However, in a real-world scenario, these may *not* fit into our machine's memory!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list(stream)" + ] + }, + { + "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 an *iterator*, 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": [ + "**Q22.11**: What line causes the `TypeError`? What line is really the problem in the latest implementation of `product()`? Describe what happens on each line in the function's body until the exception is raised!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q22.12**: Adapt `product()` one last time to make it work with *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 *iterators* also exhibit? You may want to look at the documentations on the built-in [max()](https://docs.python.org/3/library/functions.html#max), [min()](https://docs.python.org/3/library/functions.html#min), and [sum()](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", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The final version of `product()` behaves like built-ins in edge cases, ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "... works with the arguments passed either as independent *positional* arguments, *packed* 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 did in this exercise, or if that is over-engineered.\n", + "\n", + "Yet, two lessons are important to take away:\n", + "- It is always a good idea to *mimic* the behavior of *built-ins* when in doubt.\n", + "- Make functions capable of working with *streaming* data." + ] + }, + { + "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": [ + "**Q23.1**: `list` objects are **sequences**. What *four* behaviors do they always come with?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q23.2**: 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](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": [ + "**Q23.3**: Write a function `std()` that calculates the [standard deviation](https://en.wikipedia.org/wiki/Standard_deviation) of a `sequence` of numbers! Integrate your `mean()` version from before and the [sqrt()](https://docs.python.org/3/library/math.html#math.sqrt) function from the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html) 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": [ + "**Q23.4**: Complete `standardize()` below that takes a `sequence` of numbers and returns a `list` object with the **[z-scores](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()](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()](https://docs.python.org/3/library/pprint.html#pprint.pprint) function from the [pprint](https://docs.python.org/3/library/pprint.html) module in the [standard library](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", + "**Q23.5.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": [ + "**Q23.5.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": [ + "**Q23.6**: 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()](https://docs.python.org/3/library/functions.html#abs) and [zip()](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*](https://github.com/webartifex/intro-to-python/blob/master/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": [ + "Being a `generator`, it is an `Iterator` in the abstract sense ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import collections.abc as abc" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "isinstance(data, abc.Iterator)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "... and so the only thing we can do with it is to pass it to the built-in [next()](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": [ + "**Q23.7**: 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": [ + " " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mean(data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q23.8**: Write a function `take_sample()` that takes an `iterator` 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()](https://docs.python.org/3/library/functions.html#next) and the [range()](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()](https://docs.python.org/3/library/itertools.html#itertools.islice) in the [itertools](https://docs.python.org/3/library/itertools.html) module in the [standard library](https://docs.python.org/3/library/index.html)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def take_sample(iterator, *, 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": [ + "**Q23.9**: 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](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()](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": [ + "**Q23.10.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": [ + "**Q23.10.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](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Example:-Averaging-Even-Numbers-%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": [ + "**Q23.10.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": [ + "**Q23.10.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": [ + "**Q23.11**: 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": [ + " " + ] } ], "metadata": { diff --git a/stream.py b/stream.py new file mode 100644 index 0000000..6aa6cc7 --- /dev/null +++ b/stream.py @@ -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() From b7ad171473887c7be0b738775d27670a2115ad8d Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 20 Nov 2019 11:01:43 +0100 Subject: [PATCH 050/242] Add initial version of notebook 08 --- 08_mappings.ipynb | 8202 +++++++++++++++++++++++++++++++ static/fibonacci_call_graph.png | Bin 0 -> 32706 bytes 2 files changed, 8202 insertions(+) create mode 100644 08_mappings.ipynb create mode 100644 static/fibonacci_call_graph.png diff --git a/08_mappings.ipynb b/08_mappings.ipynb new file mode 100644 index 0000000..d42237b --- /dev/null +++ b/08_mappings.ipynb @@ -0,0 +1,8202 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 8: Mappings & Sets" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb) focuses on one special kind of *collection* types, namely *sequences*, this chapter introduces two more: **Mappings** and **sets**. We present the data types belonging to these two groups in one chapter as they share the *same* underlying implementation at the C Level, known as **[hash tables](https://en.wikipedia.org/wiki/Hash_table)**.\n", + "\n", + "The most important mapping type in this chapter is the `dict` type that we have not yet seen before (cf, [documentation](https://docs.python.org/3/library/stdtypes.html#dict)). It is an essential part in a data science practitioner's toolbox for two reasons: First, Python employs `dict` objects basically \"everywhere\" internally. So, we must understand how they work to become better at Python in general. Second, after the many concepts related to *sequential* data, the ideas behind *mappings* enhance our general problem solving skills. As a concrete example, we look at the concept of **memoization** to complete our picture of *recursion*, as depicted in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Recursion). We end this chapter with a discussion of *set* types." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The `dict` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A *mapping* is a one-to-one correspondence from a set of **keys** to a set of **values**. In other words, a *mapping* is a *collection* of **key-value pairs**, also called **items** for short.\n", + "\n", + "In the context of mappings, the term *value* has a meaning different from the general *value* that *every* object has: In the \"bag\" analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Value), we descibe an object's value to be the concrete $0$s and $1$s it contains. Here, the terms *key* and *value* mean the *role* an object takes within a mapping. Both, *keys* and *values*, are real *objects* with a distinct *value*. So, the student should always remember the double meaning of the term *value* in this chapter!\n", + "\n", + "Let's continue with an example. To create a `dict` object, we commonly use the literal notation, `{..: .., ..: .., ...}`, and list all the items. `to_words` below maps the `int` objects `0`, `1`, and `2` to their English word equivalents, `\"zero\"`, `\"one\"`, and `\"two\"`, and `from_words` does the opposite. A stylistic side note: Pythonistas often expand `dict` or `list` definitions by writing each item or element on a line on their own. The commas `,` after the *last* items are *not* a mistake, as well, although they *may* be left out. Besides easier reading, such style has actual technical advantages (cf., [source](https://www.python.org/dev/peps/pep-0008/#when-to-use-trailing-commas)) that we do not go into detail about here." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "to_words = {\n", + " 0: \"zero\",\n", + " 1: \"one\",\n", + " 2: \"two\",\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "from_words = {\n", + " \"zero\": 0,\n", + " \"one\": 1,\n", + " \"two\": 2,\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As before, `dict` objects are objects on their own: They have an identity, a type, and a value. The latter is a *literal* that creates a *new* `dict` object with the *same* value when evaluated." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139752734794304" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(from_words)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "dict" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(from_words)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'zero': 0, 'one': 1, 'two': 2}" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from_words" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [dict()](https://docs.python.org/3/library/functions.html#func-dict) built-in gives us an alternative way to create a `dict` object. It is versatile and can be used in different ways.\n", + "\n", + "First, we may pass it any *mapping* type, for example, a `dict` object, to obtain a *new* `dict` object. That is the easiest way to convert a more specialized mapping type, such as the `OrderedDict`, `defaultdict`, and `Counter` types introduced further below, into an \"ordinary\" `dict` object." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'zero': 0, 'one': 1, 'two': 2}" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dict(from_words)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Second, we may pass it an *iterable* of *iterables* with *two* elements each. So, both of the following two code cells work: A `list` of `tuple` objects, or a `tuple` of `list` objects. More importantly, we could use an *iterator*, for example, a `generator` object, that produces the inner iterables \"on the fly.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'zero': 0, 'one': 1, 'two': 2}" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dict([(\"zero\", 0), (\"one\", 1), (\"two\", 2)])" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'zero': 0, 'one': 1, 'two': 2}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dict(([\"zero\", 0], [\"one\", 1], [\"two\", 2]))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Lastly, [dict()](https://docs.python.org/3/library/functions.html#func-dict) may also be called with *keyword* arguments: The keywords become the keys and the arguments the values." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'zero': 0, 'one': 1, 'two': 2}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dict(zero=0, one=1, two=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Keyword arguments may always be added in the first two cases as well. That is sometimes useful to take data as is and ensure that certain keys are *existent* and have a pre-defined value." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'zero': 0, 'one': 1, 'two': 2, 'three': 3}" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dict(from_words, three=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Nested Data" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Often, `dict` objects occur in a nested form and combined with other collection types, such as `list` or `tuple` objects, to model more complex \"objects\" from the real world.\n", + "\n", + "The reason for this popularity is that many modern [ReST APIs](https://en.wikipedia.org/wiki/Representational_state_transfer#Applied_to_Web_services) on the internet (e.g., [Google Maps API](https://cloud.google.com/maps-platform/), [Yelp API](https://www.yelp.com/developers/documentation/v3), [Twilio API](https://www.twilio.com/docs/usage/api)) provide their data in the popular [JSON](https://en.wikipedia.org/wiki/JSON) format, which looks almost like a combination of `dict` and `list` objects in Python. \n", + "\n", + "The `people` example below models three groups of people: Mathematicians, physicists, and programmers. Each person may have an arbitrary number of email addresses (e.g., [Leonhard Euler](https://en.wikipedia.org/wiki/Leonhard_Euler) has not lived long enough to get one, whereas [Guido](https://en.wikipedia.org/wiki/Guido_van_Rossum) has more than one for sure).\n", + "\n", + "`people` has many (implicit) structural assumptions built in. For example, there are a [one-to-many](https://en.wikipedia.org/wiki/One-to-many_%28data_model%29) relationship between people and their email addresses and a [one-to-one](https://en.wikipedia.org/wiki/One-to-one_%28data_model%29) relationship between each person and their name. It is important to understand that we determine this structure by choosing the data types involved in `people` and that it is impossible to model nested data without any (implicit) assumption about the structure. So, the data science practitioner should have a basic understanding of [database normalization](https://en.wikipedia.org/wiki/Database_normalization)." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "people = {\n", + " \"mathematicians\": [\n", + " {\n", + " \"name\": \"Gilbert Strang\",\n", + " \"emails\": [\"gilbert@mit.edu\"],\n", + " },\n", + " {\n", + " \"name\": \"Leonhard Euler\",\n", + " \"emails\": [],\n", + " },\n", + " ],\n", + " \"physicists\": [],\n", + " \"programmers\": [\n", + " {\n", + " \"name\": \"Guido\",\n", + " \"emails\": [\"guido@python.org\", \"guido@dropbox.com\"],\n", + " },\n", + " ],\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The literal notation of such a nested `dict` object may be hard to read." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'mathematicians': [{'name': 'Gilbert Strang', 'emails': ['gilbert@mit.edu']},\n", + " {'name': 'Leonhard Euler', 'emails': []}],\n", + " 'physicists': [],\n", + " 'programmers': [{'name': 'Guido',\n", + " 'emails': ['guido@python.org', 'guido@dropbox.com']}]}" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "people" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Luckily, the [pprint](https://docs.python.org/3/library/pprint.html) module in the [standard library](https://docs.python.org/3/library/index.html) provides a [pprint()](https://docs.python.org/3/library/pprint.html#pprint.pprint) function for \"pretty printing.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "from pprint import pprint" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'mathematicians': [{'emails': ['gilbert@mit.edu'],\n", + " 'name': 'Gilbert Strang'},\n", + " {'emails': [],\n", + " 'name': 'Leonhard Euler'}],\n", + " 'physicists': [],\n", + " 'programmers': [{'emails': ['guido@python.org',\n", + " 'guido@dropbox.com'],\n", + " 'name': 'Guido'}]}\n" + ] + } + ], + "source": [ + "pprint(people, indent=1, width=60)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Hash Tables & Key Hashability" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In [Chapter 0](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb#Isn't-C-a-lot-faster?), we argue that a major advantage of using Python is that it takes care of the memory managment for us. In line with that, we have never talked about the C level implementation thus far in the book. However, the `dict` type, among others, exhibits some behaviors that may seem \"weird\" for a beginner. To built a solid intuition that enables the student to better \"predict\" how `dict` objects behave, we describe the underlying implementation details on a conceptual level (i.e., without C code).\n", + "\n", + "The first unintuitive behavior is that we may *not* use a *mutable* object as a key. That results in a `TypeError`." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "unhashable type: 'list'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m {\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m\"zero\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"one\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m }\n", + "\u001b[0;31mTypeError\u001b[0m: unhashable type: 'list'" + ] + } + ], + "source": [ + "{\n", + " [0, 1]: [\"zero\", \"one\"],\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Similarly surprising is that items with the *same* key get \"merged\" together. The resulting `dict` object seems to keep the position of the *first* mention of a key, while at the same time only the *last* mention of a value survives." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'zero': 3, 'one': 1, 'two': 2}" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{\n", + " \"zero\": 0,\n", + " \"one\": 1,\n", + " \"two\": 2,\n", + " \"zero\": 3,\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The reason for that is that the main building block behind the `dict` type is a [data structure](https://en.wikipedia.org/wiki/Data_structure) called a [hash table](https://en.wikipedia.org/wiki/Hash_table).\n", + "\n", + "Conceptually, when we create a *new* `dict` object, Python creates a \"bag\" in memory that takes significantly more space (i.e., $0$s and $1$s) than needed to store the references to all the key and value objects. This bag is a **contiguous** chunk of memory (i.e., all the $0$s and $1$s lie right next to each other) and divided into equally sized **buckets** that have just enough space to store *two* references each. These references go to an item's key and value objects. The buckets are labeled with *index* numbers, or \"integer addresses.\" Because Python knows how wide each bucket is and where the bag begins, it can jump directly into *any* bucket by calculating its **offset** from the start. It does not have to follow a reference to some \"random\" memory location once it has followed the reference to the `dict` object's \"start\" in memory.\n", + "\n", + "The figure below visualizes how we should think of hash tables. An empty `dict` object, created with the literal `{}`, still takes a lot of memory: It is essentially one big, contiguous, and empty table." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "| Bucket | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |\n", + "| :---: |:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n", + "| Key |*...*|*...*|*...*|*...*|*...*|*...*|*...*|*...*|\n", + "| Value |*...*|*...*|*...*|*...*|*...*|*...*|*...*|*...*|" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To insert a key-value pair, the key must be translated into a bucket's address. As the first step to do so, the built-in [hash()](https://docs.python.org/3/library/functions.html#hash) function maps any **hashable** object to its **hash value**, a long `int` number, similar to the ones returned by the built-in [id()](https://docs.python.org/3/library/functions.html#id) function. This hash value is a summary of all the $0$s and $1$s that make up an object's value, and, according to the official [glossary](https://docs.python.org/3/glossary.html#term-hashable), an object is hashable *only if* \"it has a hash value which *never* changes during its *lifetime*.\" So, hashability implies immutability! This formal requirement is also absolutely useful to have: Without it, an object may have *several* addresses and, thus, we could not sort it into a *predictable* bucket. The exact logic behind [hash()](https://docs.python.org/3/library/functions.html#hash) is beyond the scope of this book.\n", + "\n", + "Let's calculate the hash value of `\"zero\"`. Because `str` objects are immutable, that works. Hash values have *no* semantic meaning. Also, everytime we re-start Python, we see *different* hash values for the *same* objects. That is a security measure, and we do not go into the technicalities here (cf. [source](https://docs.python.org/3/using/cmdline.html#envvar-PYTHONHASHSEED))." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-2242252796476499076" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hash(\"zero\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For numeric objects, we can sometimes predict the hash values. However, we must *never* interpret any meaning into them." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hash(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "230584300921369408" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hash(0.1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [glossary](https://docs.python.org/3/glossary.html#term-hashable) states a second requirement for hashability, namely that \"objects which *compare equal* must have the *same* hash value.\" The purpose of this is to ensure that if we put, for example, `1` as a key in a `dict` object, we can look it up later with `1.0`. In other words, we can look up keys by their object's value (i.e., in the meaning of [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Value)). The converse statement does *not* hold: Two objects *may* (accidentally) have the *same* hash value and *not* compare equal." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 == 1.0" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hash(1) == hash(1.0)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Because `list` objects are not immutable, they are *never* hashable, as indicated by the `TypeError`." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "unhashable type: 'list'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mhash\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: unhashable type: 'list'" + ] + } + ], + "source": [ + "hash([0, 1])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we need keys composed of several objects, we can use `tuple` objects instead. In general, the key object must be hashable as a whole. So, we must *never* put a *mutable* object in a `tuple` object used as a key, as well." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3713080549409410656" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hash((0, 1))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "There is no such restiction on objects inserted into `dict` objects as *values*." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{(0, 1): ['zero', 'one']}" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{\n", + " (0, 1): [\"zero\", \"one\"],\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A natural question to ask is how does Python know how much memory it should reserve for a `dict` object's hash table. And, the answer is: Python allocates the memory according to some internal heuristics, and whenever a hash table is roughly 2/3 full, it creates a *new* one with twice the space, and re-inserts all items, one by one, from the *old* one. So, during its lifetime, a `dict` object may have several hash tables.\n", + "\n", + "Let's see how Python translates the keys' hash values into buckets and what happens if a hash table gets too crowded. Assume the now bigger `from_words` example ..." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "from_words = {\n", + " \"zero\": 0,\n", + " \"one\": 1,\n", + " \"two\": 2,\n", + " \"three\": 3,\n", + " \"four\": 4,\n", + " \"five\": 5,\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... is to be stored in a hash table with eight buckets. Thus, to label the buckets, we need three bits (i.e., $2^3 = 8$).\n", + "\n", + "Once Python has obtained an object's hash value with [hash()](https://docs.python.org/3/library/functions.html#hash), that number's *least* significant bits in *binary* representation are used as the address. So, here, we cut off the last three digits. These digits could be converted back into an integer for nicer presentation, but Python itself does not need that.\n", + "\n", + "In summary, with a potentially infinite number of possible keys being mapped on a limited number of buckets, there is a chance that two or more keys end up in the *same* bucket. That is called a **hash collision** and sounds worse than it is. In such cases, Python uses a perturbation rule to rearrange the bits, and if the corresponding next bucket is empty, places an item there. The main disadvantage of that is that the nice offsetting logic from above breaks down, and Python needs more time on average to place items into a hash table. The remedy is to just use a bigger hash table as then the chance of colliding hashes decreases. Luckily, Python does all that for us in the background. So, the main cost we pay for that convenience is the *high* memory usage of `dict` objects.\n", + "\n", + "Another effect of this hashing logic is that items that have keys with the *same* value (i.e., $0$s and $1$s) end up in the *same* bucket, as well. The item that gets inserted last *overwrites* all previously inserted items. That is why the two `\"zero\"` keys get \"merged\" above.\n", + "\n", + "The code below shows how the key objects are transformed into their hash values in integer and binary representation, the least significant bits of the latter are cut off, and the buckets are obtained. Because we use `str` objects as the keys, their buckets are *unpredictable*. So, taking into account the randomization for security purposes from above, we may or may not see redundant buckets in the output. The chance is rather high." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def buckets(mapping, *, bits):\n", + " \"\"\"Calculate the bucket indices for a mapping's keys.\"\"\"\n", + " for key in mapping: # cf., next sub-section for details on looping\n", + " hash_value = hash(key)\n", + " binary = bin(hash_value)\n", + " address = binary[-bits:]\n", + " bucket = int(\"0b\" + address, base=2)\n", + " print(key, hash_value, \"0b...\" + binary[-12:], address, bucket, sep=\"\\t\")" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zero\t-2242252796476499076\t0b...010010000100\t100\t4\n", + "one\t6742734203392562231\t0b...010000110111\t111\t7\n", + "two\t8400825950927958036\t0b...000000010100\t100\t4\n", + "three\t-7473701886709619306\t0b...101001101010\t010\t2\n", + "four\t5854090567539607813\t0b...100100000101\t101\t5\n", + "five\t5010767421024509913\t0b...111111011001\t001\t1\n" + ] + } + ], + "source": [ + "buckets(from_words, bits=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With five of the six keys already inserted, the next insertion has a chance of more than 50% to be a hash collision." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "| Bucket | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |\n", + "| :---: | :---: |:---:| :---: | :---: |:---:| :---: |:---:| :---: |\n", + "| Key |`\"one\"`|*...*|`\"two\"`|`\"four\"`|*...*|`\"three\"`|*...*|`\"zero\"`|\n", + "| Value | `1` |*...*| `2` | `4` |*...*| `3` |*...*| `0` |" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Luckily, in the given case, Python allocates sixteen buckets trading off memory against insertion speed." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "source": [ + "| Bucket | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |\n", + "| :---: | :---: |:---:|:---:|:---:|:---:|:---:|:---:| :---: |:---:|:---:| :---: | :---: |:---:| :---: |:---:| :---: |\n", + "| Key |`\"one\"`|*...*|*...*|*...*|*...*|*...*|*...*|`\"five\"`|*...*|*...*|`\"two\"`|`\"four\"`|*...*|`\"three\"`|*...*|`\"zero\"`|\n", + "| Value | `1` |*...*|*...*|*...*|*...*|*...*|*...*| `5` |*...*|*...*| `2` | `4` |*...*| `3` |*...*| `0` |" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Sixteen buckets imply four bits (i.e., $2^4$) be cut off from the hash value's binary representation and used as the buckets' indices. It is unlikely we see redundant buckets in the code cell below." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zero\t-2242252796476499076\t0b...010010000100\t0100\t4\n", + "one\t6742734203392562231\t0b...010000110111\t0111\t7\n", + "two\t8400825950927958036\t0b...000000010100\t0100\t4\n", + "three\t-7473701886709619306\t0b...101001101010\t1010\t10\n", + "four\t5854090567539607813\t0b...100100000101\t0101\t5\n", + "five\t5010767421024509913\t0b...111111011001\t1001\t9\n" + ] + } + ], + "source": [ + "buckets(from_words, bits=4)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Although hash tables seem quite complex at first sight, they help us to make certain operations very fast as we see further below." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Mappings are Collections without \"Predictable Order\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Collections-vs.-Sequences), we show how *sequences* are a special kind of *collections*. The latter can be described as iterable containers with a finite number of elements.\n", + "\n", + "The `dict` type is a special kind of a *collection*, as well, as revealed with the `Collection` ABC from the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module in the [standard library](https://docs.python.org/3/library/index.html)." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import collections.abc as abc" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(to_words, abc.Collection)" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(from_words, abc.Collection)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "So, we may pass `to_words` or `from_words` to the built-in [len()](https://docs.python.org/3/library/functions.html#len) function to obtain the number of *items* they contain." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(to_words)" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(from_words)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In the terminology of the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module, both are `Sized` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(to_words, abc.Sized)" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(from_words, abc.Sized)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`dict` objects may be looped over, for example, with the `for` statement. For technical reasons, we could *not* rely on the iteration order to be *predictable* in any form until Python 3.7 in 2018. Looping over the *same* `dict` objects multiple times during its lifetime could result in *different* iteration orders every time. That behavior is intentional as `dict` objects are optimized for use cases where order does not matter. Starting with Python 3.7, `dict` objects remember the order in that items are *inserted* (cf., [Python 3.7 release notes](https://www.python.org/downloads/release/python-370/)). A lot of research went into this preservation of order (cf., this [PyCon 2017 talk](https://www.youtube.com/watch?v=npw4s1QTmPg) by core developer [Raymond Hettinger](https://github.com/rhettinger)).\n", + "\n", + "Because of that, the order in the two `for`-loops below is the *same* as in the *source code* that defines `to_words` and `from_words` above. In that sense, it is \"*predictable*.\" However, if we fill `dict` objects with data from real-world sources, that kind of predictability is not really helpful as such data are not written as source code, and, thus, we consider the order of items in `dict` objects to be *unpredictable*. Further, when an \"insertion\" accidentally *updates* an item, the ordering remains unchanged." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.7.3\r\n" + ] + } + ], + "source": [ + "!python --version # the order in the for-loops is predictable for Python 3.7 or higher only" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "By convention, iteration goes over the *keys* in the `dict` object only. The \"*Dictionary Methods*\" sub-section below shows how to loop over the *items* or the *values* instead." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "1\n", + "2\n" + ] + } + ], + "source": [ + "for number in to_words:\n", + " print(number)" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zero\n", + "one\n", + "two\n", + "three\n", + "four\n", + "five\n" + ] + } + ], + "source": [ + "for word in from_words:\n", + " print(word)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Without a predictable *forward* order, `dict` objects are not *reversible* either.\n", + "\n", + "So, passing a `dict` object to the [reversed()](https://docs.python.org/3/library/functions.html#reversed) built-in raises a `TypeError` ..." + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'dict' object is not reversible", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0mnumber\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mreversed\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mto_words\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnumber\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: 'dict' object is not reversible" + ] + } + ], + "source": [ + "for number in reversed(to_words):\n", + " print(number)" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'dict' object is not reversible", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0mword\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mreversed\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfrom_words\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mword\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: 'dict' object is not reversible" + ] + } + ], + "source": [ + "for word in reversed(from_words):\n", + " print(word)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... and also `Reversible` ABC in the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module confirms that." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(to_words, abc.Reversible)" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(from_words, abc.Reversible)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Of course, we could use the built-in [sorted()](https://docs.python.org/3/library/functions.html#sorted) function to loop over, for example, `to_words` in a *predictable* order. However, that *materializes* a temporary `list` object in memory containing references to all the key objects *and* creates a *new* order according to some sorting criterion that has *nothing* to do with how the items are ordered inside the hash table." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "five\n", + "four\n", + "one\n", + "three\n", + "two\n", + "zero\n" + ] + } + ], + "source": [ + "for word in sorted(from_words):\n", + " print(word)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "That approach may be combined with [reversed()](https://docs.python.org/3/library/functions.html#reversed)." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zero\n", + "two\n", + "three\n", + "one\n", + "four\n", + "five\n" + ] + } + ], + "source": [ + "for word in reversed(sorted(from_words)):\n", + " print(word)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To show the third behavior of *collection* types, we use the boolean `in` operator to check if a given and immutable object evaluates equal to a *key* in `to_words` or `from_words`." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 in to_words" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1.0 in to_words # 1.0 is not contained but compares equal to a key that is" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "10 in to_words" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"one\" in from_words" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"ten\" in from_words" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `Container` ABC in the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module may be used to \"ask\" Python to confirm that `to_words` and `from_words` are indeed *container* types." + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(to_words, abc.Container)" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(from_words, abc.Container)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Membership Testing: Lists vs. Dictionaries" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Because of the [hash table](https://en.wikipedia.org/wiki/Hash_table) implementation, the `in` operator is *extremely* fast: With the hashing/offsetting described above, Python does *not* need to initiate a [linear search](https://en.wikipedia.org/wiki/Linear_search) as in the `list` case but immediately knows the only places in memory where the searched object must be located if present in the hash table at all. Conceptually, that is like comparing the searched object against all key objects with the `==` operator *without* actually doing it.\n", + "\n", + "To show the difference, we run a little experiment. For that, we create a `haystack`, a `list` object, with `10_000_001` elements in it, *one* of which is the `needle`, namely `42`. Once again, the [randint()](https://docs.python.org/3/library/random.html#random.randint) function in the [random](https://docs.python.org/3/library/random.html) module is helpful." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import random" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(87)" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "haystack = [random.randint(99, 9999) for _ in range(10_000_000)] + [42]" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "needle = 42" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We put the elements in `haystack` in a *random* order with the [shuffle()](https://docs.python.org/3/library/random.html#random.shuffle) function in the [random](https://docs.python.org/3/library/random.html) module." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "random.shuffle(haystack)" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[8126, 7370, 3735, 213, 7922, 1434, 8557, 9609, 9704, 9564, 9601, 3444]" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "haystack[:12]" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[5842, 1894, 7237, 886, 5945, 4014, 4998, 2055, 3531, 6919, 7875, 1944]" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "haystack[-12:]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As modern computers are generally fast, we search the `needle` a total of `10` times." + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4.37 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n 1 -r 1\n", + "for _ in range(10):\n", + " needle in haystack" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Now, we convert the elements of `haystack` into the keys of `magic_haystack`, a `dict` object. We use `None` as a dummy value for all items." + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "magic_haystack = dict((x, None) for x in haystack)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Because the hash table implementation is *extremely* fast, we search the `needle` not `10` but `10_000` times. The code cell still runs in only a fraction of the time its counterpart does above." + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "426 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n 1 -r 1\n", + "for _ in range(10_000_000):\n", + " needle in magic_haystack" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, there is no fast way to look up the values the keys are mapped to. To achieve that, we have to loop over *all* items and check for each value object if it evaluates equal to searched object. That is, by definition, a linear search, as well, and rather slow. In the context of `dict` objects, we call that a **reverse look-up**." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### \"Indexing\" -> Key Look-up" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The same efficient key look-up executed in the background with the `in` operator is also behind the indexing operator `[]`. Instead of returning either `True` or `False`, it returns the value object the looked up key maps to.\n", + "\n", + "To show the similarity to indexing into `list` objects, we provide another example with `to_words_list` below." + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "to_words_list = [\"zero\", \"one\", \"two\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Without the above definitions, we could not tell the difference between `to_words` and `to_words_list`: The usage of the `[]` is the same." + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'zero'" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "to_words[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'one'" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "to_words_list[1]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Because key objects can be of any immutable type and are, in particular, not constrained to just the `int` type, the word \"*indexing*\" is an understatement here. Therefore, in the context of `dict` objects, we view the `[]` operator as a generalization of the indexing operator and refer to it as the **(key) look-up** operator. " + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from_words[\"two\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If a key is not in a `dict` object, Python raises a `KeyError`. A sequence type would raise an `IndexError` in this situation." + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "KeyError", + "evalue": "'drei'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfrom_words\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"drei\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m: 'drei'" + ] + } + ], + "source": [ + "from_words[\"drei\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While `dict` objects support the `[]` operator to look up a *single* key, the more general concept of *slicing* is *not* available. That is in line with the idea that there is *no* predictable *order* associated with a `dict` object's keys, and slicing requires an order conceptually.\n", + "\n", + "To access \"lower\" levels in nested data, like `people`, we *chain* the look-up operator `[]`. For example, let's view all the mathematicians in `people`." + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'name': 'Gilbert Strang', 'emails': ['gilbert@mit.edu']},\n", + " {'name': 'Leonhard Euler', 'emails': []}]" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "people[\"mathematicians\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's take the first mathematician on the list, ..." + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'name': 'Gilbert Strang', 'emails': ['gilbert@mit.edu']}" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "people[\"mathematicians\"][0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... and output his name ..." + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Gilbert Strang'" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "people[\"mathematicians\"][0][\"name\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... or all his emails." + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['gilbert@mit.edu']" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "people[\"mathematicians\"][0][\"emails\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Mutability" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Analogous to `list` objects, we may mutate `dict` objects *in place*.\n", + "\n", + "For example, let's translate the English words in `to_words` to their German counterparts. Behind the scenes, Python determines the bucket of the objects passed to the `[]` operator, looks them up in the hash table, and, if present, *updates* their references to the mapped value objects." + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "to_words[0] = \"null\"\n", + "to_words[1] = \"eins\"\n", + "to_words[2] = \"zwei\"" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{0: 'null', 1: 'eins', 2: 'zwei'}" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "to_words" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's add two more items. Again, Python determines their buckets, but this time finds them to be empty, and *inserts* the references to their key and value objects." + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "to_words[3] = \"drei\"\n", + "to_words[4] = \"vier\"" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{0: 'null', 1: 'eins', 2: 'zwei', 3: 'drei', 4: 'vier'}" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "to_words" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "None of these operations change the identity of the `to_words` object." + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139752734711240" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(to_words) # same memory location as before" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `del` statement removes individual items. Python just removes the *two* references to the key and value objects in the corresponding bucket." + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "del to_words[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1: 'eins', 2: 'zwei', 3: 'drei', 4: 'vier'}" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "to_words" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We may also change parts of nested data, such as `people`.\n", + "\n", + "For example, let's add [Albert Einstein](https://en.wikipedia.org/wiki/Albert_Einstein) to the list of physicists, ..." + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "people[\"physicists\"].append({\"name\": \"Albert Einstein\"})" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... complete Guido's name, ..." + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "people[\"programmers\"][0][\"name\"] = \"Guido van Rossum\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... and remove his work email because he retired." + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "del people[\"programmers\"][0][\"emails\"][1]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Now, `people` looks like this." + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'mathematicians': [{'emails': ['gilbert@mit.edu'],\n", + " 'name': 'Gilbert Strang'},\n", + " {'emails': [],\n", + " 'name': 'Leonhard Euler'}],\n", + " 'physicists': [{'name': 'Albert Einstein'}],\n", + " 'programmers': [{'emails': ['guido@python.org'],\n", + " 'name': 'Guido van Rossum'}]}\n" + ] + } + ], + "source": [ + "pprint(people, indent=1, width=60)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Dictionary Methods" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`dict` objects come with many methods bound on them, many of which are standardized by the `Mapping` and `MutableMapping` ABCs from the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module. While the former requires a data type adhering to it to implement the [keys()](https://docs.python.org/3/library/stdtypes.html#dict.keys), [values()](https://docs.python.org/3/library/stdtypes.html#dict.values), [items()](https://docs.python.org/3/library/stdtypes.html#dict.items), and [get()](https://docs.python.org/3/library/stdtypes.html#dict.get) methods, which *never* mutate an object, the latter formalizes the [update()](https://docs.python.org/3/library/stdtypes.html#dict.update), [pop()](https://docs.python.org/3/library/stdtypes.html#dict.pop), [popitem()](https://docs.python.org/3/library/stdtypes.html#dict.popitem), [clear()](https://docs.python.org/3/library/stdtypes.html#dict.clear), and [setdefault()](https://docs.python.org/3/library/stdtypes.html#dict.setdefault) methods, which *may* do so." + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(to_words, abc.Mapping)" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 83, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(from_words, abc.Mapping)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While iteration over a mapping type goes over its keys, we may emphasize this explicitly by adding the [keys()](https://docs.python.org/3/library/stdtypes.html#dict.keys) method in the `for`-loop. Again, the iteration order is equivalent to the insertion order but still considered *unpredictable*." + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zero\n", + "one\n", + "two\n", + "three\n", + "four\n", + "five\n" + ] + } + ], + "source": [ + "for word in from_words.keys():\n", + " print(word)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[keys()](https://docs.python.org/3/library/stdtypes.html#dict.keys) returns an object of type `dict_keys`. That is a dynamic **view** inside the `from_words`'s hash table, which means it does *not* copy the references to the keys, and changes to `from_words` can be seen through it. View objects behave much like `dict` objects themselves." + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_keys(['zero', 'one', 'two', 'three', 'four', 'five'])" + ] + }, + "execution_count": 85, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from_words.keys()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "iews can be materialized with the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in. However, that may introduce *semantic* errors into a program an the newly created `list` object has a \"*predictable*\" order (i.e., indexes `0`, `1`, ...) created from an *unpredictable* one." + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['zero', 'one', 'two', 'three', 'four', 'five']" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(from_words.keys())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To loop over the value objects instead, we use the [values()](https://docs.python.org/3/library/stdtypes.html#dict.values) method. That returns a *view* on the value objects inside `from_words` without copying the references to them." + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "1\n", + "2\n", + "3\n", + "4\n", + "5\n" + ] + } + ], + "source": [ + "for number in from_words.values():\n", + " print(number)" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_values([0, 1, 2, 3, 4, 5])" + ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from_words.values()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To loop over key-value pairs, we invoke the [items()](https://docs.python.org/3/library/stdtypes.html#dict.items) method. That returns a view on the key-value pairs as `tuple` objects, where the first element is the key and the second the value. Because of that, we use tuple unpacking in the `for`-loop." + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zero -> 0\n", + "one -> 1\n", + "two -> 2\n", + "three -> 3\n", + "four -> 4\n", + "five -> 5\n" + ] + } + ], + "source": [ + "for word, number in from_words.items():\n", + " print(f\"{word} -> {number}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_items([('zero', 0), ('one', 1), ('two', 2), ('three', 3), ('four', 4), ('five', 5)])" + ] + }, + "execution_count": 90, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from_words.items()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Above, we see how the look-up operator fails *loudly* if a key is not in a `dict` object. For example, `to_words` does *not* have a key `0` any more." + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1: 'eins', 2: 'zwei', 3: 'drei', 4: 'vier'}" + ] + }, + "execution_count": 91, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "to_words" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "KeyError", + "evalue": "0", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mto_words\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m: 0" + ] + } + ], + "source": [ + "to_words[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "That may be mitigated with the [get()](https://docs.python.org/3/library/stdtypes.html#dict.get) method that takes two arguments: `key` and `default`. It returns the value object `key` maps to if it is in the `dict` object; otherwise, `default` is returned. If not provided, `default` is `None`." + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'n/a'" + ] + }, + "execution_count": 93, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "to_words.get(0, \"n/a\")" + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'eins'" + ] + }, + "execution_count": 94, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "to_words.get(1, \"n/a\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`dict` objects are *mutable* as can be formally verified with the `MutableMapping` ABC from the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module." + ] + }, + { + "cell_type": "code", + "execution_count": 95, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 95, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(to_words, abc.MutableMapping)" + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 96, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(from_words, abc.MutableMapping)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [update()](https://docs.python.org/3/library/stdtypes.html#dict.update) method takes the items of another mapping and either inserts them or overwrites the ones with matching keys already in the `dict` objects. It may be used in the other two ways as the [dict()](https://docs.python.org/3/library/functions.html#func-dict) built-in allows, as well." + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1: 'eins', 2: 'zwei', 3: 'drei', 4: 'vier'}" + ] + }, + "execution_count": 97, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "to_words" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "to_spanish = {\n", + " 1: \"uno\",\n", + " 2: \"dos\",\n", + " 3: \"tres\",\n", + " 4: \"cuatro\",\n", + " 5: \"cinco\", \n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "to_words.update(to_spanish)" + ] + }, + { + "cell_type": "code", + "execution_count": 100, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1: 'uno', 2: 'dos', 3: 'tres', 4: 'cuatro', 5: 'cinco'}" + ] + }, + "execution_count": 100, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "to_words" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In contrast to the `pop()` method of the `list` type, the [pop()](https://docs.python.org/3/library/stdtypes.html#dict.pop) method of the `dict` type *requires* a `key` argument to be passed. Then, it removes the corresponding key-value pair *and* returns the value object. If the `key` is not in the `dict` object, a `KeyError` is raised. With an optional `default` argument, that loud error may be suppressed and the `default` returned instead, just as with the [get()](https://docs.python.org/3/library/stdtypes.html#dict.get) method above." + ] + }, + { + "cell_type": "code", + "execution_count": 101, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'zero': 0, 'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5}" + ] + }, + "execution_count": 101, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from_words" + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "number = from_words.pop(\"zero\")" + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 103, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "number" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5}" + ] + }, + "execution_count": 104, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from_words" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "KeyError", + "evalue": "'zero'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfrom_words\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"zero\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m: 'zero'" + ] + } + ], + "source": [ + "from_words.pop(\"zero\")" + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 106, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from_words.pop(\"zero\", 0)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Similar to the `pop()` method of the `list` type, the [popitem()](https://docs.python.org/3/library/stdtypes.html#dict.popitem) method of the `dict` type removes *and* returns an \"arbitrary\" key-value pair as a `tuple` object from a `dict` object. With the preservation of the insertion order in Python 3.7 and higher, this effectively becomes a \"last in, first out\" rule, just as with the `list` type. Once a `dict` object is empty, [popitem()](https://docs.python.org/3/library/stdtypes.html#dict.popitem) raises a `KeyError`." + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5}" + ] + }, + "execution_count": 107, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from_words" + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "word, number = from_words.popitem()" + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "('five', 5)" + ] + }, + "execution_count": 109, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "word, number" + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'one': 1, 'two': 2, 'three': 3, 'four': 4}" + ] + }, + "execution_count": 110, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from_words" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [clear()](https://docs.python.org/3/library/stdtypes.html#dict.clear) method removes all items but keeps the `dict` object alive in memory." + ] + }, + { + "cell_type": "code", + "execution_count": 111, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "to_words.clear()" + ] + }, + { + "cell_type": "code", + "execution_count": 112, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{}" + ] + }, + "execution_count": 112, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "to_words" + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "from_words.clear()" + ] + }, + { + "cell_type": "code", + "execution_count": 114, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{}" + ] + }, + "execution_count": 114, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from_words" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [setdefault()](https://docs.python.org/3/library/stdtypes.html#dict.setdefault) method may have a bit of an unfortunate name but is useful, in particular, with nested `list` objects. It takes two arguments, `key` and `default`, and returns the value mapped to `key` if `key` is in the `dict` object; otherwise, it inserts the `key`-`default` pair *and* returns a reference to the newly created value object. So, it is similar to the [get()](https://docs.python.org/3/library/stdtypes.html#dict.get) method above, but *mutates* the `dict` object.\n", + "\n", + "Consider the `people` example again and note hwo the `dict` object modeling \"Albert Einstein\" has *no* `\"emails\"` key in it." + ] + }, + { + "cell_type": "code", + "execution_count": 115, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'mathematicians': [{'emails': ['gilbert@mit.edu'],\n", + " 'name': 'Gilbert Strang'},\n", + " {'emails': [],\n", + " 'name': 'Leonhard Euler'}],\n", + " 'physicists': [{'name': 'Albert Einstein'}],\n", + " 'programmers': [{'emails': ['guido@python.org'],\n", + " 'name': 'Guido van Rossum'}]}\n" + ] + } + ], + "source": [ + "pprint(people, indent=1, width=60)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's say we want to append the imaginary emails `\"leonhard@math.org\"` and `\"albert@physics.org\"`. With the current \"messy\" structure, we cannot be sure if a `dict` object modeling a person has already a `\"emails\"` key or not. We could first use the `in` operator to check for that and create a new `list` object in a second step if one is missing. Third, we would finally append the new email.\n", + "\n", + "[setdefault()](https://docs.python.org/3/library/stdtypes.html#dict.setdefault) allows us to do all of the three steps at once. More importantly, behind the scenes Python only needs to make *one* key look-up instead of potentially three. For large nested data that could speed up the computations significantly.\n", + "\n", + "So, the first code cell below adds the email to the already existing empty `list` object, while the second one creates a new one." + ] + }, + { + "cell_type": "code", + "execution_count": 116, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "people[\"mathematicians\"][1].setdefault(\"emails\", []).append(\"leonhard@math.org\")" + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "people[\"physicists\"][0].setdefault(\"emails\", []).append(\"albert@physics.org\")" + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'mathematicians': [{'emails': ['gilbert@mit.edu'],\n", + " 'name': 'Gilbert Strang'},\n", + " {'emails': ['leonhard@math.org'],\n", + " 'name': 'Leonhard Euler'}],\n", + " 'physicists': [{'emails': ['albert@physics.org'],\n", + " 'name': 'Albert Einstein'}],\n", + " 'programmers': [{'emails': ['guido@python.org'],\n", + " 'name': 'Guido van Rossum'}]}\n" + ] + } + ], + "source": [ + "pprint(people, indent=1, width=60)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In addition to the standardized methods, `dict` objects come with a [copy()](https://docs.python.org/3/library/stdtypes.html#dict.copy) method on them that creates *shallow* copies." + ] + }, + { + "cell_type": "code", + "execution_count": 119, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "guido = people[\"programmers\"][0].copy()" + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'name': 'Guido van Rossum', 'emails': ['guido@python.org']}" + ] + }, + "execution_count": 120, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "guido" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we mutate `guido`, for example, remove all his emails with the `clear()` method on the `list` type, these changes are also visible through `people`." + ] + }, + { + "cell_type": "code", + "execution_count": 121, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "guido[\"emails\"].clear()" + ] + }, + { + "cell_type": "code", + "execution_count": 122, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'name': 'Guido van Rossum', 'emails': []}" + ] + }, + "execution_count": 122, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "guido" + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'mathematicians': [{'emails': ['gilbert@mit.edu'],\n", + " 'name': 'Gilbert Strang'},\n", + " {'emails': ['leonhard@math.org'],\n", + " 'name': 'Leonhard Euler'}],\n", + " 'physicists': [{'emails': ['albert@physics.org'],\n", + " 'name': 'Albert Einstein'}],\n", + " 'programmers': [{'emails': [],\n", + " 'name': 'Guido van Rossum'}]}\n" + ] + } + ], + "source": [ + "pprint(people, indent=1, width=60)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Packing & Unpacking (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Just as a single `*` symbol is used for packing and unpacking iterables in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Packing-&-Unpacking), a double `**` symbol implements packing and unpacking for mappings.\n", + "\n", + "Let's say we have `to_words` and `more_words` as below and want to merge the items together into a *new* `dict` object." + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "to_words = {\n", + " 0: \"zero\",\n", + " 1: \"one\",\n", + " 2: \"two\",\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 125, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "more_words = {\n", + " 2: \"TWO\", # upper case to illustrate a point\n", + " 3: \"three\",\n", + " 4: \"four\",\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "By *unpacking* the items with `**`, the newly created `dict` objects is first filled with the items from `to_words` and then from `more_words`. The item with the key `2` from `more_words` overwrites its counterpart from `to_words` as it is mentioned last." + ] + }, + { + "cell_type": "code", + "execution_count": 126, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{0: 'zero', 1: 'one', 2: 'TWO', 3: 'three', 4: 'four'}" + ] + }, + "execution_count": 126, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{**to_words, **more_words}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With the [update()](https://docs.python.org/3/library/stdtypes.html#dict.update) method from above, we can only *mutate* one of the two `dict` objects in place. So, unpacking is *no* syntactic sugar in this context." + ] + }, + { + "cell_type": "code", + "execution_count": 127, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "to_words.update(more_words)" + ] + }, + { + "cell_type": "code", + "execution_count": 128, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{0: 'zero', 1: 'one', 2: 'TWO', 3: 'three', 4: 'four'}" + ] + }, + "execution_count": 128, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "to_words # we do not want to change an existing object" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### Function Definitions & Calls (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Both, `*` and `**` may be used within the header line of a function definition, for example, as in `print_args1()` below. Here, *positional* arguments not captured by positional parameters are *packed* into the `tuple` object `args`, and *keyword* arguments not captured by keyword parameters are *packed* into the `dict` object `kwargs`.\n", + "\n", + "For `print_args1()`, all arguments are optional, and ..." + ] + }, + { + "cell_type": "code", + "execution_count": 129, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "def print_args1(*args, **kwargs):\n", + " \"\"\"Print out all arguments passed in.\"\"\"\n", + " for i, arg in enumerate(args):\n", + " print(\"position\", i, arg)\n", + " for key, value in kwargs.items():\n", + " print(\"keyword\", key, value)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... we may pass whatever we want to it, or nothing at all." + ] + }, + { + "cell_type": "code", + "execution_count": 130, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "print_args1()" + ] + }, + { + "cell_type": "code", + "execution_count": 131, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "position 0 a\n", + "position 1 b\n", + "position 2 c\n" + ] + } + ], + "source": [ + "print_args1(\"a\", \"b\", \"c\")" + ] + }, + { + "cell_type": "code", + "execution_count": 132, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "keyword first 1\n", + "keyword second 2\n", + "keyword third 3\n" + ] + } + ], + "source": [ + "print_args1(first=1, second=2, third=3)" + ] + }, + { + "cell_type": "code", + "execution_count": 133, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "position 0 x\n", + "position 1 y\n", + "keyword flag True\n" + ] + } + ], + "source": [ + "print_args1(\"x\", \"y\", flag=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The next example, `print_args2()`, requires the caller to pass one positional argument, captured in the `pos` parameter, and one keyword argument, captured in `key`." + ] + }, + { + "cell_type": "code", + "execution_count": 134, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "def print_args2(pos, *args, key, **kwargs):\n", + " \"\"\"Print out all arguments passed in.\"\"\"\n", + " print(\"required position\", pos)\n", + " for i, arg in enumerate(args):\n", + " print(\"optional position\", i, arg)\n", + " print(\"required keyword\", key)\n", + " for key, value in kwargs.items():\n", + " print(\"optional keyword\", key, value)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If the caller does not respect that, a `TypeError` is raised." + ] + }, + { + "cell_type": "code", + "execution_count": 135, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "print_args2() missing 1 required positional argument: 'pos'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mprint_args2\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: print_args2() missing 1 required positional argument: 'pos'" + ] + } + ], + "source": [ + "print_args2()" + ] + }, + { + "cell_type": "code", + "execution_count": 136, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "print_args2() missing 1 required keyword-only argument: 'key'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mprint_args2\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"p\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: print_args2() missing 1 required keyword-only argument: 'key'" + ] + } + ], + "source": [ + "print_args2(\"p\")" + ] + }, + { + "cell_type": "code", + "execution_count": 137, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "required position p\n", + "required keyword k\n" + ] + } + ], + "source": [ + "print_args2(\"p\", key=\"k\")" + ] + }, + { + "cell_type": "code", + "execution_count": 138, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "required position p\n", + "optional position 0 x\n", + "optional position 1 y\n", + "required keyword k\n", + "optional keyword flag True\n" + ] + } + ], + "source": [ + "print_args2(\"p\", \"x\", \"y\", key=\"k\", flag=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Just as above when we merge `to_words` and `more_words`, we may use the `**` symbol to unpack the items of a mapping in a function call." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Dictionary Comprehensions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Analogous to list comprehensions in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#List-Comprehensions), **dictionary comprehensions**, or **dictcomps** for short, are a concise literal notation to derive new `dict` objects out of existing ones.\n", + "\n", + "For example, let's derive `from_words` from `to_words` below by swapping the keys and values." + ] + }, + { + "cell_type": "code", + "execution_count": 139, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "to_words = {\n", + " 0: \"zero\",\n", + " 1: \"one\",\n", + " 2: \"two\",\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Without a dictionary comprehension, we would have to initialize an empty `dict` object, loop over the items of the original one, and insert the key-value pairs one by one in a \"reversed\" fashion as \"value-key\" pairs. That assumes that the values are unique as otherwise some would be merged." + ] + }, + { + "cell_type": "code", + "execution_count": 140, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'zero': 0, 'one': 1, 'two': 2}" + ] + }, + "execution_count": 140, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from_words = {}\n", + "\n", + "for number, word in to_words.items():\n", + " from_words[word] = number\n", + "\n", + "from_words" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While that code is correct, it is also unnecessarily verbose. The dictionary comprehension below works in the same way as list comprehensions except that entire expression is written within curly braces `{}` instead of brackets `[]`, and a colon `:` added to separate the keys from the values." + ] + }, + { + "cell_type": "code", + "execution_count": 141, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'zero': 0, 'one': 1, 'two': 2}" + ] + }, + "execution_count": 141, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{v: k for k, v in to_words.items()}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We may filter out items with an `if`-clause and transform the remaining key and value objects.\n", + "\n", + "For no good reason, let's filter out all words starting with a `\"z\"` and upper case the remainin words." + ] + }, + { + "cell_type": "code", + "execution_count": 142, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'ONE': 1, 'TWO': 2}" + ] + }, + "execution_count": 142, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{v.upper(): k for k, v in to_words.items() if not v.startswith(\"z\")}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Multiple `for`- and/or `if`-clauses are allowed.\n", + "\n", + "For example, let's find all pairs of two numbers from `1` through `10` whose product is \"close\" to `50` (e.g., within a delta of `5`). The resulting `dict` object maps `tuple` to `int` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 143, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{(5, 9): 45,\n", + " (5, 10): 50,\n", + " (6, 8): 48,\n", + " (6, 9): 54,\n", + " (7, 7): 49,\n", + " (8, 6): 48,\n", + " (9, 5): 45,\n", + " (9, 6): 54,\n", + " (10, 5): 50}" + ] + }, + "execution_count": 143, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "one_to_ten = range(1, 11)\n", + "\n", + "{\n", + " (x, y): x * y\n", + " for x in one_to_ten for y in one_to_ten\n", + " if abs(x * y - 50) <= 5\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Memoization" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### \"Easy at second Glance\" Example: [Fibonacci Numbers](https://en.wikipedia.org/wiki/Fibonacci_number) (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The *recursive* implementation of the [Fibonacci numbers](https://en.wikipedia.org/wiki/Fibonacci_number) in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#\"Easy-at-first-Glance\"-Example:-Fibonacci-Numbers) takes long to compute for large Fibonacci numbers as the number of function calls grows exponentially.\n", + "\n", + "The graph below visualizes what the problem is and also suggests a solution: Instead of calculating the return value of the `fibonacci()` function for the *same* argument over and over again, it makes sense to **cache** the result and reuse it. This concept is called **[memoization](https://en.wikipedia.org/wiki/Memoization)** in the computer science literature." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Below is revision of the recursive `fibonacci()` implementation that uses a **globally** defined `dict` object `memo` to store intermediate results and look them up.\n", + "\n", + "To be precise, called with a valid `i`, the the revised `fibonacci()` function first checks if the `i`th Fibonacci number has already been calculated before. If yes, it is in the `memo` dictionary. That number is then returned immediately *without* any more calculations. If no, there is no corresponding entry in `memo` and a recursive function call must be made. The number obtained by recursion is then put into `memo`.\n", + "\n", + "When we follow the flow of execution closely, we realize that the intermediate results represented by the left-most path in the graph above are calculated first. `fibonacci(1)` (i.e., the left-most leaf node) is the first base case reached, followed immediately by `fibonacci(0)`. From that moment onwards, the flow of execution moves back up the left-most path while adding together the two corresponding child nodes.\n", + "\n", + "Effectively, this mirrors the *iterative* implementation in that the order of all computational steps are *identical*.\n", + "\n", + "We added a keyword-only argument `debug` that allows the caller to print out a message every time a `i` was *not* in the `memo`." + ] + }, + { + "cell_type": "code", + "execution_count": 144, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "memo = {\n", + " 0: 0,\n", + " 1: 1,\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 145, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "import numbers # for the goose typing" + ] + }, + { + "cell_type": "code", + "execution_count": 146, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def fibonacci(i, *, debug=False):\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", + " Raises:\n", + " TypeError: if i is not an integer\n", + " ValueError: if i is not positive\n", + " \"\"\"\n", + " if not isinstance(i, numbers.Integral):\n", + " raise TypeError(\"i must be an integer\")\n", + " elif i < 0:\n", + " raise ValueError(\"i must be non-negative\")\n", + "\n", + " if i in memo:\n", + " return memo[i]\n", + "\n", + " if debug: # added for didactical purposes\n", + " print(f\"fibonacci({i}) is calculated\")\n", + "\n", + " recurse = fibonacci(i - 1, debug=debug) + fibonacci(i - 2, debug=debug)\n", + " memo[i] = recurse\n", + " return recurse" + ] + }, + { + "cell_type": "code", + "execution_count": 147, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "fibonacci(12) is calculated\n", + "fibonacci(11) is calculated\n", + "fibonacci(10) is calculated\n", + "fibonacci(9) is calculated\n", + "fibonacci(8) is calculated\n", + "fibonacci(7) is calculated\n", + "fibonacci(6) is calculated\n", + "fibonacci(5) is calculated\n", + "fibonacci(4) is calculated\n", + "fibonacci(3) is calculated\n", + "fibonacci(2) is calculated\n" + ] + }, + { + "data": { + "text/plain": [ + "144" + ] + }, + "execution_count": 147, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fibonacci(12, debug=True) # = 13th number, 11 recursive calls necessary" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Now, calling `fibonacci()` has the *side effect* of growing the `memo` in the *global scope*. So, subsequent calls to `fibonacci()` need not calculate any Fibonacci number with an index `i` smaller than the maximum `i` used so far." + ] + }, + { + "cell_type": "code", + "execution_count": 148, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "144" + ] + }, + "execution_count": 148, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fibonacci(12, debug=True) # = 13th number, no recursive calls needed" + ] + }, + { + "cell_type": "code", + "execution_count": 149, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{0: 0,\n", + " 1: 1,\n", + " 2: 1,\n", + " 3: 2,\n", + " 4: 3,\n", + " 5: 5,\n", + " 6: 8,\n", + " 7: 13,\n", + " 8: 21,\n", + " 9: 34,\n", + " 10: 55,\n", + " 11: 89,\n", + " 12: 144}" + ] + }, + "execution_count": 149, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "memo" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "##### Efficiency of Algorithms (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With memoization, the recursive `fibonacci()` implementation is as fast as its iterative counterpart, even for large numbers.\n", + "\n", + "The `%%timeit` magic, by default, runs a code cell seven times. Whereas in the first run, *new* Fibonacci numbers (i.e., intermediate results) are added to the `memo`, `fibonacci()` has no work to do in the subsequent six runs. `%%timeit` realizes this and tells us that \"an intermediate result is being cached.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 150, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The slowest run took 195.30 times longer than the fastest. This could mean that an intermediate result is being cached.\n", + "18.9 µs ± 44.4 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n 1\n", + "fibonacci(99) # = 100th number" + ] + }, + { + "cell_type": "code", + "execution_count": 151, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The slowest run took 1919.60 times longer than the fastest. This could mean that an intermediate result is being cached.\n", + "346 µs ± 844 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n 1\n", + "fibonacci(999) # = 1,000th number" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The iterative implementation still has an advantage as the `RecursionError` shows for even larger numbers.\n", + "\n", + "This exception occurs as Python must keep track of *every* function call *until* it has returned, and with large enough `i`, the recursion tree above grows too big. By default, Python has a limit of up to 3000 *simultaneous* function calls. So, theoretically this exception is not a bug in the narrow sense but the result of a \"security\" measure that is supposed to keep a computer from crashing. However, practically most high-level languages like Python incur such an overhead cost: It results from the fact that someone (i.e., Python) needs to manage each function call's *local scope*. With the `for`-loop in the iterative version, we do this managing ourselves." + ] + }, + { + "cell_type": "code", + "execution_count": 152, + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "RecursionError", + "evalue": "maximum recursion depth exceeded in comparison", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mRecursionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mget_ipython\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun_cell_magic\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'timeit'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'-n 1'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'fibonacci(9999) # = 10,000th number\\n'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.07/lib/python3.7/site-packages/IPython/core/interactiveshell.py\u001b[0m in \u001b[0;36mrun_cell_magic\u001b[0;34m(self, magic_name, line, cell)\u001b[0m\n\u001b[1;32m 2356\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbuiltin_trap\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2357\u001b[0m \u001b[0margs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mmagic_arg_s\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcell\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2358\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2359\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2360\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mtimeit\u001b[0;34m(self, line, cell, local_ns)\u001b[0m\n", + "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.07/lib/python3.7/site-packages/IPython/core/magic.py\u001b[0m in \u001b[0;36m\u001b[0;34m(f, *a, **k)\u001b[0m\n\u001b[1;32m 185\u001b[0m \u001b[0;31m# but it's overkill for just that one bit of state.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 186\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mmagic_deco\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 187\u001b[0;31m \u001b[0mcall\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mk\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mk\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 188\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 189\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcallable\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.07/lib/python3.7/site-packages/IPython/core/magics/execution.py\u001b[0m in \u001b[0;36mtimeit\u001b[0;34m(self, line, cell, local_ns)\u001b[0m\n\u001b[1;32m 1160\u001b[0m \u001b[0;32mbreak\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1161\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1162\u001b[0;31m \u001b[0mall_runs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtimer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrepeat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrepeat\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumber\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1163\u001b[0m \u001b[0mbest\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mall_runs\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mnumber\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1164\u001b[0m \u001b[0mworst\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmax\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mall_runs\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mnumber\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.07/lib/python3.7/timeit.py\u001b[0m in \u001b[0;36mrepeat\u001b[0;34m(self, repeat, number)\u001b[0m\n\u001b[1;32m 202\u001b[0m \u001b[0mr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 203\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrepeat\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 204\u001b[0;31m \u001b[0mt\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtimeit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnumber\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 205\u001b[0m \u001b[0mr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 206\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mr\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.07/lib/python3.7/site-packages/IPython/core/magics/execution.py\u001b[0m in \u001b[0;36mtimeit\u001b[0;34m(self, number)\u001b[0m\n\u001b[1;32m 167\u001b[0m \u001b[0mgc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdisable\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 168\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 169\u001b[0;31m \u001b[0mtiming\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minner\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mit\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtimer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 170\u001b[0m \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 171\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mgcold\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36minner\u001b[0;34m(_it, _timer)\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfibonacci\u001b[0;34m(i, debug)\u001b[0m\n\u001b[1;32m 24\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"fibonacci({i}) is calculated\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 25\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 26\u001b[0;31m \u001b[0mrecurse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfibonacci\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdebug\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mfibonacci\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdebug\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 27\u001b[0m \u001b[0mmemo\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrecurse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 28\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mrecurse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "... last 1 frames repeated, from the frame below ...\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfibonacci\u001b[0;34m(i, debug)\u001b[0m\n\u001b[1;32m 24\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"fibonacci({i}) is calculated\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 25\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 26\u001b[0;31m \u001b[0mrecurse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfibonacci\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdebug\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mfibonacci\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdebug\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 27\u001b[0m \u001b[0mmemo\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrecurse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 28\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mrecurse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mRecursionError\u001b[0m: maximum recursion depth exceeded in comparison" + ] + } + ], + "source": [ + "%%timeit -n 1\n", + "fibonacci(9999) # = 10,000th number" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We could \"hack\" a bit with Python's default configuration using the [sys](https://docs.python.org/3/library/sys.html) module in the [standard library](https://docs.python.org/3/library/index.html) and make it work anyhow. As we are good citizens, we reset everything to the defaults after our hack is completed." + ] + }, + { + "cell_type": "code", + "execution_count": 153, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import sys" + ] + }, + { + "cell_type": "code", + "execution_count": 154, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "old_recursion_limit = sys.getrecursionlimit()" + ] + }, + { + "cell_type": "code", + "execution_count": 155, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3000" + ] + }, + "execution_count": 155, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "old_recursion_limit" + ] + }, + { + "cell_type": "code", + "execution_count": 156, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "sys.setrecursionlimit(99999)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Computational speed is *not* the problem here." + ] + }, + { + "cell_type": "code", + "execution_count": 157, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The slowest run took 25077.61 times longer than the fastest. This could mean that an intermediate result is being cached.\n", + "2.36 ms ± 5.77 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n 1\n", + "fibonacci(9999) # = 10,000th number" + ] + }, + { + "cell_type": "code", + "execution_count": 158, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "sys.setrecursionlimit(old_recursion_limit)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### \"Easy at third Glance\" Example: [Fibonacci Numbers](https://en.wikipedia.org/wiki/Fibonacci_number) (revisited)" + ] + }, + { + "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, `memo` above could be \"manipulated.\"\n", + "\n", + "More often than not, such things happen by accident: Imagine we wrote two independent recursive functions with memoization built-in 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 pattern is to avoid global state and pass intermediate results \"down\" the recursion tree in a \"hidden\" argument. By convention, we prefix variable and argument names with a single leading underscore `_`, such as with `_memo` below, to indicate that a user of our code *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` argument is *not* even mentioned in the docstring.\n", + "\n", + "When initially called, `fibonacci()` creates a new `dict` object named `_memo` in its *local scope*. That is then shared \"internally\" between successive function calls by passing it on as an argument. Once the first call to `fibonacci()` returns, `_memo` is \"forgotten.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 159, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "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", + " Raises:\n", + " TypeError: if i is not an integer\n", + " ValueError: if i is not positive\n", + " \"\"\"\n", + " if not isinstance(i, numbers.Integral):\n", + " raise TypeError(\"i must be an integer\")\n", + " elif i < 0:\n", + " raise ValueError(\"i must be non-negative\")\n", + "\n", + " if _memo is None:\n", + " _memo = {\n", + " 0: 0,\n", + " 1: 1,\n", + " }\n", + "\n", + " if i in _memo:\n", + " return _memo[i]\n", + "\n", + " if debug: # added for didactical purposes\n", + " print(f\"fibonacci({i}) is calculated\")\n", + "\n", + " recurse = (\n", + " fibonacci(i - 1, debug=debug, _memo=_memo)\n", + " + fibonacci(i - 2, debug=debug, _memo=_memo)\n", + " )\n", + " _memo[i] = recurse\n", + " return recurse" + ] + }, + { + "cell_type": "code", + "execution_count": 160, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "fibonacci(12) is calculated\n", + "fibonacci(11) is calculated\n", + "fibonacci(10) is calculated\n", + "fibonacci(9) is calculated\n", + "fibonacci(8) is calculated\n", + "fibonacci(7) is calculated\n", + "fibonacci(6) is calculated\n", + "fibonacci(5) is calculated\n", + "fibonacci(4) is calculated\n", + "fibonacci(3) is calculated\n", + "fibonacci(2) is calculated\n" + ] + }, + { + "data": { + "text/plain": [ + "144" + ] + }, + "execution_count": 160, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fibonacci(12, debug=True) # = 13th number, 11 recursive calls necessary" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Because `fibonacci()` is now independent from *global state*, the same eleven recursive function calls are made each time." + ] + }, + { + "cell_type": "code", + "execution_count": 161, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "fibonacci(12) is calculated\n", + "fibonacci(11) is calculated\n", + "fibonacci(10) is calculated\n", + "fibonacci(9) is calculated\n", + "fibonacci(8) is calculated\n", + "fibonacci(7) is calculated\n", + "fibonacci(6) is calculated\n", + "fibonacci(5) is calculated\n", + "fibonacci(4) is calculated\n", + "fibonacci(3) is calculated\n", + "fibonacci(2) is calculated\n" + ] + }, + { + "data": { + "text/plain": [ + "144" + ] + }, + "execution_count": 161, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fibonacci(12, debug=True) # = 13th number, still 11 recursive calls necessary" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "##### Efficiency of Algorithms (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Also, the runtime is now stable (i.e., no message that \"an intermediate result is being cached\"). The limitation with respect to the maximum number of simultaneous function calls still applies." + ] + }, + { + "cell_type": "code", + "execution_count": 162, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "330 µs ± 18.7 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n 1\n", + "fibonacci(99) # = 100th number" + ] + }, + { + "cell_type": "code", + "execution_count": 163, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.53 ms ± 75.2 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n 1\n", + "fibonacci(999) # = 1,000th number" + ] + }, + { + "cell_type": "code", + "execution_count": 164, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "sys.setrecursionlimit(99999)" + ] + }, + { + "cell_type": "code", + "execution_count": 165, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "15.5 ms ± 572 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n 1\n", + "fibonacci(9999) # = 10,000th number" + ] + }, + { + "cell_type": "code", + "execution_count": 166, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "sys.setrecursionlimit(old_recursion_limit)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### Specialized Mappings" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [collections](https://docs.python.org/3/library/collections.html) module in the [standard library](https://docs.python.org/3/library/index.html) provides specialized mapping types for common enough use cases." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### The `OrderedDict` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [OrderedDict](https://docs.python.org/3/library/collections.html#collections.OrderedDict) type may be used to create a `dict`-like object with the added feature that *explicitly* remembers the *insertion* order of its items.\n", + "\n", + "Let's look at a quick example: We create an `OrderedDict` object by passing an iterable of $2$-element iterables, one of the three ways to use [dict()](https://docs.python.org/3/library/functions.html#func-dict)." + ] + }, + { + "cell_type": "code", + "execution_count": 167, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "from collections import OrderedDict" + ] + }, + { + "cell_type": "code", + "execution_count": 168, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "ordered = OrderedDict([(\"first\", 1), (\"second\", 2), (\"third\", 3)])" + ] + }, + { + "cell_type": "code", + "execution_count": 169, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "OrderedDict([('first', 1), ('second', 2), ('third', 3)])" + ] + }, + "execution_count": 169, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ordered" + ] + }, + { + "cell_type": "code", + "execution_count": 170, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "collections.OrderedDict" + ] + }, + "execution_count": 170, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(ordered)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The iteration order is the insertion order, and ..." + ] + }, + { + "cell_type": "code", + "execution_count": 171, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "first 1\n", + "second 2\n", + "third 3\n" + ] + } + ], + "source": [ + "for key, value in ordered.items():\n", + " print(key, value)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... we may also loop over `ordered` in *reverse* order." + ] + }, + { + "cell_type": "code", + "execution_count": 172, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "third 3\n", + "second 2\n", + "first 1\n" + ] + } + ], + "source": [ + "for key, value in reversed(ordered.items()):\n", + " print(key, value)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For `OrderedDict` objects, the [popitem()](https://docs.python.org/3/library/collections.html#collections.OrderedDict.popitem) method takes an optional and boolean argument `last`. That allows us to remove either the *first* or *last* item inserted.\n", + "\n", + "Further, the [move_to_end()](https://docs.python.org/3/library/collections.html#collections.OrderedDict.move_to_end) method allows us to move any item by its key to the end of the order. It also takes an optional argument `last`." + ] + }, + { + "cell_type": "code", + "execution_count": 173, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "ordered.move_to_end(\"first\")" + ] + }, + { + "cell_type": "code", + "execution_count": 174, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "second 2\n", + "third 3\n", + "first 1\n" + ] + } + ], + "source": [ + "for key, value in ordered.items():\n", + " print(key, value)" + ] + }, + { + "cell_type": "code", + "execution_count": 175, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "ordered.move_to_end(\"first\", last=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 176, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "first 1\n", + "second 2\n", + "third 3\n" + ] + } + ], + "source": [ + "for key, value in ordered.items():\n", + " print(key, value)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Otherwise, an `OrderedDict` object is no different than a normal `dict` one, starting with Python 3.7." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### The `defaultdict` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A more useful mapping is the [defaultdict](https://docs.python.org/3/library/collections.html#collections.defaultdict) type, which 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": 177, + "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": 178, + "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": 178, + "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": [ + "We could replace the `if`-`else`-logic with the [setdefault()](https://docs.python.org/3/library/stdtypes.html#dict.setdefault) method mentioned above." + ] + }, + { + "cell_type": "code", + "execution_count": 179, + "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": 179, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "goals_by_player = {}\n", + "\n", + "for _, player, minute in goals:\n", + " goals_by_player.setdefault(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()](https://docs.python.org/3/library/functions.html#func-list) built-in to `defaultdict`." + ] + }, + { + "cell_type": "code", + "execution_count": 180, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "from collections import defaultdict" + ] + }, + { + "cell_type": "code", + "execution_count": 181, + "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": 181, + "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": 182, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "collections.defaultdict" + ] + }, + "execution_count": 182, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(goals_by_player)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The factory function is stored in the `default_factory` attribute." + ] + }, + { + "cell_type": "code", + "execution_count": 183, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "list" + ] + }, + "execution_count": 183, + "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()](https://docs.python.org/3/library/functions.html#func-dict) built-in." + ] + }, + { + "cell_type": "code", + "execution_count": 184, + "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": 184, + "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 that returns another [defaultdict](https://docs.python.org/3/library/collections.html#collections.defaultdict) with [list()](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": 185, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "defaultdict(()>,\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": 185, + "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": 186, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'Brazil': {'Oscar': [90]},\n", + " 'Germany': {'Khedira': [29],\n", + " 'Klose': [23],\n", + " 'Kroos': [24, 26],\n", + " 'Müller': [11],\n", + " 'Schürrle': [69, 79]}}\n" + ] + } + ], + "source": [ + "pprint({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](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": 187, + "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": 187, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "goals" + ] + }, + { + "cell_type": "code", + "execution_count": 188, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "from collections import Counter" + ] + }, + { + "cell_type": "code", + "execution_count": 189, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "scorers = Counter(x[1] for x in goals)" + ] + }, + { + "cell_type": "code", + "execution_count": 190, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Counter({'Müller': 1,\n", + " 'Klose': 1,\n", + " 'Kroos': 2,\n", + " 'Khedira': 1,\n", + " 'Schürrle': 2,\n", + " 'Oscar': 1})" + ] + }, + "execution_count": 190, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scorers" + ] + }, + { + "cell_type": "code", + "execution_count": 191, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "collections.Counter" + ] + }, + "execution_count": 191, + "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": 192, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 192, + "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": 193, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 193, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scorers[\"Lahm\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`Counter` objects have a [most_common()](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": 194, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[('Kroos', 2), ('Schürrle', 2)]" + ] + }, + "execution_count": 194, + "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()](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](https://en.wikipedia.org/wiki/Philipp_Lahm) had also scored against Brazil." + ] + }, + { + "cell_type": "code", + "execution_count": 195, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "scorers.update([\"Lahm\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 196, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Counter({'Müller': 1,\n", + " 'Klose': 1,\n", + " 'Kroos': 2,\n", + " 'Khedira': 1,\n", + " 'Schürrle': 2,\n", + " 'Oscar': 1,\n", + " 'Lahm': 1})" + ] + }, + "execution_count": 196, + "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": 197, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "scorers.update(\"Lahm\")" + ] + }, + { + "cell_type": "code", + "execution_count": 198, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Counter({'Müller': 1,\n", + " 'Klose': 1,\n", + " 'Kroos': 2,\n", + " 'Khedira': 1,\n", + " 'Schürrle': 2,\n", + " 'Oscar': 1,\n", + " 'Lahm': 1,\n", + " 'L': 1,\n", + " 'a': 1,\n", + " 'h': 1,\n", + " 'm': 1})" + ] + }, + "execution_count": 198, + "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": 199, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "to_words = {\n", + " 0: \"zero\",\n", + " 1: \"one\",\n", + " 2: \"two\",\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 200, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "more_words = {\n", + " 2: \"TWO\", # upper case to illustrate a point\n", + " 3: \"three\",\n", + " 4: \"four\",\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 201, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "even_more_words = {\n", + " 4: \"FOUR\", # upper case to illustrate a point\n", + " 5: \"five\",\n", + " 6: \"six\",\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [ChainMap](https://docs.python.org/3/library/collections.html#collections.ChainMap) type allows us to do precisely that." + ] + }, + { + "cell_type": "code", + "execution_count": 202, + "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": 203, + "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": 204, + "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": 205, + "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)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mchain\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.07/lib/python3.7/collections/__init__.py\u001b[0m in \u001b[0;36m__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 912\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mKeyError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 913\u001b[0m \u001b[0;32mpass\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 914\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__missing__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# support subclasses that define __missing__\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 915\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 916\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkey\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.07/lib/python3.7/collections/__init__.py\u001b[0m in \u001b[0;36m__missing__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 904\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 905\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__missing__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkey\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 906\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mKeyError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 907\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 908\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__getitem__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkey\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mKeyError\u001b[0m: 10" + ] + } + ], + "source": [ + "chain[10]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The `set` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Python's provides a built-in `set` type that resembles [mathematical sets](https://en.wikipedia.org/wiki/Set_%28mathematics%29): Each element may only be a member of a set once, and there is no *predictable* order regarding the elements (cf., [documentation](https://docs.python.org/3/library/stdtypes.html#set)).\n", + "\n", + "To create a set, we use the literal notation, `{..., ...}`, and list all the elements. The redundant `7` and `4` are discarded." + ] + }, + { + "cell_type": "code", + "execution_count": 206, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers = {7, 7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4, 4}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`set` objects are objects on their own." + ] + }, + { + "cell_type": "code", + "execution_count": 207, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139751998783784" + ] + }, + "execution_count": 207, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 208, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "set" + ] + }, + "execution_count": 208, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 209, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}" + ] + }, + "execution_count": 209, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To create an empty set, we must use the [set()](https://docs.python.org/3/library/functions.html#func-set) built-in as empty curly brackets, `{}`, already creates an empty `dict` object." + ] + }, + { + "cell_type": "code", + "execution_count": 210, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "empty_dict = {}" + ] + }, + { + "cell_type": "code", + "execution_count": 211, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "dict" + ] + }, + "execution_count": 211, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(empty_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 212, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "empty_set = set()" + ] + }, + { + "cell_type": "code", + "execution_count": 213, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "set()" + ] + }, + "execution_count": 213, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "empty_set" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [set()](https://docs.python.org/3/library/functions.html#func-set) built-in takes any iterable and only keeps unique elements.\n", + "\n", + "For example, we obtain all unique letters of a long word like so." + ] + }, + { + "cell_type": "code", + "execution_count": 214, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a', 'b', 'c', 'd', 'r'}" + ] + }, + "execution_count": 214, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "set(\"abracadabra\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Sets are like \"Dictionaries without Values\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The curly brackets notation can be viewed as a hint that `dict` objects are conceptually generalizations of `set` objects, and we think of `set` objects as a collection consisting of a `dict` object's keys with all the mapped values removed.\n", + "\n", + "Like `dict` objects, `set` objects are built on top of [hash tables](https://en.wikipedia.org/wiki/Hash_table), and, thus, each element must be a *hashable* (i.e., immutable at the very least) object and can only be contained in a set once due to the buckets logic." + ] + }, + { + "cell_type": "code", + "execution_count": 215, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "unhashable type: 'list'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;34m{\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: unhashable type: 'list'" + ] + } + ], + "source": [ + "{0, [1, 2], 3}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[len()](https://docs.python.org/3/library/functions.html#len) tells us the number of elements in a `set` object, which is always `Sized`." + ] + }, + { + "cell_type": "code", + "execution_count": 216, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12" + ] + }, + "execution_count": 216, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 217, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 217, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(numbers, abc.Sized)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We may loop over the elements in a `set` object, but we must keep in mind that there is no *predictable* order. In contrast to `dict` objects, the iteration order is also *not* guaranteed to be the insertion order. Because of the special hash values for `int` objects, `numbers` seems to be \"magically\" sorted, which is not the case." + ] + }, + { + "cell_type": "code", + "execution_count": 218, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 2 3 4 5 6 7 8 9 10 11 12 " + ] + } + ], + "source": [ + "for number in numbers:\n", + " print(number, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`set` objects are `Iterable`." + ] + }, + { + "cell_type": "code", + "execution_count": 219, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 219, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(numbers, abc.Iterable)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`set` objects are not `Reversible`." + ] + }, + { + "cell_type": "code", + "execution_count": 220, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'set' object is not reversible", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0mnumber\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mreversed\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnumbers\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnumber\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mend\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\" \"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: 'set' object is not reversible" + ] + } + ], + "source": [ + "for number in reversed(numbers):\n", + " print(number, end=\" \")" + ] + }, + { + "cell_type": "code", + "execution_count": 221, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 221, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(numbers, abc.Reversible)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The boolean `in` operator checks if a given and immutable object evaluates equal to an element in a `set` object. As with `dict` objects, membership testing is an *extremely* fast operation. Conceptually, it has the same result as conducting a linear search with the `==` operator behind the scenes." + ] + }, + { + "cell_type": "code", + "execution_count": 222, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 222, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0 in numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 223, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 223, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 in numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 224, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 224, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1.0 in numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "So, a `set` object is a `Container`. " + ] + }, + { + "cell_type": "code", + "execution_count": 225, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 225, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(numbers, abc.Container)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "There is a `Set` ABC in the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module that formalizes, in particular, the operators supported by `set` objects. Furthermore, the ordinary `set` type is *mutable*, as expressed with the `MutableSet` ABC. The latter formalizes all the methods that mutate a `set` object." + ] + }, + { + "cell_type": "code", + "execution_count": 226, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 226, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(numbers, abc.Set)" + ] + }, + { + "cell_type": "code", + "execution_count": 227, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 227, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(numbers, abc.MutableSet)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### No Indexing / Key Look-up / Slicing" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As `set` objects come without a *predictable* order, indexing and slicing is not supported and results in a `TypeError`. In particular, as there are no values to be looked up, these operations are not *semantically* meaningful. Instead, we check membership via the `in` operator, as shown in the previous sub-section." + ] + }, + { + "cell_type": "code", + "execution_count": 228, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'set' object is not subscriptable", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnumbers\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: 'set' object is not subscriptable" + ] + } + ], + "source": [ + "numbers[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 229, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'set' object is not subscriptable", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnumbers\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: 'set' object is not subscriptable" + ] + } + ], + "source": [ + "numbers[:]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Mutability & Set Methods" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Because the `[]` operator does not work for `set` objects, they are mutated maily via methods (cf., [documentation](https://docs.python.org/3/library/stdtypes.html#set)).\n", + "\n", + "We may add new elements to an existing `set` object with the [add()](https://docs.python.org/3/library/stdtypes.html#frozenset.add) method." + ] + }, + { + "cell_type": "code", + "execution_count": 230, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers.add(99)" + ] + }, + { + "cell_type": "code", + "execution_count": 231, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 99}" + ] + }, + "execution_count": 231, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [update()](https://docs.python.org/3/library/stdtypes.html#frozenset.update) method takes an iterable and adds all its elements to a `set` object if they are not already contained in it." + ] + }, + { + "cell_type": "code", + "execution_count": 232, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "numbers.update(range(5))" + ] + }, + { + "cell_type": "code", + "execution_count": 233, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 99}" + ] + }, + "execution_count": 233, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To remove an element by value, we use the [remove()](https://docs.python.org/3/library/stdtypes.html#frozenset.remove) or [discard()](https://docs.python.org/3/library/stdtypes.html#frozenset.discard) methods. If the element to be removed is not in the `set` object, [remove()](https://docs.python.org/3/library/stdtypes.html#frozenset.remove) raises a loud `KeyError` while [discard()](https://docs.python.org/3/library/stdtypes.html#frozenset.discard) stays *silent*." + ] + }, + { + "cell_type": "code", + "execution_count": 234, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers.remove(99)" + ] + }, + { + "cell_type": "code", + "execution_count": 235, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "KeyError", + "evalue": "99", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mremove\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m99\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m: 99" + ] + } + ], + "source": [ + "numbers.remove(99)" + ] + }, + { + "cell_type": "code", + "execution_count": 236, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "numbers.discard(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 237, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "numbers.discard(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 238, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}" + ] + }, + "execution_count": 238, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [pop()](https://docs.python.org/3/library/stdtypes.html#frozenset.pop) method removes an *arbitrary* element. As not even the insertion order is tracked, that removes a \"random\" element in theory." + ] + }, + { + "cell_type": "code", + "execution_count": 239, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 239, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers.pop()" + ] + }, + { + "cell_type": "code", + "execution_count": 240, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}" + ] + }, + "execution_count": 240, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [clear()](https://docs.python.org/3/library/stdtypes.html#frozenset.clear) method discards all elements but keeps the `set` object alive." + ] + }, + { + "cell_type": "code", + "execution_count": 241, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "numbers.clear()" + ] + }, + { + "cell_type": "code", + "execution_count": 242, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "set()" + ] + }, + "execution_count": 242, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 243, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "139751998783784" + ] + }, + "execution_count": 243, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(numbers) # same memory location as before" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Set Operations" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The arithmetic and relational operators are overloaded with typical set operations from math. The operators have methods that do the equivalent. We omit them for brevity in this sub-section and only show them as comments in the code cells. Both the operators and the methods return *new* `set` objects without modifying the operands.\n", + "\n", + "We showcase the set operations with easy math examples." + ] + }, + { + "cell_type": "code", + "execution_count": 244, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers = set(range(1, 13))\n", + "zero = {0}\n", + "evens = set(range(2, 13, 2))" + ] + }, + { + "cell_type": "code", + "execution_count": 245, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}" + ] + }, + "execution_count": 245, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 246, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{0}" + ] + }, + "execution_count": 246, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "zero" + ] + }, + { + "cell_type": "code", + "execution_count": 247, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{2, 4, 6, 8, 10, 12}" + ] + }, + "execution_count": 247, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "evens" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The bitwise OR operator `|` returns the union of two `set` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 248, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}" + ] + }, + "execution_count": 248, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "zero | numbers # zero.union(numbers) or reversed order" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Of course, the operators may be *chained*." + ] + }, + { + "cell_type": "code", + "execution_count": 249, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}" + ] + }, + "execution_count": 249, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "zero | numbers | evens # zero.union(numbers).union(evens) or any possible order" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To obtain an intersection of two or more `set` objects, we use the bitwise AND operator `&`." + ] + }, + { + "cell_type": "code", + "execution_count": 250, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "set()" + ] + }, + "execution_count": 250, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "zero & numbers # zero.intersection(numbers) or reversed order" + ] + }, + { + "cell_type": "code", + "execution_count": 251, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{2, 4, 6, 8, 10, 12}" + ] + }, + "execution_count": 251, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers & evens # numbers.intersection(evens) or reversed order" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To calculate a `set` object containing all elements that are in one but not the other `set` object, we use the minus operator `-`. This operation is *not* symmetric." + ] + }, + { + "cell_type": "code", + "execution_count": 252, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1, 3, 5, 7, 9, 11}" + ] + }, + "execution_count": 252, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers - evens # numbers.difference(evens)" + ] + }, + { + "cell_type": "code", + "execution_count": 253, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "set()" + ] + }, + "execution_count": 253, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "evens - numbers # evens.difference(numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The *symmetric* difference is defined as the `set` object containing all elements that are in one but not both `set` objects. It is calculated with the bitwise XOR operator `^`." + ] + }, + { + "cell_type": "code", + "execution_count": 254, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1, 3, 5, 7, 9, 11}" + ] + }, + "execution_count": 254, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers ^ evens # numbers.symmetric_difference(evens)" + ] + }, + { + "cell_type": "code", + "execution_count": 255, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1, 3, 5, 7, 9, 11}" + ] + }, + "execution_count": 255, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "evens ^ numbers # evens.symmetric_difference(numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The augmented versions of the four operators (e.g., `|` becomes `|=`) are also defined: They mutate the left operand *in place*." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Set Comprehensions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Python provides a literal notation for **set comprehensions**, or **setcomps** for short, that works exactly like the one for dictionary comprehensions described above except that they use a single loop variable instead of a key-value pair.\n", + "\n", + "For example, let's create a new `set` object that consists of the squares of all the elements of `numbers`." + ] + }, + { + "cell_type": "code", + "execution_count": 256, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144}" + ] + }, + "execution_count": 256, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{x ** 2 for x in numbers}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As before, we may have multiple `for`- and/or `if`-clauses.\n", + "\n", + "For example, let's only keep the squares if they turn out to be an even number, or ..." + ] + }, + { + "cell_type": "code", + "execution_count": 257, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{4, 16, 36, 64, 100, 144}" + ] + }, + "execution_count": 257, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{x ** 2 for x in numbers if (x ** 2) % 2 == 0}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... create a `set` object with all products obtained from the Cartesian product of `numbers` with itself as long as the products are greater than `80`." + ] + }, + { + "cell_type": "code", + "execution_count": 258, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{81, 84, 88, 90, 96, 99, 100, 108, 110, 120, 121, 132, 144}" + ] + }, + "execution_count": 258, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{x * y for x in numbers for y in numbers if x * y > 80}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### The `frozenset` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As `set` objects are mutable, they may *not* be used, for example, as keys in a `dict` object. Similar to how we replace `list` with `tuple` objects, we may often use a `frozenset` object instead of an ordinary one. The `frozenset` type is a built-in, and as `frozenset` objects are immutable, the only limitation is that we must specify *all* elements *upon* creation (cf., [documentation](https://docs.python.org/3/library/stdtypes.html#frozenset)).\n", + "\n", + "`frozenset` objects are created by passing an iterable to the [frozenset()](https://docs.python.org/3/library/functions.html#func-frozenset) built-in. Note that even though `frozenset` objects are hashable, their elements do still *not* come in a predictable order." + ] + }, + { + "cell_type": "code", + "execution_count": 259, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "frozenset({1, 2, 3})" + ] + }, + "execution_count": 259, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "frozenset([1, 1, 2, 2, 3, 3])" + ] + }, + { + "cell_type": "code", + "execution_count": 260, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "frozenset({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})" + ] + }, + "execution_count": 260, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "frozenset(numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## 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." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## Further Resources" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This section provides some conference talks that go into great detail regarding the workings of the `dict` type." + ] + }, + { + "cell_type": "code", + "execution_count": 261, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 261, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from IPython.display import YouTubeVideo\n", + "YouTubeVideo(\"C4Kc8xzcA68\", width=\"60%\")" + ] + }, + { + "cell_type": "code", + "execution_count": 262, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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jaZrfV2i5bO0tdRTiioZJH1MsUU5jutrbIAy4SyuY5/A854PQ1zc73RAQH+D/s3Q22wUDKGCpiFVCytqX18O4uNTVVLQ+aouDC8kVROBjJwGtAOAFPkRAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAUW2j2wZQ1b4JYy5oo2TxCLnVFTUy1BgipIIjwfI4gY4+EnABKlKh20+xEVxrzVVAZobQcmglYSKulqeUidtTTPLcRSNLWEPHHLejC3hm39o5Vc+3sbS9qNqo6OGH4SG5qnwunlgpIqmuEEbCNb3ughJbC3UGmVwAJBVnctu4I5blAxrgaC2R3HlMsVVyN7ZBVkZlihPxQbTB2tmdWp4bktcBjL5s7eqpkTZpaWZ3I5qWUNq62jgbOXuay4bqmZ/GHPi0Zp3kBpBDXEElU67YqudBWQMdSkVuzVNZ3PdLK0w1dIyvax4aITvKd5rzzuBG66Dnh0UYbzhKdXcunyJXWbV0MM4ppJXCXVDG8tgnfBFLUFoginqWR7mnkeXsAbIQTvGeEZt9r9oZKSahpom04krnysbNVyuhp2GFgfuwWtJkqX6ubHwyI5Tnm4OArdhpXV1TLojqKatqqaqk3lwuNPuHQxUsMjORU3xFZwpWPDnluC7ByAFJNrqCqmEYhjo6uAtkjqqCu5kNQH7sxyCYQvLHMLHDQWkHenoIC1tBNWNlKo4u+rXq5mKum1lVT07zPTw09Wyhu9TuHSSTNkdbTCGSwPYwNfSu3zHc8td8YwY6cZel2opTC6SWUNdFLR007QyXm1Va2mMETW6cvDjVwAFuRz+ngVEYtgasU7Y95TMxb7/SMha+Yw0vwrJSPpKeBzmanUsTadzckD8HDQOAzDtj5DcKGp3rBTxU8XK4edmaromSx0MjBjSWAVdS4k8c09L4OGzUDWMqvh4fp5mUh2vt75nwiZ2pm/GswVDYJHUurlMcFS6Pc1ErND8sjJI3cn5JxQt+2VJUvpzDK3k00FXUb2eKqpi+KmbSPM8BnhDH0wbVNJkJxxGM4diN2fYGeBwicyGSOnNa6lqn3G5SPzUxVMMX+jn/xanlEdU9hkaTka8NGrm3902HlqYKKmkljYyKxXC01D2anP3lbDQRCWEFuHsHJZTzsdLfPg407/AOf54GVOta9l/lvUkVh2mo65zmU75NYjbKGTU9RTOkheSGzwipjBmhJGNbMjiPCsyorZbTXSV0NbXtpYnUlFPRwspJZZhMaqWklnnkMsTdyP4lCBGNWNb8uPBSpcpJJ6jvTcmtYREWp0CIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIDkLt9bR+OpfRG+0nb62j8dS+iN9pauV9s60GsowQC11XSggjIIM8YIIPAjB6F6l4WklfNXI8BHHYhu2e+ZsHt+7RePpPRWe0nb92i8fSeis9pdUN2YtuP5PovRYPYT7mLb5PovRYPYVTpdD4aPQ924r4z6+Zyv2/dovH0norPaTt+7RePpPRWe0uqPuYtvk+i9Fg9hPuYtvk+i9Fg9hNMofDRnu3FfGfXzOV+37tF4+k9FZ7Sdv3aLx9J6Kz2l1R9zFt8n0XosHsJ9zFt8n0XosHsJplD4aHduK+M+vmcr9v3aLx9J6Kz2l72+to/HUnojfaXT9z2atwgmIoKIERSEHksHAhh/mLg2E81v5o/sUzC9jXv7CVitygsThM29Vu9+htTt9bR+OpfRG+0nb62j8dS+iN9pauRS9FpcK5Fb3hiON8zaPb62j8dS+iN9pO31tH46l9Eb7S1ciaLS4VyHeGI43zNo9vraPx1L6I32k7fW0fjqX0RvtLVyJotLhXId4YjjfM2j2+to/HUvojfaTt9bR+OpfRG+0tXImi0uFch3hiON8zaPb62j8dS+iN9pO31tH46l9Eb7S1ciaLS4VyHeGI43zNo9vraPx1L6I32k7fW0fjqX0RvtLVyJotLhXId4YjjfM2j2+to/HUvojfaTt9bR+OpfRG+0tXImi0uFch3hiON8zaPb62j8dS+iN9pO31tH46l9Eb7S1ciaLS4VyHeGI43zNo9vraPx1L6I32k7fW0fjqX0RvtLVyJotLhXId4YjjfM2j2+to/HUvojfaTt9bR+OpfRG+0tXImi0uFch3hiON8zaPb62j8dS+iN9pO31tH46l9Eb7S1ciaLS4VyHeGI43zNo9vraPx1L6I32k7fW0fjqX0RvtLVyJotLhXId4YjjfM2j2+to/HUvojfaTt9bR+OpfRG+0tXImi0uFch3hiON8zaPb62j8dS+iN9pO31tH46l9Eb7S1ciaLS4VyHeGI43zNo9vraPx1L6I32k7fW0fjqX0RvtLVyJotLhXId4YjjfM2j2+to/HUvojfaTt9bR+OpfRG+0tXImi0uFch3hiON8zaPb62j8dS+iN9pO31tH46l9Eb7S1ciaLS4VyHeGI43zNodvraPx1J6I32l72+to/HUvojfaWriiaLS4VyM6fiON8zaPb62j8dS+iN9pO31tH46l9Eb7S1ciaLS4VyMd4YjjfM2j2+to/HUvojfaTt9bR+OpfRG+0tXImi0uFch3hiON8zaPb62j8dS+iN9pO31tH46l9Eb7S1ciaLS4VyHeGI43zNo9vraPx1L6I32k7fW0fjqX0RvtLVyJotLhXId4YjjfM2j2+to/HUvojfaTt9bR+OpfRG+0tXImi0uFch3hiON8zaPb62j8dS+iN9pO31tH46l9Eb7S1ciaLS4VyHeGI43zNo9vraPx1L6I32k7fW0fjqX0RvtLVyJot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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 262, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"66P5FMkWoVU\", width=\"60%\")" + ] + }, + { + "cell_type": "code", + "execution_count": 263, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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dfw1e3XmZbwsbcXO668vktwY+pI1law3eI1b8bdYSRodaOyXQlmcXhrGPrZynJYlzlnKyixi45cZka9mKsx9K/Rc/Vrd+DfDonDTe0HjAGxuiXanI5SvYfkaFao+GcxwWaF+HI47IwaeDapzx6PazUOBZIAfi+MgTVax6BOjCTZxuUfNPVdLlrjbb6mNrOpYqjux8MRUqz5HFoOp1cSOTYxpy57OQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAUDj20uBndUlWr3EcvLitGWJBbBblX4qOZsbodyUmVrXlgI5F3MkaGeFa6p9HfBdHdijpDKRZi9fFjc+XqXbtpz688vD4m+KlkAEdT4mdY112U837xXrZ+jMJJ9+eP4roeNJvtNhgcK1owyS1WyPswQT8LhWJ2Nil1jjJPwUnLwTp9XNscbFGyV9kcOSCC0x7I5ZGugtTRV68msbDyfJNGAOv4x6gdIxDstkW36tqzJA+Knkbt11l924+SWtPWyEEMTaT4+56nBZajb4BOohJ5EnWw2V2TksULLonsLJMjQGOfIySENwuIyrLVWEN0LiNO69x/U4SRHkCs8yHeaulq6radP5eegljdv8WS5plsMdHIyKZslC/GarpN3hG4HwfwSN++0tfLoCCSDoCryztbQjsmq+ZwkEsUD3iCw6vHYmDDDXlttj4EU7xJHpG5wPwsf8oa4rP7Kz2I84xj4QcmyBsG8X6MMVdkLuLozkN5pPg6q2vbL3nd1U2Gr3Bcvx3n2HySi3C3iwzz12wCLclc58Lg2XfG6JR4J3fCjNgZudVbOXj8/AzVTbTHS2G1mTSGR1ieoHGtabB3VWdK2at3S6Lg8ccGQhmupDdRqNFYv22hlvUKlQmVti1agmlfXssic2tUtSudUsvYIbBbNAxhLC7rcqFPZKwyGCMvhLos9cyj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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 263, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"npw4s1QTmPg\", width=\"60%\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.3" + }, + "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": 2 +} diff --git a/static/fibonacci_call_graph.png b/static/fibonacci_call_graph.png new file mode 100644 index 0000000000000000000000000000000000000000..64cba5d4c09a43f0c2599046e6821dfe595db40e GIT binary patch literal 32706 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
1?x4 zuc5^IQAu%fJ1)C)=GFU?eG#i8_4>HVA2IgL^*?n
C#~`IUR?-jaskC2B1>)@E2| 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 literal 0 HcmV?d00001 From 7fee5f553000aa46edc8f4874e5ffdce88a507b5 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 20 Nov 2019 11:02:48 +0100 Subject: [PATCH 051/242] Add review and exercises for notebook 08 --- 08_mappings_review_and_exercises.ipynb | 596 +++++++++++++++++++++++++ 1 file changed, 596 insertions(+) create mode 100644 08_mappings_review_and_exercises.ipynb diff --git a/08_mappings_review_and_exercises.ipynb b/08_mappings_review_and_exercises.ipynb new file mode 100644 index 0000000..c05004e --- /dev/null +++ b/08_mappings_review_and_exercises.ipynb @@ -0,0 +1,596 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "# Chapter 8: Mappings & Sets" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Content Review" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings.ipynb) of the book. Then work through the twelve review questions." + ] + }, + { + "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**: `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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: Whereas *key* **look-ups** in a `dict` object run in so-called **[constant time](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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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", + "\n", + "Compare the **recursive** formulation of `fibonacci()` in [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings.ipynb#\"Easy-at-third-Glance\"-Example:-Fibonacci-Numbers-%28revisited%29), the \"*Easy at third Glance*\" example, with the **iterative** version in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.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?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: How are the `set` and the `dict` type related? How could we use the latter to mimic the former?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: A `dict` object's [update()](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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q12**: The `set` type is both a mapping and a sequence." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Coding Exercises" + ] + }, + { + "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](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": [ + "**Q13.1**: 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": [ + "**Q13.2**: 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!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_players(team):\n", + " ..." + ] + }, + { + "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()`: It is characterized by the observation that a large set of unique keys (i.e., the players' names) is mapped onto a smaller set of non-unique values (i.e., the positions).\n", + "\n", + "**Q13.3**: 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 the [defaultdict](https://docs.python.org/3/library/collections.html#collections.defaultdict) type from the [collections](https://docs.python.org/3/library/collections.html) module in the [standard library](https://docs.python.org/3/library/index.html) or the [setdefault()](https://docs.python.org/3/library/stdtypes.html#dict.setdefault) method of the ordinary `dict` type." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def invert(mapping):\n", + " ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "invert(brazilian_players)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q13.4**: 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", + " ..." + ] + }, + { + "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": [ + "**Q13.5**: Write a `goals_by_player()` function: It 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 single dictionary comprehension." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def goals_by_player(players):\n", + " ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "goals_by_player(players)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q13.6**: Write a *dictionary comprehension* to filter out the players who did *not* score from the preceding result. Then, write a *set comprehension* that does the same but discards the number of goals scored.\n", + "\n", + "Hints: Reference the `goals_by_player()` function from before." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "{...}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "{...}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q13.7**: Write a `all_goals()` function: It 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()](https://docs.python.org/3/library/functions.html#sorted) function or the `list` type's [sort()](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", + " ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "all_goals(players)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q13.8**: Lastly, write a `summary()` function: It 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", + " ..." + ] + }, + { + "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.7.3" + }, + "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": 2 +} From 615c3f1cc34c62a39e65e5b796c59d4864312244 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 20 Nov 2019 11:05:04 +0100 Subject: [PATCH 052/242] Bump version number --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 333c176..f25a893 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "intro-to-python" -version = "0.5.0" +version = "0.6.0" description = "An introduction to Python and programming for wanna-be data scientists" authors = ["Alexander Hess "] license = "MIT" From 8af0280271220c2b384b9596a82883c98b416003 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 28 Jan 2020 11:07:14 +0100 Subject: [PATCH 053/242] Update the Python version in README.md --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 97c9120..a227cb2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -**Important**: The notebooks are being added throughout the fall semester of 2019! +**Important**: The notebooks are being updated and amended throughout the +spring semester of 2020! # An Introduction to Python and Programming @@ -50,7 +51,7 @@ It is only expected that the student has: ## Installation -To follow this course, a working installation of **Python 3.6** or higher is +To follow this course, a working installation of **Python 3.7** or higher is expected. A popular and beginner friendly way is to install the [Anaconda Distribution]( @@ -58,7 +59,7 @@ https://www.anaconda.com/distribution/) that not only ships Python but comes pre-packaged with a lot of third-party libraries from the so-called "scientific stack". Just go to the [download](https://www.anaconda.com/distribution/#download-section) -section and install the latest version (i.e., *2019-07* with Python 3.7 at the +section and install the latest version (i.e., *2019-10* with Python 3.7 at the time of this writing) for your operating system. Then, among others, you will find an entry "Jupyter Notebook" in your start @@ -84,7 +85,7 @@ used without a mouse by typing commands into it. -Assuming that you already have a working version of Python 3.6 or higher +Assuming that you already have a working version of Python 3.7 or higher installed (cf., the official [download page](https://www.python.org/downloads/)), the following summarizes the commands to be typed into a terminal emulator to get the course materials up and running on a local machine without Anaconda. From e24cf31104dda53acf752414987183517b40f695 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 28 Jan 2020 10:48:37 +0100 Subject: [PATCH 054/242] Restructure *.ipynb files - split review and exercises into files on their own - update the contents overviews to include links to reviews and exercises --- ...t_up.ipynb => 00_start_up_00_content.ipynb | 35 +- ...cises.ipynb => 00_start_up_10_review.ipynb | 81 +- 00_start_up_20_exercises.ipynb | 115 ++ ...ents.ipynb => 01_elements_00_content.ipynb | 199 ++-- ...cises.ipynb => 01_elements_10_review.ipynb | 96 +- 01_elements_20_exercises.ipynb | 139 +++ ...ons.ipynb => 02_functions_00_content.ipynb | 564 +++++---- ...ises.ipynb => 02_functions_10_review.ipynb | 192 +-- 02_functions_20_exercises.ipynb | 234 ++++ ....ipynb => 03_conditionals_00_content.ipynb | 357 +++--- 03_conditionals_10_review.ipynb | 214 ++++ ...pynb => 03_conditionals_20_exercises.ipynb | 195 +--- ...ion.ipynb => 04_iteration_00_content.ipynb | 162 ++- 04_iteration_10_review.ipynb | 281 +++++ ...s.ipynb => 04_iteration_20_exercises.ipynb | 310 +---- ...mbers.ipynb => 05_numbers_00_content.ipynb | 1032 ++++++++--------- 05_numbers_10_review.ipynb | 347 ++++++ 05_numbers_20_exercises.ipynb | 412 +++++++ 05_numbers_review_and_exercises.ipynb | 706 ----------- 06_text.ipynb => 06_text_00_content.ipynb | 586 ++++++---- ...exercises.ipynb => 06_text_10_review.ipynb | 8 +- ...ces.ipynb => 07_sequences_00_content.ipynb | 109 +- 07_sequences_10_review.ipynb | 397 +++++++ ...s.ipynb => 07_sequences_20_exercises.ipynb | 438 +------ ...ings.ipynb => 08_mappings_00_content.ipynb | 175 +-- 08_mappings_10_review.ipynb | 261 +++++ ...es.ipynb => 08_mappings_20_exercises.ipynb | 242 +--- README.md | 51 +- 28 files changed, 4335 insertions(+), 3603 deletions(-) rename 00_start_up.ipynb => 00_start_up_00_content.ipynb (94%) rename 00_start_up_review_and_exercises.ipynb => 00_start_up_10_review.ipynb (60%) create mode 100644 00_start_up_20_exercises.ipynb rename 01_elements.ipynb => 01_elements_00_content.ipynb (97%) rename 01_elements_review_and_exercises.ipynb => 01_elements_10_review.ipynb (64%) create mode 100644 01_elements_20_exercises.ipynb rename 02_functions.ipynb => 02_functions_00_content.ipynb (91%) rename 02_functions_review_and_exercises.ipynb => 02_functions_10_review.ipynb (50%) create mode 100644 02_functions_20_exercises.ipynb rename 03_conditionals.ipynb => 03_conditionals_00_content.ipynb (95%) create mode 100644 03_conditionals_10_review.ipynb rename 03_conditionals_review_and_exercises.ipynb => 03_conditionals_20_exercises.ipynb (53%) rename 04_iteration.ipynb => 04_iteration_00_content.ipynb (96%) create mode 100644 04_iteration_10_review.ipynb rename 04_iteration_review_and_exercises.ipynb => 04_iteration_20_exercises.ipynb (64%) rename 05_numbers.ipynb => 05_numbers_00_content.ipynb (96%) create mode 100644 05_numbers_10_review.ipynb create mode 100644 05_numbers_20_exercises.ipynb delete mode 100644 05_numbers_review_and_exercises.ipynb rename 06_text.ipynb => 06_text_00_content.ipynb (90%) rename 06_text_review_and_exercises.ipynb => 06_text_10_review.ipynb (94%) rename 07_sequences.ipynb => 07_sequences_00_content.ipynb (97%) create mode 100644 07_sequences_10_review.ipynb rename 07_sequences_review_and_exercises.ipynb => 07_sequences_20_exercises.ipynb (70%) rename 08_mappings.ipynb => 08_mappings_00_content.ipynb (96%) create mode 100644 08_mappings_10_review.ipynb rename 08_mappings_review_and_exercises.ipynb => 08_mappings_20_exercises.ipynb (56%) diff --git a/00_start_up.ipynb b/00_start_up_00_content.ipynb similarity index 94% rename from 00_start_up.ipynb rename to 00_start_up_00_content.ipynb index 9aed12d..6e3d84a 100644 --- a/00_start_up.ipynb +++ b/00_start_up_00_content.ipynb @@ -152,9 +152,9 @@ } }, "source": [ - "To \"read\" this book in the most meaningful way, a working installation of **Python 3.6** or higher is needed.\n", + "To \"read\" this book in the most meaningful way, a working installation of **Python 3.7** or higher is expected.\n", "\n", - "A popular and beginner-friendly way is to install the [Anaconda Distribution](https://www.anaconda.com/distribution/) that not only ships Python and the standard library but comes pre-packaged with a lot of third-party libraries from the so-called \"scientific stack.\" Just go to the [download](https://www.anaconda.com/download/) page and install the latest version (i.e., *2019-07* with Python 3.7 at the time of this writing) for your operating system.\n", + "A popular and beginner-friendly way is to install the [Anaconda Distribution](https://www.anaconda.com/distribution/) that not only ships Python and the standard library but comes pre-packaged with a lot of third-party libraries from the so-called \"scientific stack.\" Just go to the [download](https://www.anaconda.com/download/) page and install the latest version (i.e., *2019-10* with Python 3.7 at the time of this writing) for your operating system.\n", "\n", "Then, among others, you find an entry \"Jupyter Notebook\" in your start menu like below. Click on it to open a new tab in your web browser where you can switch between folders as you could in your computer's default file browser." ] @@ -330,7 +330,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Python 3.7.3\r\n" + "Python 3.7.4\n" ] } ], @@ -649,7 +649,7 @@ } }, "source": [ - "Even popular 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/)." + "[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/)." ] }, { @@ -771,14 +771,14 @@ } }, "source": [ - "**Part 1: Expressing Logic**\n", + "**Part A: Expressing Logic**\n", "\n", "- What is a programming language? What kind of words exist?\n", - " 1. [Elements of a Program](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb)\n", - " 2. [Functions & Modularization](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb)\n", + " - *Chapter 1*: [Elements of a Program](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb)\n", + " - *Chapter 2*: [Functions & Modularization](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions_00_content.ipynb)\n", "- What is the flow of execution? How can we form sentences from words?\n", - " 3. [Conditionals & Exceptions](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb)\n", - " 4. [Recursion & Looping](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb)" + " - *Chapter 3*: [Conditionals & Exceptions](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_00_content.ipynb)\n", + " - *Chapter 4*: [Recursion & Looping](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_content.ipynb)" ] }, { @@ -789,15 +789,16 @@ } }, "source": [ - "**Part 2: Managing Data and Memory**\n", + "**Part B: Managing Data and Memory**\n", "\n", "- How is data stored in memory?\n", - " 5. [Bits & Numbers](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb)\n", - " 6. [Bytes & Text](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb)\n", - " 7. [Sequential Data](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb)\n", - " 8. [Mappings & Sets](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings.ipynb)\n", + " - *Chapter 5*: [Bits & Numbers](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers_00_content.ipynb)\n", + " - *Chapter 6*: [Bytes & Text](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text_00_content.ipynb)\n", + " - *Chapter 7*: [Sequential Data](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb)\n", + " - *Chapter 8*: [Mappings & Sets](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings_00_content.ipynb)\n", + " - *Chapter 9*: [Arrays & Dataframes](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/09_arrays_00_content.ipynb)\n", "- How can we create custom data types?\n", - " 9. Object-Orientation" + " - *Chapter 10*: Object-Orientation" ] }, { @@ -863,7 +864,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.4" }, "livereveal": { "auto_select": "code", @@ -891,5 +892,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/00_start_up_review_and_exercises.ipynb b/00_start_up_10_review.ipynb similarity index 60% rename from 00_start_up_review_and_exercises.ipynb rename to 00_start_up_10_review.ipynb index 109a2fc..e8cb215 100644 --- a/00_start_up_review_and_exercises.ipynb +++ b/00_start_up_10_review.ipynb @@ -11,14 +11,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Content Review" + "## Review" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Read [Chapter 0](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb) of the book. Then work through the ten review questions." + "Read [Chapter 0](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up_00_content.ipynb) of the book. Then, work through the questions below." ] }, { @@ -32,14 +32,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Answer the following questions briefly with *at most* 300 characters per question!" + "Answer the following questions *briefly*!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "**Q1**: Describe the difference between the terms *programming* and *computer science*!" + "**Q1**: Describe the difference between the terms **programming** and **computer science**!" ] }, { @@ -53,7 +53,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q2**: Explain what is a *pull request* and elaborate on how this concept fits a *distributed* organization of work!" + "**Q2**: Explain what is a **pull request** and elaborate on how this concept fits a *distributed* organization of work!" ] }, { @@ -67,7 +67,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q3**: In what sense are open-source communities democracies? How are they near-\"perfect\" [meritocracies](https://en.wikipedia.org/wiki/Meritocracy)? How is open-source software development similar to academia?" + "**Q3**: In what sense are **open-source** communities democracies? How are they near-perfect [meritocracies](https://en.wikipedia.org/wiki/Meritocracy)? How is open-source software development similar to academia?" ] }, { @@ -81,7 +81,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q4**: What is a significant advantage of a \"slow\" programming language like Python over a faster one like C?" + "**Q4**: What is a *significant* advantage of a \"slow\" programming language like Python over a faster one like C?" ] }, { @@ -182,69 +182,6 @@ "**Q10**: The primary purpose of PEPs is to regulate how code should be documented and styled." ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## \"Coding\" Exercises" - ] - }, - { - "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": [ - "**Q11**: 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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q12**: 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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q13**: Integrate this image (https://i.ytimg.com/vi/-BoSRlzy9c4/maxresdefault.jpg) of the delicious dessert **milk rice** into this notebook." - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -269,7 +206,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.4" }, "toc": { "base_numbering": 1, @@ -286,5 +223,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/00_start_up_20_exercises.ipynb b/00_start_up_20_exercises.ipynb new file mode 100644 index 0000000..afd7f26 --- /dev/null +++ b/00_start_up_20_exercises.ipynb @@ -0,0 +1,115 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 0: Start up" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## \"Coding\" Exercises" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read [Chapter 0](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up_00_content.ipynb) of the book. Then, work through the exercises below." + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: Integrate this image (https://i.ytimg.com/vi/-BoSRlzy9c4/maxresdefault.jpg) of the delicious dessert **milk rice** into this notebook." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/01_elements.ipynb b/01_elements_00_content.ipynb similarity index 97% rename from 01_elements.ipynb rename to 01_elements_00_content.ipynb index 07d6774..38b63b1 100644 --- a/01_elements.ipynb +++ b/01_elements_00_content.ipynb @@ -936,9 +936,9 @@ "source": [ "Python is a so-called **object-oriented** language, which is a paradigm of organizing a program's memory.\n", "\n", - "An **object** may be viewed as a \"bag\" of $0$s and $1$s in a distinct memory location. The concrete $0$s and $1$s in a bag portray the idea of the object's **value**, and there exist different **types** of bags that each come with associated rules as to how the $0$s and $1$s are interpreted and may be worked with.\n", + "An **object** may be viewed as a \"bag\" of $0$s and $1$s in a given memory location. The $0$s and $1$s in a bag make up the object's **value**. There exist different **types** of bags: Each type comes with distinct rules how the $0$s and $1$s are interpreted and may be worked with.\n", "\n", - "So, an object *always* has **three** main characteristics. Let's look at the following examples and work them out." + "So, an object *always* has *three* main characteristics. Let's look at the following examples and work them out." ] }, { @@ -990,7 +990,7 @@ { "data": { "text/plain": [ - "140398796190992" + "140173037405648" ] }, "execution_count": 28, @@ -1014,7 +1014,7 @@ { "data": { "text/plain": [ - "140398796375888" + "140173037405584" ] }, "execution_count": 29, @@ -1038,7 +1038,7 @@ { "data": { "text/plain": [ - "140398796056176" + "140173037234160" ] }, "execution_count": 30, @@ -1082,7 +1082,7 @@ } }, "source": [ - "`a` and `d` indeed have the same value as is checked with the **equality operator** `==`. The resulting `True` (and the `False` further below) is yet another data type, a so-called **boolean**. We look into them closely in [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb#Boolean-Expressions)." + "`a` and `d` indeed have the same value as is checked with the **equality operator** `==`. The resulting `True` (and the `False` further below) is yet another data type, a so-called **boolean**. We look into them closely in [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_00_content.ipynb#Boolean-Expressions)." ] }, { @@ -1259,7 +1259,7 @@ "source": [ "Different types imply different behaviors for the objects. The `b` object, for example, can be \"asked\" if it could also be interpreted as an `int` with the [is_integer()](https://docs.python.org/3/library/stdtypes.html#float.is_integer) \"functionality\" that comes with every `float` object.\n", "\n", - "Formally, we call such type-specific functionalities **methods** (to differentiate them from functions) and we formally introduce them in Chapter 9. For now, it suffices to know that we access them using the **dot operator** `.`. Of course, `b` could be converted into an `int`, which the boolean value `True` tells us." + "Formally, we call such type-specific functionalities **methods** (to differentiate them from functions) and we formally introduce them in Chapter 10. For now, it suffices to know that we access them using the **dot operator** `.`. Of course, `b` could be converted into an `int`, which the boolean value `True` tells us." ] }, { @@ -1448,7 +1448,7 @@ } }, "source": [ - "Almost trivially, every object also has a value to which it \"evaluates\" when referenced. We think of the value as the **conceptual idea** of what the $0$s and $1$s in memory mean to humans as machines cannot see beyond $0$s and $1$s.\n", + "Almost trivially, every object also has a value to which it \"evaluates\" when referenced. We think of the value as the **conceptual idea** of what the $0$s and $1$s in the bag mean to *humans* as machines cannot see beyond $0$s and $1$s.\n", "\n", "For built-in data types, Python prints out the object's value as a so-called **[literal](https://docs.python.org/3/reference/lexical_analysis.html#literals)**: This means that we can copy and paste the output back into a code cell to create a *new* object with the *same* value." ] @@ -1854,7 +1854,7 @@ " " ], "text/plain": [ - "" + "" ] }, "execution_count": 52, @@ -1875,7 +1875,7 @@ } }, "source": [ - "For example, while the above code to calculate the average of the even numbers in `[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]` is correct, a Pythonista would rewrite it in a more \"Pythonic\" way and use the [sum()](https://docs.python.org/3/library/functions.html#sum) and [len()](https://docs.python.org/3/library/functions.html#len) (= \"length\") [built-in functions](https://docs.python.org/3/library/functions.html) (cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb#Built-in-Functions)) as well as a so-called **list comprehension** (cf., [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#List-Comprehensions)). Pythonic code runs faster in many cases and is less error-prone." + "For example, while the above code to calculate the average of the even numbers in `[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]` is correct, a Pythonista would rewrite it in a more \"Pythonic\" way and use the [sum()](https://docs.python.org/3/library/functions.html#sum) and [len()](https://docs.python.org/3/library/functions.html#len) (= \"length\") [built-in functions](https://docs.python.org/3/library/functions.html) (cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions_00_content.ipynb#Built-in-Functions)) as well as a so-called **list comprehension** (cf., [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb#List-Comprehensions)). Pythonic code runs faster in many cases and is less error-prone." ] }, { @@ -2077,7 +2077,7 @@ "\n", "At the same time, for a beginner's course, it is often easier to code linearly.\n", "\n", - "In real data science projects, one would probably employ a mixed approach and put reusable code into so-called Python modules (i.e., *.py* files; cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb#Local-Modules-and-Packages)) and then use Jupyter notebooks to build up a linear report or storyline for a business argument to be made." + "In real data science projects, one would probably employ a mixed approach and put reusable code into so-called Python modules (i.e., *.py* files; cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions_00_content.ipynb#Local-Modules-and-Packages)) and then use Jupyter notebooks to build up a linear report or storyline for a business argument to be made." ] }, { @@ -2367,7 +2367,7 @@ } }, "source": [ - "Some variables magically exist when we start a Python process or are added by Jupyter. We may safely ignore the former until Chapter 9 and the latter for good." + "Some variables magically exist when we start a Python process or are added by Jupyter. We may safely ignore the former until Chapter 10 and the latter for good." ] }, { @@ -2818,7 +2818,7 @@ "source": [ "Let's change the first element of `x`.\n", "\n", - "[Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#The-list-Type) discusses lists in more depth. For now, let's view a `list` object as some sort of **container** that holds an arbitrary number of references to other objects and treat the brackets `[]` attached to it as just another operator, called the **indexing operator**. `x[0]` instructs Python to first follow the reference from the global list of all names to the `x` object. Then, it follows the first reference it finds there to the `1` object. The indexing operator must be an operator as we merely read the first element and do not change anything in memory.\n", + "[Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb#The-list-Type) discusses lists in more depth. For now, let's view a `list` object as some sort of **container** that holds an arbitrary number of references to other objects and treat the brackets `[]` attached to it as just another operator, called the **indexing operator**. `x[0]` instructs Python to first follow the reference from the global list of all names to the `x` object. Then, it follows the first reference it finds there to the `1` object. The indexing operator must be an operator as we merely read the first element and do not change anything in memory.\n", "\n", "Note how Python **begins counting at 0**. This is not the case for many other languages, for example, [MATLAB](https://en.wikipedia.org/wiki/MATLAB), [R](https://en.wikipedia.org/wiki/R_%28programming_language%29), or [Stata](https://en.wikipedia.org/wiki/Stata). To understand why this makes sense, see this short [note](https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html) by one of the all-time greats in computer science, the late [Edsger Dijkstra](https://en.wikipedia.org/wiki/Edsger_W._Dijkstra)." ] @@ -3182,65 +3182,6 @@ "__name__" ] }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "### The big Picture" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "This PyCon talk by [Ned Batchelder](https://nedbatchelder.com/), a well-known Pythonista and the organizer of the [Python User Group](https://www.meetup.com/bostonpython/) in Boston, summarizes all situations where some sort of assignment is done in Python. The content is intermediate, and, thus, it might be worthwhile to come back to this talk at a later point in time. However, the contents should be known by everyone claiming to be proficient in Python." - ] - }, - { - "cell_type": "code", - "execution_count": 95, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "data": { - "image/jpeg": "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\n", - "text/html": [ - "\n", - " \n", - " " - ], - "text/plain": [ - "" - ] - }, - "execution_count": 95, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from IPython.display import YouTubeVideo\n", - "YouTubeVideo(\"_AEJHKGk9ns\", width=\"60%\")" - ] - }, { "cell_type": "markdown", "metadata": { @@ -3271,7 +3212,7 @@ }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 95, "metadata": { "slideshow": { "slide_type": "slide" @@ -3284,7 +3225,7 @@ "123" ] }, - "execution_count": 96, + "execution_count": 95, "metadata": {}, "output_type": "execute_result" } @@ -3295,7 +3236,7 @@ }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 96, "metadata": {}, "outputs": [ { @@ -3304,7 +3245,7 @@ "42" ] }, - "execution_count": 97, + "execution_count": 96, "metadata": {}, "output_type": "execute_result" } @@ -3322,7 +3263,7 @@ }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 97, "metadata": { "slideshow": { "slide_type": "-" @@ -3335,7 +3276,7 @@ "165" ] }, - "execution_count": 98, + "execution_count": 97, "metadata": {}, "output_type": "execute_result" } @@ -3357,7 +3298,7 @@ }, { "cell_type": "code", - "execution_count": 99, + "execution_count": 98, "metadata": { "slideshow": { "slide_type": "-" @@ -3370,7 +3311,7 @@ "4492125" ] }, - "execution_count": 99, + "execution_count": 98, "metadata": {}, "output_type": "execute_result" } @@ -3392,7 +3333,7 @@ }, { "cell_type": "code", - "execution_count": 100, + "execution_count": 99, "metadata": { "slideshow": { "slide_type": "-" @@ -3405,7 +3346,7 @@ "3" ] }, - "execution_count": 100, + "execution_count": 99, "metadata": {}, "output_type": "execute_result" } @@ -3427,7 +3368,7 @@ }, { "cell_type": "code", - "execution_count": 101, + "execution_count": 100, "metadata": { "slideshow": { "slide_type": "-" @@ -3440,7 +3381,7 @@ "104" ] }, - "execution_count": 101, + "execution_count": 100, "metadata": {}, "output_type": "execute_result" } @@ -3473,7 +3414,7 @@ }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 101, "metadata": { "slideshow": { "slide_type": "slide" @@ -3487,7 +3428,7 @@ }, { "cell_type": "code", - "execution_count": 103, + "execution_count": 102, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3500,7 +3441,7 @@ "'Hi class'" ] }, - "execution_count": 103, + "execution_count": 102, "metadata": {}, "output_type": "execute_result" } @@ -3522,7 +3463,7 @@ }, { "cell_type": "code", - "execution_count": 104, + "execution_count": 103, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3535,7 +3476,7 @@ "'Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi '" ] }, - "execution_count": 104, + "execution_count": 103, "metadata": {}, "output_type": "execute_result" } @@ -3570,7 +3511,7 @@ }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 104, "metadata": { "slideshow": { "slide_type": "slide" @@ -3583,7 +3524,7 @@ }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 105, "metadata": { "slideshow": { "slide_type": "-" @@ -3607,7 +3548,7 @@ }, { "cell_type": "code", - "execution_count": 107, + "execution_count": 106, "metadata": { "slideshow": { "slide_type": "skip" @@ -3654,7 +3595,7 @@ }, { "cell_type": "code", - "execution_count": 108, + "execution_count": 107, "metadata": { "slideshow": { "slide_type": "slide" @@ -3682,7 +3623,7 @@ }, { "cell_type": "code", - "execution_count": 109, + "execution_count": 108, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3695,7 +3636,7 @@ }, { "cell_type": "code", - "execution_count": 110, + "execution_count": 109, "metadata": { "slideshow": { "slide_type": "-" @@ -3751,7 +3692,7 @@ " - 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 9)\n", + " - built-in objects (incl. **literals**) vs. user-defined objects (cf., Chapter 10)\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", @@ -3786,18 +3727,76 @@ " - ignored by Python\n", "\n", "\n", - "- functions (cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb))\n", + "- functions (cf., [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/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](https://docs.python.org/3/library/functions.html) like [print()](https://docs.python.org/3/library/functions.html#print), [sum()](https://docs.python.org/3/library/functions.html#sum), or [len()](https://docs.python.org/3/library/functions.html#len)\n", "\n", "\n", - "- flow control (cf., [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb))\n", + "- flow control (cf., [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_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)" ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## Further Resources" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This PyCon 2015 talk by [Ned Batchelder](https://nedbatchelder.com/), a well-known Pythonista and the organizer of the [Python User Group](https://www.meetup.com/bostonpython/) in Boston, summarizes all situations where some sort of assignment is done in Python. The content is intermediate, and, thus, it might be worthwhile to come back to this talk at a later point in time. However, the contents should be known by everyone claiming to be proficient in Python." + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 110, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"_AEJHKGk9ns\", width=\"60%\")" + ] } ], "metadata": { @@ -3816,7 +3815,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.4" }, "livereveal": { "auto_select": "code", @@ -3844,5 +3843,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/01_elements_review_and_exercises.ipynb b/01_elements_10_review.ipynb similarity index 64% rename from 01_elements_review_and_exercises.ipynb rename to 01_elements_10_review.ipynb index 21ba4c5..1c2ee76 100644 --- a/01_elements_review_and_exercises.ipynb +++ b/01_elements_10_review.ipynb @@ -19,7 +19,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Read [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) of the book. Then work through the ten review questions." + "Read [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb) of the book. Then, work through the questions below." ] }, { @@ -33,7 +33,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Answer the following questions briefly with *at most* 300 characters per question!" + "Answer the following questions *briefly*!" ] }, { @@ -152,7 +152,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q8**: The **Zen of Python** has a high opinion on Indian genius programmers." + "**Q8**: The **Zen of Python** talks about Indian genius programmers." ] }, { @@ -189,92 +189,6 @@ "source": [ " " ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Coding Exercises" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Printing Output" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q11.1**: Read about the [print()](https://docs.python.org/3/library/functions.html#print) built-in. How can you use it to print both `greeting` and `audience` *without* concatenating the two strings with the `+` operator?\n", - "\n", - "Hint: The `*objects` in the documentation implies that we can insert several comma-seperated variables." - ] - }, - { - "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": [ - "**Q11.2**: What does the `sep=\" \"` mean in the documentation? Use it to print out the three names in `first`, `second`, and `third` on one line seperated by commas with one [print()](https://docs.python.org/3/library/functions.html#print) statement." - ] - }, - { - "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": [ - "**Q11.3**: Lastly, what does the `end=\"\\n\"` mean in the documentation? Use it in the `for`-loop to print the numbers 1 through 10 in just one line." - ] - }, - { - "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": { @@ -293,7 +207,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.4" }, "toc": { "base_numbering": 1, @@ -310,5 +224,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/01_elements_20_exercises.ipynb b/01_elements_20_exercises.ipynb new file mode 100644 index 0000000..e34097c --- /dev/null +++ b/01_elements_20_exercises.ipynb @@ -0,0 +1,139 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "# Chapter 1: Elements of a Program" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Coding Exercises" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb) of the book. Then, work through the exercises below." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Printing Output" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1.1**: Read about the [print()](https://docs.python.org/3/library/functions.html#print) built-in. How can you use it to print both `greeting` and `audience` *without* concatenating the two strings with the `+` operator?\n", + "\n", + "Hint: The `*objects` in the documentation implies that we can insert several comma-seperated variables." + ] + }, + { + "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": [ + "**Q1.2**: What does the `sep=\" \"` mean in the documentation? Use it to print out the three names in `first`, `second`, and `third` on one line seperated by commas with one [print()](https://docs.python.org/3/library/functions.html#print) statement." + ] + }, + { + "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": [ + "**Q1.3**: Lastly, what does the `end=\"\\n\"` mean in the documentation? Use it in the `for`-loop to print the numbers 1 through 10 in just one line." + ] + }, + { + "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.7.4" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/02_functions.ipynb b/02_functions_00_content.ipynb similarity index 91% rename from 02_functions.ipynb rename to 02_functions_00_content.ipynb index 39d7084..d0000d8 100644 --- a/02_functions.ipynb +++ b/02_functions_00_content.ipynb @@ -19,7 +19,7 @@ } }, "source": [ - "In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Example:-Averaging-Even-Numbers), we typed the code to calculate the average of the even numbers in a list into several code cells. Then, we executed them one after another. We had no way of **reusing** the code except for either executing cells multiple times or copying and pasting their contents into other cells. And, whenever we find ourselves doing repetitive manual work, we can be sure that there must be a way of automating what we are doing.\n", + "In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#Example:-Averaging-Even-Numbers), we typed the code to calculate the average of the even numbers in a list into several code cells. Then, we executed them one after another. We had no way of **reusing** the code except for either executing cells multiple times or copying and pasting their contents into other cells. And, whenever we find ourselves doing repetitive manual work, we can be sure that there must be a way of automating what we are doing.\n", "\n", "At the same time, we executed built-in functions (e.g., [print()](https://docs.python.org/3/library/functions.html#print), [sum()](https://docs.python.org/3/library/functions.html#sum), [len()](https://docs.python.org/3/library/functions.html#len), [id()](https://docs.python.org/3/library/functions.html#id), or [type()](https://docs.python.org/3/library/functions.html#type)) that obviously must be reusing the same parts inside core Python every time we use them.\n", "\n", @@ -45,7 +45,7 @@ } }, "source": [ - "So-called **[user-defined functions](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)** may be created with the `def` statement. To extend an already familiar example, we reuse the introductory example from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Best-Practices) in its final Pythonic version and transform it into the function `average_evens()` below. \n", + "So-called **[user-defined functions](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)** may be created with the `def` statement. To extend an already familiar example, we reuse the introductory example from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#Best-Practices) in its final Pythonic version and transform it into the function `average_evens()` below. \n", "\n", "A function's **name** must be chosen according to the same naming rules as ordinary variables as Python manages function names like variables. In this book, we further adopt the convention of ending function names with parentheses `()` in text cells for faster comprehension when reading (i.e., `average_evens()` vs. `average_evens`). These are *not* part of the name but must always be written out in the `def` statement for syntactic reasons.\n", "\n", @@ -57,7 +57,7 @@ "\n", "A function may come with an *explicit* **return value** (i.e., \"result\" or \"output\") specified with the `return` statement (cf., [reference](https://docs.python.org/3/reference/simple_stmts.html#the-return-statement)): Functions that have one are considered **fruitful**; otherwise, they are **void**. Functions of the latter kind are still useful because of their **side effects** as, for example, the built-in [print()](https://docs.python.org/3/library/functions.html#print) function. Strictly speaking, they also have an *implicit* return value of `None`.\n", "\n", - "A function should define a **docstring** that describes what it does in a short subject line, what parameters it expects (i.e., their types), and what it returns (if anything). A docstring is a syntactically valid multi-line string (i.e., type `str`) defined within **triple-double quotes** `\"\"\"`. Strings are covered in depth in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb#The-str-Type). Widely adopted standards as to how to format a docstring are [PEP 257](https://www.python.org/dev/peps/pep-0257/) and section 3.8 in [Google's Python Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md)." + "A function should define a **docstring** that describes what it does in a short subject line, what parameters it expects (i.e., their types), and what it returns (if anything). A docstring is a syntactically valid multi-line string (i.e., type `str`) defined within **triple-double quotes** `\"\"\"`. Strings are covered in depth in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text_00_content.ipynb#The-str-Type). Widely adopted standards as to how to format a docstring are [PEP 257](https://www.python.org/dev/peps/pep-0257/) and section 3.8 in [Google's Python Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md)." ] }, { @@ -142,7 +142,7 @@ { "data": { "text/plain": [ - "140384699277792" + "140371816700960" ] }, "execution_count": 3, @@ -241,7 +241,27 @@ "slide_type": "skip" } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "\u001b[0;31mSignature:\u001b[0m \u001b[0maverage_evens\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnumbers\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mDocstring:\u001b[0m\n", + "Calculate the average of all even numbers in a list.\n", + "\n", + "Args:\n", + " numbers (list): a list of numbers; may be integers or floats\n", + "\n", + "Returns:\n", + " float: average\n", + "\u001b[0;31mFile:\u001b[0m ~/repos/intro-to-python/\n", + "\u001b[0;31mType:\u001b[0m function\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "average_evens?" ] @@ -265,7 +285,32 @@ "slide_type": "skip" } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "\u001b[0;31mSignature:\u001b[0m \u001b[0maverage_evens\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnumbers\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mSource:\u001b[0m \n", + "\u001b[0;32mdef\u001b[0m \u001b[0maverage_evens\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnumbers\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Calculate the average of all even numbers in a list.\u001b[0m\n", + "\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m Args:\u001b[0m\n", + "\u001b[0;34m numbers (list): a list of numbers; may be integers or floats\u001b[0m\n", + "\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m Returns:\u001b[0m\n", + "\u001b[0;34m float: average\u001b[0m\n", + "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0mevens\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mn\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mn\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mnumbers\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0;36m2\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0maverage\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mevens\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mevens\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0maverage\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mFile:\u001b[0m ~/repos/intro-to-python/\n", + "\u001b[0;31mType:\u001b[0m function\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "average_evens??" ] @@ -337,12 +382,71 @@ } }, "source": [ - "The return value is commonly assigned to a variable for later reference. Otherwise, we would lose access to it in memory right away." + "If we are ever unsure if a variable references a `function` object that may be called, we can verify that with the built-in [callable()](https://docs.python.org/3/library/functions.html#callable) function that takes any object as its only argument and tells us `True` if that is **callable** and `False` otherwise." ] }, { "cell_type": "code", "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "callable(average_evens)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "callable(nums)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The return value is commonly assigned to a variable for later reference. Otherwise, we would lose access to it in memory right away." + ] + }, + { + "cell_type": "code", + "execution_count": 12, "metadata": { "slideshow": { "slide_type": "fragment" @@ -355,7 +459,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 13, "metadata": { "slideshow": { "slide_type": "-" @@ -368,7 +472,7 @@ "7.0" ] }, - "execution_count": 11, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -412,7 +516,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 14, "metadata": { "slideshow": { "slide_type": "slide" @@ -426,7 +530,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnumbers\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnumbers\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'numbers' is not defined" ] } @@ -437,7 +541,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 15, "metadata": { "slideshow": { "slide_type": "-" @@ -451,7 +555,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mevens\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mevens\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'evens' is not defined" ] } @@ -462,7 +566,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 16, "metadata": { "slideshow": { "slide_type": "skip" @@ -476,7 +580,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0maverage\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0maverage\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'average' is not defined" ] } @@ -520,7 +624,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 17, "metadata": { "slideshow": { "slide_type": "slide" @@ -555,7 +659,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 18, "metadata": { "slideshow": { "slide_type": "slide" @@ -568,7 +672,7 @@ "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" ] }, - "execution_count": 16, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -590,7 +694,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 19, "metadata": { "slideshow": { "slide_type": "-" @@ -603,7 +707,7 @@ "7.0" ] }, - "execution_count": 17, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } @@ -625,7 +729,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 20, "metadata": { "slideshow": { "slide_type": "-" @@ -638,7 +742,7 @@ "7.0" ] }, - "execution_count": 18, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -657,7 +761,7 @@ "source": [ "[PythonTutor](http://pythontutor.com/visualize.html#code=nums%20%3D%20%5B1,%202,%203,%204,%205,%206,%207,%208,%209,%2010,%2011,%2012%5D%0A%0Adef%20average_wrong%28numbers%29%3A%0A%20%20%20%20evens%20%3D%20%5Bn%20for%20n%20in%20nums%20if%20n%20%25%202%20%3D%3D%200%5D%0A%20%20%20%20average%20%3D%20sum%28evens%29%20/%20len%28evens%29%0A%20%20%20%20return%20average%0A%0Arv%20%3D%20average_wrong%28%5B123,%20456,%20789%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) is again helpful at visualizing the error interactively: Creating the `list` object `evens` eventually references takes *16* computational steps, namely two for managing the list comprehension, one for setting up an empty `list` object, *twelve* for filling it with elements derived from `nums` in the global scope (i.e., that is the error), and one to make `evens` reference it (cf., steps 6-21).\n", "\n", - "The frames logic shown by PythonTutor is the mechanism by which Python not only manages the names inside *one* function call but also for *many* potentially *simultaneous* calls, as revealed in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Trivial-Example:-Countdown). It is the reason why we may reuse the same names for the parameters and variables inside both `average_evens()` and `average_wrong()` without Python mixing them up. So, as we already read in the [Zen of Python](https://www.python.org/dev/peps/pep-0020/), \"namespaces are one honking great idea\" (cf., `import this`), and a frame is just a special kind of namespace." + "The frames logic shown by PythonTutor is the mechanism by which Python not only manages the names inside *one* function call but also for *many* potentially *simultaneous* calls, as revealed in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_content.ipynb#Trivial-Example:-Countdown). It is the reason why we may reuse the same names for the parameters and variables inside both `average_evens()` and `average_wrong()` without Python mixing them up. So, as we already read in the [Zen of Python](https://www.python.org/dev/peps/pep-0020/), \"namespaces are one honking great idea\" (cf., `import this`), and a frame is just a special kind of namespace." ] }, { @@ -686,7 +790,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 21, "metadata": { "slideshow": { "slide_type": "slide" @@ -722,7 +826,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 22, "metadata": { "slideshow": { "slide_type": "slide" @@ -735,7 +839,7 @@ "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" ] }, - "execution_count": 20, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } @@ -757,7 +861,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 23, "metadata": { "slideshow": { "slide_type": "-" @@ -770,7 +874,7 @@ "3.0" ] }, - "execution_count": 21, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -792,7 +896,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 24, "metadata": { "slideshow": { "slide_type": "-" @@ -805,7 +909,7 @@ "6.0" ] }, - "execution_count": 22, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -827,7 +931,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 25, "metadata": { "slideshow": { "slide_type": "-" @@ -840,7 +944,7 @@ "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" ] }, - "execution_count": 23, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } @@ -892,7 +996,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 26, "metadata": { "slideshow": { "slide_type": "slide" @@ -905,7 +1009,7 @@ "12" ] }, - "execution_count": 24, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -916,7 +1020,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 27, "metadata": { "slideshow": { "slide_type": "-" @@ -929,7 +1033,7 @@ "78" ] }, - "execution_count": 25, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -951,7 +1055,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 28, "metadata": { "slideshow": { "slide_type": "slide" @@ -964,7 +1068,7 @@ "7.0" ] }, - "execution_count": 26, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } @@ -975,7 +1079,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 29, "metadata": { "slideshow": { "slide_type": "-" @@ -988,7 +1092,7 @@ "7" ] }, - "execution_count": 27, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -999,7 +1103,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 30, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1012,7 +1116,7 @@ "7" ] }, - "execution_count": 28, + "execution_count": 30, "metadata": {}, "output_type": "execute_result" } @@ -1034,7 +1138,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 31, "metadata": { "slideshow": { "slide_type": "slide" @@ -1047,7 +1151,7 @@ "7" ] }, - "execution_count": 29, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } @@ -1058,7 +1162,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 32, "metadata": { "slideshow": { "slide_type": "-" @@ -1071,7 +1175,7 @@ "8" ] }, - "execution_count": 30, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -1093,7 +1197,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 33, "metadata": { "slideshow": { "slide_type": "slide" @@ -1107,7 +1211,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"seven\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"seven\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mValueError\u001b[0m: invalid literal for int() with base 10: 'seven'" ] } @@ -1129,7 +1233,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 34, "metadata": { "slideshow": { "slide_type": "skip" @@ -1142,7 +1246,7 @@ "42.0" ] }, - "execution_count": 32, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } @@ -1170,12 +1274,12 @@ } }, "source": [ - "So far, we have only specified one parameter in each of our user-defined functions. In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#%28Arithmetic%29-Operators), however, we saw the built-in function [divmod()](https://docs.python.org/3/library/functions.html#divmod) take two arguments. And, the order of the numbers passed in mattered! Whenever we call a function and list its arguments in a comma separated manner, we say that we pass in the arguments by position or refer to them as **positional arguments**." + "So far, we have only specified one parameter in each of our user-defined functions. In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#%28Arithmetic%29-Operators), however, we saw the built-in function [divmod()](https://docs.python.org/3/library/functions.html#divmod) take two arguments. And, the order of the numbers passed in mattered! Whenever we call a function and list its arguments in a comma separated manner, we say that we pass in the arguments by position or refer to them as **positional arguments**." ] }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 35, "metadata": { "slideshow": { "slide_type": "slide" @@ -1188,7 +1292,7 @@ "(4, 2)" ] }, - "execution_count": 33, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } @@ -1199,7 +1303,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 36, "metadata": { "slideshow": { "slide_type": "-" @@ -1212,7 +1316,7 @@ "(0, 10)" ] }, - "execution_count": 34, + "execution_count": 36, "metadata": {}, "output_type": "execute_result" } @@ -1234,7 +1338,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 37, "metadata": { "slideshow": { "slide_type": "slide" @@ -1271,7 +1375,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 38, "metadata": { "slideshow": { "slide_type": "slide" @@ -1284,7 +1388,7 @@ "14.0" ] }, - "execution_count": 36, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } @@ -1306,54 +1410,6 @@ "Luckily, we may also reference the formal parameter names as **keyword arguments**. We can even combine positional and keyword arguments in the same function call. Each of the following does the *same*." ] }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "14.0" - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "scaled_average_evens(nums, scalar=2)" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "14.0" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "scaled_average_evens(numbers=nums, scalar=2)" - ] - }, { "cell_type": "code", "execution_count": 39, @@ -1374,6 +1430,54 @@ "output_type": "execute_result" } ], + "source": [ + "scaled_average_evens(nums, scalar=2)" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "14.0" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scaled_average_evens(numbers=nums, scalar=2)" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "14.0" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "scaled_average_evens(scalar=2, numbers=nums)" ] @@ -1391,7 +1495,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 42, "metadata": { "slideshow": { "slide_type": "slide" @@ -1400,10 +1504,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "positional argument follows keyword argument (, line 1)", + "evalue": "positional argument follows keyword argument (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m scaled_average_evens(numbers=nums, 2)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m positional argument follows keyword argument\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m scaled_average_evens(numbers=nums, 2)\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m positional argument follows keyword argument\n" ] } ], @@ -1439,7 +1543,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 43, "metadata": { "slideshow": { "slide_type": "slide" @@ -1450,7 +1554,13 @@ "def scaled_average_evens(numbers, scalar):\n", " \"\"\"Calculate a scaled average of all even numbers in a list.\n", "\n", - " ...\n", + " Args:\n", + " numbers (list): a list of numbers; may be integers or floats\n", + " scalar (float): the scalar that multiplies the average\n", + " of the even numbers\n", + "\n", + " Returns:\n", + " float: scaled average\n", " \"\"\"\n", " evens = [n for n in numbers if n % 2 == 0]\n", " average = sum(evens) / len(evens)\n", @@ -1459,7 +1569,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 44, "metadata": { "slideshow": { "slide_type": "-" @@ -1470,7 +1580,11 @@ "def average_evens(numbers):\n", " \"\"\"Calculate the average of all even numbers in a list.\n", "\n", - " ...\n", + " Args:\n", + " numbers (list): a list of numbers; may be integers or floats\n", + "\n", + " Returns:\n", + " float: average\n", " \"\"\"\n", " return scaled_average_evens(numbers, scalar=1) # refactored to use the logic in scaled_average_evens()" ] @@ -1488,7 +1602,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 45, "metadata": { "slideshow": { "slide_type": "-" @@ -1501,7 +1615,7 @@ "7.0" ] }, - "execution_count": 43, + "execution_count": 45, "metadata": {}, "output_type": "execute_result" } @@ -1523,7 +1637,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 46, "metadata": { "slideshow": { "slide_type": "slide" @@ -1562,7 +1676,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 47, "metadata": { "slideshow": { "slide_type": "slide" @@ -1575,7 +1689,7 @@ "7.0" ] }, - "execution_count": 45, + "execution_count": 47, "metadata": {}, "output_type": "execute_result" } @@ -1586,7 +1700,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 48, "metadata": { "slideshow": { "slide_type": "-" @@ -1599,7 +1713,7 @@ "14.0" ] }, - "execution_count": 46, + "execution_count": 48, "metadata": {}, "output_type": "execute_result" } @@ -1610,7 +1724,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 49, "metadata": { "slideshow": { "slide_type": "-" @@ -1623,7 +1737,7 @@ "14.0" ] }, - "execution_count": 47, + "execution_count": 49, "metadata": {}, "output_type": "execute_result" } @@ -1658,7 +1772,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 50, "metadata": { "slideshow": { "slide_type": "slide" @@ -1695,7 +1809,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 51, "metadata": { "slideshow": { "slide_type": "slide" @@ -1708,7 +1822,7 @@ "7.0" ] }, - "execution_count": 49, + "execution_count": 51, "metadata": {}, "output_type": "execute_result" } @@ -1719,7 +1833,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 52, "metadata": { "slideshow": { "slide_type": "-" @@ -1732,7 +1846,7 @@ "14.0" ] }, - "execution_count": 50, + "execution_count": 52, "metadata": {}, "output_type": "execute_result" } @@ -1754,7 +1868,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 53, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1768,7 +1882,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0maverage_evens\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnums\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0maverage_evens\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnums\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: average_evens() takes 1 positional argument but 2 were given" ] } @@ -1811,7 +1925,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 54, "metadata": { "slideshow": { "slide_type": "slide" @@ -1824,7 +1938,7 @@ "(x)>" ] }, - "execution_count": 52, + "execution_count": 54, "metadata": {}, "output_type": "execute_result" } @@ -1850,7 +1964,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 55, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1874,7 +1988,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 56, "metadata": { "slideshow": { "slide_type": "-" @@ -1887,7 +2001,7 @@ "13" ] }, - "execution_count": 54, + "execution_count": 56, "metadata": {}, "output_type": "execute_result" } @@ -1909,7 +2023,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 57, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1922,7 +2036,7 @@ "42" ] }, - "execution_count": 55, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" } @@ -1941,7 +2055,7 @@ "source": [ "The main point of having functions without a name is to use them in a situation where we know ahead of time that we use the function only *once*.\n", "\n", - "Popular applications of lambda expressions occur in combination with the **map-filter-reduce** paradigm or when we do \"number crunching\" with **arrays** and **data frames**. We look at both in detail in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb)." + "Popular applications of lambda expressions occur in combination with the **map-filter-reduce** paradigm or when we do \"number crunching\" with **arrays** and **data frames**. We look at both in detail in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb)." ] }, { @@ -2026,7 +2140,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 58, "metadata": { "slideshow": { "slide_type": "slide" @@ -2050,7 +2164,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 59, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2060,10 +2174,10 @@ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 57, + "execution_count": 59, "metadata": {}, "output_type": "execute_result" } @@ -2074,7 +2188,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 60, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2084,10 +2198,10 @@ { "data": { "text/plain": [ - "140384754610488" + "140371899337520" ] }, - "execution_count": 58, + "execution_count": 60, "metadata": {}, "output_type": "execute_result" } @@ -2098,7 +2212,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 61, "metadata": { "slideshow": { "slide_type": "-" @@ -2111,7 +2225,7 @@ "module" ] }, - "execution_count": 59, + "execution_count": 61, "metadata": {}, "output_type": "execute_result" } @@ -2137,7 +2251,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 62, "metadata": { "slideshow": { "slide_type": "slide" @@ -2205,7 +2319,7 @@ " 'trunc']" ] }, - "execution_count": 60, + "execution_count": 62, "metadata": {}, "output_type": "execute_result" } @@ -2227,7 +2341,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 63, "metadata": { "slideshow": { "slide_type": "slide" @@ -2240,7 +2354,7 @@ "3.141592653589793" ] }, - "execution_count": 61, + "execution_count": 63, "metadata": {}, "output_type": "execute_result" } @@ -2251,7 +2365,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 64, "metadata": { "slideshow": { "slide_type": "-" @@ -2264,7 +2378,7 @@ "2.718281828459045" ] }, - "execution_count": 62, + "execution_count": 64, "metadata": {}, "output_type": "execute_result" } @@ -2275,7 +2389,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 65, "metadata": { "slideshow": { "slide_type": "slide" @@ -2288,7 +2402,7 @@ "" ] }, - "execution_count": 63, + "execution_count": 65, "metadata": {}, "output_type": "execute_result" } @@ -2299,7 +2413,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 66, "metadata": { "slideshow": { "slide_type": "-" @@ -2324,7 +2438,7 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 67, "metadata": { "slideshow": { "slide_type": "-" @@ -2337,7 +2451,7 @@ "1.4142135623730951" ] }, - "execution_count": 65, + "execution_count": 67, "metadata": {}, "output_type": "execute_result" } @@ -2356,14 +2470,14 @@ "source": [ "Observe how the arguments passed to functions do not need to be just variables or simple literals. Instead, we may pass in any *expression* that evaluates to a *new* object of the type the function expects.\n", "\n", - "So just as a reminder from the expression vs. statement discussion in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Expressions): An expression is *any* syntactically correct combination of variables and literals with operators. And the call operator `()` is yet another operator. So both of the next two code cells are just expressions! They have no permanent side effects in memory. We may execute them as often as we want *without* changing the state of the program (i.e., this Jupyter notebook).\n", + "So just as a reminder from the expression vs. statement discussion in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#Expressions): An expression is *any* syntactically correct combination of variables and literals with operators. And the call operator `()` is yet another operator. So both of the next two code cells are just expressions! They have no permanent side effects in memory. We may execute them as often as we want *without* changing the state of the program (i.e., this Jupyter notebook).\n", "\n", "So, regarding the very next cell in particular: Although the `2 ** 2` creates a *new* object `4` in memory that is then immediately passed into the [math.sqrt()](https://docs.python.org/3/library/math.html#math.sqrt) function, once that function call returns, \"all is lost\" and the newly created `4` object is forgotten again, as well as the return value of [math.sqrt()](https://docs.python.org/3/library/math.html#math.sqrt)." ] }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 68, "metadata": { "slideshow": { "slide_type": "-" @@ -2376,7 +2490,7 @@ "2.0" ] }, - "execution_count": 66, + "execution_count": 68, "metadata": {}, "output_type": "execute_result" } @@ -2398,7 +2512,7 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 69, "metadata": { "slideshow": { "slide_type": "-" @@ -2411,7 +2525,7 @@ "10.0" ] }, - "execution_count": 67, + "execution_count": 69, "metadata": {}, "output_type": "execute_result" } @@ -2435,7 +2549,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 70, "metadata": { "slideshow": { "slide_type": "skip" @@ -2448,7 +2562,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 71, "metadata": { "slideshow": { "slide_type": "skip" @@ -2461,7 +2575,7 @@ "4.0" ] }, - "execution_count": 69, + "execution_count": 71, "metadata": {}, "output_type": "execute_result" } @@ -2494,7 +2608,7 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 72, "metadata": { "slideshow": { "slide_type": "slide" @@ -2507,7 +2621,7 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 73, "metadata": { "slideshow": { "slide_type": "-" @@ -2517,10 +2631,10 @@ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 71, + "execution_count": 73, "metadata": {}, "output_type": "execute_result" } @@ -2537,12 +2651,12 @@ } }, "source": [ - "Besides the usual dunder-style attributes, the built-in [dir()](https://docs.python.org/3/library/functions.html#dir) function lists some attributes in an upper case naming convention and many others starting with a *single* underscore `_`. To understand the former, we must wait until Chapter 9, while the latter is explained further below." + "Besides the usual dunder-style attributes, the built-in [dir()](https://docs.python.org/3/library/functions.html#dir) function lists some attributes in an upper case naming convention and many others starting with a *single* underscore `_`. To understand the former, we must wait until Chapter 10, while the latter is explained further below." ] }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 74, "metadata": { "slideshow": { "slide_type": "slide" @@ -2616,7 +2730,7 @@ " 'weibullvariate']" ] }, - "execution_count": 72, + "execution_count": 74, "metadata": {}, "output_type": "execute_result" } @@ -2638,7 +2752,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 75, "metadata": { "slideshow": { "slide_type": "slide" @@ -2651,7 +2765,7 @@ "" ] }, - "execution_count": 73, + "execution_count": 75, "metadata": {}, "output_type": "execute_result" } @@ -2662,7 +2776,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 76, "metadata": { "slideshow": { "slide_type": "-" @@ -2687,7 +2801,7 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 77, "metadata": { "slideshow": { "slide_type": "-" @@ -2697,10 +2811,10 @@ { "data": { "text/plain": [ - "0.17184403163109208" + "0.64575874518665" ] }, - "execution_count": 75, + "execution_count": 77, "metadata": {}, "output_type": "execute_result" } @@ -2722,7 +2836,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 78, "metadata": { "slideshow": { "slide_type": "slide" @@ -2732,10 +2846,10 @@ { "data": { "text/plain": [ - ">" + ">" ] }, - "execution_count": 76, + "execution_count": 78, "metadata": {}, "output_type": "execute_result" } @@ -2746,7 +2860,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 79, "metadata": { "slideshow": { "slide_type": "-" @@ -2771,7 +2885,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 80, "metadata": { "slideshow": { "slide_type": "-" @@ -2781,10 +2895,10 @@ { "data": { "text/plain": [ - "2" + "12" ] }, - "execution_count": 78, + "execution_count": 80, "metadata": {}, "output_type": "execute_result" } @@ -2808,7 +2922,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 81, "metadata": { "slideshow": { "slide_type": "slide" @@ -2821,7 +2935,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 82, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2834,7 +2948,7 @@ "0.6394267984578837" ] }, - "execution_count": 80, + "execution_count": 82, "metadata": {}, "output_type": "execute_result" } @@ -2845,7 +2959,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 83, "metadata": { "slideshow": { "slide_type": "-" @@ -2858,7 +2972,7 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 84, "metadata": { "slideshow": { "slide_type": "-" @@ -2871,7 +2985,7 @@ "0.6394267984578837" ] }, - "execution_count": 82, + "execution_count": 84, "metadata": {}, "output_type": "execute_result" } @@ -2927,16 +3041,16 @@ } }, "source": [ - "[numpy](http://www.numpy.org/) is the de-facto standard in the Python world for handling **array-like** data. That is a fancy word for data that can be put into a matrix or vector format. We look at it in depth in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb).\n", + "[numpy](http://www.numpy.org/) is the de-facto standard in the Python world for handling **array-like** data. That is a fancy word for data that can be put into a matrix or vector format. We look at it in depth in [Chapter 9](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/09_arrays_00_content.ipynb).\n", "\n", - "As [numpy](http://www.numpy.org/) is *not* in the [standard library](https://docs.python.org/3/library/index.html), it must be *manually* installed, for example, with the [pip](https://pip.pypa.io/en/stable/) tool. As mentioned in [Chapter 0](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb#Markdown-vs.-Code-Cells), to execute terminal commands from within a Jupyter notebook, we start a code cell with an exclamation mark.\n", + "As [numpy](http://www.numpy.org/) is *not* in the [standard library](https://docs.python.org/3/library/index.html), it must be *manually* installed, for example, with the [pip](https://pip.pypa.io/en/stable/) tool. As mentioned in [Chapter 0](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up_00_content.ipynb#Markdown-vs.-Code-Cells), to execute terminal commands from within a Jupyter notebook, we start a code cell with an exclamation mark.\n", "\n", "If you are running this notebook with an installation of the [Anaconda Distribution](https://www.anaconda.com/distribution/), then [numpy](http://www.numpy.org/) is probably already installed. Running the cell below confirms that." ] }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 85, "metadata": { "slideshow": { "slide_type": "slide" @@ -2947,7 +3061,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Requirement already satisfied: numpy in /home/webartifex/.pyenv/versions/anaconda3-2019.07/lib/python3.7/site-packages (1.16.4)\r\n" + "Requirement already satisfied: numpy in /home/webartifex/.pyenv/versions/anaconda3-2019.10/lib/python3.7/site-packages (1.17.2)\n" ] } ], @@ -2968,7 +3082,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 86, "metadata": { "slideshow": { "slide_type": "slide" @@ -2992,7 +3106,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 87, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3002,10 +3116,10 @@ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 85, + "execution_count": 87, "metadata": {}, "output_type": "execute_result" } @@ -3027,7 +3141,7 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 88, "metadata": { "slideshow": { "slide_type": "slide" @@ -3040,7 +3154,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 89, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3053,7 +3167,7 @@ "array([ 7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4])" ] }, - "execution_count": 87, + "execution_count": 89, "metadata": {}, "output_type": "execute_result" } @@ -3064,7 +3178,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 90, "metadata": { "slideshow": { "slide_type": "-" @@ -3077,7 +3191,7 @@ "numpy.ndarray" ] }, - "execution_count": 88, + "execution_count": 90, "metadata": {}, "output_type": "execute_result" } @@ -3101,7 +3215,7 @@ }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 91, "metadata": { "slideshow": { "slide_type": "slide" @@ -3114,7 +3228,7 @@ "array([14, 22, 16, 10, 6, 24, 4, 12, 18, 20, 2, 8])" ] }, - "execution_count": 89, + "execution_count": 91, "metadata": {}, "output_type": "execute_result" } @@ -3136,7 +3250,7 @@ }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 92, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3149,7 +3263,7 @@ "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4, 7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" ] }, - "execution_count": 90, + "execution_count": 92, "metadata": {}, "output_type": "execute_result" } @@ -3171,7 +3285,7 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 93, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3184,7 +3298,7 @@ "78" ] }, - "execution_count": 91, + "execution_count": 93, "metadata": {}, "output_type": "execute_result" } @@ -3195,7 +3309,7 @@ }, { "cell_type": "code", - "execution_count": 92, + "execution_count": 94, "metadata": { "slideshow": { "slide_type": "-" @@ -3208,7 +3322,7 @@ "7" ] }, - "execution_count": 92, + "execution_count": 94, "metadata": {}, "output_type": "execute_result" } @@ -3247,7 +3361,7 @@ }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 95, "metadata": { "slideshow": { "slide_type": "slide" @@ -3260,7 +3374,7 @@ }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 96, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3273,7 +3387,7 @@ "" ] }, - "execution_count": 94, + "execution_count": 96, "metadata": {}, "output_type": "execute_result" } @@ -3299,7 +3413,7 @@ }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 97, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3324,7 +3438,7 @@ " 'average_odds']" ] }, - "execution_count": 95, + "execution_count": 97, "metadata": {}, "output_type": "execute_result" } @@ -3346,7 +3460,7 @@ }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 98, "metadata": { "slideshow": { "slide_type": "slide" @@ -3379,7 +3493,7 @@ }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 99, "metadata": { "slideshow": { "slide_type": "slide" @@ -3392,7 +3506,7 @@ "7.0" ] }, - "execution_count": 97, + "execution_count": 99, "metadata": {}, "output_type": "execute_result" } @@ -3403,7 +3517,7 @@ }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 100, "metadata": { "slideshow": { "slide_type": "-" @@ -3416,7 +3530,7 @@ "14.0" ] }, - "execution_count": 98, + "execution_count": 100, "metadata": {}, "output_type": "execute_result" } @@ -3433,7 +3547,7 @@ } }, "source": [ - "Packages are a generalization of modules, and we look at one in detail in Chapter 9. You may, however, already look at a [sample package](https://github.com/webartifex/intro-to-python/tree/master/sample_package) in the repository, which is nothing but a folder with *.py* files in it.\n", + "Packages are a generalization of modules, and we look at one in detail in Chapter 10. You may, however, already look at a [sample package](https://github.com/webartifex/intro-to-python/tree/master/sample_package) in the repository, which is nothing but a folder with *.py* files in it.\n", "\n", "As a further reading on modules and packages, we refer to the [official tutorial](https://docs.python.org/3/tutorial/modules.html)." ] @@ -3494,7 +3608,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.4" }, "livereveal": { "auto_select": "code", @@ -3522,5 +3636,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/02_functions_review_and_exercises.ipynb b/02_functions_10_review.ipynb similarity index 50% rename from 02_functions_review_and_exercises.ipynb rename to 02_functions_10_review.ipynb index ca631a7..a320a3d 100644 --- a/02_functions_review_and_exercises.ipynb +++ b/02_functions_10_review.ipynb @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Read [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) of the book. Then work through the ten review questions." + "Read [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions_00_content.ipynb) of the book. Then, work through the questions below." ] }, { @@ -32,14 +32,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Answer the following questions briefly with *at most* 300 characters per question!" + "Answer the following questions *briefly*!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "**Q1**: What property of the `def` statement makes it a **statement**? Is there a way to use an **expression** to create a function?" + "**Q1**: What behavior of the `def` statement makes it a **statement**? Is there a way to use an **expression** to create a function?" ] }, { @@ -182,188 +182,6 @@ "**Q10**: The [standard library](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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Coding Exercises" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Volume of a Sphere" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q11.1**: The [volume of a sphere](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. Use the [standard library](https://docs.python.org/3/library/index.html) to obtain a good approximation of $\\pi$." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import ..." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "r = ..." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q11.2**: 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. Document your work appropriately in a docstring according to [Google's Python Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def sphere_volume(...):\n", - " ..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q11.3**: 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": [ - "**Q11.4**: What observation do you make?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q11.5**: Using the [range()](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()](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": [ - "**Q11.6**: What lesson do you learn about the `float` type?" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -388,7 +206,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.4" }, "toc": { "base_numbering": 1, @@ -405,5 +223,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/02_functions_20_exercises.ipynb b/02_functions_20_exercises.ipynb new file mode 100644 index 0000000..0f449e4 --- /dev/null +++ b/02_functions_20_exercises.ipynb @@ -0,0 +1,234 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 2: Functions & Modularization" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Coding Exercises" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions_00_content.ipynb) of the book. Then, work through the exercises below." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Volume of a Sphere" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1.1**: The [volume of a sphere](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. Use the [standard library](https://docs.python.org/3/library/index.html) to obtain a good approximation of $\\pi$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "r = ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1.2**: 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. Document your work appropriately in a docstring according to [Google's Python Style Guide](https://github.com/google/styleguide/blob/gh-pages/pyguide.md)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def sphere_volume(...):\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1.3**: 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": [ + "**Q1.4**: What observation do you make?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1.5**: Using the [range()](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()](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": [ + "**Q1.6**: What lesson do you learn about the `float` type?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/03_conditionals.ipynb b/03_conditionals_00_content.ipynb similarity index 95% rename from 03_conditionals.ipynb rename to 03_conditionals_00_content.ipynb index 9ffe09e..a9113a3 100644 --- a/03_conditionals.ipynb +++ b/03_conditionals_00_content.ipynb @@ -19,7 +19,7 @@ } }, "source": [ - "We analyzed every aspect of the `average_evens()` function in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) except for the `if`-related parts. While it seems to do what we expect it to, there is a whole lot more we learn from taking it apart. In particular, the `if` may occur within both a **statement** or an **expression**, analogous as to how a noun in a natural language is *either* the subject of *or* an object in a sentence. What is common to both usages is that it leads to code being executed for *parts* of the input only. It is our first way of **controlling** the **flow of execution** in a program.\n", + "We analyzed every aspect of the `average_evens()` function in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions_00_content.ipynb) except for the `if`-related parts. While it seems to do what we expect it to, there is a whole lot more we learn from taking it apart. In particular, the `if` may occur within both a **statement** or an **expression**, analogous as to how a noun in a natural language is *either* the subject of *or* an object in a sentence. What is common to both usages is that it leads to code being executed for *parts* of the input only. It is our first way of **controlling** the **flow of execution** in a program.\n", "\n", "After deconstructing `if` in the first part of this chapter, we take a close look at a similar concept, namely handling **exceptions**." ] @@ -139,7 +139,7 @@ } }, "source": [ - "There are, however, cases where even well-behaved Python does not make us happy. [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb#Imprecision) provides more insights into this \"bug.\"" + "There are, however, cases where even well-behaved Python does not make us happy. [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers_00_content.ipynb#Imprecision) provides more insights into this \"bug.\"" ] }, { @@ -189,7 +189,7 @@ { "data": { "text/plain": [ - "93918328484832" + "94426021241472" ] }, "execution_count": 5, @@ -213,7 +213,7 @@ { "data": { "text/plain": [ - "93918328484800" + "94426021241440" ] }, "execution_count": 6, @@ -281,7 +281,7 @@ } }, "source": [ - "Let's not confuse the boolean `False` with `None`, another built-in object! We saw the latter before in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb#Function-Definition) as the *implicit* return value of a function without a `return` statement.\n", + "Let's not confuse the boolean `False` with `None`, another built-in object! We saw the latter before in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions_00_content.ipynb#Function-Definition) as the *implicit* return value of a function without a `return` statement.\n", "\n", "We might think of `None` in a boolean context indicating a \"maybe\" or even an \"unknown\" answer; however, for Python, there are no \"maybe\" or \"unknown\" objects, as we see further below!\n", "\n", @@ -313,7 +313,7 @@ { "data": { "text/plain": [ - "93918328471792" + "94426021228432" ] }, "execution_count": 10, @@ -357,7 +357,7 @@ } }, "source": [ - "`True`, `False`, and `None` have the property that they each exist in memory only *once*. Objects designed this way are so-called **singletons**. This **[design pattern](https://en.wikipedia.org/wiki/Design_Patterns)** was originally developed to keep a program's memory usage at a minimum. It may only be employed in situations where we know that an object does *not* mutate its value (i.e., to reuse the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Objects-vs.-Types-vs.-Values), no flipping of $0$s and $1$s in the bag is allowed). In languages \"closer\" to the memory like C, we would have to code this singleton logic ourselves, but Python has this built in for *some* types.\n", + "`True`, `False`, and `None` have the property that they each exist in memory only *once*. Objects designed this way are so-called **singletons**. This **[design pattern](https://en.wikipedia.org/wiki/Design_Patterns)** was originally developed to keep a program's memory usage at a minimum. It may only be employed in situations where we know that an object does *not* mutate its value (i.e., to reuse the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#Objects-vs.-Types-vs.-Values), no flipping of $0$s and $1$s in the bag is allowed). In languages \"closer\" to the memory like C, we would have to code this singleton logic ourselves, but Python has this built in for *some* types.\n", "\n", "We verify this with either the `is` operator or by comparing memory addresses." ] @@ -1116,6 +1116,65 @@ "bool([False])" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With `str` objects, the empty `\"\"` evaluates to `False`, and any other to `True`." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bool(\"\")" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bool(\"Lorem ipsum dolor sit amet, ...\")" + ] + }, { "cell_type": "markdown", "metadata": { @@ -1162,7 +1221,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 38, "metadata": { "slideshow": { "slide_type": "slide" @@ -1187,75 +1246,12 @@ "With the `or` operator, the first truthy sub-expression is returned." ] }, - { - "cell_type": "code", - "execution_count": 37, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Arg: 0\n", - "Arg: 1\n" - ] - }, - { - "data": { - "text/plain": [ - "1" - ] - }, - "execution_count": 37, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "expr(0) or expr(1)" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Arg: 1\n" - ] - }, - { - "data": { - "text/plain": [ - "1" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "expr(1) or expr(2) # 2 is not evaluated due to short-circuiting" - ] - }, { "cell_type": "code", "execution_count": 39, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "slide" } }, "outputs": [ @@ -1278,6 +1274,69 @@ "output_type": "execute_result" } ], + "source": [ + "expr(0) or expr(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Arg: 1\n" + ] + }, + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr(1) or expr(2) # 2 is not evaluated due to short-circuiting" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Arg: 0\n", + "Arg: 1\n" + ] + }, + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "expr(0) or expr(1) or expr(2) # 2 is not evaluated due to short-circuiting" ] @@ -1295,7 +1354,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 42, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1317,7 +1376,7 @@ "0" ] }, - "execution_count": 40, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } @@ -1339,7 +1398,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 43, "metadata": { "slideshow": { "slide_type": "slide" @@ -1359,7 +1418,7 @@ "0" ] }, - "execution_count": 41, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } @@ -1370,7 +1429,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 44, "metadata": { "slideshow": { "slide_type": "-" @@ -1391,7 +1450,7 @@ "0" ] }, - "execution_count": 42, + "execution_count": 44, "metadata": {}, "output_type": "execute_result" } @@ -1402,7 +1461,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 45, "metadata": { "slideshow": { "slide_type": "-" @@ -1423,7 +1482,7 @@ "0" ] }, - "execution_count": 43, + "execution_count": 45, "metadata": {}, "output_type": "execute_result" } @@ -1445,7 +1504,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 46, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1466,7 +1525,7 @@ "2" ] }, - "execution_count": 44, + "execution_count": 46, "metadata": {}, "output_type": "execute_result" } @@ -1522,7 +1581,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 47, "metadata": { "code_folding": [], "slideshow": { @@ -1536,7 +1595,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 48, "metadata": { "code_folding": [], "slideshow": { @@ -1578,7 +1637,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 49, "metadata": { "slideshow": { "slide_type": "slide" @@ -1591,7 +1650,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 50, "metadata": { "slideshow": { "slide_type": "-" @@ -1616,7 +1675,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 51, "metadata": { "slideshow": { "slide_type": "skip" @@ -1653,7 +1712,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 52, "metadata": { "slideshow": { "slide_type": "slide" @@ -1696,7 +1755,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 53, "metadata": { "slideshow": { "slide_type": "slide" @@ -1769,7 +1828,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 54, "metadata": { "slideshow": { "slide_type": "slide" @@ -1793,7 +1852,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 55, "metadata": { "slideshow": { "slide_type": "-" @@ -1807,60 +1866,12 @@ " y = x ** 2" ] }, - { - "cell_type": "code", - "execution_count": 54, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "9" - ] - }, - "execution_count": 54, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "y" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "On the contrary, the `if` expression fits into one line. The main downside is a potential loss in readability, in particular, if the functional relationship is not that simple. Also, some practitioners do *not* like that the condition is in the middle of the expression." - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [], - "source": [ - "y = 0 if x <= 0 else x ** 2" - ] - }, { "cell_type": "code", "execution_count": 56, "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "-" } }, "outputs": [ @@ -1887,7 +1898,7 @@ } }, "source": [ - "In this example, however, the most elegant solution is to use the built-in [max()](https://docs.python.org/3/library/functions.html#max) function." + "On the contrary, the `if` expression fits into one line. The main downside is a potential loss in readability, in particular, if the functional relationship is not that simple. Also, some practitioners do *not* like that the condition is in the middle of the expression." ] }, { @@ -1900,7 +1911,7 @@ }, "outputs": [], "source": [ - "y = max(0, x) ** 2" + "y = 0 if x <= 0 else x ** 2" ] }, { @@ -1927,6 +1938,54 @@ "y" ] }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In this example, however, the most elegant solution is to use the built-in [max()](https://docs.python.org/3/library/functions.html#max) function." + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "y = max(0, x) ** 2" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "9" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "y" + ] + }, { "cell_type": "markdown", "metadata": { @@ -1955,7 +2014,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 61, "metadata": { "slideshow": { "slide_type": "slide" @@ -1968,7 +2027,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 62, "metadata": { "slideshow": { "slide_type": "-" @@ -2007,7 +2066,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 63, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2046,7 +2105,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 64, "metadata": { "slideshow": { "slide_type": "slide" @@ -2057,7 +2116,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Oops. Division by 0. How does that work?\n", + "Yes, division worked smoothly.\n", "I am always printed\n" ] } @@ -2119,7 +2178,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.4" }, "livereveal": { "auto_select": "code", @@ -2147,5 +2206,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/03_conditionals_10_review.ipynb b/03_conditionals_10_review.ipynb new file mode 100644 index 0000000..de1a419 --- /dev/null +++ b/03_conditionals_10_review.ipynb @@ -0,0 +1,214 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "# Chapter 3: Conditionals & Exceptions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Content Review" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_00_content.ipynb) of the book. Then, work through the questions below." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Essay Questions " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Answer the following questions *briefly*!" + ] + }, + { + "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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: What do we mean when we talk about **truthy** and **falsy** expressions?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: Why is the use of **temporary variables** encouraged in Python?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: The `try` statement is useful for handling **syntax** errors." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/03_conditionals_review_and_exercises.ipynb b/03_conditionals_20_exercises.ipynb similarity index 53% rename from 03_conditionals_review_and_exercises.ipynb rename to 03_conditionals_20_exercises.ipynb index 90b4ff9..23692a3 100644 --- a/03_conditionals_review_and_exercises.ipynb +++ b/03_conditionals_20_exercises.ipynb @@ -8,174 +8,6 @@ "# Chapter 3: Conditionals & Exceptions" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Content Review" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Read [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb) of the book. Then work through the eight review questions." - ] - }, - { - "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**: What is the **singleton** design pattern? How many objects does the expression `[True, False, True, False]` generate in memory?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q2**: What do we mean when we talk about **truthy** and **falsy** expressions?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "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": [ - " " - ] - }, - { - "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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q5**: Why is the use of **temporary variables** encouraged in Python?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "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": [ - " " - ] - }, - { - "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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q8**: The `try` statement is useful for handling **syntax** errors." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -183,6 +15,13 @@ "## Coding Exercises" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_00_content.ipynb) of the book. Then, work through the exercises below." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -194,7 +33,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q9.1**: 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", + "**Q1.1**: 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", @@ -218,7 +57,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q9.2**: Calculate the final price for the following line items of an order:\n", + "**Q1.2**: 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", @@ -265,7 +104,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q9.3**: Calculate the last two line items with order quantities of $20$ and $15$. What do you observe?" + "**Q1.3**: Calculate the last two line items with order quantities of $20$ and $15$. What do you observe?" ] }, { @@ -297,7 +136,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q9.4**: Looking at the `if`-`else`-logic in the function, why do you think the four example line items in **Q9.2** were chosen as they were?" + "**Q1.4**: Looking at the `if`-`else`-logic in the function, why do you think the four example line items in **Q9.2** were chosen as they were?" ] }, { @@ -327,7 +166,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q10.1**: 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()](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()](https://docs.python.org/3/library/functions.html#func-range) a `list` object, you have to wrap it with the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in (i.e., `list(range(...))`)." + "**Q2.1**: 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()](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()](https://docs.python.org/3/library/functions.html#func-range) a `list` object, you have to wrap it with the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in (i.e., `list(range(...))`)." ] }, { @@ -343,9 +182,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q10.2**: 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", + "**Q2.2**: 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](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb), we saw that Python starts indexing with `0` as the first element. Keep that in mind.\n", + "In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb), 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", @@ -366,7 +205,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q10.3**: Create a loop that prints out either the number or any of the Fizz Buzz substitutes. Do it in such a way that we do not end up with 100 lines of output here." + "**Q2.3**: Create a loop that prints out either the number or any of the Fizz Buzz substitutes. Do it in such a way that we do not end up with 100 lines of output here." ] }, { @@ -396,7 +235,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.4" }, "toc": { "base_numbering": 1, @@ -413,5 +252,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/04_iteration.ipynb b/04_iteration_00_content.ipynb similarity index 96% rename from 04_iteration.ipynb rename to 04_iteration_00_content.ipynb index aabaaef..ba6467d 100644 --- a/04_iteration.ipynb +++ b/04_iteration_00_content.ipynb @@ -839,9 +839,9 @@ "source": [ "This implementation is *highly* **inefficient** as small Fibonacci numbers already take a very long time to compute. The reason for this is **exponential growth** in the number of function calls. As [PythonTutor](http://pythontutor.com/visualize.html#code=def%20fibonacci%28i%29%3A%0A%20%20%20%20if%20i%20%3D%3D%200%3A%0A%20%20%20%20%20%20%20%20return%200%0A%20%20%20%20elif%20i%20%3D%3D%201%3A%0A%20%20%20%20%20%20%20%20return%201%0A%20%20%20%20return%20fibonacci%28i%20-%201%29%20%2B%20fibonacci%28i%20-%202%29%0A%0Arv%20%3D%20fibonacci%285%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows, `fibonacci()` is called again and again with the same `i` arguments.\n", "\n", - "To understand this in detail, we would have to study algorithms and data structures (e.g., with [this book](https://www.amazon.de/Introduction-Algorithms-Press-Thomas-Cormen/dp/0262033844/ref=sr_1_1?__mk_de_DE=%C3%85M%C3%85%C5%BD%C3%95%C3%91&crid=1JNE8U0VZGU0O&qid=1569837169&s=gateway&sprefix=algorithms+an%2Caps%2C180&sr=8-1)), a discipline within computer science, and dive into the analysis of **[time complexity of algorithms](https://en.wikipedia.org/wiki/Time_complexity)**.\n", + "To understand this in detail, we have to study algorithms and data structures (e.g., with [this book](https://www.amazon.de/Introduction-Algorithms-Press-Thomas-Cormen/dp/0262033844/ref=sr_1_1?__mk_de_DE=%C3%85M%C3%85%C5%BD%C3%95%C3%91&crid=1JNE8U0VZGU0O&qid=1569837169&s=gateway&sprefix=algorithms+an%2Caps%2C180&sr=8-1)), a discipline within computer science, and dive into the analysis of **[time complexity of algorithms](https://en.wikipedia.org/wiki/Time_complexity)**.\n", "\n", - "Luckily, in the Fibonacci case, the inefficiency can be resolved with a **caching** (i.e., \"reuse\") strategy from the field of **[dynamic programming](https://en.wikipedia.org/wiki/Dynamic_programming)**, namely **[memoization](https://en.wikipedia.org/wiki/Memoization)**. We do so in [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings.ipynb#Memoization), after introducing the [dictionaries](https://docs.python.org/3/library/stdtypes.html#dict) data type.\n", + "Luckily, in the Fibonacci case, the inefficiency can be resolved with a **caching** (i.e., \"reuse\") strategy from the field of **[dynamic programming](https://en.wikipedia.org/wiki/Dynamic_programming)**, namely **[memoization](https://en.wikipedia.org/wiki/Memoization)**. We do so in [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings_00_content.ipynb#Memoization), after introducing the `dict` data type.\n", "\n", "Let's measure the average run times for `fibonacci()` and varying `i` arguments with the `%%timeit` [cell magic](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-timeit) that comes with Jupyter." ] @@ -859,7 +859,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "37.5 µs ± 4.26 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + "49.8 µs ± 20.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], @@ -881,7 +881,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "1.49 ms ± 22.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + "1.63 ms ± 14.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], @@ -903,7 +903,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "199 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "202 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -925,7 +925,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "2.04 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "2.25 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -947,7 +947,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "5.33 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "5.79 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -4538,7 +4538,7 @@ } }, "source": [ - "##### Efficiency of Algorithms (revisited)" + "##### Efficiency of Algorithms (continued)" ] }, { @@ -4565,7 +4565,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "4.6 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "5.14 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -4877,7 +4877,7 @@ } }, "source": [ - "For sequences of integers, the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in makes the `for` statement even more convenient: It creates a `list`-like object of type `range` that generates integers \"on the fly,\" and we look closely at the underlying effects in memory in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Mapping)." + "For sequences of integers, the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in makes the `for` statement even more convenient: It creates a `list`-like object of type `range` that generates integers \"on the fly,\" and we look closely at the underlying effects in memory in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb#Mapping)." ] }, { @@ -5008,11 +5008,11 @@ "\n", "Now, just as we classify objects by data type, we also classify these data types (e.g., `int`, `float`, `str`, or `list`) into **abstract concepts**.\n", "\n", - "We did this already in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?) when we described a `list` object as \"some sort of container that holds [...] references to other objects\". So, abstractly speaking, **containers** are any objects that are \"composed\" of other objects and also \"manage\" how these objects are organized. `list` objects, for example, have the property that they model an order associated with their elements. There exist, however, other container types, many of which do *not* come with an order. So, containers primarily \"contain\" other objects and have *nothing* to do with looping.\n", + "We did this already in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#Who-am-I?-And-how-many?) when we described a `list` object as \"some sort of container that holds [...] references to other objects\". So, abstractly speaking, **containers** are any objects that are \"composed\" of other objects and also \"manage\" how these objects are organized. `list` objects, for example, have the property that they model an order associated with their elements. There exist, however, other container types, many of which do *not* come with an order. So, containers primarily \"contain\" other objects and have *nothing* to do with looping.\n", "\n", "On the contrary, the abstract concept of **iterables** is all about looping: Any object that we can loop over is, by definition, an iterable. So, `range` objects, for example, are iterables, even though they hold no references to other objects. Moreover, looping does *not* have to occur in a *predictable* order, although this is the case for both `list` and `range` objects.\n", "\n", - "Typically, containers are iterables, and iterables are containers. Yet, only because these two concepts coincide often, we must not think of them as the same. In [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Collections-vs.-Sequences), we formalize these two concepts and introduce many more. Finally, Chapter 9 gives an explanation how abstract concepts are implemented and play together.\n", + "Typically, containers are iterables, and iterables are containers. Yet, only because these two concepts coincide often, we must not think of them as the same. In [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb#Collections-vs.-Sequences), we formalize these two concepts and introduce many more. Finally, Chapter 10 gives an explanation how abstract concepts are implemented and play together.\n", "\n", "Let's continue with `first_names` below as an example an illustrate what iterable containers are." ] @@ -5399,7 +5399,7 @@ } }, "source": [ - "##### Efficiency of Algorithms (revisited)" + "##### Efficiency of Algorithms (continued)" ] }, { @@ -5574,7 +5574,7 @@ } }, "source": [ - "Let's say we have a `samples` list and want to check if the square of at least one of its elements is above a `threshold` of `100`." + "Let's say we have a list of `numbers` and want to check if the square of at least one of its elements is above a `threshold` of `100`." ] }, { @@ -5587,7 +5587,7 @@ }, "outputs": [], "source": [ - "samples = [3, 7, 2, 9, 11, 4, 7, 9, 4, 5]" + "numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" ] }, { @@ -5611,7 +5611,7 @@ } }, "source": [ - "A first naive implementation could look like this: We loop over *every* element in `samples` and set an **indicator variable** `is_above`, initialized as `False`, to `True` once we encounter an element satisfying the search condition." + "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 search condition." ] }, { @@ -5627,22 +5627,22 @@ "name": "stdout", "output_type": "stream", "text": [ - "3 7 2 9 11 4 7 9 4 5 => at least one sample's square is above 100\n" + "7 11 8 5 3 12 2 6 9 10 1 4 => at least one number's square is above 100\n" ] } ], "source": [ "is_above = False\n", "\n", - "for sample in samples:\n", - " print(sample, end=\" \") # added for didactical purposes\n", - " if sample ** 2 > threshold:\n", + "for number in numbers:\n", + " print(number, end=\" \") # added for didactical purposes\n", + " if number ** 2 > threshold:\n", " is_above = True\n", "\n", "if is_above:\n", - " print(\"=> at least one sample's square is above\", threshold)\n", + " print(\"=> at least one number's square is above\", threshold)\n", "else:\n", - " print(\"=> no sample's square is above\", threshold)" + " print(\"=> no number's square is above\", threshold)" ] }, { @@ -5653,7 +5653,7 @@ } }, "source": [ - "This implementation is *inefficient* as even if the *first* element in `samples` has a square greater than `100`, we loop until the last element: This could take a long time for a big list.\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* clear right away.\n", "\n", @@ -5673,23 +5673,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "3 7 2 9 11 => at least one sample's square is above 100\n" + "7 11 => at least one number's square is above 100\n" ] } ], "source": [ "is_above = False\n", "\n", - "for sample in samples:\n", - " print(sample, end=\" \") # added for didactical purposes\n", - " if sample ** 2 > threshold:\n", + "for number in numbers:\n", + " print(number, end=\" \") # added for didactical purposes\n", + " if number ** 2 > threshold:\n", " is_above = True\n", " break\n", "\n", "if is_above:\n", - " print(\"=> at least one sample's square is above\", threshold)\n", + " print(\"=> at least one number's square is above\", threshold)\n", "else:\n", - " print(\"=> no sample's square is above\", threshold)" + " print(\"=> no number's square is above\", threshold)" ] }, { @@ -5751,23 +5751,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "3 7 2 9 11 => at least one sample's square is above 100\n" + "7 11 => at least one number's square is above 100\n" ] } ], "source": [ - "for sample in samples:\n", - " print(sample, end=\" \") # added for didactical purposes\n", - " if sample ** 2 > threshold:\n", + "for number in numbers:\n", + " print(number, end=\" \") # added for didactical purposes\n", + " if number ** 2 > threshold:\n", " is_above = True\n", " break\n", "else:\n", " is_above = False\n", "\n", "if is_above:\n", - " print(\"=> at least one sample's square is above\", threshold)\n", + " print(\"=> at least one number's square is above\", threshold)\n", "else:\n", - " print(\"=> no sample's square is above\", threshold)" + " print(\"=> no number's square is above\", threshold)" ] }, { @@ -5794,18 +5794,18 @@ "name": "stdout", "output_type": "stream", "text": [ - "3 7 2 9 11 => at least one sample's square is above 100\n" + "7 11 => at least one number's square is above 100\n" ] } ], "source": [ - "for sample in samples:\n", - " print(sample, end=\" \") # added for didactical purposes\n", - " if sample ** 2 > threshold:\n", - " print(\"=> at least one sample's square is above\", threshold)\n", + "for number in numbers:\n", + " print(number, end=\" \") # added for didactical purposes\n", + " if number ** 2 > threshold:\n", + " print(\"=> at least one number's square is above\", threshold)\n", " break\n", "else:\n", - " print(\"=> no sample's square is above\", threshold)" + " print(\"=> no number's square is above\", threshold)" ] }, { @@ -5816,7 +5816,7 @@ } }, "source": [ - "Of course, if we set the `threshold` an element's square has to pass higher, for example, to `200`, we have to loop over all `samples`. There is *no way* to optimize this **[linear search](https://en.wikipedia.org/wiki/Linear_search)** further, at least as long as we model `samples` as a `list` object. More advanced data types, however, exist that mitigate that downside." + "Of course, if we set the `threshold` an element's square has to pass higher, for example, to `200`, we have to loop over all `numbers`. There is *no way* to optimize this **[linear search](https://en.wikipedia.org/wiki/Linear_search)** further, at least as long as we model `numbers` as a `list` object. More advanced data types, however, exist that mitigate that downside." ] }, { @@ -5832,20 +5832,20 @@ "name": "stdout", "output_type": "stream", "text": [ - "3 7 2 9 11 4 7 9 4 5 => no sample's square is above 200\n" + "7 11 8 5 3 12 2 6 9 10 1 4 => no number's square is above 200\n" ] } ], "source": [ "threshold = 200\n", "\n", - "for sample in samples:\n", - " print(sample, end=\" \") # added for didactical purposes\n", - " if sample ** 2 > threshold:\n", - " print(\"=> at least one sample's square is above\", threshold)\n", + "for number in numbers:\n", + " print(number, end=\" \") # added for didactical purposes\n", + " if number ** 2 > threshold:\n", + " print(\"=> at least one number's square is above\", threshold)\n", " break\n", "else:\n", - " print(\"=> no sample's square is above\", threshold)" + " print(\"=> no number's square is above\", threshold)" ] }, { @@ -5867,17 +5867,17 @@ } }, "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](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Example:-Averaging-Even-Numbers) or, more realistically, data from a CSV file with many rows and columns.\n", + "Often, we process some iterable with numeric data, for example, a list of `numbers` as in this book's introductory example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#Example:-Averaging-Even-Numbers) 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 sample according to some functional relationship $y = f(x)$\n", - "- **filtering**: throw away individual samples (e.g., statistical outliers)\n", - "- **reducing**: collect individual samples into summary statistics\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 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#The-Map-Filter-Reduce-Paradigm) after introducing more advanced data types that are needed to work with \"big\" data.\n", + "We study this **map-filter-reduce** paradigm extensively in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb#The-Map-Filter-Reduce-Paradigm) after introducing more advanced data types that are needed to work with \"big\" data.\n", "\n", - "In the remainder of this section, we focus on *filtering out* some samples within a `for`-loop." + "Here, we focus on *filtering out* some numbers with a `for`-loop." ] }, { @@ -6074,7 +6074,7 @@ } }, "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](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb#The-if-Statement). Then, however, we trade off horizontal space against a more \"complex\" `if` logic, and this is *not* a real improvement.\n", + "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](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/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.\n", "\n", "A Pythonista would instead make use of the `continue` statement (cf., [reference](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", @@ -6236,13 +6236,31 @@ } }, "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Guess if the coin comes up as heads or tails: Tails\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": [ - "Guess if the coin comes up as heads or tails: Tails\n", - "Ooops, it was tails\n", - "Guess if the coin comes up as heads or tails: heads\n", "Yes, it was heads\n" ] } @@ -6402,13 +6420,31 @@ } }, "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": [ - "Guess if the coin comes up as heads or tails: invalid\n", - "Make sure to enter your guess correctly!\n", - "Guess if the coin comes up as heads or tails: Heads\n", "Yes, it was heads\n" ] } @@ -6485,7 +6521,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.4" }, "livereveal": { "auto_select": "code", @@ -6513,5 +6549,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/04_iteration_10_review.ipynb b/04_iteration_10_review.ipynb new file mode 100644 index 0000000..808e309 --- /dev/null +++ b/04_iteration_10_review.ipynb @@ -0,0 +1,281 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "# Chapter 4: Recursion & Looping" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Content Review" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_content.ipynb) of the book. Then, work through the questions below." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Essay Questions " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Answer the following questions *briefly*!" + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: What is **syntactic sugar**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: What is the conceptual difference between a **container** and a **list**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: Formulating a computational problem as a **recursion** results in an **efficient** implementation." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/04_iteration_review_and_exercises.ipynb b/04_iteration_20_exercises.ipynb similarity index 64% rename from 04_iteration_review_and_exercises.ipynb rename to 04_iteration_20_exercises.ipynb index 9e2b28f..81f6928 100644 --- a/04_iteration_review_and_exercises.ipynb +++ b/04_iteration_20_exercises.ipynb @@ -8,255 +8,6 @@ "# Chapter 4: Recursion & Looping" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Content Review" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Read [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb) of the book. Then work through the fourteen review questions." - ] - }, - { - "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**: What is so \"special\" about the number **7919**?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q2**: 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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q3**: 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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q4**: What is **syntactic sugar**?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q5**: 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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q6**: What is the conceptual difference between a **container** and a **list**?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q7**: What is a good use case for the `for`-loop's optional `else`-clause?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "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**: When a **recursion** does **not** reach the base case, this is an example of the **early exit** strategy." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q9**: Any programming language **without** looping constructs like the `for` or `while` statements is **not** Turing complete." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q10**: A **recursive** formulation is the same as a **circular** one: The terms are **synonyms**." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q11**: Formulating a computational problem as a **recursion** results in an **efficient** implementation." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q12**: 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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q13**: Before writing **any** kind of **loop**, we **always** need to think about a **stopping criterion** ahead of time." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q14**: **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": [ - " " - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -264,6 +15,13 @@ "## Coding Exercises" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_content.ipynb) of the book. Then, work through the exercises below." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -330,7 +88,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q15.1**: Explain for the $n = 3$ case why it can be solved as a **recursion**!" + "**Q1.1**: Explain for the $n = 3$ case why it can be solved as a **recursion**!" ] }, { @@ -344,7 +102,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q15.2**: 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 **Q15.1**?" + "**Q1.2**: 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.1**?" ] }, { @@ -358,7 +116,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q15.3**: The **[Towers of Hanoi](https://en.wikipedia.org/wiki/Tower_of_Hanoi)** problem is of **exponential growth**. What does that mean? What does that imply for large $n$?" + "**Q1.3**: The **[Towers of Hanoi](https://en.wikipedia.org/wiki/Tower_of_Hanoi)** problem is of **exponential growth**. What does that mean? What does that imply for large $n$?" ] }, { @@ -372,7 +130,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q15.4**: 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 **Q15.1**?" + "**Q1.4**: 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.1**?" ] }, { @@ -439,7 +197,7 @@ "\n", "Once completed, `sol()` should print out all the moves in the correct order. With **printing a move**, we mean a line like \"1 -> 3\", short for \"Move the top-most disk from spot 1 to spot 3\".\n", "\n", - "Write your answers to **Q15.5** to **Q15.7** into the subsequent code cell and finalize `sol()`! No need to write a docstring or validate the input here." + "Write your answers to **Q1.5** to **Q1.7** into the subsequent code cell and finalize `sol()`! No need to write a docstring or validate the input here." ] }, { @@ -464,14 +222,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q15.5**: What is the `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!" + "**Q1.5**: What is the `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!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "**Q15.6**: If not in the base case, `sol()` needs to determine the `intermediate` spot given concrete `origin` and `destination` arguments. For example, if called with `origin=1` and `destination=2`, `intermediate` must be `3`.\n", + "**Q1.6**: If not in the base case, `sol()` needs to determine 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 sets a variable `intermediate` to the correct temporary spot. **How many** branches will there be?" ] @@ -480,7 +238,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q15.7**: `sol()` needs to call itself **two more times** with the correct 2-pairs chosen from the three available spots `origin`, `intermediate`, and `destination`.\n", + "**Q1.7**: `sol()` needs to call itself **two more times** with the correct 2-pairs chosen from the three available spots `origin`, `intermediate`, and `destination`.\n", "\n", "In between the two recursive function calls, write a `print()` statement that prints out from where to where the \"remaining and largest\" disk has to be moved!" ] @@ -489,7 +247,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q15.8**: Execute the code cells below and confirm that the printed moves are correct!" + "**Q1.8**: Execute the code cells below and confirm that the printed moves are correct!" ] }, { @@ -543,7 +301,7 @@ "\n", "Let's create a more concise `hanoi()` function that, in addition to a positional `disks` argument, takes three keyword-only arguments `origin`, `intermediate`, and `destination` with default values `\"left\"`, `\"center\"`, and `\"right\"`.\n", "\n", - "Write your answers to **Q15.9** and **Q15.10** into the subsequent code cell and finalize `hanoi()`! No need to write a docstring or validate the input here." + "Write your answers to **Q1.9** and **Q1.10** into the subsequent code cell and finalize `hanoi()`! No need to write a docstring or validate the input here." ] }, { @@ -565,14 +323,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q15.9**: Copy the base case from `sol()`!" + "**Q1.9**: Copy the base case from `sol()`!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "**Q15.10**: Instead of conditional logic, `hanoi()` calls itself **two times** with the **three** arguments `origin`, `intermediate`, and `destination` passed on in a **different** order.\n", + "**Q1.10**: 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!\n", "\n", @@ -583,7 +341,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q15.11**: Execute the code cells below and confirm that the printed moves are correct!" + "**Q1.11**: Execute the code cells below and confirm that the printed moves are correct!" ] }, { @@ -653,11 +411,11 @@ "\n", "In such cases, we could modify a recursive function to return a count value to be passed up the recursion tree.\n", "\n", - "This is similar to what we do in the recursive versions of `factorial()` and `fibonacci()` in [Chapter 4](https://github.com/webartifex/intro-to-python/blob/master/04_iteration.ipynb), where we pass up an intermediate result.\n", + "This is similar to what we do in the recursive versions of `factorial()` and `fibonacci()` in [Chapter 4](https://github.com/webartifex/intro-to-python/blob/master/04_iteration_00_content.ipynb), where we pass up an intermediate result.\n", "\n", "Let's create a `hanoi_moves()` function that follows the same internal logic as `hanoi()`, but, instead of printing out the moves, returns the number of steps done so far in the recursion.\n", "\n", - "Write your answers to **Q15.12** to **Q15.14** into the subsequent code cell and finalize `hanoi_moves()`! No need to write a docstring or validate the input here." + "Write your answers to **Q1.12** to **Q1.14** into the subsequent code cell and finalize `hanoi_moves()`! No need to write a docstring or validate the input here." ] }, { @@ -682,21 +440,21 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q15.12**: Copy the base case from `hanoi()`! What count should be returned when it is reached?" + "**Q1.12**: Copy the base case from `hanoi()`! What count should be returned when it is reached?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "**Q15.13**: Initialize the variable `moves` with an appropriate count! This is the number of moves that corresponds to **one** recursive function call." + "**Q1.13**: Initialize the variable `moves` with an appropriate count! This is the number of moves that corresponds to **one** recursive function call." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "**Q15.14**: `moves` is updated with the counts passed up from the two recursive calls.\n", + "**Q1.14**: `moves` is updated with the counts passed up from the two recursive calls.\n", "\n", "Complete the two recursive function calls with the same arguments as in `hanoi()`!" ] @@ -705,7 +463,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q15.15**: Write a `for`-loop that prints out the **minimum number** of moves needed to solve Towers of Hanoi for any number of `disks` from `1` through `20` to confirm your answer to **Q15.2**." + "**Q1.15**: Write a `for`-loop that prints out the **minimum number** of moves needed to solve Towers of Hanoi for any number of `disks` from `1` through `20` to confirm your answer to **Q1.2**." ] }, { @@ -733,7 +491,7 @@ "\n", "With `disks` in the range from `24` through `26`, the computation time roughly doubles for each increase of `disks` by 1.\n", "\n", - "**Q15.16**: Execute the code cells below and see for yourself!" + "**Q1.16**: Execute the code cells below and see for yourself!" ] }, { @@ -779,7 +537,7 @@ "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 can be built in by passing on one more argument `offset` down the recursion tree. As the logic gets a bit \"involved,\" `hanoi_ordered()` below is almost finished.\n", "\n", - "Write your answers to **Q15.17** and **Q15.18** into the subsequent code cell and finalize `hanoi_ordered()`! No need to write a docstring or validate the input here." + "Write your answers to **Q1.17** and **Q1.18** into the subsequent code cell and finalize `hanoi_ordered()`! No need to write a docstring or validate the input here." ] }, { @@ -809,14 +567,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q15.17**: Copy the base case from the original `hanoi()`!" + "**Q1.17**: Copy the base case from the original `hanoi()`!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "**Q15.18**: Complete the two recursive function calls with the same arguments as in `hanoi()` or `hanoi_moves()`! Do not change the already filled in `offset` arguments!\n", + "**Q1.18**: Complete the two recursive function calls with the same arguments as in `hanoi()` or `hanoi_moves()`! Do not change the already filled in `offset` arguments!\n", "\n", "Then, copy the `print()` statement from `hanoi()` and adjust it to print out `count` as well!" ] @@ -825,7 +583,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q15.19**: Execute the code cells below and confirm that the order numbers are correct!" + "**Q1.19**: Execute the code cells below and confirm that the order numbers are correct!" ] }, { @@ -882,7 +640,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q15.20**: Conducting your own research on the internet (max. 15 minutes), what can you say about generalizing the **[Towers of Hanoi](https://en.wikipedia.org/wiki/Tower_of_Hanoi)** problem to a setting with **more than three** landing spots?" + "**Q1.20**: Conducting your own research on the internet (max. 15 minutes), what can you say about generalizing the **[Towers of Hanoi](https://en.wikipedia.org/wiki/Tower_of_Hanoi)** problem to a setting with **more than three** landing spots?" ] }, { @@ -909,7 +667,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.4" }, "toc": { "base_numbering": 1, @@ -926,5 +684,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/05_numbers.ipynb b/05_numbers_00_content.ipynb similarity index 96% rename from 05_numbers.ipynb rename to 05_numbers_00_content.ipynb index 48d8623..1f7ebc2 100644 --- a/05_numbers.ipynb +++ b/05_numbers_00_content.ipynb @@ -21,15 +21,15 @@ "source": [ "After learning about the basic building blocks of expressing and structuring the business logic in programs, we focus our attention on the **data types** Python offers us, both built-in and available via the [standard library](https://docs.python.org/3/library/index.html) or third-party packages.\n", "\n", - "We start with the \"simple\" ones: Numeric types in this chapter and textual data in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb). An important fact that holds for all objects of these types is that they are **immutable**. To reuse the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Objects-vs.-Types-vs.-Values), this means that the $0$s and $1$s making up an object's *value* cannot be changed once the bag is created in memory, implying that any operation with or method on the object creates a *new* object in a *different* memory location.\n", + "We start with the \"simple\" ones: Numeric types in this chapter and textual data in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text_00_content.ipynb). An important fact that holds for all objects of these types is that they are **immutable**. To reuse the bag analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#Objects-vs.-Types-vs.-Values), this means that the $0$s and $1$s making up an object's *value* cannot be changed once the bag is created in memory, implying that any operation with or method on the object creates a *new* object in a *different* memory location.\n", "\n", - "[Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb) and [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings.ipynb) then cover the more \"complex\" data types, including, for example, the `list` type. Finally, Chapter 9 completes the picture by introducing language constructs to create custom types.\n", + "[Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb), [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings_00_content.ipynb), and [Chapter 9](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/09_arrays_00_content.ipynb) then cover the more \"complex\" data types, including, for example, the `list` type. Finally, Chapter 10 completes the picture by introducing language constructs to create custom types.\n", "\n", "We have already seen many hints indicating that numbers are not as trivial to work with as it seems at first sight:\n", "\n", - "- [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#%28Data%29-Type-%2F-%22Behavior%22) reveals that numbers may come in *different* data types (i.e., `int` vs. `float` so far),\n", - "- [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb#Boolean-Expressions) raises questions regarding the **limited precision** of `float` numbers (e.g., `42 == 42.000000000000001` evaluates to `True`), and\n", - "- [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Infinite-Recursion) shows that sometimes a `float` \"walks\" and \"quacks\" like an `int`, whereas the reverse is true in other cases.\n", + "- [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#%28Data%29-Type-%2F-%22Behavior%22) reveals that numbers may come in *different* data types (i.e., `int` vs. `float` so far),\n", + "- [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_00_content.ipynb#Boolean-Expressions) raises questions regarding the **limited precision** of `float` numbers (e.g., `42 == 42.000000000000001` evaluates to `True`), and\n", + "- [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_content.ipynb#Infinite-Recursion) shows that sometimes a `float` \"walks\" and \"quacks\" like an `int`, whereas the reverse is true in other cases.\n", "\n", "This chapter introduces all the [built-in numeric types](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex): `int`, `float`, and `complex`. To mitigate the limited precision of floating-point numbers, we also look at two replacements for the `float` type in the [standard library](https://docs.python.org/3/library/index.html), namely the `Decimal` type in the [decimals](https://docs.python.org/3/library/decimal.html#decimal.Decimal) and the `Fraction` type in the [fractions](https://docs.python.org/3/library/fractions.html#fractions.Fraction) module." ] @@ -53,7 +53,7 @@ } }, "source": [ - "The simplest numeric type is the `int` type: It behaves like an [integer in ordinary math](https://en.wikipedia.org/wiki/Integer) (i.e., the set $\\mathbb{Z}$) and supports operators in the way we saw in the section on arithmetic operators in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#%28Arithmetic%29-Operators)." + "The simplest numeric type is the `int` type: It behaves like an [integer in ordinary math](https://en.wikipedia.org/wiki/Integer) (i.e., the set $\\mathbb{Z}$) and supports operators in the way we saw in the section on arithmetic operators in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#%28Arithmetic%29-Operators)." ] }, { @@ -92,7 +92,7 @@ { "data": { "text/plain": [ - "140673805309168" + "140166838695792" ] }, "execution_count": 2, @@ -1481,7 +1481,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 47, "metadata": { "slideshow": { "slide_type": "slide" @@ -1494,7 +1494,7 @@ "'0b1011 & 0b1101'" ] }, - "execution_count": 42, + "execution_count": 47, "metadata": {}, "output_type": "execute_result" } @@ -1505,7 +1505,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 48, "metadata": { "slideshow": { "slide_type": "-" @@ -1518,7 +1518,7 @@ "'0b1001'" ] }, - "execution_count": 43, + "execution_count": 48, "metadata": {}, "output_type": "execute_result" } @@ -1540,7 +1540,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 49, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1553,7 +1553,7 @@ "9" ] }, - "execution_count": 44, + "execution_count": 49, "metadata": {}, "output_type": "execute_result" } @@ -1564,7 +1564,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 50, "metadata": { "slideshow": { "slide_type": "-" @@ -1577,7 +1577,7 @@ "9" ] }, - "execution_count": 45, + "execution_count": 50, "metadata": {}, "output_type": "execute_result" } @@ -1599,7 +1599,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 51, "metadata": { "slideshow": { "slide_type": "slide" @@ -1612,7 +1612,7 @@ "'0b1001 | 0b1101'" ] }, - "execution_count": 46, + "execution_count": 51, "metadata": {}, "output_type": "execute_result" } @@ -1623,7 +1623,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 52, "metadata": { "slideshow": { "slide_type": "-" @@ -1636,7 +1636,7 @@ "'0b1101'" ] }, - "execution_count": 47, + "execution_count": 52, "metadata": {}, "output_type": "execute_result" } @@ -1658,7 +1658,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 53, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1671,7 +1671,7 @@ "13" ] }, - "execution_count": 48, + "execution_count": 53, "metadata": {}, "output_type": "execute_result" } @@ -1682,7 +1682,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 54, "metadata": { "slideshow": { "slide_type": "-" @@ -1695,7 +1695,7 @@ "13" ] }, - "execution_count": 49, + "execution_count": 54, "metadata": {}, "output_type": "execute_result" } @@ -1717,7 +1717,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 55, "metadata": { "slideshow": { "slide_type": "slide" @@ -1730,7 +1730,7 @@ "'0b1001 ^ 0b1101'" ] }, - "execution_count": 50, + "execution_count": 55, "metadata": {}, "output_type": "execute_result" } @@ -1741,7 +1741,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 56, "metadata": { "slideshow": { "slide_type": "-" @@ -1754,7 +1754,7 @@ "'0b100'" ] }, - "execution_count": 51, + "execution_count": 56, "metadata": {}, "output_type": "execute_result" } @@ -1776,7 +1776,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 57, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1789,7 +1789,7 @@ "4" ] }, - "execution_count": 52, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" } @@ -1800,7 +1800,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 58, "metadata": { "slideshow": { "slide_type": "-" @@ -1813,7 +1813,7 @@ "4" ] }, - "execution_count": 53, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } @@ -1837,7 +1837,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 59, "metadata": { "slideshow": { "slide_type": "slide" @@ -1850,7 +1850,7 @@ "'-0b1001'" ] }, - "execution_count": 54, + "execution_count": 59, "metadata": {}, "output_type": "execute_result" } @@ -1861,7 +1861,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 60, "metadata": { "slideshow": { "slide_type": "-" @@ -1874,7 +1874,7 @@ "'-0b1001'" ] }, - "execution_count": 55, + "execution_count": 60, "metadata": {}, "output_type": "execute_result" } @@ -1896,7 +1896,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 61, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1909,7 +1909,7 @@ "-1" ] }, - "execution_count": 56, + "execution_count": 61, "metadata": {}, "output_type": "execute_result" } @@ -1933,7 +1933,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 62, "metadata": { "slideshow": { "slide_type": "slide" @@ -1946,7 +1946,7 @@ "'0b1001'" ] }, - "execution_count": 57, + "execution_count": 62, "metadata": {}, "output_type": "execute_result" } @@ -1957,7 +1957,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 63, "metadata": { "slideshow": { "slide_type": "-" @@ -1970,7 +1970,7 @@ "'0b100100'" ] }, - "execution_count": 58, + "execution_count": 63, "metadata": {}, "output_type": "execute_result" } @@ -1981,7 +1981,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 64, "metadata": { "slideshow": { "slide_type": "-" @@ -1994,7 +1994,7 @@ "36" ] }, - "execution_count": 59, + "execution_count": 64, "metadata": {}, "output_type": "execute_result" } @@ -2016,7 +2016,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 65, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2029,7 +2029,7 @@ "'0b10'" ] }, - "execution_count": 60, + "execution_count": 65, "metadata": {}, "output_type": "execute_result" } @@ -2040,7 +2040,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 66, "metadata": { "slideshow": { "slide_type": "-" @@ -2053,7 +2053,7 @@ "2" ] }, - "execution_count": 61, + "execution_count": 66, "metadata": {}, "output_type": "execute_result" } @@ -2092,7 +2092,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 67, "metadata": { "slideshow": { "slide_type": "slide" @@ -2105,7 +2105,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 68, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2115,10 +2115,10 @@ { "data": { "text/plain": [ - "140673805486768" + "140166567715664" ] }, - "execution_count": 63, + "execution_count": 68, "metadata": {}, "output_type": "execute_result" } @@ -2129,7 +2129,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 69, "metadata": { "slideshow": { "slide_type": "-" @@ -2142,7 +2142,7 @@ "float" ] }, - "execution_count": 64, + "execution_count": 69, "metadata": {}, "output_type": "execute_result" } @@ -2153,7 +2153,7 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 70, "metadata": { "slideshow": { "slide_type": "-" @@ -2166,7 +2166,7 @@ "1.23" ] }, - "execution_count": 65, + "execution_count": 70, "metadata": {}, "output_type": "execute_result" } @@ -2188,7 +2188,7 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 71, "metadata": { "slideshow": { "slide_type": "skip" @@ -2201,7 +2201,7 @@ "0.123456789" ] }, - "execution_count": 66, + "execution_count": 71, "metadata": {}, "output_type": "execute_result" } @@ -2223,7 +2223,7 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 72, "metadata": { "slideshow": { "slide_type": "slide" @@ -2236,7 +2236,7 @@ "1.0" ] }, - "execution_count": 67, + "execution_count": 72, "metadata": {}, "output_type": "execute_result" } @@ -2247,7 +2247,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 73, "metadata": { "slideshow": { "slide_type": "-" @@ -2260,7 +2260,7 @@ "1.0" ] }, - "execution_count": 68, + "execution_count": 73, "metadata": {}, "output_type": "execute_result" } @@ -2271,7 +2271,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 74, "metadata": { "slideshow": { "slide_type": "skip" @@ -2284,7 +2284,7 @@ "1.0" ] }, - "execution_count": 69, + "execution_count": 74, "metadata": {}, "output_type": "execute_result" } @@ -2306,7 +2306,7 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 75, "metadata": { "slideshow": { "slide_type": "slide" @@ -2319,7 +2319,7 @@ "0.3333333333333333" ] }, - "execution_count": 70, + "execution_count": 75, "metadata": {}, "output_type": "execute_result" } @@ -2341,7 +2341,7 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 76, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2354,7 +2354,7 @@ "42.0" ] }, - "execution_count": 71, + "execution_count": 76, "metadata": {}, "output_type": "execute_result" } @@ -2365,7 +2365,7 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 77, "metadata": { "slideshow": { "slide_type": "-" @@ -2378,7 +2378,7 @@ "42.0" ] }, - "execution_count": 72, + "execution_count": 77, "metadata": {}, "output_type": "execute_result" } @@ -2411,7 +2411,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 78, "metadata": { "slideshow": { "slide_type": "slide" @@ -2424,7 +2424,7 @@ "1.23" ] }, - "execution_count": 73, + "execution_count": 78, "metadata": {}, "output_type": "execute_result" } @@ -2446,7 +2446,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 79, "metadata": { "slideshow": { "slide_type": "skip" @@ -2455,10 +2455,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "invalid syntax (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23 e0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23 e0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], @@ -2468,7 +2468,7 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 80, "metadata": { "slideshow": { "slide_type": "skip" @@ -2477,10 +2477,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "invalid syntax (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23e 0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23e 0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], @@ -2490,7 +2490,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 81, "metadata": { "slideshow": { "slide_type": "skip" @@ -2499,10 +2499,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "invalid syntax (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23e0.0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 1.23e0.0\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], @@ -2523,7 +2523,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 82, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2537,7 +2537,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0me1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0me1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'e1' is not defined" ] } @@ -2559,7 +2559,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 83, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2572,7 +2572,7 @@ "10.0" ] }, - "execution_count": 78, + "execution_count": 83, "metadata": {}, "output_type": "execute_result" } @@ -2605,7 +2605,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 84, "metadata": { "slideshow": { "slide_type": "slide" @@ -2618,166 +2618,13 @@ "nan" ] }, - "execution_count": 79, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "float(\"nan\") # also works as float(\"NaN\")" - ] - }, - { - "cell_type": "code", - "execution_count": 80, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "inf" - ] - }, - "execution_count": 80, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "float(\"+inf\") # also works as float(\"+infinity\")" - ] - }, - { - "cell_type": "code", - "execution_count": 81, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "inf" - ] - }, - "execution_count": 81, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "float(\"inf\") # by omitting the plus sign we mean positive infinity" - ] - }, - { - "cell_type": "code", - "execution_count": 82, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "-inf" - ] - }, - "execution_count": 82, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "float(\"-inf\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "`nan` objects *never* compare equal to *anything*, not even to themselves. This happens in accordance with the [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) standard." - ] - }, - { - "cell_type": "code", - "execution_count": 83, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 83, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "float(\"nan\") == float(\"nan\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "On the contrary, as two values go to infinity, there is no such concept as difference and *everything* compares equal." - ] - }, - { - "cell_type": "code", - "execution_count": 84, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "float(\"inf\") == float(\"inf\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "Adding `42` to `inf` makes no difference." + "float(\"nan\") # also works as float(\"NaN\")" ] }, { @@ -2801,12 +2648,106 @@ } ], "source": [ - "float(\"inf\") + 42" + "float(\"+inf\") # also works as float(\"+infinity\")" ] }, { "cell_type": "code", "execution_count": 86, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "inf" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"inf\") # by omitting the plus sign we mean positive infinity" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-inf" + ] + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"-inf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`nan` objects *never* compare equal to *anything*, not even to themselves. This happens in accordance with the [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) standard." + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"nan\") == float(\"nan\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "On the contrary, as two values go to infinity, there is no such concept as difference and *everything* compares equal." + ] + }, + { + "cell_type": "code", + "execution_count": 89, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2819,7 +2760,66 @@ "True" ] }, - "execution_count": 86, + "execution_count": 89, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"inf\") == float(\"inf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Adding `42` to `inf` makes no difference." + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "inf" + ] + }, + "execution_count": 90, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"inf\") + 42" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 91, "metadata": {}, "output_type": "execute_result" } @@ -2841,7 +2841,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 92, "metadata": { "slideshow": { "slide_type": "skip" @@ -2854,7 +2854,7 @@ "inf" ] }, - "execution_count": 87, + "execution_count": 92, "metadata": {}, "output_type": "execute_result" } @@ -2865,7 +2865,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 93, "metadata": { "slideshow": { "slide_type": "skip" @@ -2878,7 +2878,7 @@ "True" ] }, - "execution_count": 88, + "execution_count": 93, "metadata": {}, "output_type": "execute_result" } @@ -2900,7 +2900,7 @@ }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 94, "metadata": { "slideshow": { "slide_type": "skip" @@ -2913,7 +2913,7 @@ "inf" ] }, - "execution_count": 89, + "execution_count": 94, "metadata": {}, "output_type": "execute_result" } @@ -2924,7 +2924,7 @@ }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 95, "metadata": { "slideshow": { "slide_type": "skip" @@ -2937,7 +2937,7 @@ "True" ] }, - "execution_count": 90, + "execution_count": 95, "metadata": {}, "output_type": "execute_result" } @@ -2959,7 +2959,7 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 96, "metadata": { "slideshow": { "slide_type": "skip" @@ -2972,7 +2972,7 @@ "inf" ] }, - "execution_count": 91, + "execution_count": 96, "metadata": {}, "output_type": "execute_result" } @@ -2983,7 +2983,7 @@ }, { "cell_type": "code", - "execution_count": 92, + "execution_count": 97, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2996,7 +2996,7 @@ "True" ] }, - "execution_count": 92, + "execution_count": 97, "metadata": {}, "output_type": "execute_result" } @@ -3018,7 +3018,7 @@ }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 98, "metadata": { "slideshow": { "slide_type": "slide" @@ -3031,7 +3031,7 @@ "nan" ] }, - "execution_count": 93, + "execution_count": 98, "metadata": {}, "output_type": "execute_result" } @@ -3042,7 +3042,7 @@ }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 99, "metadata": { "slideshow": { "slide_type": "-" @@ -3055,7 +3055,7 @@ "nan" ] }, - "execution_count": 94, + "execution_count": 99, "metadata": {}, "output_type": "execute_result" } @@ -3085,12 +3085,12 @@ "source": [ "`float` objects are *inherently* imprecise, and there is *nothing* we can do about it! In particular, arithmetic operations with two `float` objects may result in \"weird\" rounding \"errors\" that are strictly deterministic and occur in accordance with the [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) standard.\n", "\n", - "For example, let's add `1e0` to `1e15` and `1e16`, respectively. In the latter case, the `1e0` somehow gets \"lost\"." + "For example, let's add `1e0` to `1e15` and `1e16`, respectively. In the latter case, the `1e0` somehow gets \"lost.\"" ] }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 100, "metadata": { "slideshow": { "slide_type": "skip" @@ -3103,7 +3103,7 @@ "1000000000000001.0" ] }, - "execution_count": 95, + "execution_count": 100, "metadata": {}, "output_type": "execute_result" } @@ -3114,7 +3114,7 @@ }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 101, "metadata": { "slideshow": { "slide_type": "skip" @@ -3127,7 +3127,7 @@ "1e+16" ] }, - "execution_count": 96, + "execution_count": 101, "metadata": {}, "output_type": "execute_result" } @@ -3149,7 +3149,7 @@ }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 102, "metadata": { "slideshow": { "slide_type": "slide" @@ -3162,7 +3162,7 @@ "1000000000000001.0" ] }, - "execution_count": 97, + "execution_count": 102, "metadata": {}, "output_type": "execute_result" } @@ -3173,7 +3173,7 @@ }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 103, "metadata": { "slideshow": { "slide_type": "-" @@ -3186,7 +3186,7 @@ "1e+16" ] }, - "execution_count": 98, + "execution_count": 103, "metadata": {}, "output_type": "execute_result" } @@ -3208,7 +3208,7 @@ }, { "cell_type": "code", - "execution_count": 99, + "execution_count": 104, "metadata": { "slideshow": { "slide_type": "slide" @@ -3221,7 +3221,7 @@ }, { "cell_type": "code", - "execution_count": 100, + "execution_count": 105, "metadata": { "slideshow": { "slide_type": "-" @@ -3234,7 +3234,7 @@ "2.0000000000000004" ] }, - "execution_count": 100, + "execution_count": 105, "metadata": {}, "output_type": "execute_result" } @@ -3245,7 +3245,7 @@ }, { "cell_type": "code", - "execution_count": 101, + "execution_count": 106, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3258,7 +3258,7 @@ "0.30000000000000004" ] }, - "execution_count": 101, + "execution_count": 106, "metadata": {}, "output_type": "execute_result" } @@ -3280,7 +3280,7 @@ }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 107, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3293,7 +3293,7 @@ "False" ] }, - "execution_count": 102, + "execution_count": 107, "metadata": {}, "output_type": "execute_result" } @@ -3304,7 +3304,7 @@ }, { "cell_type": "code", - "execution_count": 103, + "execution_count": 108, "metadata": { "slideshow": { "slide_type": "-" @@ -3317,7 +3317,7 @@ "False" ] }, - "execution_count": 103, + "execution_count": 108, "metadata": {}, "output_type": "execute_result" } @@ -3339,7 +3339,7 @@ }, { "cell_type": "code", - "execution_count": 104, + "execution_count": 109, "metadata": { "slideshow": { "slide_type": "slide" @@ -3352,7 +3352,7 @@ }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 110, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3365,7 +3365,7 @@ "True" ] }, - "execution_count": 105, + "execution_count": 110, "metadata": {}, "output_type": "execute_result" } @@ -3376,7 +3376,7 @@ }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 111, "metadata": { "slideshow": { "slide_type": "-" @@ -3389,7 +3389,7 @@ "True" ] }, - "execution_count": 106, + "execution_count": 111, "metadata": {}, "output_type": "execute_result" } @@ -3413,7 +3413,7 @@ }, { "cell_type": "code", - "execution_count": 107, + "execution_count": 112, "metadata": { "slideshow": { "slide_type": "slide" @@ -3426,7 +3426,7 @@ "'0.10000000000000000555111512312578270211815834045410'" ] }, - "execution_count": 107, + "execution_count": 112, "metadata": {}, "output_type": "execute_result" } @@ -3437,7 +3437,7 @@ }, { "cell_type": "code", - "execution_count": 108, + "execution_count": 113, "metadata": { "slideshow": { "slide_type": "-" @@ -3450,7 +3450,7 @@ "'0.20000000000000001110223024625156540423631668090820'" ] }, - "execution_count": 108, + "execution_count": 113, "metadata": {}, "output_type": "execute_result" } @@ -3461,7 +3461,7 @@ }, { "cell_type": "code", - "execution_count": 109, + "execution_count": 114, "metadata": { "slideshow": { "slide_type": "-" @@ -3474,7 +3474,7 @@ "'0.29999999999999998889776975374843459576368331909180'" ] }, - "execution_count": 109, + "execution_count": 114, "metadata": {}, "output_type": "execute_result" } @@ -3485,7 +3485,7 @@ }, { "cell_type": "code", - "execution_count": 110, + "execution_count": 115, "metadata": { "slideshow": { "slide_type": "slide" @@ -3498,7 +3498,7 @@ "'0.33333333333333331482961625624739099293947219848633'" ] }, - "execution_count": 110, + "execution_count": 115, "metadata": {}, "output_type": "execute_result" } @@ -3524,7 +3524,7 @@ }, { "cell_type": "code", - "execution_count": 111, + "execution_count": 116, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3537,7 +3537,7 @@ }, { "cell_type": "code", - "execution_count": 112, + "execution_count": 117, "metadata": { "slideshow": { "slide_type": "-" @@ -3550,7 +3550,7 @@ "0.33333" ] }, - "execution_count": 112, + "execution_count": 117, "metadata": {}, "output_type": "execute_result" } @@ -3561,7 +3561,7 @@ }, { "cell_type": "code", - "execution_count": 113, + "execution_count": 118, "metadata": { "slideshow": { "slide_type": "-" @@ -3574,7 +3574,7 @@ "'0.33333000000000001517008740847813896834850311279297'" ] }, - "execution_count": 113, + "execution_count": 118, "metadata": {}, "output_type": "execute_result" } @@ -3596,7 +3596,7 @@ }, { "cell_type": "code", - "execution_count": 114, + "execution_count": 119, "metadata": { "slideshow": { "slide_type": "slide" @@ -3609,7 +3609,7 @@ "'0.12500000000000000000000000000000000000000000000000'" ] }, - "execution_count": 114, + "execution_count": 119, "metadata": {}, "output_type": "execute_result" } @@ -3620,7 +3620,7 @@ }, { "cell_type": "code", - "execution_count": 115, + "execution_count": 120, "metadata": { "slideshow": { "slide_type": "-" @@ -3633,7 +3633,7 @@ "'0.25000000000000000000000000000000000000000000000000'" ] }, - "execution_count": 115, + "execution_count": 120, "metadata": {}, "output_type": "execute_result" } @@ -3644,7 +3644,7 @@ }, { "cell_type": "code", - "execution_count": 116, + "execution_count": 121, "metadata": { "slideshow": { "slide_type": "-" @@ -3657,7 +3657,7 @@ "True" ] }, - "execution_count": 116, + "execution_count": 121, "metadata": {}, "output_type": "execute_result" } @@ -3689,7 +3689,7 @@ "\n", "$$float = (-1)^{sign} * 1.fraction * 2^{exponent-1023}$$\n", "\n", - "A $1.$ is implicitly prepended as the first digit, and both, $fraction$ and $exponent$, are stored in base $2$ representation (i.e., they both are interpreted like integers above). As $exponent$ is consequently non-negative, between $0_{10}$ and $2047_{10}$ to be precise, the $-1023$ centers the entire $2^{exponent-1023}$ term around $1$ and allows the period within the $1.fraction$ part be shifted into either direction by the same amount. Floating-point numbers received their name as the period, formally called the **[radix point](https://en.wikipedia.org/wiki/Radix_point)**, \"floats\" along the significant digits. As an aside, an $exponent$ of all $0$s or all $1$s is used to model the special values `nan` or `inf`.\n", + "A $1.$ is implicitly prepended as the first digit, and both, $fraction$ and $exponent$, are stored in base $2$ representation (i.e., they both are interpreted like integers above). As $exponent$ is consequently non-negative, between $0_{10}$ and $2047_{10}$ to be precise, the $-1023$, called the exponent bias, centers the entire $2^{exponent-1023}$ term around $1$ and allows the period within the $1.fraction$ part be shifted into either direction by the same amount. Floating-point numbers received their name as the period, formally called the **[radix point](https://en.wikipedia.org/wiki/Radix_point)**, \"floats\" along the significant digits. As an aside, an $exponent$ of all $0$s or all $1$s is used to model the special values `nan` or `inf`.\n", "\n", "As the standard defines the exponent part to come as a power of $2$, we now see why `0.125` is a *precise* float: It can be represented as a power of $2$, i.e., $0.125 = (-1)^0 * 1.0 * 2^{1020-1023} = 2^{-3} = \\frac{1}{8}$. In other words, the floating-point representation of $0.125_{10}$ is $0_2$, $1111111100_2 = 1020_{10}$, and $0_2$ for the three groups, respectively." ] @@ -3726,7 +3726,7 @@ }, { "cell_type": "code", - "execution_count": 117, + "execution_count": 122, "metadata": { "slideshow": { "slide_type": "slide" @@ -3739,7 +3739,7 @@ }, { "cell_type": "code", - "execution_count": 118, + "execution_count": 123, "metadata": { "slideshow": { "slide_type": "-" @@ -3752,7 +3752,7 @@ "'0x1.0000000000000p-3'" ] }, - "execution_count": 118, + "execution_count": 123, "metadata": {}, "output_type": "execute_result" } @@ -3774,7 +3774,7 @@ }, { "cell_type": "code", - "execution_count": 119, + "execution_count": 124, "metadata": { "slideshow": { "slide_type": "-" @@ -3787,7 +3787,7 @@ "(1, 8)" ] }, - "execution_count": 119, + "execution_count": 124, "metadata": {}, "output_type": "execute_result" } @@ -3798,7 +3798,7 @@ }, { "cell_type": "code", - "execution_count": 120, + "execution_count": 125, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3811,7 +3811,7 @@ "'0x1.555475a31a4bep-2'" ] }, - "execution_count": 120, + "execution_count": 125, "metadata": {}, "output_type": "execute_result" } @@ -3822,7 +3822,7 @@ }, { "cell_type": "code", - "execution_count": 121, + "execution_count": 126, "metadata": { "slideshow": { "slide_type": "-" @@ -3835,7 +3835,7 @@ "(3002369727582815, 9007199254740992)" ] }, - "execution_count": 121, + "execution_count": 126, "metadata": {}, "output_type": "execute_result" } @@ -3857,7 +3857,7 @@ }, { "cell_type": "code", - "execution_count": 122, + "execution_count": 127, "metadata": { "slideshow": { "slide_type": "slide" @@ -3870,7 +3870,7 @@ }, { "cell_type": "code", - "execution_count": 123, + "execution_count": 128, "metadata": { "slideshow": { "slide_type": "-" @@ -3883,7 +3883,7 @@ "'0x0.0p+0'" ] }, - "execution_count": 123, + "execution_count": 128, "metadata": {}, "output_type": "execute_result" } @@ -3894,7 +3894,7 @@ }, { "cell_type": "code", - "execution_count": 124, + "execution_count": 129, "metadata": { "slideshow": { "slide_type": "-" @@ -3907,7 +3907,7 @@ "(0, 1)" ] }, - "execution_count": 124, + "execution_count": 129, "metadata": {}, "output_type": "execute_result" } @@ -3924,12 +3924,12 @@ } }, "source": [ - "As seen in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#%28Data%29-Type-%2F-%22Behavior%22), the [is_integer()](https://docs.python.org/3/library/stdtypes.html#float.is_integer) method tells us if a `float` can be casted as an `int` object without any loss in precision." + "As seen in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#%28Data%29-Type-%2F-%22Behavior%22), the [is_integer()](https://docs.python.org/3/library/stdtypes.html#float.is_integer) method tells us if a `float` can be casted as an `int` object without any loss in precision." ] }, { "cell_type": "code", - "execution_count": 125, + "execution_count": 130, "metadata": { "slideshow": { "slide_type": "skip" @@ -3942,7 +3942,7 @@ "False" ] }, - "execution_count": 125, + "execution_count": 130, "metadata": {}, "output_type": "execute_result" } @@ -3953,7 +3953,7 @@ }, { "cell_type": "code", - "execution_count": 126, + "execution_count": 131, "metadata": { "slideshow": { "slide_type": "skip" @@ -3966,7 +3966,7 @@ "True" ] }, - "execution_count": 126, + "execution_count": 131, "metadata": {}, "output_type": "execute_result" } @@ -3990,7 +3990,7 @@ }, { "cell_type": "code", - "execution_count": 127, + "execution_count": 132, "metadata": { "slideshow": { "slide_type": "skip" @@ -4003,7 +4003,7 @@ }, { "cell_type": "code", - "execution_count": 128, + "execution_count": 133, "metadata": { "slideshow": { "slide_type": "skip" @@ -4016,7 +4016,7 @@ "sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)" ] }, - "execution_count": 128, + "execution_count": 133, "metadata": {}, "output_type": "execute_result" } @@ -4051,7 +4051,7 @@ }, { "cell_type": "code", - "execution_count": 129, + "execution_count": 134, "metadata": { "slideshow": { "slide_type": "slide" @@ -4075,7 +4075,7 @@ }, { "cell_type": "code", - "execution_count": 130, + "execution_count": 135, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4088,7 +4088,7 @@ "Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow])" ] }, - "execution_count": 130, + "execution_count": 135, "metadata": {}, "output_type": "execute_result" } @@ -4110,7 +4110,7 @@ }, { "cell_type": "code", - "execution_count": 131, + "execution_count": 136, "metadata": { "slideshow": { "slide_type": "slide" @@ -4123,7 +4123,7 @@ "Decimal('42')" ] }, - "execution_count": 131, + "execution_count": 136, "metadata": {}, "output_type": "execute_result" } @@ -4134,7 +4134,7 @@ }, { "cell_type": "code", - "execution_count": 132, + "execution_count": 137, "metadata": { "slideshow": { "slide_type": "-" @@ -4147,7 +4147,7 @@ "Decimal('0.1')" ] }, - "execution_count": 132, + "execution_count": 137, "metadata": {}, "output_type": "execute_result" } @@ -4158,7 +4158,7 @@ }, { "cell_type": "code", - "execution_count": 133, + "execution_count": 138, "metadata": { "slideshow": { "slide_type": "-" @@ -4171,7 +4171,7 @@ "Decimal('1E+5')" ] }, - "execution_count": 133, + "execution_count": 138, "metadata": {}, "output_type": "execute_result" } @@ -4193,7 +4193,7 @@ }, { "cell_type": "code", - "execution_count": 134, + "execution_count": 139, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4206,7 +4206,7 @@ "Decimal('0.1000000000000000055511151231257827021181583404541015625')" ] }, - "execution_count": 134, + "execution_count": 139, "metadata": {}, "output_type": "execute_result" } @@ -4228,7 +4228,7 @@ }, { "cell_type": "code", - "execution_count": 135, + "execution_count": 140, "metadata": { "slideshow": { "slide_type": "slide" @@ -4241,7 +4241,7 @@ "Decimal('0.3')" ] }, - "execution_count": 135, + "execution_count": 140, "metadata": {}, "output_type": "execute_result" } @@ -4252,7 +4252,7 @@ }, { "cell_type": "code", - "execution_count": 136, + "execution_count": 141, "metadata": { "slideshow": { "slide_type": "-" @@ -4265,7 +4265,7 @@ "True" ] }, - "execution_count": 136, + "execution_count": 141, "metadata": {}, "output_type": "execute_result" } @@ -4287,7 +4287,7 @@ }, { "cell_type": "code", - "execution_count": 137, + "execution_count": 142, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4300,7 +4300,7 @@ "Decimal('0.30000')" ] }, - "execution_count": 137, + "execution_count": 142, "metadata": {}, "output_type": "execute_result" } @@ -4311,7 +4311,7 @@ }, { "cell_type": "code", - "execution_count": 138, + "execution_count": 143, "metadata": { "slideshow": { "slide_type": "-" @@ -4324,7 +4324,7 @@ "True" ] }, - "execution_count": 138, + "execution_count": 143, "metadata": {}, "output_type": "execute_result" } @@ -4346,7 +4346,7 @@ }, { "cell_type": "code", - "execution_count": 139, + "execution_count": 144, "metadata": { "slideshow": { "slide_type": "slide" @@ -4359,7 +4359,7 @@ "Decimal('42')" ] }, - "execution_count": 139, + "execution_count": 144, "metadata": {}, "output_type": "execute_result" } @@ -4370,7 +4370,7 @@ }, { "cell_type": "code", - "execution_count": 140, + "execution_count": 145, "metadata": { "slideshow": { "slide_type": "-" @@ -4383,7 +4383,7 @@ "Decimal('42')" ] }, - "execution_count": 140, + "execution_count": 145, "metadata": {}, "output_type": "execute_result" } @@ -4394,7 +4394,7 @@ }, { "cell_type": "code", - "execution_count": 141, + "execution_count": 146, "metadata": { "slideshow": { "slide_type": "slide" @@ -4407,7 +4407,7 @@ "Decimal('0.1')" ] }, - "execution_count": 141, + "execution_count": 146, "metadata": {}, "output_type": "execute_result" } @@ -4429,7 +4429,7 @@ }, { "cell_type": "code", - "execution_count": 142, + "execution_count": 147, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4442,7 +4442,7 @@ "'0.10000000000000000000000000000000000000000000000000'" ] }, - "execution_count": 142, + "execution_count": 147, "metadata": {}, "output_type": "execute_result" } @@ -4453,7 +4453,7 @@ }, { "cell_type": "code", - "execution_count": 143, + "execution_count": 148, "metadata": { "slideshow": { "slide_type": "-" @@ -4466,7 +4466,7 @@ "'0.10000000000000000555111512312578270211815834045410'" ] }, - "execution_count": 143, + "execution_count": 148, "metadata": {}, "output_type": "execute_result" } @@ -4488,7 +4488,7 @@ }, { "cell_type": "code", - "execution_count": 144, + "execution_count": 149, "metadata": { "slideshow": { "slide_type": "slide" @@ -4502,7 +4502,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m1.0\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m42\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m1.0\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m42\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for *: 'float' and 'decimal.Decimal'" ] } @@ -4524,7 +4524,7 @@ }, { "cell_type": "code", - "execution_count": 145, + "execution_count": 150, "metadata": { "slideshow": { "slide_type": "slide" @@ -4537,7 +4537,7 @@ "Decimal('2')" ] }, - "execution_count": 145, + "execution_count": 150, "metadata": {}, "output_type": "execute_result" } @@ -4548,7 +4548,7 @@ }, { "cell_type": "code", - "execution_count": 146, + "execution_count": 151, "metadata": { "slideshow": { "slide_type": "-" @@ -4561,7 +4561,7 @@ "Decimal('1.414213562373095048801688724')" ] }, - "execution_count": 146, + "execution_count": 151, "metadata": {}, "output_type": "execute_result" } @@ -4585,7 +4585,7 @@ }, { "cell_type": "code", - "execution_count": 147, + "execution_count": 152, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4598,7 +4598,7 @@ "Decimal('1.999999999999999999999999999')" ] }, - "execution_count": 147, + "execution_count": 152, "metadata": {}, "output_type": "execute_result" } @@ -4624,7 +4624,7 @@ }, { "cell_type": "code", - "execution_count": 148, + "execution_count": 153, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4637,7 +4637,7 @@ "Decimal('2.0000')" ] }, - "execution_count": 148, + "execution_count": 153, "metadata": {}, "output_type": "execute_result" } @@ -4655,7 +4655,7 @@ }, { "cell_type": "code", - "execution_count": 149, + "execution_count": 154, "metadata": {}, "outputs": [ { @@ -4665,7 +4665,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtwo\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquantize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"0.1\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m**\u001b[0m \u001b[0;36m28\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtwo\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquantize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"0.1\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m**\u001b[0m \u001b[0;36m28\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m: []" ] } @@ -4687,7 +4687,7 @@ }, { "cell_type": "code", - "execution_count": 150, + "execution_count": 155, "metadata": { "slideshow": { "slide_type": "skip" @@ -4700,7 +4700,7 @@ "True" ] }, - "execution_count": 150, + "execution_count": 155, "metadata": {}, "output_type": "execute_result" } @@ -4722,7 +4722,7 @@ }, { "cell_type": "code", - "execution_count": 151, + "execution_count": 156, "metadata": { "slideshow": { "slide_type": "fragment" @@ -4735,7 +4735,7 @@ "True" ] }, - "execution_count": 151, + "execution_count": 156, "metadata": {}, "output_type": "execute_result" } @@ -4757,7 +4757,7 @@ }, { "cell_type": "code", - "execution_count": 152, + "execution_count": 157, "metadata": { "slideshow": { "slide_type": "skip" @@ -4770,7 +4770,7 @@ "Decimal('NaN')" ] }, - "execution_count": 152, + "execution_count": 157, "metadata": {}, "output_type": "execute_result" } @@ -4781,7 +4781,7 @@ }, { "cell_type": "code", - "execution_count": 153, + "execution_count": 158, "metadata": { "slideshow": { "slide_type": "skip" @@ -4794,7 +4794,7 @@ "False" ] }, - "execution_count": 153, + "execution_count": 158, "metadata": {}, "output_type": "execute_result" } @@ -4805,7 +4805,7 @@ }, { "cell_type": "code", - "execution_count": 154, + "execution_count": 159, "metadata": { "slideshow": { "slide_type": "skip" @@ -4818,7 +4818,7 @@ "Decimal('Infinity')" ] }, - "execution_count": 154, + "execution_count": 159, "metadata": {}, "output_type": "execute_result" } @@ -4829,7 +4829,7 @@ }, { "cell_type": "code", - "execution_count": 155, + "execution_count": 160, "metadata": { "slideshow": { "slide_type": "skip" @@ -4842,7 +4842,7 @@ "Decimal('-Infinity')" ] }, - "execution_count": 155, + "execution_count": 160, "metadata": {}, "output_type": "execute_result" } @@ -4853,7 +4853,7 @@ }, { "cell_type": "code", - "execution_count": 156, + "execution_count": 161, "metadata": { "slideshow": { "slide_type": "skip" @@ -4866,7 +4866,7 @@ "Decimal('Infinity')" ] }, - "execution_count": 156, + "execution_count": 161, "metadata": {}, "output_type": "execute_result" } @@ -4877,7 +4877,7 @@ }, { "cell_type": "code", - "execution_count": 157, + "execution_count": 162, "metadata": { "slideshow": { "slide_type": "skip" @@ -4890,7 +4890,7 @@ "True" ] }, - "execution_count": 157, + "execution_count": 162, "metadata": {}, "output_type": "execute_result" } @@ -4912,7 +4912,7 @@ }, { "cell_type": "code", - "execution_count": 158, + "execution_count": 163, "metadata": { "slideshow": { "slide_type": "skip" @@ -4926,7 +4926,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"-inf\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"-inf\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m: []" ] } @@ -4937,7 +4937,7 @@ }, { "cell_type": "code", - "execution_count": 159, + "execution_count": 164, "metadata": { "slideshow": { "slide_type": "skip" @@ -4951,7 +4951,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"inf\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mInvalidOperation\u001b[0m: []" ] } @@ -4997,7 +4997,7 @@ }, { "cell_type": "code", - "execution_count": 160, + "execution_count": 165, "metadata": { "slideshow": { "slide_type": "slide" @@ -5021,7 +5021,7 @@ }, { "cell_type": "code", - "execution_count": 161, + "execution_count": 166, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5034,7 +5034,7 @@ "Fraction(1, 3)" ] }, - "execution_count": 161, + "execution_count": 166, "metadata": {}, "output_type": "execute_result" } @@ -5045,7 +5045,7 @@ }, { "cell_type": "code", - "execution_count": 162, + "execution_count": 167, "metadata": { "slideshow": { "slide_type": "-" @@ -5058,7 +5058,7 @@ "Fraction(1, 3)" ] }, - "execution_count": 162, + "execution_count": 167, "metadata": {}, "output_type": "execute_result" } @@ -5069,7 +5069,7 @@ }, { "cell_type": "code", - "execution_count": 163, + "execution_count": 168, "metadata": { "slideshow": { "slide_type": "-" @@ -5082,7 +5082,7 @@ "Fraction(3333333333, 10000000000)" ] }, - "execution_count": 163, + "execution_count": 168, "metadata": {}, "output_type": "execute_result" } @@ -5093,7 +5093,7 @@ }, { "cell_type": "code", - "execution_count": 164, + "execution_count": 169, "metadata": { "slideshow": { "slide_type": "-" @@ -5106,7 +5106,7 @@ "Fraction(3333333333, 10000000000)" ] }, - "execution_count": 164, + "execution_count": 169, "metadata": {}, "output_type": "execute_result" } @@ -5128,7 +5128,7 @@ }, { "cell_type": "code", - "execution_count": 165, + "execution_count": 170, "metadata": { "slideshow": { "slide_type": "slide" @@ -5141,7 +5141,7 @@ "Fraction(3, 2)" ] }, - "execution_count": 165, + "execution_count": 170, "metadata": {}, "output_type": "execute_result" } @@ -5152,7 +5152,7 @@ }, { "cell_type": "code", - "execution_count": 166, + "execution_count": 171, "metadata": { "slideshow": { "slide_type": "-" @@ -5165,7 +5165,7 @@ "Fraction(3, 2)" ] }, - "execution_count": 166, + "execution_count": 171, "metadata": {}, "output_type": "execute_result" } @@ -5187,7 +5187,7 @@ }, { "cell_type": "code", - "execution_count": 167, + "execution_count": 172, "metadata": { "slideshow": { "slide_type": "slide" @@ -5200,7 +5200,7 @@ "Fraction(1, 10)" ] }, - "execution_count": 167, + "execution_count": 172, "metadata": {}, "output_type": "execute_result" } @@ -5222,7 +5222,7 @@ }, { "cell_type": "code", - "execution_count": 168, + "execution_count": 173, "metadata": { "slideshow": { "slide_type": "-" @@ -5235,7 +5235,7 @@ "Fraction(3602879701896397, 36028797018963968)" ] }, - "execution_count": 168, + "execution_count": 173, "metadata": {}, "output_type": "execute_result" } @@ -5257,7 +5257,7 @@ }, { "cell_type": "code", - "execution_count": 169, + "execution_count": 174, "metadata": { "slideshow": { "slide_type": "slide" @@ -5270,7 +5270,7 @@ "Fraction(7, 4)" ] }, - "execution_count": 169, + "execution_count": 174, "metadata": {}, "output_type": "execute_result" } @@ -5281,7 +5281,7 @@ }, { "cell_type": "code", - "execution_count": 170, + "execution_count": 175, "metadata": { "slideshow": { "slide_type": "-" @@ -5294,7 +5294,7 @@ "Fraction(1, 2)" ] }, - "execution_count": 170, + "execution_count": 175, "metadata": {}, "output_type": "execute_result" } @@ -5305,7 +5305,7 @@ }, { "cell_type": "code", - "execution_count": 171, + "execution_count": 176, "metadata": { "slideshow": { "slide_type": "-" @@ -5318,7 +5318,7 @@ "Fraction(1, 1)" ] }, - "execution_count": 171, + "execution_count": 176, "metadata": {}, "output_type": "execute_result" } @@ -5329,7 +5329,7 @@ }, { "cell_type": "code", - "execution_count": 172, + "execution_count": 177, "metadata": { "slideshow": { "slide_type": "-" @@ -5342,7 +5342,7 @@ "Fraction(1, 1)" ] }, - "execution_count": 172, + "execution_count": 177, "metadata": {}, "output_type": "execute_result" } @@ -5364,7 +5364,7 @@ }, { "cell_type": "code", - "execution_count": 173, + "execution_count": 178, "metadata": { "slideshow": { "slide_type": "slide" @@ -5377,7 +5377,7 @@ "0.1" ] }, - "execution_count": 173, + "execution_count": 178, "metadata": {}, "output_type": "execute_result" } @@ -5388,7 +5388,7 @@ }, { "cell_type": "code", - "execution_count": 174, + "execution_count": 179, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5401,7 +5401,7 @@ "'0.10000000000000000555111512312578270211815834045410'" ] }, - "execution_count": 174, + "execution_count": 179, "metadata": {}, "output_type": "execute_result" } @@ -5475,7 +5475,7 @@ }, { "cell_type": "code", - "execution_count": 175, + "execution_count": 180, "metadata": { "slideshow": { "slide_type": "slide" @@ -5488,7 +5488,7 @@ }, { "cell_type": "code", - "execution_count": 176, + "execution_count": 181, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5498,10 +5498,10 @@ { "data": { "text/plain": [ - "140673804641712" + "140166567772848" ] }, - "execution_count": 176, + "execution_count": 181, "metadata": {}, "output_type": "execute_result" } @@ -5512,7 +5512,7 @@ }, { "cell_type": "code", - "execution_count": 177, + "execution_count": 182, "metadata": { "slideshow": { "slide_type": "-" @@ -5525,7 +5525,7 @@ "complex" ] }, - "execution_count": 177, + "execution_count": 182, "metadata": {}, "output_type": "execute_result" } @@ -5536,7 +5536,7 @@ }, { "cell_type": "code", - "execution_count": 178, + "execution_count": 183, "metadata": { "slideshow": { "slide_type": "-" @@ -5549,7 +5549,7 @@ "1j" ] }, - "execution_count": 178, + "execution_count": 183, "metadata": {}, "output_type": "execute_result" } @@ -5571,7 +5571,7 @@ }, { "cell_type": "code", - "execution_count": 179, + "execution_count": 184, "metadata": { "slideshow": { "slide_type": "slide" @@ -5584,7 +5584,7 @@ "True" ] }, - "execution_count": 179, + "execution_count": 184, "metadata": {}, "output_type": "execute_result" } @@ -5606,7 +5606,7 @@ }, { "cell_type": "code", - "execution_count": 180, + "execution_count": 185, "metadata": { "slideshow": { "slide_type": "slide" @@ -5619,7 +5619,7 @@ "(2+0.5j)" ] }, - "execution_count": 180, + "execution_count": 185, "metadata": {}, "output_type": "execute_result" } @@ -5641,7 +5641,7 @@ }, { "cell_type": "code", - "execution_count": 181, + "execution_count": 186, "metadata": { "slideshow": { "slide_type": "-" @@ -5654,7 +5654,7 @@ "(2+0.5j)" ] }, - "execution_count": 181, + "execution_count": 186, "metadata": {}, "output_type": "execute_result" } @@ -5665,7 +5665,7 @@ }, { "cell_type": "code", - "execution_count": 182, + "execution_count": 187, "metadata": { "slideshow": { "slide_type": "-" @@ -5678,7 +5678,7 @@ "(2+0j)" ] }, - "execution_count": 182, + "execution_count": 187, "metadata": {}, "output_type": "execute_result" } @@ -5689,7 +5689,7 @@ }, { "cell_type": "code", - "execution_count": 183, + "execution_count": 188, "metadata": { "slideshow": { "slide_type": "skip" @@ -5702,7 +5702,7 @@ "(2+0.5j)" ] }, - "execution_count": 183, + "execution_count": 188, "metadata": {}, "output_type": "execute_result" } @@ -5713,7 +5713,7 @@ }, { "cell_type": "code", - "execution_count": 184, + "execution_count": 189, "metadata": { "slideshow": { "slide_type": "skip" @@ -5726,7 +5726,7 @@ "(2+0.5j)" ] }, - "execution_count": 184, + "execution_count": 189, "metadata": {}, "output_type": "execute_result" } @@ -5748,7 +5748,7 @@ }, { "cell_type": "code", - "execution_count": 185, + "execution_count": 190, "metadata": { "slideshow": { "slide_type": "slide" @@ -5762,7 +5762,7 @@ }, { "cell_type": "code", - "execution_count": 186, + "execution_count": 191, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5775,7 +5775,7 @@ "(4+6j)" ] }, - "execution_count": 186, + "execution_count": 191, "metadata": {}, "output_type": "execute_result" } @@ -5786,7 +5786,7 @@ }, { "cell_type": "code", - "execution_count": 187, + "execution_count": 192, "metadata": { "slideshow": { "slide_type": "-" @@ -5799,7 +5799,7 @@ "(-2-2j)" ] }, - "execution_count": 187, + "execution_count": 192, "metadata": {}, "output_type": "execute_result" } @@ -5810,7 +5810,7 @@ }, { "cell_type": "code", - "execution_count": 188, + "execution_count": 193, "metadata": { "slideshow": { "slide_type": "skip" @@ -5823,7 +5823,7 @@ "(40+2j)" ] }, - "execution_count": 188, + "execution_count": 193, "metadata": {}, "output_type": "execute_result" } @@ -5834,7 +5834,7 @@ }, { "cell_type": "code", - "execution_count": 189, + "execution_count": 194, "metadata": { "slideshow": { "slide_type": "skip" @@ -5847,7 +5847,7 @@ "-4j" ] }, - "execution_count": 189, + "execution_count": 194, "metadata": {}, "output_type": "execute_result" } @@ -5858,7 +5858,7 @@ }, { "cell_type": "code", - "execution_count": 190, + "execution_count": 195, "metadata": { "slideshow": { "slide_type": "skip" @@ -5871,7 +5871,7 @@ "(5+10j)" ] }, - "execution_count": 190, + "execution_count": 195, "metadata": {}, "output_type": "execute_result" } @@ -5882,7 +5882,7 @@ }, { "cell_type": "code", - "execution_count": 191, + "execution_count": 196, "metadata": { "slideshow": { "slide_type": "skip" @@ -5895,7 +5895,7 @@ "(0.5+0.6666666666666666j)" ] }, - "execution_count": 191, + "execution_count": 196, "metadata": {}, "output_type": "execute_result" } @@ -5906,7 +5906,7 @@ }, { "cell_type": "code", - "execution_count": 192, + "execution_count": 197, "metadata": { "slideshow": { "slide_type": "fragment" @@ -5919,7 +5919,7 @@ "(-5+10j)" ] }, - "execution_count": 192, + "execution_count": 197, "metadata": {}, "output_type": "execute_result" } @@ -5930,7 +5930,7 @@ }, { "cell_type": "code", - "execution_count": 193, + "execution_count": 198, "metadata": { "slideshow": { "slide_type": "-" @@ -5943,7 +5943,7 @@ "(0.44+0.08j)" ] }, - "execution_count": 193, + "execution_count": 198, "metadata": {}, "output_type": "execute_result" } @@ -5965,7 +5965,7 @@ }, { "cell_type": "code", - "execution_count": 194, + "execution_count": 199, "metadata": { "slideshow": { "slide_type": "slide" @@ -5978,7 +5978,7 @@ "5.0" ] }, - "execution_count": 194, + "execution_count": 199, "metadata": {}, "output_type": "execute_result" } @@ -6000,7 +6000,7 @@ }, { "cell_type": "code", - "execution_count": 195, + "execution_count": 200, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6013,7 +6013,7 @@ "1.0" ] }, - "execution_count": 195, + "execution_count": 200, "metadata": {}, "output_type": "execute_result" } @@ -6024,7 +6024,7 @@ }, { "cell_type": "code", - "execution_count": 196, + "execution_count": 201, "metadata": { "slideshow": { "slide_type": "-" @@ -6037,7 +6037,7 @@ "2.0" ] }, - "execution_count": 196, + "execution_count": 201, "metadata": {}, "output_type": "execute_result" } @@ -6059,7 +6059,7 @@ }, { "cell_type": "code", - "execution_count": 197, + "execution_count": 202, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6072,7 +6072,7 @@ "(1-2j)" ] }, - "execution_count": 197, + "execution_count": 202, "metadata": {}, "output_type": "execute_result" } @@ -6111,7 +6111,7 @@ } }, "source": [ - "Analogous to the discussion of *containers* and *iterables* in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Containers-vs.-Iterables), we contrast the *concrete* numeric data types in this chapter with the *abstract* ideas behind [numbers in mathematics](https://en.wikipedia.org/wiki/Number).\n", + "Analogous to the discussion of *containers* and *iterables* in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_content.ipynb#Containers-vs.-Iterables), we contrast the *concrete* numeric data types in this chapter with the *abstract* ideas behind [numbers in mathematics](https://en.wikipedia.org/wiki/Number).\n", "\n", "The figure below summarizes five *major* sets of [numbers in mathematics](https://en.wikipedia.org/wiki/Number) as we know them from high school:\n", "\n", @@ -6151,16 +6151,16 @@ "\n", "The abstract concepts behind the four outer-most mathematical sets are formalized in Python since [PEP 3141](https://www.python.org/dev/peps/pep-3141/) in 2007. The [numbers](https://docs.python.org/3/library/numbers.html) module in the [standard library](https://docs.python.org/3/library/index.html) defines what programmers call the **[numerical tower](https://en.wikipedia.org/wiki/Numerical_tower)**, a collection of five **[abstract data types](https://en.wikipedia.org/wiki/Abstract_data_type)**, or **abstract base classes** (ABCs) as they are called in Python jargon:\n", "\n", - "- `numbers.Number`: \"any number\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Number))\n", - "- `numbers.Complex`: \"all complex numbers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Complex))\n", - "- `numbers.Real`: \"all real numbers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Real))\n", - "- `numbers.Rational`: \"all rational numbers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Rational))\n", - "- `numbers.Integral`: \"all integers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Integral))" + "- `Number`: \"any number\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Number))\n", + "- `Complex`: \"all complex numbers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Complex))\n", + "- `Real`: \"all real numbers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Real))\n", + "- `Rational`: \"all rational numbers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Rational))\n", + "- `Integral`: \"all integers\" (cf., [documentation](https://docs.python.org/3/library/numbers.html#numbers.Integral))" ] }, { "cell_type": "code", - "execution_count": 198, + "execution_count": 203, "metadata": { "slideshow": { "slide_type": "slide" @@ -6173,7 +6173,7 @@ }, { "cell_type": "code", - "execution_count": 199, + "execution_count": 204, "metadata": { "slideshow": { "slide_type": "-" @@ -6201,7 +6201,7 @@ " 'abstractmethod']" ] }, - "execution_count": 199, + "execution_count": 204, "metadata": {}, "output_type": "execute_result" } @@ -6227,7 +6227,7 @@ }, { "cell_type": "code", - "execution_count": 200, + "execution_count": 205, "metadata": { "scrolled": true, "slideshow": { @@ -6338,7 +6338,7 @@ }, { "cell_type": "code", - "execution_count": 201, + "execution_count": 206, "metadata": { "slideshow": { "slide_type": "skip" @@ -6505,7 +6505,7 @@ }, { "cell_type": "code", - "execution_count": 202, + "execution_count": 207, "metadata": { "slideshow": { "slide_type": "slide" @@ -6518,7 +6518,7 @@ "42" ] }, - "execution_count": 202, + "execution_count": 207, "metadata": {}, "output_type": "execute_result" } @@ -6529,7 +6529,7 @@ }, { "cell_type": "code", - "execution_count": 203, + "execution_count": 208, "metadata": { "slideshow": { "slide_type": "-" @@ -6542,7 +6542,7 @@ "Decimal('0.1')" ] }, - "execution_count": 203, + "execution_count": 208, "metadata": {}, "output_type": "execute_result" } @@ -6553,7 +6553,7 @@ }, { "cell_type": "code", - "execution_count": 204, + "execution_count": 209, "metadata": { "slideshow": { "slide_type": "-" @@ -6566,7 +6566,7 @@ "5.0" ] }, - "execution_count": 204, + "execution_count": 209, "metadata": {}, "output_type": "execute_result" } @@ -6588,7 +6588,7 @@ }, { "cell_type": "code", - "execution_count": 205, + "execution_count": 210, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6601,7 +6601,7 @@ "42" ] }, - "execution_count": 205, + "execution_count": 210, "metadata": {}, "output_type": "execute_result" } @@ -6612,7 +6612,7 @@ }, { "cell_type": "code", - "execution_count": 206, + "execution_count": 211, "metadata": { "slideshow": { "slide_type": "-" @@ -6625,7 +6625,7 @@ "0" ] }, - "execution_count": 206, + "execution_count": 211, "metadata": {}, "output_type": "execute_result" } @@ -6647,7 +6647,7 @@ }, { "cell_type": "code", - "execution_count": 207, + "execution_count": 212, "metadata": { "slideshow": { "slide_type": "-" @@ -6661,7 +6661,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mround\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m3j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mround\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m4\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m3j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: type complex doesn't define __round__ method" ] } @@ -6683,7 +6683,7 @@ }, { "cell_type": "code", - "execution_count": 208, + "execution_count": 213, "metadata": { "slideshow": { "slide_type": "slide" @@ -6707,7 +6707,7 @@ }, { "cell_type": "code", - "execution_count": 209, + "execution_count": 214, "metadata": { "slideshow": { "slide_type": "-" @@ -6720,7 +6720,7 @@ "0" ] }, - "execution_count": 209, + "execution_count": 214, "metadata": {}, "output_type": "execute_result" } @@ -6742,7 +6742,7 @@ }, { "cell_type": "code", - "execution_count": 210, + "execution_count": 215, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6756,7 +6756,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0.9\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0.9\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: type complex doesn't define __trunc__ method" ] } @@ -6780,7 +6780,7 @@ }, { "cell_type": "code", - "execution_count": 211, + "execution_count": 216, "metadata": { "slideshow": { "slide_type": "slide" @@ -6793,7 +6793,7 @@ "True" ] }, - "execution_count": 211, + "execution_count": 216, "metadata": {}, "output_type": "execute_result" } @@ -6804,7 +6804,7 @@ }, { "cell_type": "code", - "execution_count": 212, + "execution_count": 217, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6817,7 +6817,7 @@ "True" ] }, - "execution_count": 212, + "execution_count": 217, "metadata": {}, "output_type": "execute_result" } @@ -6828,7 +6828,7 @@ }, { "cell_type": "code", - "execution_count": 213, + "execution_count": 218, "metadata": { "slideshow": { "slide_type": "-" @@ -6841,7 +6841,7 @@ "True" ] }, - "execution_count": 213, + "execution_count": 218, "metadata": {}, "output_type": "execute_result" } @@ -6852,7 +6852,7 @@ }, { "cell_type": "code", - "execution_count": 214, + "execution_count": 219, "metadata": { "slideshow": { "slide_type": "-" @@ -6865,7 +6865,7 @@ "True" ] }, - "execution_count": 214, + "execution_count": 219, "metadata": {}, "output_type": "execute_result" } @@ -6887,7 +6887,7 @@ }, { "cell_type": "code", - "execution_count": 215, + "execution_count": 220, "metadata": { "slideshow": { "slide_type": "slide" @@ -6900,7 +6900,7 @@ "False" ] }, - "execution_count": 215, + "execution_count": 220, "metadata": {}, "output_type": "execute_result" } @@ -6922,7 +6922,7 @@ }, { "cell_type": "code", - "execution_count": 216, + "execution_count": 221, "metadata": { "slideshow": { "slide_type": "fragment" @@ -6935,7 +6935,7 @@ "True" ] }, - "execution_count": 216, + "execution_count": 221, "metadata": {}, "output_type": "execute_result" } @@ -6946,7 +6946,7 @@ }, { "cell_type": "code", - "execution_count": 217, + "execution_count": 222, "metadata": { "slideshow": { "slide_type": "-" @@ -6959,7 +6959,7 @@ "False" ] }, - "execution_count": 217, + "execution_count": 222, "metadata": {}, "output_type": "execute_result" } @@ -7005,7 +7005,7 @@ }, { "cell_type": "code", - "execution_count": 218, + "execution_count": 223, "metadata": { "slideshow": { "slide_type": "slide" @@ -7055,7 +7055,7 @@ }, { "cell_type": "code", - "execution_count": 219, + "execution_count": 224, "metadata": { "slideshow": { "slide_type": "slide" @@ -7068,7 +7068,7 @@ "1" ] }, - "execution_count": 219, + "execution_count": 224, "metadata": {}, "output_type": "execute_result" } @@ -7079,7 +7079,7 @@ }, { "cell_type": "code", - "execution_count": 220, + "execution_count": 225, "metadata": { "slideshow": { "slide_type": "-" @@ -7092,7 +7092,7 @@ "6" ] }, - "execution_count": 220, + "execution_count": 225, "metadata": {}, "output_type": "execute_result" } @@ -7103,7 +7103,7 @@ }, { "cell_type": "code", - "execution_count": 221, + "execution_count": 226, "metadata": { "slideshow": { "slide_type": "fragment" @@ -7116,7 +7116,7 @@ "6" ] }, - "execution_count": 221, + "execution_count": 226, "metadata": {}, "output_type": "execute_result" } @@ -7138,7 +7138,7 @@ }, { "cell_type": "code", - "execution_count": 222, + "execution_count": 227, "metadata": { "slideshow": { "slide_type": "slide" @@ -7152,8 +7152,8 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mReal\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mstrict\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 18\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"n is not an integer-like value; it has decimals\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 19\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mReal\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mstrict\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 18\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"n is not an integer-like value; it has decimals\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 19\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mTypeError\u001b[0m: n is not an integer-like value; it has decimals" ] } @@ -7175,7 +7175,7 @@ }, { "cell_type": "code", - "execution_count": 223, + "execution_count": 228, "metadata": { "slideshow": { "slide_type": "fragment" @@ -7188,7 +7188,7 @@ "6" ] }, - "execution_count": 223, + "execution_count": 228, "metadata": {}, "output_type": "execute_result" } @@ -7210,7 +7210,7 @@ }, { "cell_type": "code", - "execution_count": 224, + "execution_count": 229, "metadata": { "slideshow": { "slide_type": "slide" @@ -7224,8 +7224,8 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m2j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 21\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 22\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m2j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmath\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 21\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 22\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mTypeError\u001b[0m: Factorial is only defined for integers" ] } @@ -7287,7 +7287,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.4" }, "livereveal": { "auto_select": "code", @@ -7315,5 +7315,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/05_numbers_10_review.ipynb b/05_numbers_10_review.ipynb new file mode 100644 index 0000000..819d668 --- /dev/null +++ b/05_numbers_10_review.ipynb @@ -0,0 +1,347 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "# Chapter 5: Bits & Numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Content Review" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers_00_content.ipynb) of the book. Then, work through the questions below." + ] + }, + { + "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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: Why may objects of type `bool` be regarded a **numeric type** as well?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: Why is it *inefficient* to store `bool` objects in bits resembling a **hexadecimal representation**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: Colors are commonly expressed in the **hexadecimal system** in websites (cf., the [HTML](https://en.wikipedia.org/wiki/HTML) and [CSS](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](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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: What does it mean for a code fragment to **fail silently**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: How do we deal with a `float` object's imprecision if we need to **check for equality**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: What data type, built-in or from the [standard library](https://docs.python.org/3/library/index.html), is best suited to represent the [transcendental numbers](https://en.wikipedia.org/wiki/Transcendental_number) $\\pi$ and $\\text{e}$?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: How can **abstract base classes**, for example, as defined in the **numerical tower**, be helpful in enabling **duck typing**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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": [ + "**Q10**: 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: With the built-in [round()](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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q12**: 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q13**: The [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) standard's **special values** provide no benefit in practice as we could always use a **[sentinel](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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q14**: 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15**: 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q16**: From a practitioner's point of view, the built-in [format()](https://docs.python.org/3/library/functions.html#format) function does the *same* as the built-in [round()](https://docs.python.org/3/library/functions.html#round) function." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q17**: The `Decimal` type from the [decimal](https://docs.python.org/3/library/decimal.html) module in the [standard library](https://docs.python.org/3/library/index.html) allows us to model the set of the real numbers $\\mathbb{R}$ *precisely*." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q18**: The `Fraction` type from the [fractions](https://docs.python.org/3/library/fractions.html) module in the [standard library](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": [ + " " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/05_numbers_20_exercises.ipynb b/05_numbers_20_exercises.ipynb new file mode 100644 index 0000000..d00ef2e --- /dev/null +++ b/05_numbers_20_exercises.ipynb @@ -0,0 +1,412 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "# Chapter 5: Bits & Numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Coding Exercises" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers_00_content.ipynb) of the book. Then, work through the exercises below." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Discounting Customer Orders (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1** in [Chapter 2's Exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions_20_exercises.ipynb#Volume-of-a-Sphere) section already revealed that we must consider the effects of the `float` type's imprecision.\n", + "\n", + "This becomes even more important when we deal with numeric data modeling accounting or finance data (cf., [this comment](https://stackoverflow.com/a/24976426) on \"falsehoods programmers believe about money\").\n", + "\n", + "In addition to the *inherent imprecision* of numbers in general, the topic of **[rounding numbers](https://en.wikipedia.org/wiki/Rounding)** is also not as trivial as we might expect! [This article](https://realpython.com/python-rounding/) summarizes everything the data science practitioner needs to know.\n", + "\n", + "In this exercise, we revisit **Q1** from [Chapter 3's Exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_20_exercises.ipynb#Discounting-Customer-Orders) section, and make the `discounted_price()` function work *correctly* for real-life sales data." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1.1**: Execute the code cells below! What results would you have *expected*, and why?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "round(1.5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "round(2.5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "round(2.675, 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1.2**: The built-in [round()](https://docs.python.org/3/library/functions.html#round) function implements the \"**[round half to even](https://en.wikipedia.org/wiki/Rounding#Round_half_to_even)**\" strategy. Describe in one or two sentences what that means!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1.3**: For the revised `discounted_price()` function, we have to tackle *two* issues: First, we have to replace the built-in `float` type with a data type that allows us to control the precision. Second, the discounted price should be rounded according to a more human-friendly rounding strategy, namely \"**[round half away from zero](https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero)**.\"\n", + "\n", + "Describe in one or two sentences how \"**[round half away from zero](https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero)**\" is more in line with how humans think of rounding!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1.4**: We use the `Decimal` type from the [decimal](https://docs.python.org/3/library/decimal.html) module in the [standard library](https://docs.python.org/3/library/index.html) to tackle *both* issues simultaneously.\n", + "\n", + "Assign `euro` a numeric object such that both `Decimal(\"1.5\")` and `Decimal(\"2.5\")` are rounded to `Decimal(\"2\")` (i.e., no decimal) with the [quantize()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize) method!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from decimal import Decimal" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "euro = ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Decimal(\"1.5\").quantize(...)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Decimal(\"2.5\").quantize(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1.5**: Obviously, the two preceding code cells still [round half to even](https://en.wikipedia.org/wiki/Rounding#Round_half_to_even).\n", + "\n", + "The [decimal](https://docs.python.org/3/library/decimal.html) module defines a `ROUND_HALF_UP` flag that we can pass as the second argument to the [quantize()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize) method. Then, it [rounds half away from zero](https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero).\n", + "\n", + "Add `ROUND_HALF_UP` to the code cells! `Decimal(\"2.5\")` should now be rounded to `Decimal(\"3\")`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from decimal import ROUND_HALF_UP" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Decimal(\"1.5\").quantize(...)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Decimal(\"2.5\").quantize(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1.6**: Instead of `euro`, define `cents` such that rounding occurs to *two* decimals! `Decimal(\"2.675\")` should now be rounded to `Decimal(\"2.68\")`. Do *not* forget to include the `ROUND_HALF_UP` flag!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cents = ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Decimal(\"2.675\").quantize(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1.7**: Rewrite the function `discounted_price()` from [Chapter 3's Exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_20_exercises.ipynb#Discounting-Customer-Orders) section!\n", + "\n", + "It takes the *positional* arguments `unit_price` and `quantity` and implements a discount scheme for a line item in a customer order as follows:\n", + "\n", + "- 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 then returns the overall price for the line item as a `Decimal` number with a precision of *two* decimals.\n", + "\n", + "Enable **duck typing** by allowing the function to be called with various numeric types as the arguments, in particular, `quantity` may be a non-integer as well: Use an appropriate **abstract base class** from the [numbers](https://docs.python.org/3/library/numbers.html) module in the [standard library](https://docs.python.org/3/library/index.html) to verify the arguments' types and also that they are both positive!\n", + "\n", + "It is considered a *best practice* to only round towards the *end* of the calculations." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numbers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def discounted_price(...):\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1.8**: Execute the code cells below and verify the final price for the following four test cases:\n", + "\n", + "- $7$ smartphones @ $99.00$ USD\n", + "- $3$ workstations @ $999.00$ USD\n", + "- $19$ GPUs @ $879.95$ USD\n", + "- $14$ Raspberry Pis @ $35.00$ USD\n", + "\n", + "The output should now *always* be a `Decimal` number with *two* decimals!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(99, 7)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(999, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(879.95, 19)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(35, 14)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This also works if `quantity` is passed in as a `float` type." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(99, 7.0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Decimals beyond the first two are gracefully discarded (i.e., *without* rounding errors accumulating)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(99.0001, 7)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The basic input validation ensures that the user of `discounted_price()` does not pass in invalid data. Here, the `\"abc\"` creates a `TypeError`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(\"abc\", 7)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A `-1` passed in as `unit_price` results in a `ValueError`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(-1, 7)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/05_numbers_review_and_exercises.ipynb b/05_numbers_review_and_exercises.ipynb deleted file mode 100644 index 2ae8301..0000000 --- a/05_numbers_review_and_exercises.ipynb +++ /dev/null @@ -1,706 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "# Chapter 5: Bits & Numbers" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Content Review" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Read [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb) of the book. Then work through the eighteen review questions." - ] - }, - { - "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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q2**: Why may objects of type `bool` be regarded a **numeric type** as well?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q3**: Why is it *inefficient* to store `bool` objects in bits resembling a **hexadecimal representation**?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q4**: Colors are commonly expressed in the **hexadecimal system** in websites (cf., the [HTML](https://en.wikipedia.org/wiki/HTML) and [CSS](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](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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q5**: What does it mean for a code fragment to **fail silently**?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q6**: 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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q7**: How do we deal with a `float` object's imprecision if we need to **check for equality**?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q8**: What data type, built-in or from the [standard library](https://docs.python.org/3/library/index.html), is best suited to represent the [transcendental numbers](https://en.wikipedia.org/wiki/Transcendental_number) $\\pi$ and $\\text{e}$?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q9**: How can **abstract base classes**, for example, as defined in the **numerical tower**, be helpful in enabling **duck typing**?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "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": [ - "**Q10**: 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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q11**: With the built-in [round()](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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q12**: 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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q13**: The [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) standard's **special values** provide no benefit in practice as we could always use a **[sentinel](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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q14**: 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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q15**: 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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q16**: From a practitioner's point of view, the built-in [format()](https://docs.python.org/3/library/functions.html#format) function does the *same* as the built-in [round()](https://docs.python.org/3/library/functions.html#round) function." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q17**: The `Decimal` type from the [decimal](https://docs.python.org/3/library/decimal.html) module in the [standard library](https://docs.python.org/3/library/index.html) allows us to model the set of the real numbers $\\mathbb{R}$ *precisely*." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q18**: The `Fraction` type from the [fractions](https://docs.python.org/3/library/fractions.html) module in the [standard library](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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Coding Exercises" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Discounting Customer Orders (revisited)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q11** in [Chapter 2's Review & Exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions_review_and_exercises.ipynb#Volume-of-a-Sphere) section already revealed that we must consider the effects of the `float` type's imprecision.\n", - "\n", - "This becomes even more important when we deal with numeric data modeling accounting or finance data (cf., [this comment](https://stackoverflow.com/a/24976426) on \"falsehoods programmers believe about money\").\n", - "\n", - "In addition to the *inherent imprecision* of numbers in general, the topic of **[rounding numbers](https://en.wikipedia.org/wiki/Rounding)** is also not as trivial as we might expect! [This article](https://realpython.com/python-rounding/) summarizes everything the data science practitioner needs to know.\n", - "\n", - "In this exercise, we revisit **Q9** from [Chapter 3's Review & Exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_review_and_exercises.ipynb#Discounting-Customer-Orders) section, and make the `discounted_price()` function work *correctly* for real-life sales data." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q19.1**: Execute the code cells below! What results would you have *expected*, and why?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "round(1.5)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "round(2.5)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "round(2.675, 2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q19.2**: The built-in [round()](https://docs.python.org/3/library/functions.html#round) function implements the \"**[round half to even](https://en.wikipedia.org/wiki/Rounding#Round_half_to_even)**\" strategy. Describe in one or two sentences what that means!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q19.3**: For the revised `discounted_price()` function, we have to tackle *two* issues: First, we have to replace the built-in `float` type with a data type that allows us to control the precision. Second, the discounted price should be rounded according to a more human-friendly rounding strategy, namely \"**[round half away from zero](https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero)**.\"\n", - "\n", - "Describe in one or two sentences how \"**[round half away from zero](https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero)**\" is more in line with how humans think of rounding!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q19.4**: We use the `Decimal` type from the [decimal](https://docs.python.org/3/library/decimal.html) module in the [standard library](https://docs.python.org/3/library/index.html) to tackle *both* issues simultaneously.\n", - "\n", - "Assign `euro` a numeric object such that both `Decimal(\"1.5\")` and `Decimal(\"2.5\")` are rounded to `Decimal(\"2\")` (i.e., no decimal) with the [quantize()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize) method!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from decimal import Decimal" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "euro = ..." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "Decimal(\"1.5\").quantize(...)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "Decimal(\"2.5\").quantize(...)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q19.5**: Obviously, the two preceding code cells still [round half to even](https://en.wikipedia.org/wiki/Rounding#Round_half_to_even).\n", - "\n", - "The [decimal](https://docs.python.org/3/library/decimal.html) module defines a `ROUND_HALF_UP` flag that we can pass as the second argument to the [quantize()](https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize) method. Then, it [rounds half away from zero](https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero).\n", - "\n", - "Add `ROUND_HALF_UP` to the code cells! `Decimal(\"2.5\")` should now be rounded to `Decimal(\"3\")`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from decimal import ROUND_HALF_UP" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "Decimal(\"1.5\").quantize(...)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "Decimal(\"2.5\").quantize(...)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q19.6**: Instead of `euro`, define `cents` such that rounding occurs to *two* decimals! `Decimal(\"2.675\")` should now be rounded to `Decimal(\"2.68\")`. Do *not* forget to include the `ROUND_HALF_UP` flag!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "cents = ..." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "Decimal(\"2.675\").quantize(...)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q19.7**: Rewrite the function `discounted_price()` from [Chapter 3's Review & Exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_review_and_exercises.ipynb#Discounting-Customer-Orders) section!\n", - "\n", - "It takes the *positional* arguments `unit_price` and `quantity` and implements a discount scheme for a line item in a customer order as follows:\n", - "\n", - "- 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 then returns the overall price for the line item as a `Decimal` number with a precision of *two* decimals.\n", - "\n", - "Enable **duck typing** by allowing the function to be called with various numeric types as the arguments, in particular, `quantity` may be a non-integer as well: Use an appropriate **abstract base class** from the [numbers](https://docs.python.org/3/library/numbers.html) module in the [standard library](https://docs.python.org/3/library/index.html) to verify the arguments' types and also that they are both positive!\n", - "\n", - "It is considered a *best practice* to only round towards the *end* of the calculations." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numbers" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def discounted_price(...):\n", - " ..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q19.8**: Execute the code cells below and verify the final price for the following four test cases:\n", - "\n", - "- $7$ smartphones @ $99.00$ USD\n", - "- $3$ workstations @ $999.00$ USD\n", - "- $19$ GPUs @ $879.95$ USD\n", - "- $14$ Raspberry Pis @ $35.00$ USD\n", - "\n", - "The output should now *always* be a `Decimal` number with *two* decimals!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "discounted_price(99, 7)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "discounted_price(999, 3)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "discounted_price(879.95, 19)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "discounted_price(35, 14)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This also works if `quantity` is passed in as a `float` type." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "discounted_price(99, 7.0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Decimals beyond the first two are gracefully discarded (i.e., *without* rounding errors accumulating)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "discounted_price(99.0001, 7)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The basic input validation ensures that the user of `discounted_price()` does not pass in invalid data. Here, the `\"abc\"` creates a `TypeError`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "discounted_price(\"abc\", 7)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A `-1` passed in as `unit_price` results in a `ValueError`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "discounted_price(-1, 7)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.3" - }, - "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": 2 -} diff --git a/06_text.ipynb b/06_text_00_content.ipynb similarity index 90% rename from 06_text.ipynb rename to 06_text_00_content.ipynb index 3fbbcb5..912142e 100644 --- a/06_text.ipynb +++ b/06_text_00_content.ipynb @@ -54,7 +54,7 @@ }, "outputs": [], "source": [ - "text = \"Lorem ipsum dolor sit amet, consectetur ...\"" + "text = \"Lorem ipsum dolor sit amet, ...\"" ] }, { @@ -80,7 +80,7 @@ { "data": { "text/plain": [ - "139674889049360" + "140483431254256" ] }, "execution_count": 2, @@ -124,7 +124,7 @@ } }, "source": [ - "A `str` object evaluates to itself in a literal notation with enclosing **single quotes** `'` by default. In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Value), we already specified the double quotes `\"` convention we stick to in this book. Yet, single quotes `'` and double quotes `\"` are *perfect* substitutes for all `str` objects that do *not* contain any of the two symbols in it. We could use the reverse convention, as well.\n", + "A `str` object evaluates to itself in a literal notation with enclosing **single quotes** `'` by default. In [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#Value), we already specified the double quotes `\"` convention we stick to in this book. Yet, single quotes `'` and double quotes `\"` are *perfect* substitutes for all `str` objects that do *not* contain any of the two symbols in it. We could use the reverse convention, as well.\n", "\n", "As [this discussion](https://stackoverflow.com/questions/56011/single-quotes-vs-double-quotes-in-python) shows, many programmers have *strong* opinions about that and make up *new* conventions for their projects. Consequently, the discussion was \"closed as not constructive\" by the moderators." ] @@ -141,7 +141,7 @@ { "data": { "text/plain": [ - "'Lorem ipsum dolor sit amet, consectetur ...'" + "'Lorem ipsum dolor sit amet, ...'" ] }, "execution_count": 4, @@ -161,7 +161,7 @@ } }, "source": [ - "As the single quote `'` is often used in the English language as a shortener, we could make an argument in favor of using the double quotes `\"`: There are possibly fewer situations like in the two code cells below, in which we must revert to using a `\\` to **escape** a single quote `'` in a text (cf., the [Special Characters](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb#Special-Characters) section further below). However, double quotes `\"` are often used as well. So, this argument is somewhat not convincing.\n", + "As the single quote `'` is often used in the English language as a shortener, we could make an argument in favor of using the double quotes `\"`: There are possibly fewer situations like in the two code cells below, in which we must revert to using a `\\` to **escape** a single quote `'` in a text (cf., the [Special Characters](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text_00_content.ipynb#Special-Characters) section further below). However, double quotes `\"` are often used as well. So, this argument is somewhat not convincing.\n", "\n", "Many proponents of the single quote `'` usage claim that double quotes `\"` make more **visual noise** on the screen. This argument is also not convincing. On the contrary, one could claim that *two* single quotes `''` look so similar to *one* double quote `\"` that it might not be apparent right away what we are looking at. By sticking to double quotes `\"`, we avoid such danger of confusion.\n", "\n", @@ -182,7 +182,7 @@ { "data": { "text/plain": [ - "\"It's cool that strings are so versatile!\"" + "'It\\'s cool that \"strings\" are versatile'" ] }, "execution_count": 5, @@ -191,7 +191,7 @@ } ], "source": [ - "\"It's cool that strings are so versatile!\"" + "\"It's cool that \\\"strings\\\" are versatile\"" ] }, { @@ -206,7 +206,7 @@ { "data": { "text/plain": [ - "\"It's cool that strings are so versatile!\"" + "'It\\'s cool that \"strings\" are versatile'" ] }, "execution_count": 6, @@ -215,7 +215,7 @@ } ], "source": [ - "'It\\'s cool that strings are so versatile!'" + "'It\\'s cool that \"strings\" are versatile'" ] }, { @@ -615,7 +615,7 @@ } }, "source": [ - "To use constructs familiar from [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb#The-try-Statement) to explain what `with open(...) as file:` does, below is a formulation with a `try` statement *equivalent* to the `with` statement above. The `finally`-branch is always executed, even if an exception is raised in the `for`-loop. So, `file` is sure to be closed too, with a somewhat less expressive formulation." + "To use constructs familiar from [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_00_content.ipynb#The-try-Statement) to explain what `with open(...) as file:` does, below is a formulation with a `try` statement *equivalent* to the `with` statement above. The `finally`-branch is *always* executed, even if an exception is raised in the `for`-loop. So, `file` is sure to be closed too, with a somewhat less expressive formulation." ] }, { @@ -737,7 +737,7 @@ } }, "source": [ - "A **sequence** is yet another *abstract* concept (cf., the \"*Containers vs. Iterables*\" section in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Containers-vs.-Iterables)).\n", + "A **sequence** is yet another *abstract* concept (cf., the \"*Containers vs. Iterables*\" section in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_content.ipynb#Containers-vs.-Iterables)).\n", "\n", "It unifies *four* [orthogonal](https://en.wikipedia.org/wiki/Orthogonality) (i.e., \"independent\") behaviors into one idea: Any data type, such as `str`, is considered a sequence if it simultaneously\n", "\n", @@ -746,9 +746,9 @@ "3. comes with a *predictable* **order** of its\n", "4. **finite** number of \"things.\"\n", "\n", - "[Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Collections-vs.-Sequences) formalizes sequences in great detail. Here, we keep our focus on the `str` type that historically received its name as it models a \"**[string of characters](https://en.wikipedia.org/wiki/String_%28computer_science%29)**,\" and a \"string\" is more formally called a sequence in the computer science literature.\n", + "[Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb#Collections-vs.-Sequences) formalizes sequences in great detail. Here, we keep our focus on the `str` type that historically received its name as it models a \"**[string of characters](https://en.wikipedia.org/wiki/String_%28computer_science%29)**,\" and a \"string\" is more formally called a sequence in the computer science literature.\n", "\n", - "Behaving like a sequence, `str` objects may be treated like `list` objects in many cases. For example, the built-in [len()](https://docs.python.org/3/library/functions.html#len) function tells us how many elements (i.e., characters) make up `text`. [len()](https://docs.python.org/3/library/functions.html#len) would not work with an *infinite* object: As anything modeled in a program must fit into a computer's finite memory at runtime, there cannot exist objects containing a truly infinite number of elements; however, [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Iterators-vs.-Iterables) introduces iterable data types that can be used to model an *infinite* series of elements and that, consequently, have no concept of \"length.\"" + "Behaving like a sequence, `str` objects may be treated like `list` objects in many cases. For example, the built-in [len()](https://docs.python.org/3/library/functions.html#len) function tells us how many elements (i.e., characters) make up `text`. [len()](https://docs.python.org/3/library/functions.html#len) would not work with an *infinite* object: As anything modeled in a program must fit into a computer's finite memory at runtime, there cannot exist objects containing a truly infinite number of elements; however, [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb#Iterators-vs.-Iterables) introduces iterable data types that can be used to model an *infinite* series of elements and that, consequently, have no concept of \"length.\"" ] }, { @@ -763,7 +763,7 @@ { "data": { "text/plain": [ - "43" + "31" ] }, "execution_count": 23, @@ -799,7 +799,7 @@ "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 , c o n s e c t e t u r . . . " + "L o r e m i p s u m d o l o r s i t a m e t , . . . " ] } ], @@ -912,7 +912,7 @@ } }, "source": [ - "As `str` objects have the additional property of being *ordered*, we may **index** into them to obtain individual characters with the **indexing operator** `[]`. This is analogous to how we obtained individual elements of a `list` object in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?)." + "As `str` objects have the additional property of being *ordered*, we may **index** into them to obtain individual characters with the **indexing operator** `[]`. This is analogous to how we obtained individual elements of a `list` object in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#Who-am-I?-And-how-many?)." ] }, { @@ -1031,7 +1031,7 @@ } ], "source": [ - "text[42] # = len(text) - 1" + "text[30] # = len(text) - 1" ] }, { @@ -1061,13 +1061,13 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m43\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;31m# = len(text)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m31\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;31m# = len(text)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mIndexError\u001b[0m: string index out of range" ] } ], "source": [ - "text[43] # = len(text)" + "text[31] # = len(text)" ] }, { @@ -1078,7 +1078,21 @@ } }, "source": [ - "We may use *negative* indexes to start counting from the end of the `str` object. That only works because sequences are *finite*." + "We may use *negative* indexes to start counting from the end of the `str` object, as shown in the figure below. That only works because sequences are *finite*." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "| Slot | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12| 13| 14| 15| 16| 17| 18| 19| 20| 21| 22| 23| 24| 25| 26| 27| 28| 29| 30|\n", + "|:---------:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|\n", + "|**Reverse**|-31|-30|-29|-28|-27|-26|-25|-24|-23|-22|-21|-20|-19|-18|-17|-16|-15|-14|-13|-12|-11|-10|-9 |-8 |-7 |-6 |-5 |-4 |-3 |-2 |-1 |\n", + "| **Char** |`L`|`o`|`r`|`e`|`m`|` `|`i`|`p`|`s`|`u`|`m`|` `|`d`|`o`|`l`|`o`|`r`|` `|`s`|`i`|`t`|` `|`a`|`m`|`e`|`t`|`,`|` `|`.`|`.`|`.`|" ] }, { @@ -1086,7 +1100,7 @@ "execution_count": 33, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "-" } }, "outputs": [ @@ -1105,17 +1119,6 @@ "text[-1]" ] }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "One reason why programmers like to start counting at 0 is that a positive index and its *corresponding* negative index always add up to the length of the sequence." - ] - }, { "cell_type": "code", "execution_count": 34, @@ -1128,7 +1131,7 @@ { "data": { "text/plain": [ - "'i'" + "'L'" ] }, "execution_count": 34, @@ -1137,7 +1140,18 @@ } ], "source": [ - "text[6]" + "text[-31] # = -len(text)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "One reason why programmers like to start counting at 0 is that a positive index and its *corresponding* negative index always add up to the length of the sequence. Here, `6` and `25` add to `31`." ] }, { @@ -1145,7 +1159,7 @@ "execution_count": 35, "metadata": { "slideshow": { - "slide_type": "-" + "slide_type": "fragment" } }, "outputs": [ @@ -1161,7 +1175,31 @@ } ], "source": [ - "text[-37]" + "text[6]" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'i'" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[-25]" ] }, { @@ -1192,7 +1230,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 37, "metadata": { "slideshow": { "slide_type": "slide" @@ -1205,7 +1243,7 @@ "'Lorem'" ] }, - "execution_count": 36, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } @@ -1222,14 +1260,14 @@ } }, "source": [ - "Whereas the *start* is always included in the result, the *end* is not. Counter-intuitive at first, this makes working with individual slices easier as they add up to the original `str` object again.\n", + "Whereas the *start* is always included in the result, the *end* is not. Counter-intuitive at first, this makes working with individual slices easier as they \"add\" up to the original `str` object again (cf., the \"*String Operations*\" sub-section below regarding the overloaded `+` operator). Because the *end* is *not* included, we end the second slice below with `len(text)` or `31` below.\n", "\n", - "So, as the *end* is *not* included, we must end the second slice with `len(text)` or `43` below." + "Not including the *end* has another advantage: The difference \"*end* minus *start*\" tells us how many elements the resulting slice has. Above, for example, `5 - 0` implies that `\"Lorem\"` consists of `5` characters. So, colloquially, `0:5` means \"taking the first five characters.\" That rule only works if both *start* and *end* are *positive*." ] }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 38, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1239,10 +1277,10 @@ { "data": { "text/plain": [ - "'Lorem ipsum dolor sit amet, consectetur ...'" + "'Lorem ipsum dolor sit amet, ...'" ] }, - "execution_count": 37, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } @@ -1259,12 +1297,12 @@ } }, "source": [ - "For convenience, the indexes do not need to lie within the range from `0` to `len(text)` when slicing." + "By combining a *positive* start with a *negative* end index, we specify both ends of the slice *relative* to the ends of the entire `str` object. So, colloquially, `6:-5` below means \"drop the first six and last five characters.\" The length of the resulting slice can *not* be calculated from the indexes and depends only on the length of the original `str` object." ] }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 39, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1274,10 +1312,45 @@ { "data": { "text/plain": [ - "'Lorem ipsum dolor sit amet, consectetur ...'" + "'ipsum dolor sit amet'" ] }, - "execution_count": 38, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[6:-5]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For convenience, the indexes do not need to lie within the range from `0` to `len(text)` when slicing. So, no `IndexError` is raised here." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum dolor sit amet, ...'" + ] + }, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } @@ -1294,12 +1367,12 @@ } }, "source": [ - "Commonly, we leave out the `0` for the *start* and the *end* if it is equal to the \"length.\"" + "If left out, *start* defaults to `0` and *end* to the \"length\" of the `str` object. Here, we take a \"full\" slice that is essentially a copy of the original `str` object." ] }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 41, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1309,16 +1382,16 @@ { "data": { "text/plain": [ - "'Lorem ipsum dolor sit amet, consectetur ...'" + "'Lorem ipsum dolor sit amet, ...'" ] }, - "execution_count": 39, + "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "text[:5] + text[5:]" + "text[:]" ] }, { @@ -1329,12 +1402,12 @@ } }, "source": [ - "Slicing makes it easy to obtain shorter versions of the original `str` object." + "Slicing (and indexing) makes it easy to obtain shorter versions of the original `str` object." ] }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 42, "metadata": { "slideshow": { "slide_type": "skip" @@ -1347,13 +1420,13 @@ "'Lorem ipsum dolor sit amet.'" ] }, - "execution_count": 40, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "text[:26] + text[42]" + "text[:26] + text[30]" ] }, { @@ -1369,7 +1442,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 43, "metadata": { "slideshow": { "slide_type": "slide" @@ -1379,10 +1452,10 @@ { "data": { "text/plain": [ - "'Lrmismdlrstae,cnettr..'" + "'Lrmismdlrstae,..'" ] }, - "execution_count": 41, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } @@ -1404,7 +1477,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 44, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1414,10 +1487,10 @@ { "data": { "text/plain": [ - "'... rutetcesnoc ,tema tis rolod muspi meroL'" + "'... ,tema tis rolod muspi meroL'" ] }, - "execution_count": 42, + "execution_count": 44, "metadata": {}, "output_type": "execute_result" } @@ -1445,16 +1518,16 @@ } }, "source": [ - "Whereas elements of a `list` object *may* be *re-assigned*, as shortly hinted at in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?), this is *not* allowed for `str` objects. Once created, they *cannot* be *changed*. Formally, we say that they are **immutable**. In that regard, `str` objects and all the numeric types in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb) are alike.\n", + "Whereas elements of a `list` object *may* be *re-assigned*, as shortly hinted at in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#Who-am-I?-And-how-many?), this is *not* allowed for `str` objects. Once created, they *cannot* be *changed*. Formally, we say that they are **immutable**. In that regard, `str` objects and all the numeric types in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers_00_content.ipynb) are alike.\n", "\n", - "On the contrary, objects that may be changed after creation, are called **mutable**. We already saw in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Who-am-I?-And-how-many?) how mutable objects are more difficult to reason about for a beginner, in particular, if more than *one* variable references it. Yet, mutability does have its place in a programmer's toolbox, and we revisit this idea in the next chapters.\n", + "On the contrary, objects that may be changed after creation, are called **mutable**. We already saw in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#Who-am-I?-And-how-many?) how mutable objects are more difficult to reason about for a beginner, in particular, if more than *one* variable references it. Yet, mutability does have its place in a programmer's toolbox, and we revisit this idea in the next chapters.\n", "\n", "The `TypeError` indicates that `str` objects are *immutable*: Assignment to an index or a slice are *not* supported." ] }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 45, "metadata": { "slideshow": { "slide_type": "slide" @@ -1468,7 +1541,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"Z\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"Z\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: 'str' object does not support item assignment" ] } @@ -1479,7 +1552,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 46, "metadata": { "slideshow": { "slide_type": "-" @@ -1493,7 +1566,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"random\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"random\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: 'str' object does not support item assignment" ] } @@ -1528,7 +1601,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 47, "metadata": { "slideshow": { "slide_type": "slide" @@ -1538,10 +1611,10 @@ { "data": { "text/plain": [ - "'Lorem ipsum dolor sit amet, consectetur ...'" + "'Lorem ipsum dolor sit amet, ...'" ] }, - "execution_count": 45, + "execution_count": 47, "metadata": {}, "output_type": "execute_result" } @@ -1552,7 +1625,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 48, "metadata": { "slideshow": { "slide_type": "-" @@ -1565,7 +1638,7 @@ "22" ] }, - "execution_count": 46, + "execution_count": 48, "metadata": {}, "output_type": "execute_result" } @@ -1576,7 +1649,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 49, "metadata": { "slideshow": { "slide_type": "-" @@ -1589,7 +1662,7 @@ "-1" ] }, - "execution_count": 47, + "execution_count": 49, "metadata": {}, "output_type": "execute_result" } @@ -1600,7 +1673,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 50, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1613,7 +1686,7 @@ "12" ] }, - "execution_count": 48, + "execution_count": 50, "metadata": {}, "output_type": "execute_result" } @@ -1635,7 +1708,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 51, "metadata": { "slideshow": { "slide_type": "slide" @@ -1648,7 +1721,7 @@ "1" ] }, - "execution_count": 49, + "execution_count": 51, "metadata": {}, "output_type": "execute_result" } @@ -1659,7 +1732,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 52, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1672,7 +1745,7 @@ "13" ] }, - "execution_count": 50, + "execution_count": 52, "metadata": {}, "output_type": "execute_result" } @@ -1683,7 +1756,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 53, "metadata": { "slideshow": { "slide_type": "skip" @@ -1696,7 +1769,7 @@ "-1" ] }, - "execution_count": 51, + "execution_count": 53, "metadata": {}, "output_type": "execute_result" } @@ -1718,7 +1791,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 54, "metadata": { "slideshow": { "slide_type": "slide" @@ -1731,7 +1804,7 @@ "1" ] }, - "execution_count": 52, + "execution_count": 54, "metadata": {}, "output_type": "execute_result" } @@ -1753,7 +1826,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 55, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1766,7 +1839,7 @@ "2" ] }, - "execution_count": 53, + "execution_count": 55, "metadata": {}, "output_type": "execute_result" } @@ -1788,7 +1861,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 56, "metadata": { "slideshow": { "slide_type": "skip" @@ -1801,7 +1874,7 @@ "2" ] }, - "execution_count": 54, + "execution_count": 56, "metadata": {}, "output_type": "execute_result" } @@ -1823,7 +1896,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 57, "metadata": { "slideshow": { "slide_type": "slide" @@ -1834,43 +1907,6 @@ "example = \"random\"" ] }, - { - "cell_type": "code", - "execution_count": 56, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "139674947440512" - ] - }, - "execution_count": 56, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "id(example)" - ] - }, - { - "cell_type": "code", - "execution_count": 57, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [], - "source": [ - "lower = example.lower()" - ] - }, { "cell_type": "code", "execution_count": 58, @@ -1883,7 +1919,7 @@ { "data": { "text/plain": [ - "139674887826488" + "140483525349552" ] }, "execution_count": 58, @@ -1891,6 +1927,43 @@ "output_type": "execute_result" } ], + "source": [ + "id(example)" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "lower = example.lower()" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140483422704176" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "id(lower)" ] @@ -1908,7 +1981,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 61, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1921,7 +1994,7 @@ "False" ] }, - "execution_count": 59, + "execution_count": 61, "metadata": {}, "output_type": "execute_result" } @@ -1932,7 +2005,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 62, "metadata": { "slideshow": { "slide_type": "-" @@ -1945,7 +2018,7 @@ "True" ] }, - "execution_count": 60, + "execution_count": 62, "metadata": {}, "output_type": "execute_result" } @@ -1969,7 +2042,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 63, "metadata": { "slideshow": { "slide_type": "slide" @@ -1985,7 +2058,6 @@ "dolor\n", "sit\n", "amet,\n", - "consectetur\n", "...\n" ] } @@ -2008,7 +2080,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 64, "metadata": { "slideshow": { "slide_type": "slide" @@ -2021,7 +2093,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 65, "metadata": { "slideshow": { "slide_type": "-" @@ -2034,7 +2106,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 66, "metadata": { "slideshow": { "slide_type": "-" @@ -2047,7 +2119,7 @@ "'This will become a sentence'" ] }, - "execution_count": 64, + "execution_count": 66, "metadata": {}, "output_type": "execute_result" } @@ -2069,7 +2141,7 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 67, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2082,7 +2154,7 @@ "'a b c d e'" ] }, - "execution_count": 65, + "execution_count": 67, "metadata": {}, "output_type": "execute_result" } @@ -2104,7 +2176,7 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 68, "metadata": { "slideshow": { "slide_type": "slide" @@ -2117,7 +2189,7 @@ "'This is a sentence'" ] }, - "execution_count": 66, + "execution_count": 68, "metadata": {}, "output_type": "execute_result" } @@ -2145,12 +2217,12 @@ } }, "source": [ - "As mentioned in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Operator-Overloading), the `+` and `*` operators are *overloaded* and used for **string concatenation**. They always create *new* `str` objects. That has nothing to do with the `str` type's immutability, but is the default behavior of operators." + "As mentioned in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#Operator-Overloading), the `+` and `*` operators are *overloaded* and used for **string concatenation**. They always create *new* `str` objects. That has nothing to do with the `str` type's immutability, but is the default behavior of operators." ] }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 69, "metadata": { "slideshow": { "slide_type": "slide" @@ -2163,7 +2235,7 @@ "'Hello Lore'" ] }, - "execution_count": 67, + "execution_count": 69, "metadata": {}, "output_type": "execute_result" } @@ -2174,7 +2246,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 70, "metadata": { "slideshow": { "slide_type": "-" @@ -2187,7 +2259,7 @@ "'Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum '" ] }, - "execution_count": 68, + "execution_count": 70, "metadata": {}, "output_type": "execute_result" } @@ -2220,7 +2292,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 71, "metadata": { "slideshow": { "slide_type": "slide" @@ -2233,65 +2305,6 @@ "B = \"Banana\"" ] }, - { - "cell_type": "code", - "execution_count": 70, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 70, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "A < B" - ] - }, - { - "cell_type": "code", - "execution_count": 71, - "metadata": { - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 71, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "a < B" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "One way to fix this is to only compare lower-cased `str` objects." - ] - }, { "cell_type": "code", "execution_count": 72, @@ -2312,6 +2325,65 @@ "output_type": "execute_result" } ], + "source": [ + "A < B" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a < B" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "One way to fix this is to only compare lower-cased `str` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "a < B.lower()" ] @@ -2329,7 +2401,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 75, "metadata": { "slideshow": { "slide_type": "slide" @@ -2425,7 +2497,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 76, "metadata": { "slideshow": { "slide_type": "slide" @@ -2439,7 +2511,7 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 77, "metadata": { "slideshow": { "slide_type": "-" @@ -2452,7 +2524,7 @@ "'Hello Alexander! Good morning.'" ] }, - "execution_count": 75, + "execution_count": 77, "metadata": {}, "output_type": "execute_result" } @@ -2474,7 +2546,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 78, "metadata": { "slideshow": { "slide_type": "slide" @@ -2487,7 +2559,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 79, "metadata": { "slideshow": { "slide_type": "-" @@ -2500,7 +2572,7 @@ "'Pi is 3.14'" ] }, - "execution_count": 77, + "execution_count": 79, "metadata": {}, "output_type": "execute_result" } @@ -2511,7 +2583,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 80, "metadata": { "slideshow": { "slide_type": "-" @@ -2524,7 +2596,7 @@ "'Pi is 3.142'" ] }, - "execution_count": 78, + "execution_count": 80, "metadata": {}, "output_type": "execute_result" } @@ -2557,7 +2629,7 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 81, "metadata": { "slideshow": { "slide_type": "slide" @@ -2570,7 +2642,7 @@ "'Hello Alexander! Good morning.'" ] }, - "execution_count": 79, + "execution_count": 81, "metadata": {}, "output_type": "execute_result" } @@ -2592,7 +2664,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 82, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2605,7 +2677,7 @@ "'Good morning, Alexander'" ] }, - "execution_count": 80, + "execution_count": 82, "metadata": {}, "output_type": "execute_result" } @@ -2627,7 +2699,7 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 83, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2640,7 +2712,7 @@ "'Hello Alexander! Good morning.'" ] }, - "execution_count": 81, + "execution_count": 83, "metadata": {}, "output_type": "execute_result" } @@ -2662,7 +2734,7 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 84, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2675,7 +2747,7 @@ "'Pi is 3.14'" ] }, - "execution_count": 82, + "execution_count": 84, "metadata": {}, "output_type": "execute_result" } @@ -2703,14 +2775,14 @@ } }, "source": [ - "The `%` operator that we saw in the context of modulo division in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#%28Arithmetic%29-Operators) is overloaded with string interpolation when its first operand is a `str` object. The second operand consists of all expressions to be filled in. Format specifiers work with a `%` instead of curly braces and according to a different set of rules referred to as **[printf-style string formatting](https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting)**. So, `{:.2f}` becomes `%.2f`.\n", + "The `%` operator that we saw in the context of modulo division in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#%28Arithmetic%29-Operators) is overloaded with string interpolation when its first operand is a `str` object. The second operand consists of all expressions to be filled in. Format specifiers work with a `%` instead of curly braces and according to a different set of rules referred to as **[printf-style string formatting](https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting)**. So, `{:.2f}` becomes `%.2f`.\n", "\n", "This way of string interpolation is the oldest and originates from the [C language](https://en.wikipedia.org/wiki/C_%28programming_language%29). It is still widely spread, but we should use one of the other two ways instead. We show it here mainly for completeness sake." ] }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 85, "metadata": { "slideshow": { "slide_type": "skip" @@ -2723,7 +2795,7 @@ "'Pi is 3.14'" ] }, - "execution_count": 83, + "execution_count": 85, "metadata": {}, "output_type": "execute_result" } @@ -2740,12 +2812,12 @@ } }, "source": [ - "To insert more than one expression, we must list them in order and between parenthesis `(` and `)`. As [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#The-tuple-Type) reveals, this literal syntax creates an object of type `tuple`. Also, to format an expression as text, we use the format specifier `%s`." + "To insert more than one expression, we must list them in order and between parenthesis `(` and `)`. As [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb#The-tuple-Type) reveals, this literal syntax creates an object of type `tuple`. Also, to format an expression as text, we use the format specifier `%s`." ] }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 86, "metadata": { "slideshow": { "slide_type": "skip" @@ -2758,7 +2830,7 @@ "'Hello Alexander! Good morning.'" ] }, - "execution_count": 84, + "execution_count": 86, "metadata": {}, "output_type": "execute_result" } @@ -2793,7 +2865,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 87, "metadata": { "slideshow": { "slide_type": "slide" @@ -2816,7 +2888,7 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 88, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2848,7 +2920,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 89, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2880,7 +2952,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 90, "metadata": { "slideshow": { "slide_type": "skip" @@ -2893,7 +2965,7 @@ "'This is a sentence\\nthat is printed\\non three lines.'" ] }, - "execution_count": 88, + "execution_count": 90, "metadata": {}, "output_type": "execute_result" } @@ -2928,7 +3000,7 @@ }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 91, "metadata": { "slideshow": { "slide_type": "slide" @@ -2961,7 +3033,7 @@ }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 92, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2970,10 +3042,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "(unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape (, line 1)", + "evalue": "(unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print(\"C:\\Users\\Administrator\\Desktop\\Project_Folder\")\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print(\"C:\\Users\\Administrator\\Desktop\\Project_Folder\")\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape\n" ] } ], @@ -2994,7 +3066,7 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 93, "metadata": { "slideshow": { "slide_type": "slide" @@ -3015,7 +3087,7 @@ }, { "cell_type": "code", - "execution_count": 92, + "execution_count": 94, "metadata": { "slideshow": { "slide_type": "-" @@ -3047,7 +3119,7 @@ }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 95, "metadata": { "slideshow": { "slide_type": "slide" @@ -3068,7 +3140,7 @@ }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 96, "metadata": { "slideshow": { "slide_type": "-" @@ -3111,7 +3183,7 @@ }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 97, "metadata": { "slideshow": { "slide_type": "slide" @@ -3120,10 +3192,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "EOL while scanning string literal (, line 1)", + "evalue": "EOL while scanning string literal (, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m \"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m EOL while scanning string literal\n" + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m \"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m EOL while scanning string literal\n" ] } ], @@ -3146,7 +3218,7 @@ }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 98, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3173,7 +3245,7 @@ }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 99, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3186,7 +3258,7 @@ "'\\nI am a multi-line string\\nconsisting of 4 lines.\\n'" ] }, - "execution_count": 97, + "execution_count": 99, "metadata": {}, "output_type": "execute_result" } @@ -3208,7 +3280,7 @@ }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 100, "metadata": { "slideshow": { "slide_type": "fragment" @@ -3243,7 +3315,7 @@ }, { "cell_type": "code", - "execution_count": 99, + "execution_count": 101, "metadata": { "slideshow": { "slide_type": "slide" @@ -3279,7 +3351,7 @@ }, { "cell_type": "code", - "execution_count": 100, + "execution_count": 102, "metadata": { "slideshow": { "slide_type": "slide" @@ -3293,7 +3365,7 @@ }, { "cell_type": "code", - "execution_count": 101, + "execution_count": 103, "metadata": { "slideshow": { "slide_type": "-" @@ -3306,7 +3378,7 @@ "\"Lorem Ipsum is simply dummy text of the printing and typesetting industry.\\nLorem Ipsum has been the industry's standard dummy text ever since the 1500s\\nwhen an unknown printer took a galley of type and scrambled it to make a type\\nspecimen book. It has survived not only five centuries but also the leap into\\nelectronic typesetting, remaining essentially unchanged. It was popularised in\\nthe 1960s with the release of Letraset sheets.\\n\"" ] }, - "execution_count": 101, + "execution_count": 103, "metadata": {}, "output_type": "execute_result" } @@ -3317,7 +3389,7 @@ }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 104, "metadata": { "slideshow": { "slide_type": "-" @@ -3383,7 +3455,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.4" }, "livereveal": { "auto_select": "code", @@ -3411,5 +3483,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/06_text_review_and_exercises.ipynb b/06_text_10_review.ipynb similarity index 94% rename from 06_text_review_and_exercises.ipynb rename to 06_text_10_review.ipynb index 40ac8c4..fe9588e 100644 --- a/06_text_review_and_exercises.ipynb +++ b/06_text_10_review.ipynb @@ -19,7 +19,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Read [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb) of the book. Then work through the eight review questions." + "Read [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text_00_content.ipynb) of the book. Then, work through the questions below." ] }, { @@ -33,7 +33,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Answer the following questions briefly with *at most* 300 characters per question!" + "Answer the following questions *briefly*!" ] }, { @@ -184,7 +184,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.4" }, "toc": { "base_numbering": 1, @@ -201,5 +201,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/07_sequences.ipynb b/07_sequences_00_content.ipynb similarity index 97% rename from 07_sequences.ipynb rename to 07_sequences_00_content.ipynb index f20b670..924467a 100644 --- a/07_sequences.ipynb +++ b/07_sequences_00_content.ipynb @@ -19,11 +19,11 @@ } }, "source": [ - "We studied numbers (cf., [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb)) and textual data (cf., [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb)) first, mainly because objects of the presented data types are \"simple,\" for two reasons: First, they are *immutable*, and, as we saw in the \"*Who am I? And how many?*\" section in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.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", + "We studied numbers (cf., [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers_00_content.ipynb)) and textual data (cf., [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text_00_content.ipynb)) first, mainly because objects of the presented data types are \"simple,\" for two reasons: First, they are *immutable*, and, as we saw in the \"*Who am I? And how many?*\" section in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_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 and [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings.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. Unsurprisingly, computer scientists refer to the ideas and theories behind these data types as **[data structures](https://en.wikipedia.org/wiki/Data_structure)**.\n", + "This chapter, [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings_00_content.ipynb), and [Chapter 9](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/09_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. Unsurprisingly, computer scientists refer to the ideas and theories behind these data types as **[data structures](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](https://en.wikipedia.org/wiki/Spreadsheet) or [matrices](https://en.wikipedia.org/wiki/Matrix_%28mathematics%29)/[vectors](https://en.wikipedia.org/wiki/Vector_%28mathematics_and_physics%29). Such 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." ] @@ -47,9 +47,9 @@ } }, "source": [ - "[Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb#A-\"String\"-of-Characters) already describes the *sequence* properties of `str` objects. Here, we take a step back and study these properties on their own before looking at bigger ideas.\n", + "[Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text_00_content.ipynb#A-\"String\"-of-Characters) already describes the *sequence* properties of `str` objects. Here, we take a step back and study these properties on their own before looking at bigger ideas.\n", "\n", - "The [collections.abc](https://docs.python.org/3/library/collections.abc.html) module in the [standard library](https://docs.python.org/3/library/index.html) defines a variety of **abstract base classes** (ABCs). We saw ABCs already in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb#The-Numerical-Tower), where we use the ones from the [numbers](https://docs.python.org/3/library/numbers.html) module in the [standard library](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](https://docs.python.org/3/library/collections.abc.html) module to classify the data types in this chapter and [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings.ipynb) according to their behavior in various contexts.\n", + "The [collections.abc](https://docs.python.org/3/library/collections.abc.html) module in the [standard library](https://docs.python.org/3/library/index.html) defines a variety of **abstract base classes** (ABCs). We saw ABCs already in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers_00_content.ipynb#The-Numerical-Tower), where we use the ones from the [numbers](https://docs.python.org/3/library/numbers.html) module in the [standard library](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](https://docs.python.org/3/library/collections.abc.html) module to classify the data types in this chapter and [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings_00_content.ipynb) according to their behavior in various contexts.\n", "\n", "As an illustration, consider `numbers` and `text` below, two objects of *different* types." ] @@ -65,7 +65,7 @@ "outputs": [], "source": [ "numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]\n", - "text = \"Lorem ipsum dolor sit amet, consectetur ...\"" + "text = \"Lorem ipsum dolor sit amet, ...\"" ] }, { @@ -114,7 +114,7 @@ "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 , c o n s e c t e t u r . . . " + "L o r e m i p s u m d o l o r s i t a m e t , . . . " ] } ], @@ -131,7 +131,7 @@ } }, "source": [ - "In [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.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](https://docs.python.org/3/library/collections.abc.html) module.\n", + "In [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_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](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](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](https://docs.python.org/3/library/functions.html) include [enumerate()](https://docs.python.org/3/library/functions.html#enumerate), [sum()](https://docs.python.org/3/library/functions.html#sum), or [zip()](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." ] @@ -181,7 +181,7 @@ } }, "source": [ - "As in the context of *goose typing* in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb#Goose-Typing), we can use ABCs with the built-in [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function to check if an object supports a behavior.\n", + "As in the context of *goose typing* in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers_00_content.ipynb#Goose-Typing), we can use ABCs with the built-in [isinstance()](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`." ] @@ -536,7 +536,7 @@ { "data": { "text/plain": [ - "43" + "31" ] }, "execution_count": 17, @@ -767,7 +767,7 @@ "name": "stdout", "output_type": "stream", "text": [ - ". . . r u t e t c e s n o c , t e m a t i s r o l o d m u s p i m e r o L " + ". . . , t e m a t i s r o l o d m u s p i m e r o L " ] } ], @@ -1028,7 +1028,7 @@ { "data": { "text/plain": [ - "140157873498184" + "140417317434464" ] }, "execution_count": 34, @@ -1074,7 +1074,7 @@ "source": [ "Alternatively, we use the [list()](https://docs.python.org/3/library/functions.html#func-list) built-in to create a `list` object out of an iterable we pass to it as the argument.\n", "\n", - "For example, we can wrap the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in with [list()](https://docs.python.org/3/library/functions.html#func-list): As described in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Containers-vs.-Iterables), `range` objects, like `range(1, 13)` below, are iterable and generate `int` objects \"on the fly\" (i.e., one by one). The [list()](https://docs.python.org/3/library/functions.html#func-list) around it acts like a `for`-loop and **materializes** twelve `int` objects in memory that then become the elements of the newly created `list` object. [PythonTutor](http://www.pythontutor.com/visualize.html#code=r%20%3D%20range%281,%2013%29%0Al%20%3D%20list%28range%281,%2013%29%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows this difference visually." + "For example, we can wrap the [range()](https://docs.python.org/3/library/functions.html#func-range) built-in with [list()](https://docs.python.org/3/library/functions.html#func-list): As described in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_content.ipynb#Containers-vs.-Iterables), `range` objects, like `range(1, 13)` below, are iterable and generate `int` objects \"on the fly\" (i.e., one by one). The [list()](https://docs.python.org/3/library/functions.html#func-list) around it acts like a `for`-loop and **materializes** twelve `int` objects in memory that then become the elements of the newly created `list` object. [PythonTutor](http://www.pythontutor.com/visualize.html#code=r%20%3D%20range%281,%2013%29%0Al%20%3D%20list%28range%281,%2013%29%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows this difference visually." ] }, { @@ -1299,11 +1299,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "[] \t140157882223304 \t\n", - "10 \t93987402609984 \t\n", - "20.0 \t140157882328912 \t\n", - "Thirty \t140157873509520 \t\n", - "[40, 50] \t140157873031176 \t\n" + "[] \t140417315676720 \t\n", + "10 \t94860849595456 \t\n", + "20.0 \t140417316476784 \t\n", + "Thirty \t140417315739568 \t\n", + "[40, 50] \t140417315720800 \t\n" ] } ], @@ -2017,7 +2017,7 @@ { "data": { "text/plain": [ - "140157882223304" + "140417315676720" ] }, "execution_count": 65, @@ -2041,7 +2041,7 @@ { "data": { "text/plain": [ - "140157882223304" + "140417315676720" ] }, "execution_count": 66, @@ -2065,7 +2065,7 @@ { "data": { "text/plain": [ - "140157882223304" + "140417315676720" ] }, "execution_count": 67, @@ -2191,7 +2191,7 @@ { "data": { "text/plain": [ - "140157882223304" + "140417315676720" ] }, "execution_count": 72, @@ -2215,7 +2215,7 @@ { "data": { "text/plain": [ - "140157873029448" + "140417376809600" ] }, "execution_count": 73, @@ -2392,7 +2392,7 @@ { "data": { "text/plain": [ - "140157873498184" + "140417317434464" ] }, "execution_count": 79, @@ -2401,7 +2401,7 @@ } ], "source": [ - "id(nested)" + "id(nested) # same memory location as before" ] }, { @@ -2678,7 +2678,7 @@ "source": [ "The `list` type is an essential data structure in any real-world Python application, and many typical `list`-related algorithms from computer science theory are already built into it at the C level (cf., the [documentation](https://docs.python.org/3/library/stdtypes.html#mutable-sequence-types) or the [tutorial](https://docs.python.org/3/tutorial/datastructures.html#more-on-lists) for a full overview; unfortunately, not all methods have direct links). So, understanding and applying the built-in methods of the `list` type not only speeds up the development process but also makes programs significantly faster.\n", "\n", - "In contrast to the `str` type's methods in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb#String-Methods) (e.g., [upper()](https://docs.python.org/3/library/stdtypes.html#str.upper) or [lower()](https://docs.python.org/3/library/stdtypes.html#str.lower)), the `list` type's methods that mutate an object do so *in place*. That means they *never* create *new* `list` objects and return `None` to indicate that. So, we must *never* assign the return value of `list` methods to the variable holding the list!\n", + "In contrast to the `str` type's methods in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text_00_content.ipynb#String-Methods) (e.g., [upper()](https://docs.python.org/3/library/stdtypes.html#str.upper) or [lower()](https://docs.python.org/3/library/stdtypes.html#str.lower)), the `list` type's methods that mutate an object do so *in place*. That means they *never* create *new* `list` objects and return `None` to indicate that. So, we must *never* assign the return value of `list` methods to the variable holding the list!\n", "\n", "Let's look at the following `names` example." ] @@ -3016,7 +3016,7 @@ "source": [ "The [sort()](https://docs.python.org/3/library/stdtypes.html#list.sort) method and the [sorted()](https://docs.python.org/3/library/functions.html#sorted) function sort the elements in `names` in alphabetical order, forward or backward. However, that does *not* hold in general.\n", "\n", - "We mention above that `list` objects may contain objects of *any* type and even of *mixed* types. Because of that, the sorting is **[delegated](https://en.wikipedia.org/wiki/Delegation_(object-oriented_programming))** to the elements in a `list` object. In a way, Python \"asks\" the elements in a `list` object to sort themselves. As `names` contains only `str` objects, they are sorted according the the comparison rules explained in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb#String-Comparison).\n", + "We mention above that `list` objects may contain objects of *any* type and even of *mixed* types. Because of that, the sorting is **[delegated](https://en.wikipedia.org/wiki/Delegation_(object-oriented_programming))** to the elements in a `list` object. In a way, Python \"asks\" the elements in a `list` object to sort themselves. As `names` contains only `str` objects, they are sorted according the the comparison rules explained in [Chapter 6](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text_00_content.ipynb#String-Comparison).\n", "\n", "To customize the sorting, we pass a keyword-only `key` argument to [sort()](https://docs.python.org/3/library/stdtypes.html#list.sort) or [sorted()](https://docs.python.org/3/library/functions.html#sorted), which must be a `function` object accepting *one* positional argument. Then, the elements in the `list` object are passed to that one by one, and the return values are used as the **sort keys**. The `key` argument is also a popular use case for `lambda` expressions.\n", "\n", @@ -4150,7 +4150,7 @@ "\n", "The revised `add_xyz()` function below is more natural to reason about as it does *not* modify the passed in `arg` internally. This approach is following the **[functional programming](https://en.wikipedia.org/wiki/Functional_programming)** paradigm that is going through a \"renaissance\" currently. Two essential characteristics of functional programming are that a function *never* changes its inputs and *always* returns the same output given the same inputs.\n", "\n", - "For a beginner, it is probably better to stick to this idea and not change any arguments as the original `add_xyz()` above. However, functions that modify and return the argument passed in are an important aspect of object-oriented programming, as explained in Chapter 9." + "For a beginner, it is probably better to stick to this idea and not change any arguments as the original `add_xyz()` above. However, functions that modify and return the argument passed in are an important aspect of object-oriented programming, as explained in Chapter 10." ] }, { @@ -4511,7 +4511,7 @@ { "data": { "text/plain": [ - "140157873009144" + "140417315626832" ] }, "execution_count": 158, @@ -5227,7 +5227,6 @@ "cell_type": "code", "execution_count": 184, "metadata": { - "scrolled": false, "slideshow": { "slide_type": "skip" } @@ -6154,7 +6153,7 @@ } }, "source": [ - "Unpacking allows us to rewrite the iterative `fibonacci()` function from [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#\"Hard-at-first-Glance\"-Example:-Fibonacci-Numbers-%28revisited%29) in a concise way, now also supporting *goose typing* with the [numbers](https://docs.python.org/3/library/numbers.html) module from the [standard library](https://docs.python.org/3/library/index.html)." + "Unpacking allows us to rewrite the iterative `fibonacci()` function from [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_content.ipynb#\"Hard-at-first-Glance\"-Example:-Fibonacci-Numbers-%28revisited%29) in a concise way, now also supporting *goose typing* with the [numbers](https://docs.python.org/3/library/numbers.html) module from the [standard library](https://docs.python.org/3/library/index.html)." ] }, { @@ -6577,7 +6576,7 @@ } }, "source": [ - "In the \"*Packing & Unpacking with Functions*\" [exercise](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_review_and_exercises.ipynb#Packing-&-Unpacking-with-Functions) at the end of this chapter, we look at `product()` in more detail.\n", + "In the \"*Packing & Unpacking with Functions*\" [exercise](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_review_and_exercises_00_content.ipynb#Packing-&-Unpacking-with-Functions) at the end of this chapter, we look at `product()` in more detail.\n", "\n", "While we needed to unpack `one_hundred` above to avoid the semantic error, unpacking an argument in a function call may also be a convenience in general.\n", "\n", @@ -6699,7 +6698,7 @@ "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.\n", "\n", - "A better way is to create a *custom* data type. While that is covered in depth in Chapter 9, the [collections](https://docs.python.org/3/library/collections.html) module in the [standard library](https://docs.python.org/3/library/index.html) provides a [namedtuple()](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." + "A better way is to create a *custom* data type. While that is covered in depth in Chapter 10, the [collections](https://docs.python.org/3/library/collections.html) module in the [standard library](https://docs.python.org/3/library/index.html) provides a [namedtuple()](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." ] }, { @@ -6762,7 +6761,7 @@ { "data": { "text/plain": [ - "140157466523464" + "140416708056240" ] }, "execution_count": 238, @@ -6915,7 +6914,7 @@ { "data": { "text/plain": [ - "140157872590424" + "140417315422528" ] }, "execution_count": 243, @@ -7354,7 +7353,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 258, @@ -7654,7 +7653,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 269, @@ -7875,7 +7874,7 @@ } }, "source": [ - "Using the [map()](https://docs.python.org/3/library/functions.html#map) and [filter()](https://docs.python.org/3/library/functions.html#filter) built-ins, we can quickly switch the order: Filter first and then transform the remaining elements. This variant equals the \"*A simple Filter*\" example from [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Example:-A-simple-Filter). On the contrary, code with `for`-loops and `if` statements is more tedious to adapt. Additionally, `map` and `filter` objects are optimized at the C level and, therefore, a lot faster as well." + "Using the [map()](https://docs.python.org/3/library/functions.html#map) and [filter()](https://docs.python.org/3/library/functions.html#filter) built-ins, we can quickly switch the order: Filter first and then transform the remaining elements. This variant equals the \"*A simple Filter*\" example from [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_content.ipynb#Example:-A-simple-Filter). On the contrary, code with `for`-loops and `if` statements is more tedious to adapt. Additionally, `map` and `filter` objects are optimized at the C level and, therefore, a lot faster as well." ] }, { @@ -8209,7 +8208,7 @@ "\n", "Often, such functions are used *only once* in a program. However, the primary purpose of functions is to *reuse* them. In such cases, it makes more sense to define them \"anonymously\" right at the position where the first argument goes.\n", "\n", - "As mentioned in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb#Anonymous-Functions), Python provides `lambda` expressions to create `function` objects *without* a name referencing them.\n", + "As mentioned in [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions_00_content.ipynb#Anonymous-Functions), Python provides `lambda` expressions to create `function` objects *without* a name referencing them.\n", "\n", "So, the above `add()` function could be rewritten as a `lambda` expression like so ..." ] @@ -8423,7 +8422,7 @@ "source": [ "For [map()](https://docs.python.org/3/library/functions.html#map) and [filter()](https://docs.python.org/3/library/functions.html#filter), Python provides a nice syntax appealing to people who like mathematics.\n", "\n", - "Consider again the \"*A simple Filter*\" example from [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Example:-A-simple-Filter), written with combined `for` and `if` statements. So, the mapping and filtering steps happen simultaneously." + "Consider again the \"*A simple Filter*\" example from [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_content.ipynb#Example:-A-simple-Filter), written with combined `for` and `if` statements. So, the mapping and filtering steps happen simultaneously." ] }, { @@ -8488,7 +8487,7 @@ } }, "source": [ - "**List comprehensions**, or **list-comps** for short, are *expressions* to derive *new* `list` objects out of *existing* ones (cf., [reference](https://docs.python.org/3/reference/expressions.html#displays-for-lists-sets-and-dictionaries)). A single *expression* like below can replace the compound `for` *statement* from above." + "**List comprehensions**, or **listcomps** for short, are *expressions* to derive *new* `list` objects out of *existing* ones (cf., [reference](https://docs.python.org/3/reference/expressions.html#displays-for-lists-sets-and-dictionaries)). A single *expression* like below can replace the compound `for` *statement* from above." ] }, { @@ -9108,7 +9107,7 @@ { "data": { "text/plain": [ - " at 0x7f790c3a55e8>" + " at 0x7fb57436f3d0>" ] }, "execution_count": 312, @@ -9263,7 +9262,7 @@ { "data": { "text/plain": [ - " at 0x7f790c3a5a20>" + " at 0x7fb57436f650>" ] }, "execution_count": 317, @@ -9728,7 +9727,7 @@ { "data": { "text/plain": [ - " at 0x7f790c3a5b10>" + " at 0x7fb57436fad0>" ] }, "execution_count": 333, @@ -10074,7 +10073,7 @@ } }, "source": [ - "[all()](https://docs.python.org/3/library/functions.html#all) can be viewed as syntactic sugar replacing a `for`-loop: Internally, [all()](https://docs.python.org/3/library/functions.html#all) implements the *short-circuiting* strategy from [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb#Short-Circuiting), and we mimic that by testing for the *opposite* condition in the `if` statement and leaving the `for`-loop early with the `break` statement. In the worst case, if `threshold` were, for example, `150`, we would loop over *all* elements in the *iterable*, which must be *finite* for the code to work. So, [all()](https://docs.python.org/3/library/functions.html#all) is a *linear search* in disguise." + "[all()](https://docs.python.org/3/library/functions.html#all) can be viewed as syntactic sugar replacing a `for`-loop: Internally, [all()](https://docs.python.org/3/library/functions.html#all) implements the *short-circuiting* strategy from [Chapter 3](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_00_content.ipynb#Short-Circuiting), and we mimic that by testing for the *opposite* condition in the `if` statement and leaving the `for`-loop early with the `break` statement. In the worst case, if `threshold` were, for example, `150`, we would loop over *all* elements in the *iterable*, which must be *finite* for the code to work. So, [all()](https://docs.python.org/3/library/functions.html#all) is a *linear search* in disguise." ] }, { @@ -10388,13 +10387,13 @@ } }, "source": [ - "With the new concepts in this chapter, let's rewrite the book's introductory \"*Averaging Even Numbers*\" example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Example:-Averaging-Even-Numbers) such that it efficiently handles a large sequence of numbers.\n", + "With the new concepts in this chapter, let's rewrite the book's introductory \"*Averaging Even Numbers*\" example in [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#Example:-Averaging-Even-Numbers) such that it efficiently handles a large sequence of numbers.\n", "\n", "We assume the `average_evens()` function below is called with a *finite* and *iterable* object, which generates a \"stream\" of numeric objects that can be cast as `int` objects because the idea of even and odd numbers only makes sense in the context of whole numbers.\n", "\n", "The generator expression `(int(n) for n in numbers)` implements the type casting, and when it is evaluated, *nothing* happens except that a `generator` object is stored in `integers`. Then, with the [reduce()](https://docs.python.org/3/library/functools.html#functools.reduce) function from the [functools](https://docs.python.org/3/library/functools.html) module, we *simultaneously* add up *and* count the even numbers produced by the inner generator expression `((n, 1) for n in integers if n % 2 == 0)`. That results in a `generator` object producing `tuple` objects consisting of the next *even* number in line and `1`. Two such `tuple` objects are then iteratively passed to the `lambda` expression as the `x` and `y` arguments. `x` represents the total and the count of the even numbers processed so far, while `y`'s first element, `y[0]`, is the next even number to be added to the running total. The result of the [reduce()](https://docs.python.org/3/library/functools.html#functools.reduce) function is again a `tuple` object, namely the final `total` and `count`. Lastly, we calculate the simple average.\n", "\n", - "In summary, the implementation of `average_evens()` does *not* keep materialized `list` objects internally like its predecessors from [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb), but processes the elements of the `numbers` argument on a one-by-one basis." + "In summary, the implementation of `average_evens()` does *not* keep materialized `list` objects internally like its predecessors from [Chapter 2](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions_00_content.ipynb), but processes the elements of the `numbers` argument on a one-by-one basis." ] }, { @@ -11014,7 +11013,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 375, @@ -11110,7 +11109,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 379, @@ -11318,7 +11317,7 @@ } }, "source": [ - "In [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#The-for-Statement), we argue that the `for` statement is syntactic sugar, replacing the `while` statement in many scenarios. In particular, a `for`-loop saves us two tasks: Managing an index variable *and* obtaining the individual elements by indexing. In this sub-section, we look at a more realistic picture, using the new terminology as well.\n", + "In [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_content.ipynb#The-for-Statement), we argue that the `for` statement is syntactic sugar, replacing the `while` statement in many scenarios. In particular, a `for`-loop saves us two tasks: Managing an index variable *and* obtaining the individual elements by indexing. In this sub-section, we look at a more realistic picture, using the new terminology as well.\n", "\n", "Let's print out the elements of a `list` object as the *iterable* to be looped over." ] @@ -11460,7 +11459,7 @@ "source": [ "Now that we know the concept of an *iterator*, let's compare some of the built-ins introduced in this chapter in detail and make sure we understand what is going on in memory. This sub-section is thus a great summary of this chapter as well.\n", "\n", - "We use two simple examples, `numbers` and `memoryless`, to guide us through the discussion. `numbers` creates *thirteen* objects in memory and `memoryless` only *one* (cf., [PythonTutor](http://www.pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%5D%0Amemoryless%20%3D%20range%281,%2013%29&cumulative=false&curInstr=2&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false))" + "We use two simple examples, `numbers` and `memoryless`, to guide us through the discussion. `numbers` creates *thirteen* objects in memory and `memoryless` only *one* (cf., [PythonTutor](http://www.pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%5D%0Amemoryless%20%3D%20range%281,%2013%29&cumulative=false&curInstr=2&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false))." ] }, { @@ -11499,7 +11498,7 @@ "source": [ "The [sorted()](https://docs.python.org/3/library/functions.html#sorted) function takes a *finite* and *iterable* object as its argument and *materializes* its elements into a *new* `list` object that is returned.\n", "\n", - "The argument may already be materialized, as is the case with `numbers`, but could also be an *iterator* that generates *new* objects, such as `memoryless`. In both cases, we end up with materialized `list` objects with the elements sorted in *forward* order (cf., [PythonTutor](http://www.pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%5D%0Amemoryless%20%3D%20range%281,%2013%29%0Aresult1%20%3D%20sorted%28numbers%29%0Aresult2%20%3D%20sorted%28memoryless%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false))." + "The argument may already be materialized, as is the case with `numbers`, but may also be an *iterable* without any objects in it, such as `memoryless`. In both cases, we end up with materialized `list` objects with the elements sorted in *forward* order (cf., [PythonTutor](http://www.pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%5D%0Amemoryless%20%3D%20range%281,%2013%29%0Aresult1%20%3D%20sorted%28numbers%29%0Aresult2%20%3D%20sorted%28memoryless%29&cumulative=false&curInstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false))." ] }, { @@ -11693,7 +11692,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 397, @@ -11717,7 +11716,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 398, @@ -12216,7 +12215,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.4" }, "livereveal": { "auto_select": "code", @@ -12244,5 +12243,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/07_sequences_10_review.ipynb b/07_sequences_10_review.ipynb new file mode 100644 index 0000000..a2d2f5c --- /dev/null +++ b/07_sequences_10_review.ipynb @@ -0,0 +1,397 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "# Chapter 7: Sequential Data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Content Review" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb) of the book. Then, work through the questions below." + ] + }, + { + "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](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_content.ipynb#Containers-vs.-Iterables). How do they relate to **sequences**? " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: What are **abstract base classes**? How can we make use of the ones from the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module in the [standard library](https://docs.python.org/3/library/index.html)?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: With the [map()](https://docs.python.org/3/library/functions.html#map) and [filter()](https://docs.python.org/3/library/functions.html#filter) built-ins and the [reduce()](https://docs.python.org/3/library/functools.html#functools.reduce) function from the [functools](https://docs.python.org/3/library/functools.html) module in the [standard library](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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: Looking at the `lambda` expression inside [reduce()](https://docs.python.org/3/library/functools.html#functools.reduce) below, what \"simple\" [built-in function](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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: What is the primary use case of **list comprehensions**? Why do we describe them as **eager**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q12**: **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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q13**: What does it mean for the **boolean reducers**, the built-in [all()](https://docs.python.org/3/library/functions.html#all) and [any()](https://docs.python.org/3/library/functions.html#any) functions, to follow the **short-circuiting** strategy?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q14**: What is an **iterator**? How does it relate to an **iterable**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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": [ + "**Q15**: `sequence` objects are *not* part of core Python but may be imported from the [standard library](https://docs.python.org/3/library/index.html)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q16**: The built-in [sort()](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()](https://docs.python.org/3/library/functions.html#sorted) method on `list` objects *mutates* them *in place*." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q17**: 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q18**: `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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q19**: 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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q20**: Just as **list comprehensions** create `list` objects, **tuple comprehensions** create `tuple` objects." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/07_sequences_review_and_exercises.ipynb b/07_sequences_20_exercises.ipynb similarity index 70% rename from 07_sequences_review_and_exercises.ipynb rename to 07_sequences_20_exercises.ipynb index 68582b2..b2a8169 100644 --- a/07_sequences_review_and_exercises.ipynb +++ b/07_sequences_20_exercises.ipynb @@ -8,357 +8,6 @@ "# Chapter 7: Sequential Data" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Content Review" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Read [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb) of the book. Then work through the twenty review questions." - ] - }, - { - "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**: We have seen **containers** and **iterables** before in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Containers-vs.-Iterables). How do they relate to **sequences**? " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q2**: What are **abstract base classes**? How can we make use of the ones from the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module in the [standard library](https://docs.python.org/3/library/index.html)?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "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": [ - " " - ] - }, - { - "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": [ - " " - ] - }, - { - "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": [ - " " - ] - }, - { - "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": [ - " " - ] - }, - { - "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": [ - " " - ] - }, - { - "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": [ - " " - ] - }, - { - "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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q9**: With the [map()](https://docs.python.org/3/library/functions.html#map) and [filter()](https://docs.python.org/3/library/functions.html#filter) built-ins and the [reduce()](https://docs.python.org/3/library/functools.html#functools.reduce) function from the [functools](https://docs.python.org/3/library/functools.html) module in the [standard library](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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q10**: Looking at the `lambda` expression inside [reduce()](https://docs.python.org/3/library/functools.html#functools.reduce) below, what \"simple\" [built-in function](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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q11**: What is the primary use case of **list comprehensions**? Why do we describe them as **eager**?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q12**: **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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q13**: What does it mean for the **boolean reducers**, the built-in [all()](https://docs.python.org/3/library/functions.html#all) and [any()](https://docs.python.org/3/library/functions.html#any) functions, to follow the **short-circuiting** strategy?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q14**: What is an **iterator**? How does it relate to an **iterable**?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "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": [ - "**Q15**: `sequence` objects are *not* part of core Python but may be imported from the [standard library](https://docs.python.org/3/library/index.html)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q16**: The built-in [sort()](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()](https://docs.python.org/3/library/functions.html#sorted) method on `list` objects *mutates* them *in place*." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q17**: 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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q18**: `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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q19**: 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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q20**: Just as **list comprehensions** create `list` objects, **tuple comprehensions** create `tuple` objects." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -366,6 +15,13 @@ "## Coding Exercises" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb) of the book. Then, work through the exercises below." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -377,7 +33,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q21.1**: 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", + "**Q1.1**: 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." ] @@ -414,7 +70,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q21.2**: Provide a one-line expression to obtain the *same* result as `nested_sum()`!\n", + "**Q1.2**: Provide a one-line expression to obtain the *same* result as `nested_sum()`!\n", "\n", "Hints: Use a *list comprehension*, or maybe even a *generator expression*. You may want to use the built-in [sum()](https://docs.python.org/3/library/functions.html#sum) function several times." ] @@ -432,9 +88,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q21.3**: 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", + "**Q1.3**: 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()](https://docs.python.org/3/library/functions.html#isinstance) function to check how an element is to be processed. Get extra credit for adhering to *goose typing*, as explained in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb#Goose-Typing)." + "Hints: Use the built-in [isinstance()](https://docs.python.org/3/library/functions.html#isinstance) function to check how an element is to be processed. Get extra credit for adhering to *goose typing*, as explained in [Chapter 5](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers_00_content.ipynb#Goose-Typing)." ] }, { @@ -478,7 +134,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q21.4.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", + "**Q1.4.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](https://en.wikipedia.org/wiki/Cumulative_distribution_function) from statistics." ] @@ -515,7 +171,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q21.4.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? Describe everything in the docstring.\n", + "**Q1.4.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? Describe everything in the docstring.\n", "\n", "Hint: It is possible to write this without any extra input validation." ] @@ -540,7 +196,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In the \"*Function Definitions & Calls*\" section in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Function-Definitions-&-Calls), we define the following function `product()`. In this exercise, you will improve it by making it more \"user-friendly.\"" + "In the \"*Function Definitions & Calls*\" section in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb#Function-Definitions-&-Calls), we define the following function `product()`. In this exercise, you will improve it by making it more \"user-friendly.\"" ] }, { @@ -565,7 +221,7 @@ "source": [ "The `*` in the function's header line *packs* all *positional* arguments passed to `product()` into one *iterable* called `args`.\n", "\n", - "**Q22.1**: What is the data type of `args` within the function's body?" + "**Q2.1**: What is the data type of `args` within the function's body?" ] }, { @@ -620,7 +276,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q22.2**: What line in the body of `product()` causes this exception? What is the exact problem?" + "**Q2.2**: What line in the body of `product()` causes this exception? What is the exact problem?" ] }, { @@ -634,7 +290,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Function-Definitions-&-Calls), we also pass a `list` object, like `one_hundred`, to `product()`, and *no* exception is raised." + "In [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb#Function-Definitions-&-Calls), we also pass a `list` object, like `one_hundred`, to `product()`, and *no* exception is raised." ] }, { @@ -659,7 +315,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q22.3**: What is wrong with that? What *kind* of error (cf., [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Formal-vs.-Natural-Languages)) is that conceptually? Describe precisely what happens to the passed in `one_hundred` in every line within `product()`!" + "**Q2.3**: What is wrong with that? What *kind* of error (cf., [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/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()`!" ] }, { @@ -723,7 +379,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q22.4**: Describe why no error occurs by going over every line in `product()`!" + "**Q2.4**: Describe why no error occurs by going over every line in `product()`!" ] }, { @@ -762,7 +418,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q22.5**: What line causes troubles now? What is the exact problem?" + "**Q2.5**: What line causes troubles now? What is the exact problem?" ] }, { @@ -776,7 +432,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q22.6**: 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", + "**Q2.6**: 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()](https://docs.python.org/3/library/functions.html#sum) function for some inspiration." ] @@ -835,9 +491,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q22.7**: 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", + "**Q2.7**: 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](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb#Keyword-only-Arguments))." + "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](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions_00_content.ipynb#Keyword-only-Arguments))." ] }, { @@ -946,7 +602,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q22.8**: What is a **collection**? How is that different from a **sequence**?" + "**Q2.8**: What is a **collection**? How is that different from a **sequence**?" ] }, { @@ -960,7 +616,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q22.9**: 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", + "**Q2.9**: 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()](https://docs.python.org/3/library/functions.html#len) and [isinstance()](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](https://docs.python.org/3/library/collections.abc.html) module in the [standard library](https://docs.python.org/3/library/index.html). You may want to *re-assign* `args` inside the body." ] @@ -1041,7 +697,7 @@ "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 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings.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!" + "**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 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_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!" ] }, { @@ -1091,7 +747,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q22.10**: Adapt the latest version of `product()` to also raise a `TypeError` if called *without* any *positional* arguments!" + "**Q2.10**: Adapt the latest version of `product()` to also raise a `TypeError` if called *without* any *positional* arguments!" ] }, { @@ -1279,7 +935,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q22.11**: What line causes the `TypeError`? What line is really the problem in the latest implementation of `product()`? Describe what happens on each line in the function's body until the exception is raised!" + "**Q2.11**: What line causes the `TypeError`? What line is really the problem in the latest implementation of `product()`? Describe what happens on each line in the function's body until the exception is raised!" ] }, { @@ -1293,7 +949,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q22.12**: Adapt `product()` one last time to make it work with *iterators* as well!\n", + "**Q2.12**: Adapt `product()` one last time to make it work with *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 *iterators* also exhibit? You may want to look at the documentations on the built-in [max()](https://docs.python.org/3/library/functions.html#max), [min()](https://docs.python.org/3/library/functions.html#min), and [sum()](https://docs.python.org/3/library/functions.html#sum) functions: What kind of argument do they take?" ] @@ -1442,7 +1098,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q23.1**: `list` objects are **sequences**. What *four* behaviors do they always come with?" + "**Q3.1**: `list` objects are **sequences**. What *four* behaviors do they always come with?" ] }, { @@ -1456,7 +1112,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q23.2**: Write a function `mean()` that calculates the simple arithmetic mean of a given `sequence` with numbers!\n", + "**Q3.2**: 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](https://docs.python.org/3/library/functions.html) only. A `for`-loop is *not* needed." ] @@ -1493,7 +1149,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q23.3**: Write a function `std()` that calculates the [standard deviation](https://en.wikipedia.org/wiki/Standard_deviation) of a `sequence` of numbers! Integrate your `mean()` version from before and the [sqrt()](https://docs.python.org/3/library/math.html#math.sqrt) function from the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html) provided to you below. Make sure `std()` calls `mean()` only *once* internally! Repeated calls to `mean()` would be a waste of computational resources.\n", + "**Q3.3**: Write a function `std()` that calculates the [standard deviation](https://en.wikipedia.org/wiki/Standard_deviation) of a `sequence` of numbers! Integrate your `mean()` version from before and the [sqrt()](https://docs.python.org/3/library/math.html#math.sqrt) function from the [math](https://docs.python.org/3/library/math.html) module in the [standard library](https://docs.python.org/3/library/index.html) 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*." ] @@ -1539,7 +1195,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q23.4**: Complete `standardize()` below that takes a `sequence` of numbers and returns a `list` object with the **[z-scores](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()](https://docs.python.org/3/library/functions.html#round) function and pass on the keyword-only argument `digits` to it.\n", + "**Q3.4**: Complete `standardize()` below that takes a `sequence` of numbers and returns a `list` object with the **[z-scores](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()](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." ] @@ -1610,7 +1266,7 @@ "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", - "**Q23.5.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", + "**Q3.5.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." ] @@ -1654,7 +1310,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q23.5.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." + "**Q3.5.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." ] }, { @@ -1689,7 +1345,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q23.6**: 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", + "**Q3.6**: 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()](https://docs.python.org/3/library/functions.html#abs) and [zip()](https://docs.python.org/3/library/functions.html#zip) built-ins." ] @@ -1798,7 +1454,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q23.7**: What happens if you call `mean()` with `data` as the argument? What is the problem?\n", + "**Q3.7**: 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." ] @@ -1823,7 +1479,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q23.8**: Write a function `take_sample()` that takes an `iterator` as its argument, like `data`, and creates a *materialized* `list` object out of its first `n` elements, defaulting to `1_000`!\n", + "**Q3.8**: Write a function `take_sample()` that takes an `iterator` 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()](https://docs.python.org/3/library/functions.html#next) and the [range()](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()](https://docs.python.org/3/library/itertools.html#itertools.islice) in the [itertools](https://docs.python.org/3/library/itertools.html) module in the [standard library](https://docs.python.org/3/library/index.html)." ] @@ -1885,7 +1541,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q23.9**: 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", + "**Q3.9**: 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", @@ -1949,7 +1605,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q23.10.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", + "**Q3.10.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." ] @@ -1969,9 +1625,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q23.10.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", + "**Q3.10.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](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Example:-Averaging-Even-Numbers-%28revisited%29) for some inspiration, which also contains a generator expression producing `tuple` objects." + "Hint: Look at the revisited \"*Averaging Even Numbers*\" example in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb#Example:-Averaging-Even-Numbers-%28revisited%29) for some inspiration, which also contains a generator expression producing `tuple` objects." ] }, { @@ -2003,7 +1659,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q23.10.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*." + "**Q3.10.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*." ] }, { @@ -2044,7 +1700,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q23.10.4**: Lastly, put everything together in the `skip_outliers()` function! Make sure you refer to `iterable` inside the function and not the global `data`." + "**Q3.10.4**: Lastly, put everything together in the `skip_outliers()` function! Make sure you refer to `iterable` inside the function and not the global `data`." ] }, { @@ -2109,7 +1765,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q23.11**: 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?" + "**Q3.11**: 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?" ] }, { @@ -2136,7 +1792,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.4" }, "toc": { "base_numbering": 1, @@ -2153,5 +1809,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/08_mappings.ipynb b/08_mappings_00_content.ipynb similarity index 96% rename from 08_mappings.ipynb rename to 08_mappings_00_content.ipynb index d42237b..2e19dbe 100644 --- a/08_mappings.ipynb +++ b/08_mappings_00_content.ipynb @@ -19,9 +19,9 @@ } }, "source": [ - "While [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb) focuses on one special kind of *collection* types, namely *sequences*, this chapter introduces two more: **Mappings** and **sets**. We present the data types belonging to these two groups in one chapter as they share the *same* underlying implementation at the C Level, known as **[hash tables](https://en.wikipedia.org/wiki/Hash_table)**.\n", + "While [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb) focuses on one special kind of *collection* types, namely *sequences*, this chapter introduces two more kinds: **Mappings** and **sets**. We present the data types belonging to these two groups in one chapter as they share the *same* underlying implementation at the C Level, known as **[hash tables](https://en.wikipedia.org/wiki/Hash_table)**.\n", "\n", - "The most important mapping type in this chapter is the `dict` type that we have not yet seen before (cf, [documentation](https://docs.python.org/3/library/stdtypes.html#dict)). It is an essential part in a data science practitioner's toolbox for two reasons: First, Python employs `dict` objects basically \"everywhere\" internally. So, we must understand how they work to become better at Python in general. Second, after the many concepts related to *sequential* data, the ideas behind *mappings* enhance our general problem solving skills. As a concrete example, we look at the concept of **memoization** to complete our picture of *recursion*, as depicted in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#Recursion). We end this chapter with a discussion of *set* types." + "The most important mapping type in this chapter is the `dict` type that we have not yet seen before (cf, [documentation](https://docs.python.org/3/library/stdtypes.html#dict)). It is an essential part in a data science practitioner's toolbox for two reasons: First, Python employs `dict` objects basically \"everywhere\" internally. So, we must understand how they work to become better at Python in general. Second, after the many concepts related to *sequential* data, the ideas behind *mappings* enhance our general problem solving skills. As a concrete example, we look at the concept of **memoization** to complete our picture of *recursion*, as depicted in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_content.ipynb#Recursion). We end this chapter with a discussion of *set* types." ] }, { @@ -45,7 +45,7 @@ "source": [ "A *mapping* is a one-to-one correspondence from a set of **keys** to a set of **values**. In other words, a *mapping* is a *collection* of **key-value pairs**, also called **items** for short.\n", "\n", - "In the context of mappings, the term *value* has a meaning different from the general *value* that *every* object has: In the \"bag\" analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Value), we descibe an object's value to be the concrete $0$s and $1$s it contains. Here, the terms *key* and *value* mean the *role* an object takes within a mapping. Both, *keys* and *values*, are real *objects* with a distinct *value*. So, the student should always remember the double meaning of the term *value* in this chapter!\n", + "In the context of mappings, the term *value* has a meaning different from the general *value* that *every* object has: In the \"bag\" analogy from [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#Value), we descibe an object's value to be the concrete $0$s and $1$s it contains. Here, the terms *key* and *value* mean the *role* an object takes within a mapping. Both, *keys* and *values*, are real *objects* with a distinct *value*. So, the student should always remember the double meaning of the term *value* in this chapter!\n", "\n", "Let's continue with an example. To create a `dict` object, we commonly use the literal notation, `{..: .., ..: .., ...}`, and list all the items. `to_words` below maps the `int` objects `0`, `1`, and `2` to their English word equivalents, `\"zero\"`, `\"one\"`, and `\"two\"`, and `from_words` does the opposite. A stylistic side note: Pythonistas often expand `dict` or `list` definitions by writing each item or element on a line on their own. The commas `,` after the *last* items are *not* a mistake, as well, although they *may* be left out. Besides easier reading, such style has actual technical advantages (cf., [source](https://www.python.org/dev/peps/pep-0008/#when-to-use-trailing-commas)) that we do not go into detail about here." ] @@ -107,7 +107,7 @@ { "data": { "text/plain": [ - "139752734794304" + "139953979690352" ] }, "execution_count": 3, @@ -502,7 +502,7 @@ } }, "source": [ - "In [Chapter 0](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb#Isn't-C-a-lot-faster?), we argue that a major advantage of using Python is that it takes care of the memory managment for us. In line with that, we have never talked about the C level implementation thus far in the book. However, the `dict` type, among others, exhibits some behaviors that may seem \"weird\" for a beginner. To built a solid intuition that enables the student to better \"predict\" how `dict` objects behave, we describe the underlying implementation details on a conceptual level (i.e., without C code).\n", + "In [Chapter 0](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up_00_content.ipynb#Isn't-C-a-lot-faster?), we argue that a major advantage of using Python is that it takes care of the memory managment for us. In line with that, we have never talked about the C level implementation thus far in the book. However, the `dict` type, among others, exhibits some behaviors that may seem \"weird\" for a beginner. To built a solid intuition that enables the student to better \"predict\" how `dict` objects behave, we describe the underlying implementation details on a conceptual level (i.e., without C code).\n", "\n", "The first unintuitive behavior is that we may *not* use a *mutable* object as a key. That results in a `TypeError`." ] @@ -597,10 +597,10 @@ } }, "source": [ - "| Bucket | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |\n", - "| :---: |:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n", - "| Key |*...*|*...*|*...*|*...*|*...*|*...*|*...*|*...*|\n", - "| Value |*...*|*...*|*...*|*...*|*...*|*...*|*...*|*...*|" + "| Bucket | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |\n", + "| :---: |:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n", + "| **Key** |*...*|*...*|*...*|*...*|*...*|*...*|*...*|*...*|\n", + "|**Value**|*...*|*...*|*...*|*...*|*...*|*...*|*...*|*...*|" ] }, { @@ -628,7 +628,7 @@ { "data": { "text/plain": [ - "-2242252796476499076" + "-1033934764475250610" ] }, "execution_count": 17, @@ -707,7 +707,7 @@ } }, "source": [ - "The [glossary](https://docs.python.org/3/glossary.html#term-hashable) states a second requirement for hashability, namely that \"objects which *compare equal* must have the *same* hash value.\" The purpose of this is to ensure that if we put, for example, `1` as a key in a `dict` object, we can look it up later with `1.0`. In other words, we can look up keys by their object's value (i.e., in the meaning of [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb#Value)). The converse statement does *not* hold: Two objects *may* (accidentally) have the *same* hash value and *not* compare equal." + "The [glossary](https://docs.python.org/3/glossary.html#term-hashable) states a second requirement for hashability, namely that \"objects which *compare equal* must have the *same* hash value.\" The purpose of this is to ensure that if we put, for example, `1` as a key in a `dict` object, we can look it up later with `1.0`. In other words, we can look up keys by their object's value (i.e., in the meaning of [Chapter 1](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb#Value)). The converse statement does *not* hold: Two objects *may* (accidentally) have the *same* hash value and *not* compare equal." ] }, { @@ -951,12 +951,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "zero\t-2242252796476499076\t0b...010010000100\t100\t4\n", - "one\t6742734203392562231\t0b...010000110111\t111\t7\n", - "two\t8400825950927958036\t0b...000000010100\t100\t4\n", - "three\t-7473701886709619306\t0b...101001101010\t010\t2\n", - "four\t5854090567539607813\t0b...100100000101\t101\t5\n", - "five\t5010767421024509913\t0b...111111011001\t001\t1\n" + "zero\t-1033934764475250610\t0b...011110110010\t010\t2\n", + "one\t-3496683303036773129\t0b...001100001001\t001\t1\n", + "two\t-4468022785493412346\t0b...100111111010\t010\t2\n", + "three\t1491334499222274950\t0b...001110000110\t110\t6\n", + "four\t490235692124377396\t0b...000100110100\t100\t4\n", + "five\t-550474145318920636\t0b...000110111100\t100\t4\n" ] } ], @@ -983,10 +983,10 @@ } }, "source": [ - "| Bucket | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |\n", - "| :---: | :---: |:---:| :---: | :---: |:---:| :---: |:---:| :---: |\n", - "| Key |`\"one\"`|*...*|`\"two\"`|`\"four\"`|*...*|`\"three\"`|*...*|`\"zero\"`|\n", - "| Value | `1` |*...*| `2` | `4` |*...*| `3` |*...*| `0` |" + "| Bucket | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |\n", + "| :---: | :---: |:---:| :---: | :---: |:---:| :---: |:---:| :---: |\n", + "| **Key** |`\"one\"`|*...*|`\"two\"`|`\"four\"`|*...*|`\"three\"`|*...*|`\"zero\"`|\n", + "|**Value**| `1` |*...*| `2` | `4` |*...*| `3` |*...*| `0` |" ] }, { @@ -1008,10 +1008,10 @@ } }, "source": [ - "| Bucket | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |\n", - "| :---: | :---: |:---:|:---:|:---:|:---:|:---:|:---:| :---: |:---:|:---:| :---: | :---: |:---:| :---: |:---:| :---: |\n", - "| Key |`\"one\"`|*...*|*...*|*...*|*...*|*...*|*...*|`\"five\"`|*...*|*...*|`\"two\"`|`\"four\"`|*...*|`\"three\"`|*...*|`\"zero\"`|\n", - "| Value | `1` |*...*|*...*|*...*|*...*|*...*|*...*| `5` |*...*|*...*| `2` | `4` |*...*| `3` |*...*| `0` |" + "| Bucket | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |\n", + "| :---: | :---: |:---:|:---:|:---:|:---:|:---:|:---:| :---: |:---:|:---:| :---: | :---: |:---:| :---: |:---:| :---: |\n", + "| **Key** |`\"one\"`|*...*|*...*|*...*|*...*|*...*|*...*|`\"five\"`|*...*|*...*|`\"two\"`|`\"four\"`|*...*|`\"three\"`|*...*|`\"zero\"`|\n", + "|**Value**| `1` |*...*|*...*|*...*|*...*|*...*|*...*| `5` |*...*|*...*| `2` | `4` |*...*| `3` |*...*| `0` |" ] }, { @@ -1038,12 +1038,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "zero\t-2242252796476499076\t0b...010010000100\t0100\t4\n", - "one\t6742734203392562231\t0b...010000110111\t0111\t7\n", - "two\t8400825950927958036\t0b...000000010100\t0100\t4\n", - "three\t-7473701886709619306\t0b...101001101010\t1010\t10\n", - "four\t5854090567539607813\t0b...100100000101\t0101\t5\n", - "five\t5010767421024509913\t0b...111111011001\t1001\t9\n" + "zero\t-1033934764475250610\t0b...011110110010\t0010\t2\n", + "one\t-3496683303036773129\t0b...001100001001\t1001\t9\n", + "two\t-4468022785493412346\t0b...100111111010\t1010\t10\n", + "three\t1491334499222274950\t0b...001110000110\t0110\t6\n", + "four\t490235692124377396\t0b...000100110100\t0100\t4\n", + "five\t-550474145318920636\t0b...000110111100\t1100\t12\n" ] } ], @@ -1070,7 +1070,7 @@ } }, "source": [ - "### Mappings are Collections without \"Predictable Order\"" + "### Mappings are Collections without \"Predictable\" Order" ] }, { @@ -1081,7 +1081,7 @@ } }, "source": [ - "In [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Collections-vs.-Sequences), we show how *sequences* are a special kind of *collections*. The latter can be described as iterable containers with a finite number of elements.\n", + "In [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb#Collections-vs.-Sequences), we show how *sequences* are a special kind of *collections*. The latter can be described as iterable containers with a finite number of elements.\n", "\n", "The `dict` type is a special kind of a *collection*, as well, as revealed with the `Collection` ABC from the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module in the [standard library](https://docs.python.org/3/library/index.html)." ] @@ -1291,12 +1291,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "Python 3.7.3\r\n" + "Python 3.7.4\n" ] } ], "source": [ - "!python --version # the order in the for-loops is predictable for Python 3.7 or higher only" + "!python --version # the order in the for-loops is predictable only for Python 3.7 or higher" ] }, { @@ -1811,7 +1811,7 @@ }, "outputs": [], "source": [ - "haystack = [random.randint(99, 9999) for _ in range(10_000_000)] + [42]" + "needle = 42" ] }, { @@ -1824,7 +1824,8 @@ }, "outputs": [], "source": [ - "needle = 42" + "haystack = [random.randint(99, 9999) for _ in range(10_000_000)]\n", + "haystack.append(needle)" ] }, { @@ -1863,7 +1864,7 @@ { "data": { "text/plain": [ - "[8126, 7370, 3735, 213, 7922, 1434, 8557, 9609, 9704, 9564, 9601, 3444]" + "[8126, 7370, 3735, 213, 7922, 1434, 8557, 9609, 9704, 9564]" ] }, "execution_count": 57, @@ -1872,7 +1873,7 @@ } ], "source": [ - "haystack[:12]" + "haystack[:10]" ] }, { @@ -1887,7 +1888,7 @@ { "data": { "text/plain": [ - "[5842, 1894, 7237, 886, 5945, 4014, 4998, 2055, 3531, 6919, 7875, 1944]" + "[7237, 886, 5945, 4014, 4998, 2055, 3531, 6919, 7875, 1944]" ] }, "execution_count": 58, @@ -1896,7 +1897,7 @@ } ], "source": [ - "haystack[-12:]" + "haystack[-10:]" ] }, { @@ -1923,7 +1924,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "4.37 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "4.32 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -1965,7 +1966,7 @@ } }, "source": [ - "Because the hash table implementation is *extremely* fast, we search the `needle` not `10` but `10_000` times. The code cell still runs in only a fraction of the time its counterpart does above." + "To show the *massive* effect of the hash table implementation, we search the `needle` not `10` but `10_000_000` times. The code cell still runs in only a fraction of the time its counterpart does above." ] }, { @@ -1981,7 +1982,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "426 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + "419 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" ] } ], @@ -2447,7 +2448,7 @@ { "data": { "text/plain": [ - "139752734711240" + "139953979638320" ] }, "execution_count": 75, @@ -2638,7 +2639,7 @@ } }, "source": [ - "`dict` objects come with many methods bound on them, many of which are standardized by the `Mapping` and `MutableMapping` ABCs from the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module. While the former requires a data type adhering to it to implement the [keys()](https://docs.python.org/3/library/stdtypes.html#dict.keys), [values()](https://docs.python.org/3/library/stdtypes.html#dict.values), [items()](https://docs.python.org/3/library/stdtypes.html#dict.items), and [get()](https://docs.python.org/3/library/stdtypes.html#dict.get) methods, which *never* mutate an object, the latter formalizes the [update()](https://docs.python.org/3/library/stdtypes.html#dict.update), [pop()](https://docs.python.org/3/library/stdtypes.html#dict.pop), [popitem()](https://docs.python.org/3/library/stdtypes.html#dict.popitem), [clear()](https://docs.python.org/3/library/stdtypes.html#dict.clear), and [setdefault()](https://docs.python.org/3/library/stdtypes.html#dict.setdefault) methods, which *may* do so." + "`dict` objects come with many methods bound on them (cf., [documentation](https://docs.python.org/3/library/stdtypes.html#dict)), many of which are standardized by the `Mapping` and `MutableMapping` ABCs from the [collections.abc](https://docs.python.org/3/library/collections.abc.html) module. While the former requires a data type adhering to it to implement the [keys()](https://docs.python.org/3/library/stdtypes.html#dict.keys), [values()](https://docs.python.org/3/library/stdtypes.html#dict.values), [items()](https://docs.python.org/3/library/stdtypes.html#dict.items), and [get()](https://docs.python.org/3/library/stdtypes.html#dict.get) methods, which *never* mutate an object, the latter formalizes the [update()](https://docs.python.org/3/library/stdtypes.html#dict.update), [pop()](https://docs.python.org/3/library/stdtypes.html#dict.pop), [popitem()](https://docs.python.org/3/library/stdtypes.html#dict.popitem), [clear()](https://docs.python.org/3/library/stdtypes.html#dict.clear), and [setdefault()](https://docs.python.org/3/library/stdtypes.html#dict.setdefault) methods, which *may* do so." ] }, { @@ -2929,7 +2930,7 @@ } }, "source": [ - "Above, we see how the look-up operator fails *loudly* if a key is not in a `dict` object. For example, `to_words` does *not* have a key `0` any more." + "Above, we see how the look-up operator fails *loudly* with a `KeyError` if a key is *not* in a `dict` object. For example, `to_words` does *not* have a key `0` any more." ] }, { @@ -3768,7 +3769,7 @@ } }, "source": [ - "Just as a single `*` symbol is used for packing and unpacking iterables in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#Packing-&-Unpacking), a double `**` symbol implements packing and unpacking for mappings.\n", + "Just as a single `*` symbol is used for packing and unpacking iterables in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb#Packing-&-Unpacking), a double `**` symbol implements packing and unpacking for mappings.\n", "\n", "Let's say we have `to_words` and `more_words` as below and want to merge the items together into a *new* `dict` object." ] @@ -3815,7 +3816,7 @@ } }, "source": [ - "By *unpacking* the items with `**`, the newly created `dict` objects is first filled with the items from `to_words` and then from `more_words`. The item with the key `2` from `more_words` overwrites its counterpart from `to_words` as it is mentioned last." + "By *unpacking* the items with `**`, the newly created `dict` object is first filled with the items from `to_words` and then from `more_words`. The item with the key `2` from `more_words` overwrites its counterpart from `to_words` as it is mentioned last." ] }, { @@ -4194,7 +4195,7 @@ } }, "source": [ - "Analogous to list comprehensions in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb#List-Comprehensions), **dictionary comprehensions**, or **dictcomps** for short, are a concise literal notation to derive new `dict` objects out of existing ones.\n", + "Analogous to list comprehensions in [Chapter 7](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb#List-Comprehensions), **dictionary comprehensions**, or **dictcomps** for short, are a concise literal notation to derive new `dict` objects out of existing ones.\n", "\n", "For example, let's derive `from_words` from `to_words` below by swapping the keys and values." ] @@ -4409,7 +4410,7 @@ } }, "source": [ - "The *recursive* implementation of the [Fibonacci numbers](https://en.wikipedia.org/wiki/Fibonacci_number) in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb#\"Easy-at-first-Glance\"-Example:-Fibonacci-Numbers) takes long to compute for large Fibonacci numbers as the number of function calls grows exponentially.\n", + "The *recursive* implementation of the [Fibonacci numbers](https://en.wikipedia.org/wiki/Fibonacci_number) in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_content.ipynb#\"Easy-at-first-Glance\"-Example:-Fibonacci-Numbers) takes long to compute for large Fibonacci numbers as the number of function calls grows exponentially.\n", "\n", "The graph below visualizes what the problem is and also suggests a solution: Instead of calculating the return value of the `fibonacci()` function for the *same* argument over and over again, it makes sense to **cache** the result and reuse it. This concept is called **[memoization](https://en.wikipedia.org/wiki/Memoization)** in the computer science literature." ] @@ -4563,7 +4564,7 @@ } }, "source": [ - "Now, calling `fibonacci()` has the *side effect* of growing the `memo` in the *global scope*. So, subsequent calls to `fibonacci()` need not calculate any Fibonacci number with an index `i` smaller than the maximum `i` used so far." + "Now, calling `fibonacci()` has the *side effect* of growing the `memo` in the *global scope*. So, subsequent calls to `fibonacci()` need not calculate any Fibonacci number with an index `i` smaller than the maximum `i` used so far. Because of that, this `fibonacci()` is *not* a *pure* function." ] }, { @@ -4663,8 +4664,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "The slowest run took 195.30 times longer than the fastest. This could mean that an intermediate result is being cached.\n", - "18.9 µs ± 44.4 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + "The slowest run took 183.70 times longer than the fastest. This could mean that an intermediate result is being cached.\n", + "21.4 µs ± 50.4 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" ] } ], @@ -4686,8 +4687,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "The slowest run took 1919.60 times longer than the fastest. This could mean that an intermediate result is being cached.\n", - "346 µs ± 844 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + "The slowest run took 1974.48 times longer than the fastest. This could mean that an intermediate result is being cached.\n", + "580 µs ± 1.41 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" ] } ], @@ -4727,12 +4728,12 @@ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mRecursionError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mget_ipython\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun_cell_magic\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'timeit'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'-n 1'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'fibonacci(9999) # = 10,000th number\\n'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.07/lib/python3.7/site-packages/IPython/core/interactiveshell.py\u001b[0m in \u001b[0;36mrun_cell_magic\u001b[0;34m(self, magic_name, line, cell)\u001b[0m\n\u001b[1;32m 2356\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbuiltin_trap\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2357\u001b[0m \u001b[0margs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mmagic_arg_s\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcell\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2358\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2359\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2360\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36mtimeit\u001b[0;34m(self, line, cell, local_ns)\u001b[0m\n", - "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.07/lib/python3.7/site-packages/IPython/core/magic.py\u001b[0m in \u001b[0;36m\u001b[0;34m(f, *a, **k)\u001b[0m\n\u001b[1;32m 185\u001b[0m \u001b[0;31m# but it's overkill for just that one bit of state.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 186\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mmagic_deco\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 187\u001b[0;31m \u001b[0mcall\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mk\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mk\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 188\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 189\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcallable\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.07/lib/python3.7/site-packages/IPython/core/magics/execution.py\u001b[0m in \u001b[0;36mtimeit\u001b[0;34m(self, line, cell, local_ns)\u001b[0m\n\u001b[1;32m 1160\u001b[0m \u001b[0;32mbreak\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1161\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1162\u001b[0;31m \u001b[0mall_runs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtimer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrepeat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrepeat\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumber\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1163\u001b[0m \u001b[0mbest\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mall_runs\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mnumber\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1164\u001b[0m \u001b[0mworst\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmax\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mall_runs\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mnumber\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.07/lib/python3.7/timeit.py\u001b[0m in \u001b[0;36mrepeat\u001b[0;34m(self, repeat, number)\u001b[0m\n\u001b[1;32m 202\u001b[0m \u001b[0mr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 203\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrepeat\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 204\u001b[0;31m \u001b[0mt\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtimeit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnumber\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 205\u001b[0m \u001b[0mr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 206\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mr\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.07/lib/python3.7/site-packages/IPython/core/magics/execution.py\u001b[0m in \u001b[0;36mtimeit\u001b[0;34m(self, number)\u001b[0m\n\u001b[1;32m 167\u001b[0m \u001b[0mgc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdisable\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 168\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 169\u001b[0;31m \u001b[0mtiming\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minner\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mit\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtimer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 170\u001b[0m \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 171\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mgcold\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.10/lib/python3.7/site-packages/IPython/core/interactiveshell.py\u001b[0m in \u001b[0;36mrun_cell_magic\u001b[0;34m(self, magic_name, line, cell)\u001b[0m\n\u001b[1;32m 2357\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbuiltin_trap\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2358\u001b[0m \u001b[0margs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mmagic_arg_s\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcell\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2359\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2360\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2361\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mtimeit\u001b[0;34m(self, line, cell, local_ns)\u001b[0m\n", + "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.10/lib/python3.7/site-packages/IPython/core/magic.py\u001b[0m in \u001b[0;36m\u001b[0;34m(f, *a, **k)\u001b[0m\n\u001b[1;32m 185\u001b[0m \u001b[0;31m# but it's overkill for just that one bit of state.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 186\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mmagic_deco\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 187\u001b[0;31m \u001b[0mcall\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mk\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mk\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 188\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 189\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcallable\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.10/lib/python3.7/site-packages/IPython/core/magics/execution.py\u001b[0m in \u001b[0;36mtimeit\u001b[0;34m(self, line, cell, local_ns)\u001b[0m\n\u001b[1;32m 1160\u001b[0m \u001b[0;32mbreak\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1161\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1162\u001b[0;31m \u001b[0mall_runs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtimer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrepeat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrepeat\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumber\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1163\u001b[0m \u001b[0mbest\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mall_runs\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mnumber\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1164\u001b[0m \u001b[0mworst\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmax\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mall_runs\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mnumber\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.10/lib/python3.7/timeit.py\u001b[0m in \u001b[0;36mrepeat\u001b[0;34m(self, repeat, number)\u001b[0m\n\u001b[1;32m 202\u001b[0m \u001b[0mr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 203\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrepeat\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 204\u001b[0;31m \u001b[0mt\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtimeit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnumber\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 205\u001b[0m \u001b[0mr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 206\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mr\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.10/lib/python3.7/site-packages/IPython/core/magics/execution.py\u001b[0m in \u001b[0;36mtimeit\u001b[0;34m(self, number)\u001b[0m\n\u001b[1;32m 167\u001b[0m \u001b[0mgc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdisable\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 168\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 169\u001b[0;31m \u001b[0mtiming\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minner\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mit\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtimer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 170\u001b[0m \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 171\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mgcold\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m\u001b[0m in \u001b[0;36minner\u001b[0;34m(_it, _timer)\u001b[0m\n", "\u001b[0;32m\u001b[0m in \u001b[0;36mfibonacci\u001b[0;34m(i, debug)\u001b[0m\n\u001b[1;32m 24\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"fibonacci({i}) is calculated\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 25\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 26\u001b[0;31m \u001b[0mrecurse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfibonacci\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdebug\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mfibonacci\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdebug\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 27\u001b[0m \u001b[0mmemo\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrecurse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 28\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mrecurse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "... last 1 frames repeated, from the frame below ...\n", @@ -4844,8 +4845,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "The slowest run took 25077.61 times longer than the fastest. This could mean that an intermediate result is being cached.\n", - "2.36 ms ± 5.77 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + "The slowest run took 27724.52 times longer than the fastest. This could mean that an intermediate result is being cached.\n", + "3.11 ms ± 7.61 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" ] } ], @@ -4994,7 +4995,7 @@ } }, "source": [ - "Because `fibonacci()` is now independent from *global state*, the same eleven recursive function calls are made each time." + "Because `fibonacci()` is now independent from *global state*, the same eleven recursive function calls are made each time. So, this `fibonacci()` is a *pure* function." ] }, { @@ -5073,7 +5074,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "330 µs ± 18.7 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + "138 µs ± 5.01 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" ] } ], @@ -5095,7 +5096,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "3.53 ms ± 75.2 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + "1.55 ms ± 56.7 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" ] } ], @@ -5130,7 +5131,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "15.5 ms ± 572 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + "20.8 ms ± 8.09 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" ] } ], @@ -5193,7 +5194,7 @@ } }, "source": [ - "The [OrderedDict](https://docs.python.org/3/library/collections.html#collections.OrderedDict) type may be used to create a `dict`-like object with the added feature that *explicitly* remembers the *insertion* order of its items.\n", + "The [OrderedDict](https://docs.python.org/3/library/collections.html#collections.OrderedDict) type may be used to create a `dict`-like object with the added feature of *explicitly* remembering the *insertion* order of its items.\n", "\n", "Let's look at a quick example: We create an `OrderedDict` object by passing an iterable of $2$-element iterables, one of the three ways to use [dict()](https://docs.python.org/3/library/functions.html#func-dict)." ] @@ -6360,8 +6361,8 @@ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mchain\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.07/lib/python3.7/collections/__init__.py\u001b[0m in \u001b[0;36m__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 912\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mKeyError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 913\u001b[0m \u001b[0;32mpass\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 914\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__missing__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# support subclasses that define __missing__\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 915\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 916\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkey\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.07/lib/python3.7/collections/__init__.py\u001b[0m in \u001b[0;36m__missing__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 904\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 905\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__missing__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkey\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 906\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mKeyError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 907\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 908\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__getitem__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkey\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.10/lib/python3.7/collections/__init__.py\u001b[0m in \u001b[0;36m__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 914\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mKeyError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 915\u001b[0m \u001b[0;32mpass\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 916\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__missing__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# support subclasses that define __missing__\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 917\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 918\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkey\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdefault\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.pyenv/versions/anaconda3-2019.10/lib/python3.7/collections/__init__.py\u001b[0m in \u001b[0;36m__missing__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 906\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 907\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__missing__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkey\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 908\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mKeyError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 909\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 910\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__getitem__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkey\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mKeyError\u001b[0m: 10" ] } @@ -6430,7 +6431,7 @@ { "data": { "text/plain": [ - "139751998783784" + "139953088728672" ] }, "execution_count": 207, @@ -6929,7 +6930,7 @@ } ], "source": [ - "1.0 in numbers" + "2.0 in numbers" ] }, { @@ -7421,7 +7422,7 @@ { "data": { "text/plain": [ - "139751998783784" + "139953088728672" ] }, "execution_count": 243, @@ -8041,7 +8042,7 @@ } }, "source": [ - "This section provides some conference talks that go into great detail regarding the workings of the `dict` type." + "Next, we list some conference talks that go into great detail regarding the workings of the `dict` type." ] }, { @@ -8068,7 +8069,7 @@ " " ], "text/plain": [ - "" + "" ] }, "execution_count": 261, @@ -8105,7 +8106,7 @@ " " ], "text/plain": [ - "" + "" ] }, "execution_count": 262, @@ -8141,7 +8142,7 @@ " " ], "text/plain": [ - "" + "" ] }, "execution_count": 263, @@ -8152,6 +8153,20 @@ "source": [ "YouTubeVideo(\"npw4s1QTmPg\", width=\"60%\")" ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `dict` type's order has been worked on in many PEPs in recent years:\n", + "- [PEP 412](https://www.python.org/dev/peps/pep-0412/): Key-Sharing Dictionary\n", + "- [PEP 468](https://www.python.org/dev/peps/pep-0468/): Preserving the order of \\*\\*kwargs in a function\n", + "- [PEP 520](https://www.python.org/dev/peps/pep-0520/): Preserving Class Attribute Definition Order" + ] } ], "metadata": { @@ -8170,7 +8185,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.4" }, "livereveal": { "auto_select": "code", @@ -8198,5 +8213,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/08_mappings_10_review.ipynb b/08_mappings_10_review.ipynb new file mode 100644 index 0000000..471bd41 --- /dev/null +++ b/08_mappings_10_review.ipynb @@ -0,0 +1,261 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "# Chapter 8: Mappings & Sets" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Content Review" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings_00_content.ipynb) of the book. Then, work through the questions below." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Essay Questions " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Answer the following questions *briefly*!" + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: Whereas *key* **look-ups** in a `dict` object run in so-called **[constant time](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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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", + "\n", + "Compare the **recursive** formulation of `fibonacci()` in [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings_00_content.ipynb#\"Easy-at-third-Glance\"-Example:-Fibonacci-Numbers-%28revisited%29), the \"*Easy at third Glance*\" example, with the **iterative** version in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_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?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: How are the `set` and the `dict` type related? How could we use the latter to mimic the former?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: A `dict` object's [update()](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": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q12**: The `set` type is both a mapping and a sequence." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/08_mappings_review_and_exercises.ipynb b/08_mappings_20_exercises.ipynb similarity index 56% rename from 08_mappings_review_and_exercises.ipynb rename to 08_mappings_20_exercises.ipynb index c05004e..0f3f1a5 100644 --- a/08_mappings_review_and_exercises.ipynb +++ b/08_mappings_20_exercises.ipynb @@ -8,221 +8,6 @@ "# Chapter 8: Mappings & Sets" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Content Review" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Read [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings.ipynb) of the book. Then work through the twelve review questions." - ] - }, - { - "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**: `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": [ - " " - ] - }, - { - "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": [ - " " - ] - }, - { - "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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q4**: Whereas *key* **look-ups** in a `dict` object run in so-called **[constant time](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": [ - " " - ] - }, - { - "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": [ - " " - ] - }, - { - "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", - "\n", - "Compare the **recursive** formulation of `fibonacci()` in [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings.ipynb#\"Easy-at-third-Glance\"-Example:-Fibonacci-Numbers-%28revisited%29), the \"*Easy at third Glance*\" example, with the **iterative** version in [Chapter 4](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.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?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q7**: How are the `set` and the `dict` type related? How could we use the latter to mimic the former?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, - { - "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": [ - " " - ] - }, - { - "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": [ - " " - ] - }, - { - "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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q11**: A `dict` object's [update()](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": [ - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q12**: The `set` type is both a mapping and a sequence." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " " - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -230,6 +15,13 @@ "## Coding Exercises" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Read [Chapter 8](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings_00_content.ipynb) of the book. Then, work through the exercises below." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -291,7 +83,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q13.1**: Write a dictionary comprehension to derive a new `dict` object, called `brazilian_players`, that maps a Brazilian player's name to his position!" + "**Q1.1**: Write a dictionary comprehension to derive a new `dict` object, called `brazilian_players`, that maps a Brazilian player's name to his position!" ] }, { @@ -316,7 +108,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q13.2**: 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!" + "**Q1.2**: 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!" ] }, { @@ -344,7 +136,7 @@ "source": [ "Often, we are given a `dict` object like the one returned from `get_players()`: It is characterized by the observation that a large set of unique keys (i.e., the players' names) is mapped onto a smaller set of non-unique values (i.e., the positions).\n", "\n", - "**Q13.3**: 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", + "**Q1.3**: 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 the [defaultdict](https://docs.python.org/3/library/collections.html#collections.defaultdict) type from the [collections](https://docs.python.org/3/library/collections.html) module in the [standard library](https://docs.python.org/3/library/index.html) or the [setdefault()](https://docs.python.org/3/library/stdtypes.html#dict.setdefault) method of the ordinary `dict` type." ] @@ -372,7 +164,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q13.4**: 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", + "**Q1.4**: 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*." ] @@ -441,7 +233,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q13.5**: Write a `goals_by_player()` function: It takes an argument like the global `players`, and returns a `dict` object mapping the players to the number of goals they scored.\n", + "**Q1.5**: Write a `goals_by_player()` function: It 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 single dictionary comprehension." ] @@ -469,7 +261,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q13.6**: Write a *dictionary comprehension* to filter out the players who did *not* score from the preceding result. Then, write a *set comprehension* that does the same but discards the number of goals scored.\n", + "**Q1.6**: Write a *dictionary comprehension* to filter out the players who did *not* score from the preceding result. Then, write a *set comprehension* that does the same but discards the number of goals scored.\n", "\n", "Hints: Reference the `goals_by_player()` function from before." ] @@ -496,7 +288,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q13.7**: Write a `all_goals()` function: It 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", + "**Q1.7**: Write a `all_goals()` function: It 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()](https://docs.python.org/3/library/functions.html#sorted) function or the `list` type's [sort()](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*." ] @@ -524,7 +316,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q13.8**: Lastly, write a `summary()` function: It 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", + "**Q1.8**: Lastly, write a `summary()` function: It 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", @@ -575,7 +367,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.4" }, "toc": { "base_numbering": 1, @@ -592,5 +384,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 4 } diff --git a/README.md b/README.md index a227cb2..0da3f5e 100644 --- a/README.md +++ b/README.md @@ -13,23 +13,52 @@ studies** in the "field" of **data science**. The "chapters" are written in [Jupyter notebooks](https://jupyter-notebook.readthedocs.io/en/stable/) which are a de-facto standard for exchanging code and results among data science professionals and researchers. -As such they can be viewed in a plain web browser: +They can be viewed in a plain web browser with the help of +[nbviewer](https://nbviewer.jupyter.org/): -- [00 - Start up](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb) -- [01 - Elements of a Program](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements.ipynb) -- [02 - Functions & Modularization](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions.ipynb) -- [03 - Conditionals & Exceptions](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals.ipynb) -- [04 - Recursion & Looping](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration.ipynb) -- [05 - Bits & Numbers](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers.ipynb) -- [06 - Bytes & Text](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text.ipynb) -- [07 - Sequential Data](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences.ipynb) -- [08 - Mappings & Sets](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings.ipynb) +- *Introduction*: Start up + ([content](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up_00_content.ipynb) + | [review](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up_10_review.ipynb) + | [exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up_20_exercises.ipynb)) +- **Part A: Expressing Logic** + - *Chapter 1*: Elements of a Program + ([content](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_00_content.ipynb) + | [review](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_10_review.ipynb) + | [exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/01_elements_20_exercises.ipynb)) + - *Chapter 2*: Functions & Modularization + ([content](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions_00_content.ipynb) + | [review](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions_10_review.ipynb) + | [exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/02_functions_20_exercises.ipynb)) + - *Chapter 3*: Conditionals & Exceptions + ([content](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_00_content.ipynb) + | [review](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_10_review.ipynb) + | [exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/03_conditionals_20_exercises.ipynb)) + - *Chapter 4*: Recursion & Looping + ([content](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_00_content.ipynb) + | [review](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_10_review.ipynb) + | [exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/04_iteration_20_exercises.ipynb)) +- **Part B: Managing Data and Memory** + - *Chapter 5*: Bits & Numbers + ([content](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers_00_content.ipynb) + | [review](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers_10_review.ipynb) + | [exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/05_numbers_20_exercises.ipynb)) + - *Chapter 6*: Bytes & Text + ([content](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text_00_content.ipynb) + | [review](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/06_text_10_review.ipynb)) + - *Chapter 7*: Sequential Data + ([content](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_00_content.ipynb) + | [review](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_10_review.ipynb) + | [exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/07_sequences_20_exercises.ipynb)) + - *Chapter 8*: Mappings & Sets + ([content](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings_00_content.ipynb) + | [review](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings_10_review.ipynb) + | [exercises](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/08_mappings_20_exercises.ipynb)) However, it is recommended that students **install Python and Jupyter locally** and run the code in the notebooks on their own. This way, the student can play with the code and learn more efficiently. Precise **installation instructions** are either in the [00th notebook]( -https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up.ipynb) +https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/master/00_start_up_00_content.ipynb) or further below. Feedback is encouraged and will be incorporated. From 759271b6a144d4823c37b1060f6772020d60f39c Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 28 Jan 2020 13:07:53 +0100 Subject: [PATCH 055/242] Update pyproject.toml, poetry.lock, requirements.txt - use JupyterLab instead of just Jupyter - update dependencies --- poetry.lock | 814 ++++++++++++++++++++++++++++++++--------------- pyproject.toml | 13 +- requirements.txt | 74 ++--- 3 files changed, 595 insertions(+), 306 deletions(-) diff --git a/poetry.lock b/poetry.lock index ad1610a..07e1126 100644 --- a/poetry.lock +++ b/poetry.lock @@ -9,7 +9,7 @@ version = "1.4.3" [[package]] category = "main" description = "Disable App Nap on OS X 10.9" -marker = "python_version >= \"3.3\" and sys_platform == \"darwin\" or sys_platform == \"darwin\"" +marker = "sys_platform == \"darwin\" or platform_system == \"Darwin\"" name = "appnope" optional = false python-versions = "*" @@ -21,7 +21,13 @@ description = "Classes Without Boilerplate" name = "attrs" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "19.1.0" +version = "19.3.0" + +[package.extras] +azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "pytest-azurepipelines"] +dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "pre-commit"] +docs = ["sphinx", "zope.interface"] +tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] [[package]] category = "main" @@ -37,13 +43,19 @@ description = "The uncompromising code formatter." name = "black" optional = false python-versions = ">=3.6" -version = "18.9b0" +version = "19.10b0" [package.dependencies] appdirs = "*" -attrs = ">=17.4.0" +attrs = ">=18.1.0" click = ">=6.5" +pathspec = ">=0.6,<1" +regex = "*" toml = ">=0.9.4" +typed-ast = ">=1.4.0" + +[package.extras] +d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] [[package]] category = "dev" @@ -80,19 +92,19 @@ version = "7.0" [[package]] category = "main" description = "Cross-platform colored terminal text." -marker = "python_version >= \"3.3\" and sys_platform == \"win32\" or sys_platform == \"win32\"" +marker = "sys_platform == \"win32\"" name = "colorama" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.4.1" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "0.4.3" [[package]] category = "main" -description = "Better living through Python with decorators" +description = "Decorators for Humans" name = "decorator" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "4.4.0" +version = "4.4.1" [[package]] category = "main" @@ -110,27 +122,47 @@ optional = false python-versions = ">=2.7" version = "0.3" +[[package]] +category = "main" +description = "Read metadata from Python packages" +marker = "python_version < \"3.8\"" +name = "importlib-metadata" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +version = "1.4.0" + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx", "rst.linker"] +testing = ["packaging", "importlib-resources"] + [[package]] category = "main" description = "IPython Kernel for Jupyter" name = "ipykernel" optional = false python-versions = ">=3.4" -version = "5.1.2" +version = "5.1.4" [package.dependencies] +appnope = "*" ipython = ">=5.0.0" jupyter-client = "*" tornado = ">=4.2" traitlets = ">=4.1.0" +[package.extras] +test = ["pytest", "pytest-cov", "flaky", "nose"] + [[package]] category = "main" description = "IPython: Productive Interactive Computing" name = "ipython" optional = false -python-versions = ">=3.5" -version = "7.8.0" +python-versions = ">=3.6" +version = "7.11.1" [package.dependencies] appnope = "*" @@ -140,11 +172,22 @@ decorator = "*" jedi = ">=0.10" pexpect = "*" pickleshare = "*" -prompt-toolkit = ">=2.0.0,<2.1.0" +prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" pygments = "*" setuptools = ">=18.5" traitlets = ">=4.2" +[package.extras] +all = ["ipywidgets", "ipyparallel", "qtconsole", "ipykernel", "nbconvert", "notebook", "nbformat", "testpath", "pygments", "requests", "numpy (>=1.14)", "nose (>=0.10.1)", "Sphinx (>=1.3)"] +doc = ["Sphinx (>=1.3)"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["notebook", "ipywidgets"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.14)"] + [[package]] category = "main" description = "Vestigial utilities from IPython" @@ -153,53 +196,50 @@ optional = false python-versions = "*" version = "0.2.0" -[[package]] -category = "main" -description = "IPython HTML widgets for Jupyter" -name = "ipywidgets" -optional = false -python-versions = "*" -version = "7.5.1" - -[package.dependencies] -ipykernel = ">=4.5.1" -nbformat = ">=4.2.0" -traitlets = ">=4.3.1" -widgetsnbextension = ">=3.5.0,<3.6.0" - -[package.dependencies.ipython] -python = ">=3.3" -version = ">=4.0.0" - [[package]] category = "main" description = "An autocompletion tool for Python that can be used for text editors." name = "jedi" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.15.1" +version = "0.16.0" [package.dependencies] -parso = ">=0.5.0" +parso = ">=0.5.2" + +[package.extras] +qa = ["flake8 (3.7.9)"] +testing = ["colorama (0.4.1)", "docopt", "pytest (>=3.9.0,<5.0.0)"] [[package]] category = "main" -description = "A small but fast and easy to use stand-alone template engine written in pure python." +description = "A very fast and expressive template engine." name = "jinja2" optional = false -python-versions = "*" -version = "2.10.1" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "2.11.0" [package.dependencies] MarkupSafe = ">=0.23" +[package.extras] +i18n = ["Babel (>=0.8)"] + +[[package]] +category = "main" +description = "A Python implementation of the JSON5 data format." +name = "json5" +optional = false +python-versions = "*" +version = "0.8.5" + [[package]] category = "main" description = "An implementation of JSON Schema validation for Python" name = "jsonschema" optional = false python-versions = "*" -version = "3.0.2" +version = "3.2.0" [package.dependencies] attrs = ">=17.4.0" @@ -207,21 +247,13 @@ pyrsistent = ">=0.14.0" setuptools = "*" six = ">=1.11.0" -[[package]] -category = "main" -description = "Jupyter metapackage. Install all the Jupyter components in one go." -name = "jupyter" -optional = false -python-versions = "*" -version = "1.0.0" +[package.dependencies.importlib-metadata] +python = "<3.8" +version = "*" -[package.dependencies] -ipykernel = "*" -ipywidgets = "*" -jupyter-console = "*" -nbconvert = "*" -notebook = "*" -qtconsole = "*" +[package.extras] +format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"] +format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator (>0.1.0)", "rfc3339-validator"] [[package]] category = "main" @@ -229,67 +261,18 @@ description = "Jupyter protocol implementation and client libraries" name = "jupyter-client" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "5.3.2" +version = "5.3.4" [package.dependencies] -jupyter-core = "*" +jupyter-core = ">=4.6.0" python-dateutil = ">=2.1" pywin32 = ">=1.0" pyzmq = ">=13" tornado = ">=4.1" traitlets = "*" -[[package]] -category = "main" -description = "Jupyter terminal console" -name = "jupyter-console" -optional = false -python-versions = ">=3.5" -version = "6.0.0" - -[package.dependencies] -ipykernel = "*" -ipython = "*" -jupyter-client = "*" -prompt-toolkit = ">=2.0.0,<2.1.0" -pygments = "*" - -[[package]] -category = "dev" -description = "Common utilities for jupyter-contrib projects." -name = "jupyter-contrib-core" -optional = false -python-versions = "*" -version = "0.3.3" - -[package.dependencies] -jupyter-core = "*" -notebook = ">=4.0" -setuptools = "*" -tornado = "*" -traitlets = "*" - -[[package]] -category = "dev" -description = "A collection of Jupyter nbextensions." -name = "jupyter-contrib-nbextensions" -optional = false -python-versions = "*" -version = "0.5.1" - -[package.dependencies] -ipython-genutils = "*" -jupyter-contrib-core = ">=0.3.3" -jupyter-core = "*" -jupyter-highlight-selected-word = ">=0.1.1" -jupyter-latex-envs = ">=1.3.8" -jupyter-nbextensions-configurator = ">=0.4.0" -lxml = "*" -nbconvert = ">=4.2" -notebook = ">=4.0" -pyyaml = "*" -tornado = "*" -traitlets = ">=4.1" +[package.extras] +test = ["ipykernel", "ipython", "mock", "pytest"] [[package]] category = "main" @@ -297,57 +280,46 @@ description = "Jupyter core package. A base package on which Jupyter projects re name = "jupyter-core" optional = false python-versions = ">=2.7, !=3.0, !=3.1, !=3.2" -version = "4.5.0" +version = "4.6.1" [package.dependencies] +pywin32 = ">=1.0" traitlets = "*" [[package]] -category = "dev" -description = "Jupyter notebook extension that enables highlighting every instance of the current word in the notebook." -name = "jupyter-highlight-selected-word" +category = "main" +description = "The JupyterLab notebook server extension." +name = "jupyterlab" optional = false -python-versions = "*" -version = "0.2.0" - -[[package]] -category = "dev" -description = "Jupyter notebook extension which supports (some) LaTeX environments within markdown cells. Also provides support for labels and crossreferences, document wide numbering, bibliography, and more..." -name = "jupyter-latex-envs" -optional = false -python-versions = "*" -version = "1.4.6" +python-versions = ">=3.5" +version = "1.2.6" [package.dependencies] -ipython = "*" -jupyter_core = "*" -nbconvert = "*" -notebook = ">=4.0" -traitlets = ">=4.1" +jinja2 = ">=2.10" +jupyterlab-server = ">=1.0.0,<1.1.0" +notebook = ">=4.3.1" +tornado = "<6.0.0 || >6.0.0,<6.0.1 || >6.0.1,<6.0.2 || >6.0.2" + +[package.extras] +docs = ["sphinx", "recommonmark", "sphinx-rtd-theme", "sphinx-copybutton"] +test = ["pytest", "pytest-check-links", "requests"] [[package]] -category = "dev" -description = "jupyter serverextension providing configuration interfaces for nbextensions." -name = "jupyter-nbextensions-configurator" +category = "main" +description = "JupyterLab Server" +name = "jupyterlab-server" optional = false -python-versions = "*" -version = "0.4.1" +python-versions = ">=3.5" +version = "1.0.6" [package.dependencies] -jupyter_contrib_core = ">=0.3.3" -jupyter_core = "*" -notebook = ">=4.0" -pyyaml = "*" -tornado = "*" -traitlets = "*" +jinja2 = ">=2.10" +json5 = "*" +jsonschema = ">=3.0.1" +notebook = ">=4.2.0" -[[package]] -category = "dev" -description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." -name = "lxml" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" -version = "4.4.1" +[package.extras] +test = ["pytest", "requests"] [[package]] category = "main" @@ -371,7 +343,7 @@ description = "Converting Jupyter Notebooks" name = "nbconvert" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "5.6.0" +version = "5.6.1" [package.dependencies] bleach = "*" @@ -386,13 +358,20 @@ pygments = "*" testpath = "*" traitlets = ">=4.2" +[package.extras] +all = ["pytest", "pytest-cov", "ipykernel", "jupyter-client (>=5.3.1)", "ipywidgets (>=7)", "pebble", "tornado (>=4.0)", "sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "sphinxcontrib-github-alt", "ipython", "mock"] +docs = ["sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "sphinxcontrib-github-alt", "ipython", "jupyter-client (>=5.3.1)"] +execute = ["jupyter-client (>=5.3.1)"] +serve = ["tornado (>=4.0)"] +test = ["pytest", "pytest-cov", "ipykernel", "jupyter-client (>=5.3.1)", "ipywidgets (>=7)", "pebble", "mock"] + [[package]] category = "main" description = "The Jupyter Notebook format" name = "nbformat" optional = false -python-versions = "*" -version = "4.4.0" +python-versions = ">=3.5" +version = "5.0.4" [package.dependencies] ipython-genutils = "*" @@ -400,21 +379,24 @@ jsonschema = ">=2.4,<2.5.0 || >2.5.0" jupyter-core = "*" traitlets = ">=4.1" +[package.extras] +test = ["testpath", "pytest", "pytest-cov"] + [[package]] category = "main" description = "A web-based notebook environment for interactive computing" name = "notebook" optional = false python-versions = ">=3.5" -version = "6.0.1" +version = "6.0.3" [package.dependencies] Send2Trash = "*" ipykernel = "*" ipython-genutils = "*" jinja2 = "*" -jupyter-client = ">=5.3.1" -jupyter-core = ">=4.4.0" +jupyter-client = ">=5.3.4" +jupyter-core = ">=4.6.1" nbconvert = "*" nbformat = "*" prometheus-client = "*" @@ -423,13 +405,16 @@ terminado = ">=0.8.1" tornado = ">=5.0" traitlets = ">=4.2.1" +[package.extras] +test = ["nose", "coverage", "requests", "nose-warnings-filters", "nbval", "nose-exclude", "selenium", "pytest", "pytest-cov", "nose-exclude"] + [[package]] category = "main" description = "NumPy is the fundamental package for array computing with Python." name = "numpy" optional = false python-versions = ">=3.5" -version = "1.17.2" +version = "1.18.1" [[package]] category = "main" @@ -444,17 +429,28 @@ category = "main" description = "A Python Parser" name = "parso" optional = false -python-versions = "*" -version = "0.5.1" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.6.0" + +[package.extras] +testing = ["docopt", "pytest (>=3.0.7)"] + +[[package]] +category = "dev" +description = "Utility library for gitignore style pattern matching of file paths." +name = "pathspec" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "0.7.0" [[package]] category = "main" description = "Pexpect allows easy control of interactive console applications." -marker = "python_version >= \"3.3\" and sys_platform != \"win32\" or sys_platform != \"win32\"" +marker = "sys_platform != \"win32\"" name = "pexpect" optional = false python-versions = "*" -version = "4.7.0" +version = "4.8.0" [package.dependencies] ptyprocess = ">=0.5" @@ -475,22 +471,24 @@ optional = false python-versions = "*" version = "0.7.1" +[package.extras] +twisted = ["twisted"] + [[package]] category = "main" description = "Library for building powerful interactive command lines in Python" name = "prompt-toolkit" optional = false -python-versions = "*" -version = "2.0.9" +python-versions = ">=3.6" +version = "3.0.3" [package.dependencies] -six = ">=1.9.0" wcwidth = "*" [[package]] category = "main" description = "Run a subprocess in a pseudo terminal" -marker = "sys_platform != \"win32\" or os_name != \"nt\" or python_version >= \"3.3\" and sys_platform != \"win32\"" +marker = "sys_platform != \"win32\" or os_name != \"nt\"" name = "ptyprocess" optional = false python-versions = "*" @@ -502,7 +500,7 @@ description = "Pygments is a syntax highlighting package written in Python." name = "pygments" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.4.2" +version = "2.5.2" [[package]] category = "main" @@ -510,7 +508,7 @@ description = "Persistent/Functional/Immutable data structures" name = "pyrsistent" optional = false python-versions = "*" -version = "0.15.4" +version = "0.15.7" [package.dependencies] six = "*" @@ -520,8 +518,8 @@ category = "main" description = "Extensions to the standard Python datetime module" name = "python-dateutil" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -version = "2.8.0" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +version = "2.8.1" [package.dependencies] six = ">=1.5" @@ -533,7 +531,7 @@ marker = "sys_platform == \"win32\"" name = "pywin32" optional = false python-versions = "*" -version = "225" +version = "227" [[package]] category = "main" @@ -542,15 +540,7 @@ marker = "os_name == \"nt\"" name = "pywinpty" optional = false python-versions = "*" -version = "0.5.5" - -[[package]] -category = "dev" -description = "YAML parser and emitter for Python" -name = "pyyaml" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "5.1.2" +version = "0.5.7" [[package]] category = "main" @@ -558,23 +548,15 @@ description = "Python bindings for 0MQ" name = "pyzmq" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*" -version = "18.1.0" +version = "18.1.1" [[package]] -category = "main" -description = "Jupyter Qt console" -name = "qtconsole" +category = "dev" +description = "Alternative regular expression module, to replace re." +name = "regex" optional = false python-versions = "*" -version = "4.5.5" - -[package.dependencies] -ipykernel = ">=4.1" -ipython-genutils = "*" -jupyter-client = ">=4.1" -jupyter-core = "*" -pygments = "*" -traitlets = "*" +version = "2020.1.8" [[package]] category = "dev" @@ -582,7 +564,7 @@ description = "Reveal.js - Jupyter/IPython Slideshow Extension" name = "rise" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4" -version = "5.5.1" +version = "5.6.0" [package.dependencies] notebook = ">=5.5.0" @@ -600,8 +582,8 @@ category = "main" description = "Python 2 and 3 compatibility utilities" name = "six" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "1.12.0" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +version = "1.14.0" [[package]] category = "main" @@ -609,7 +591,7 @@ description = "Terminals served to xterm.js using Tornado websockets" name = "terminado" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.8.2" +version = "0.8.3" [package.dependencies] ptyprocess = "*" @@ -622,7 +604,10 @@ description = "Test utilities for code working with files and commands" name = "testpath" optional = false python-versions = "*" -version = "0.4.2" +version = "0.4.4" + +[package.extras] +test = ["pathlib2"] [[package]] category = "dev" @@ -646,20 +631,31 @@ description = "Traitlets Python config system" name = "traitlets" optional = false python-versions = "*" -version = "4.3.2" +version = "4.3.3" [package.dependencies] decorator = "*" ipython-genutils = "*" six = "*" +[package.extras] +test = ["pytest", "mock"] + +[[package]] +category = "dev" +description = "a fork of Python 2 and 3 ast modules with type comment support" +name = "typed-ast" +optional = false +python-versions = "*" +version = "1.4.1" + [[package]] category = "main" description = "Measures number of Terminal column cells of wide-character codes" name = "wcwidth" optional = false python-versions = "*" -version = "0.1.7" +version = "0.1.8" [[package]] category = "main" @@ -671,78 +667,376 @@ version = "0.5.1" [[package]] category = "main" -description = "IPython HTML widgets for Jupyter" -name = "widgetsnbextension" +description = "Backport of pathlib-compatible object wrapper for zip files" +marker = "python_version < \"3.8\"" +name = "zipp" optional = false -python-versions = "*" -version = "3.5.1" +python-versions = ">=3.6" +version = "2.1.0" -[package.dependencies] -notebook = ">=4.4.1" +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] +testing = ["jaraco.itertools"] [metadata] -content-hash = "7c3d541c65a27324b49fc7ed2b84067e223eb4e20c20310ff9e485b016f65f91" -python-versions = "^3.6" +content-hash = "c7cc19bb83c0f0869c6fbe7c1a65a57cf18f5d46d115e007080e497af615c1a3" +python-versions = "^3.7" -[metadata.hashes] -appdirs = ["9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", "d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"] -appnope = ["5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0", "8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"] -attrs = ["69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", "f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"] -backcall = ["38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4", "bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"] -black = ["817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739", "e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5"] -blackcellmagic = ["089b98d28aa647f2d86c3161b60cbf09793384ea94ceb1227f4f1d425db4002c"] -bleach = ["213336e49e102af26d9cde77dd2d0397afabc5a6bf2fed985dc35b5d1e285a16", "3fdf7f77adcf649c9911387df51254b813185e32b2c6619f690b593a617e19fa"] -click = ["2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", "5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"] -colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"] -decorator = ["86156361c50488b84a3f148056ea716ca587df2f0de1d34750d35c21312725de", "f069f3a01830ca754ba5258fde2278454a0b5b79e0d7f5c13b3b97e57d4acff6"] -defusedxml = ["6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93", "f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"] -entrypoints = ["589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19", "c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"] -ipykernel = ["167c3ef08450f5e060b76c749905acb0e0fbef9365899377a4a1eae728864383", "b503913e0b4cce7ed2de965457dfb2edd633e8234161a60e23f2fe2161345d12"] -ipython = ["c4ab005921641e40a68e405e286e7a1fcc464497e14d81b6914b4fd95e5dee9b", "dd76831f065f17bddd7eaa5c781f5ea32de5ef217592cf019e34043b56895aa1"] -ipython-genutils = ["72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", "eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"] -ipywidgets = ["13ffeca438e0c0f91ae583dc22f50379b9d6b28390ac7be8b757140e9a771516", "e945f6e02854a74994c596d9db83444a1850c01648f1574adf144fbbabe05c97"] -jedi = ["786b6c3d80e2f06fd77162a07fed81b8baa22dde5d62896a790a331d6ac21a27", "ba859c74fa3c966a22f2aeebe1b74ee27e2a462f56d3f5f7ca4a59af61bfe42e"] -jinja2 = ["065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013", "14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b"] -jsonschema = ["5f9c0a719ca2ce14c5de2fd350a64fd2d13e8539db29836a86adc990bb1a068f", "8d4a2b7b6c2237e0199c8ea1a6d3e05bf118e289ae2b9d7ba444182a2959560d"] -jupyter = ["3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7", "5b290f93b98ffbc21c0c7e749f054b3267782166d72fa5e3ed1ed4eaf34a2b78", "d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f"] -jupyter-client = ["4274f2eef4265bfd00216f44993aa50d890a1abe55e414c817128cf0338ae66a", "4c274aaa3d87b83fa6c6853463f54b8bbff137aeb71e65896ab111c17a62ae63"] -jupyter-console = ["308ce876354924fb6c540b41d5d6d08acfc946984bf0c97777c1ddcb42e0b2f5", "cc80a97a5c389cbd30252ffb5ce7cefd4b66bde98219edd16bf5cb6f84bb3568"] -jupyter-contrib-core = ["1ec81e275a8f5858d56b0c4c6cd85335aa8e915001b8657fe51c620c3cdde50f", "e65bc0e932ff31801003cef160a4665f2812efe26a53801925a634735e9a5794"] -jupyter-contrib-nbextensions = ["2c071f0aa208c569666f656bdc0f66906ca493cf9f06f46db6350db11030ff40", "eecd28ecc2fc410226c0a3d4932ed2fac4860ccf8d9e9b1b29548835a35b22ab"] -jupyter-core = ["2c6e7c1e9f2ac45b5c2ceea5730bc9008d92fe59d0725eac57b04c0edfba24f7", "f4fa22d6cf25f34807c995f22d2923693575c70f02557bcbfbe59bd5ec8d8b84"] -jupyter-highlight-selected-word = ["9545dfa9cb057eebe3a5795604dcd3a5294ea18637e553f61a0b67c1b5903c58", "9fa740424859a807950ca08d2bfd28a35154cd32dd6d50ac4e0950022adc0e7b"] -jupyter-latex-envs = ["070a31eb2dc488bba983915879a7c2939247bf5c3b669b398bdb36a9b5343872"] -jupyter-nbextensions-configurator = ["e5e86b5d9d898e1ffb30ebb08e4ad8696999f798fef3ff3262d7b999076e4e83"] -lxml = ["02ca7bf899da57084041bb0f6095333e4d239948ad3169443f454add9f4e9cb4", "096b82c5e0ea27ce9138bcbb205313343ee66a6e132f25c5ed67e2c8d960a1bc", "0a920ff98cf1aac310470c644bc23b326402d3ef667ddafecb024e1713d485f1", "17cae1730a782858a6e2758fd20dd0ef7567916c47757b694a06ffafdec20046", "17e3950add54c882e032527795c625929613adbd2ce5162b94667334458b5a36", "1f4f214337f6ee5825bf90a65d04d70aab05526c08191ab888cb5149501923c5", "2e8f77db25b0a96af679e64ff9bf9dddb27d379c9900c3272f3041c4d1327c9d", "4dffd405390a45ecb95ab5ab1c1b847553c18b0ef8ed01e10c1c8b1a76452916", "6b899931a5648862c7b88c795eddff7588fb585e81cecce20f8d9da16eff96e0", "726c17f3e0d7a7200718c9a890ccfeab391c9133e363a577a44717c85c71db27", "760c12276fee05c36f95f8040180abc7fbebb9e5011447a97cdc289b5d6ab6fc", "796685d3969815a633827c818863ee199440696b0961e200b011d79b9394bbe7", "891fe897b49abb7db470c55664b198b1095e4943b9f82b7dcab317a19116cd38", "a471628e20f03dcdfde00770eeaf9c77811f0c331c8805219ca7b87ac17576c5", "a63b4fd3e2cabdcc9d918ed280bdde3e8e9641e04f3c59a2a3109644a07b9832", "b0b84408d4eabc6de9dd1e1e0bc63e7731e890c0b378a62443e5741cfd0ae90a", "be78485e5d5f3684e875dab60f40cddace2f5b2a8f7fede412358ab3214c3a6f", "c27eaed872185f047bb7f7da2d21a7d8913457678c9a100a50db6da890bc28b9", "c81cb40bff373ab7a7446d6bbca0190bccc5be3448b47b51d729e37799bb5692", "d11874b3c33ee441059464711cd365b89fa1a9cf19ae75b0c189b01fbf735b84", "e9c028b5897901361d81a4718d1db217b716424a0283afe9d6735fe0caf70f79", "fe489d486cd00b739be826e8c1be188ddb74c7a1ca784d93d06fda882a6a1681"] -markupsafe = ["00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", "09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", "43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", "62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", "717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", "88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", "98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", "ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", "b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", "ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", "c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", "cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", "e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"] -mistune = ["59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e", "88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"] -nbconvert = ["427a468ec26e7d68a529b95f578d5cbf018cb4c1f889e897681c2b6d11897695", "48d3c342057a2cf21e8df820d49ff27ab9f25fc72b8f15606bd47967333b2709"] -nbformat = ["b9a0dbdbd45bb034f4f8893cafd6f652ea08c8c1674ba83f2dc55d3955743b0b", "f7494ef0df60766b7cabe0a3651556345a963b74dbc16bc7c18479041170d402"] -notebook = ["660976fe4fe45c7aa55e04bf4bccb9f9566749ff637e9020af3422f9921f9a5d", "b0a290f5cc7792d50a21bec62b3c221dd820bf00efa916ce9aeec4b5354bde20"] -numpy = ["05dbfe72684cc14b92568de1bc1f41e5f62b00f714afc9adee42f6311738091f", "0d82cb7271a577529d07bbb05cb58675f2deb09772175fab96dc8de025d8ac05", "10132aa1fef99adc85a905d82e8497a580f83739837d7cbd234649f2e9b9dc58", "12322df2e21f033a60c80319c25011194cd2a21294cc66fee0908aeae2c27832", "16f19b3aa775dddc9814e02a46b8e6ae6a54ed8cf143962b4e53f0471dbd7b16", "3d0b0989dd2d066db006158de7220802899a1e5c8cf622abe2d0bd158fd01c2c", "438a3f0e7b681642898fd7993d38e2bf140a2d1eafaf3e89bb626db7f50db355", "5fd214f482ab53f2cea57414c5fb3e58895b17df6e6f5bca5be6a0bb6aea23bb", "73615d3edc84dd7c4aeb212fa3748fb83217e00d201875a47327f55363cef2df", "7bd355ad7496f4ce1d235e9814ec81ee3d28308d591c067ce92e49f745ba2c2f", "7d077f2976b8f3de08a0dcf5d72083f4af5411e8fddacd662aae27baa2601196", "a4092682778dc48093e8bda8d26ee8360153e2047826f95a3f5eae09f0ae3abf", "b458de8624c9f6034af492372eb2fee41a8e605f03f4732f43fc099e227858b2", "e70fc8ff03a961f13363c2c95ef8285e0cf6a720f8271836f852cc0fa64e97c8", "ee8e9d7cad5fe6dde50ede0d2e978d81eafeaa6233fb0b8719f60214cf226578", "f4a4f6aba148858a5a5d546a99280f71f5ee6ec8182a7d195af1a914195b21a2"] -pandocfilters = ["b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"] -parso = ["63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc", "666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c"] -pexpect = ["2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1", "9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"] -pickleshare = ["87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", "9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"] -prometheus-client = ["71cd24a2b3eb335cb800c7159f423df1bd4dcd5171b234be15e3f31ec9f622da"] -prompt-toolkit = ["11adf3389a996a6d45cc277580d0d53e8a5afd281d0c9ec71b28e6f121463780", "2519ad1d8038fd5fc8e770362237ad0364d16a7650fb5724af6997ed5515e3c1", "977c6583ae813a37dc1c2e1b715892461fcbdaa57f6fc62f33a528c4886c8f55"] -ptyprocess = ["923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0", "d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"] -pygments = ["71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", "881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297"] -pyrsistent = ["34b47fa169d6006b32e99d4b3c4031f155e6e68ebcc107d6454852e8e0ee6533"] -python-dateutil = ["7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", "c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"] -pywin32 = ["0443e9bb196e72480f50cbddc2cf98fbb858a77d02e281ba79489ea3287b36e9", "09bbe7cdb29eb40ab2e83f7a232eeeedde864be7a0622b70a90f456aad07a234", "0d8e0f47808798d320c983574c36c49db642678902933a210edd40157d206fd0", "0db7c9f4b93528afd080d35912a60be2f86a1d6c49c0a9cf9cedd106eed81ea3", "749e590875051661ecefbd9dfa957a485016de0f25e43f5e70f888ef1e29587b", "779d3e9d4b934f2445d2920c3941416d99af72eb7f7fd57a63576cc8aa540ad6", "7c89d2c11a31c7aaa16dc4d25054d7e0e99d6f6b24193cf62c83850484658c87", "81f7732b662c46274d7d8c411c905d53e71999cba95457a0686467c3ebc745ca", "9db1fb8830bfa99c5bfd335d4482c14db5c6f5028db3b006787ef4200206242b", "bd8d04835db28646d9e07fd0ab7c7b18bd90e89dfdc559e60389179495ef30da", "fc6822a68afd79e97b015985dd455767c72009b81bcd18957068626c43f11e75", "fe6cfc2045931866417740b575231c7e12d69d481643be1493487ad53b089959"] -pywinpty = ["0e01321e53a230233358a6d608a1a8bc86c3882cf82769ba3c62ca387dc9cc51", "333e0bc5fca8ad9e9a1516ebedb2a65da38dc1f399f8b2ea57d6cccec1ff2cc8", "3ca3123aa6340ab31bbf9bd012b92e72f9ec905e4c9ee152cc997403e1778cd3", "44a6dddcf2abf402e22f87e2c9a341f7d0b296afbec3d28184c8de4d7f514ee4", "53d94d574c3d4da2df5b1c3ae728b8d90e4d33502b0388576bbd4ddeb4de0f77", "c3955f162c53dde968f3fc11361658f1d83b683bfe601d4b6f94bb01ea4300bc", "cec9894ecb34de3d7b1ca121dd98433035b9f8949b5095e84b103b349231509c", "dcd45912e2fe2e6f72cee997a4da6ed1ad2056165a277ce5ec7f7ac98dcdf667", "f2bcdd9a2ffd8b223752a971b3d377fb7bfed85f140ec9710f1218d760f2ccb7"] -pyyaml = ["0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9", "01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4", "5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8", "5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696", "7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34", "7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9", "87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73", "9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299", "a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b", "b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae", "b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681", "bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41", "f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8"] -pyzmq = ["01636e95a88d60118479041c6aaaaf5419c6485b7b1d37c9c4dd424b7b9f1121", "021dba0d1436516092c624359e5da51472b11ba8edffa334218912f7e8b65467", "0463bd941b6aead494d4035f7eebd70035293dd6caf8425993e85ad41de13fa3", "05fd51edd81eed798fccafdd49c936b6c166ffae7b32482e4d6d6a2e196af4e6", "1fadc8fbdf3d22753c36d4172169d184ee6654f8d6539e7af25029643363c490", "22efa0596cf245a78a99060fe5682c4cd00c58bb7614271129215c889062db80", "260c70b7c018905ec3659d0f04db735ac830fe27236e43b9dc0532cf7c9873ef", "2762c45e289732d4450406cedca35a9d4d71e449131ba2f491e0bf473e3d2ff2", "2fc6cada8dc53521c1189596f1898d45c5f68603194d3a6453d6db4b27f4e12e", "343b9710a61f2b167673bea1974e70b5dccfe64b5ed10626798f08c1f7227e72", "41bf96d5f554598a0632c3ec28e3026f1d6591a50f580df38eff0b8067efb9e7", "856b2cdf7a1e2cbb84928e1e8db0ea4018709b39804103d3a409e5584f553f57", "85b869abc894672de9aecdf032158ea8ad01e2f0c3b09ef60e3687fb79418096", "93f44739db69234c013a16990e43db1aa0af3cf5a4b8b377d028ff24515fbeb3", "98fa3e75ccb22c0dc99654e3dd9ff693b956861459e8c8e8734dd6247b89eb29", "9a22c94d2e93af8bebd4fcf5fa38830f5e3b1ff0d4424e2912b07651eb1bafb4", "a7d3f4b4bbb5d7866ae727763268b5c15797cbd7b63ea17f3b0ec1067da8994b", "b645a49376547b3816433a7e2d2a99135c8e651e50497e7ecac3bd126e4bea16", "cf0765822e78cf9e45451647a346d443f66792aba906bc340f4e0ac7870c169c", "dc398e1e047efb18bfab7a8989346c6921a847feae2cad69fedf6ca12fb99e2c", "dd5995ae2e80044e33b5077fb4bc2b0c1788ac6feaf15a6b87a00c14b4bdd682", "e03fe5e07e70f245dc9013a9d48ae8cc4b10c33a1968039c5a3b64b5d01d083d", "ea09a306144dff2795e48439883349819bef2c53c0ee62a3c2fae429451843bb", "f4e37f33da282c3c319849877e34f97f0a3acec09622ec61b7333205bdd13b52", "fa4bad0d1d173dee3e8ef3c3eb6b2bb6c723fc7a661eeecc1ecb2fa99860dd45"] -qtconsole = ["40d5d8e00d070ea266dbf6f0da74c4b9597b8b8d67cd8233c3ffd8debf923703", "b91e7412587e6cfe1644696538f73baf5611e837be5406633218443b2827c6d9"] -rise = ["5a3d529170d707ff0af42bf12f9584b5089baf401643d136d052a66cb4210052", "ba94321aee993cb2f0647c0db8688064afcff6000576f0fcb1e98652abc629e3"] -send2trash = ["60001cc07d707fe247c94f74ca6ac0d3255aabcb930529690897ca2a39db28b2", "f1691922577b6fa12821234aeb57599d887c4900b9ca537948d2dac34aea888b"] -six = ["3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"] -terminado = ["d9d012de63acb8223ac969c17c3043337c2fcfd28f3aea1ee429b345d01ef460", "de08e141f83c3a0798b050ecb097ab6259c3f0331b2f7b7750c9075ced2c20c2"] -testpath = ["46c89ebb683f473ffe2aab0ed9f12581d4d078308a3cb3765d79c6b2317b0109", "b694b3d9288dbd81685c5d2e7140b81365d46c29f5db4bc659de5aa6b98780f8"] -toml = ["229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", "235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e", "f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"] -tornado = ["349884248c36801afa19e342a77cc4458caca694b0eda633f5878e458a44cb2c", "398e0d35e086ba38a0427c3b37f4337327231942e731edaa6e9fd1865bbd6f60", "4e73ef678b1a859f0cb29e1d895526a20ea64b5ffd510a2307b5998c7df24281", "559bce3d31484b665259f50cd94c5c28b961b09315ccd838f284687245f416e5", "abbe53a39734ef4aba061fca54e30c6b4639d3e1f59653f0da37a0003de148c7", "c845db36ba616912074c5b1ee897f8e0124df269468f25e4fe21fe72f6edd7a9", "c9399267c926a4e7c418baa5cbe91c7d1cf362d505a1ef898fde44a07c9dd8a5"] -traitlets = ["9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835", "c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9"] -wcwidth = ["3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", "f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"] -webencodings = ["a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", "b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"] -widgetsnbextension = ["079f87d87270bce047512400efd70238820751a11d2d8cb137a5a5bdbaf255c7", "bd314f8ceb488571a5ffea6cc5b9fc6cba0adaf88a9d2386b93a489751938bcd"] +[metadata.files] +appdirs = [ + {file = "appdirs-1.4.3-py2.py3-none-any.whl", hash = "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"}, + {file = "appdirs-1.4.3.tar.gz", hash = "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92"}, +] +appnope = [ + {file = "appnope-0.1.0-py2.py3-none-any.whl", hash = "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0"}, + {file = "appnope-0.1.0.tar.gz", hash = "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"}, +] +attrs = [ + {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, + {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, +] +backcall = [ + {file = "backcall-0.1.0.tar.gz", hash = "sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4"}, + {file = "backcall-0.1.0.zip", hash = "sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"}, +] +black = [ + {file = "black-19.10b0-py36-none-any.whl", hash = "sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b"}, + {file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"}, +] +blackcellmagic = [ + {file = "blackcellmagic-0.0.2.tar.gz", hash = "sha256:089b98d28aa647f2d86c3161b60cbf09793384ea94ceb1227f4f1d425db4002c"}, +] +bleach = [ + {file = "bleach-3.1.0-py2.py3-none-any.whl", hash = "sha256:213336e49e102af26d9cde77dd2d0397afabc5a6bf2fed985dc35b5d1e285a16"}, + {file = "bleach-3.1.0.tar.gz", hash = "sha256:3fdf7f77adcf649c9911387df51254b813185e32b2c6619f690b593a617e19fa"}, +] +click = [ + {file = "Click-7.0-py2.py3-none-any.whl", hash = "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13"}, + {file = "Click-7.0.tar.gz", hash = "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"}, +] +colorama = [ + {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, + {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, +] +decorator = [ + {file = "decorator-4.4.1-py2.py3-none-any.whl", hash = "sha256:5d19b92a3c8f7f101c8dd86afd86b0f061a8ce4540ab8cd401fa2542756bce6d"}, + {file = "decorator-4.4.1.tar.gz", hash = "sha256:54c38050039232e1db4ad7375cfce6748d7b41c29e95a081c8a6d2c30364a2ce"}, +] +defusedxml = [ + {file = "defusedxml-0.6.0-py2.py3-none-any.whl", hash = "sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93"}, + {file = "defusedxml-0.6.0.tar.gz", hash = "sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"}, +] +entrypoints = [ + {file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"}, + {file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"}, +] +importlib-metadata = [ + {file = "importlib_metadata-1.4.0-py2.py3-none-any.whl", hash = "sha256:bdd9b7c397c273bcc9a11d6629a38487cd07154fa255a467bf704cd2c258e359"}, + {file = "importlib_metadata-1.4.0.tar.gz", hash = "sha256:f17c015735e1a88296994c0697ecea7e11db24290941983b08c9feb30921e6d8"}, +] +ipykernel = [ + {file = "ipykernel-5.1.4-py3-none-any.whl", hash = "sha256:ba8c9e5561f3223fb47ce06ad7925cb9444337ac367341c0c520ffb68ea6d120"}, + {file = "ipykernel-5.1.4.tar.gz", hash = "sha256:7f1f01df22f1229c8879501057877ccaf92a3b01c1d00db708aad5003e5f9238"}, +] +ipython = [ + {file = "ipython-7.11.1-py3-none-any.whl", hash = "sha256:387686dd7fc9caf29d2fddcf3116c4b07a11d9025701d220c589a430b0171d8a"}, + {file = "ipython-7.11.1.tar.gz", hash = "sha256:0f4bcf18293fb666df8511feec0403bdb7e061a5842ea6e88a3177b0ceb34ead"}, +] +ipython-genutils = [ + {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, + {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, +] +jedi = [ + {file = "jedi-0.16.0-py2.py3-none-any.whl", hash = "sha256:b4f4052551025c6b0b0b193b29a6ff7bdb74c52450631206c262aef9f7159ad2"}, + {file = "jedi-0.16.0.tar.gz", hash = "sha256:d5c871cb9360b414f981e7072c52c33258d598305280fef91c6cae34739d65d5"}, +] +jinja2 = [ + {file = "Jinja2-2.11.0-py2.py3-none-any.whl", hash = "sha256:6e7a3c2934694d59ad334c93dd1b6c96699cf24c53fdb8ec848ac6b23e685734"}, + {file = "Jinja2-2.11.0.tar.gz", hash = "sha256:d6609ae5ec3d56212ca7d802eda654eaf2310000816ce815361041465b108be4"}, +] +json5 = [ + {file = "json5-0.8.5-py2.py3-none-any.whl", hash = "sha256:32bd17e0553bf53927f6c29b6089f3a320c12897120a4bcfea76ea81c10b2d9c"}, + {file = "json5-0.8.5.tar.gz", hash = "sha256:124b0f0da1ed2ff3bfe3a3e9b8630abd3c650852465cb52c15ef60b8e82a73b0"}, +] +jsonschema = [ + {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"}, + {file = "jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"}, +] +jupyter-client = [ + {file = "jupyter_client-5.3.4-py2.py3-none-any.whl", hash = "sha256:d0c077c9aaa4432ad485e7733e4d91e48f87b4f4bab7d283d42bb24cbbba0a0f"}, + {file = "jupyter_client-5.3.4.tar.gz", hash = "sha256:60e6faec1031d63df57f1cc671ed673dced0ed420f4377ea33db37b1c188b910"}, +] +jupyter-core = [ + {file = "jupyter_core-4.6.1-py2.py3-none-any.whl", hash = "sha256:464769f7387d7a62a2403d067f1ddc616655b7f77f5d810c0dd62cb54bfd0fb9"}, + {file = "jupyter_core-4.6.1.tar.gz", hash = "sha256:a183e0ec2e8f6adddf62b0a3fc6a2237e3e0056d381e536d3e7c7ecc3067e244"}, +] +jupyterlab = [ + {file = "jupyterlab-1.2.6-py2.py3-none-any.whl", hash = "sha256:56c108e28934ac463754b7656441c0d92e76a81ad5dad446fe1071c6fd86245c"}, + {file = "jupyterlab-1.2.6.tar.gz", hash = "sha256:42134b13fb0c410a9f55e8492a31ba5a1a346430a22690a512b8307764b68355"}, +] +jupyterlab-server = [ + {file = "jupyterlab_server-1.0.6-py3-none-any.whl", hash = "sha256:d9c3bcf097f7ad8d8fd2f8d0c1e8a1b833671c02808e5f807088975495364447"}, + {file = "jupyterlab_server-1.0.6.tar.gz", hash = "sha256:d0977527bfce6f47c782cb6bf79d2c949ebe3f22ac695fa000b730c671445dad"}, +] +markupsafe = [ + {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"}, + {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"}, + {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, + {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, +] +mistune = [ + {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, + {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, +] +nbconvert = [ + {file = "nbconvert-5.6.1-py2.py3-none-any.whl", hash = "sha256:f0d6ec03875f96df45aa13e21fd9b8450c42d7e1830418cccc008c0df725fcee"}, + {file = "nbconvert-5.6.1.tar.gz", hash = "sha256:21fb48e700b43e82ba0e3142421a659d7739b65568cc832a13976a77be16b523"}, +] +nbformat = [ + {file = "nbformat-5.0.4-py3-none-any.whl", hash = "sha256:f4bbbd8089bd346488f00af4ce2efb7f8310a74b2058040d075895429924678c"}, + {file = "nbformat-5.0.4.tar.gz", hash = "sha256:562de41fc7f4f481b79ab5d683279bf3a168858268d4387b489b7b02be0b324a"}, +] +notebook = [ + {file = "notebook-6.0.3-py3-none-any.whl", hash = "sha256:3edc616c684214292994a3af05eaea4cc043f6b4247d830f3a2f209fa7639a80"}, + {file = "notebook-6.0.3.tar.gz", hash = "sha256:47a9092975c9e7965ada00b9a20f0cf637d001db60d241d479f53c0be117ad48"}, +] +numpy = [ + {file = "numpy-1.18.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:20b26aaa5b3da029942cdcce719b363dbe58696ad182aff0e5dcb1687ec946dc"}, + {file = "numpy-1.18.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:70a840a26f4e61defa7bdf811d7498a284ced303dfbc35acb7be12a39b2aa121"}, + {file = "numpy-1.18.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:17aa7a81fe7599a10f2b7d95856dc5cf84a4eefa45bc96123cbbc3ebc568994e"}, + {file = "numpy-1.18.1-cp35-cp35m-win32.whl", hash = "sha256:f3d0a94ad151870978fb93538e95411c83899c9dc63e6fb65542f769568ecfa5"}, + {file = "numpy-1.18.1-cp35-cp35m-win_amd64.whl", hash = "sha256:1786a08236f2c92ae0e70423c45e1e62788ed33028f94ca99c4df03f5be6b3c6"}, + {file = "numpy-1.18.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ae0975f42ab1f28364dcda3dde3cf6c1ddab3e1d4b2909da0cb0191fa9ca0480"}, + {file = "numpy-1.18.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:cf7eb6b1025d3e169989416b1adcd676624c2dbed9e3bcb7137f51bfc8cc2572"}, + {file = "numpy-1.18.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:b765ed3930b92812aa698a455847141869ef755a87e099fddd4ccf9d81fffb57"}, + {file = "numpy-1.18.1-cp36-cp36m-win32.whl", hash = "sha256:2d75908ab3ced4223ccba595b48e538afa5ecc37405923d1fea6906d7c3a50bc"}, + {file = "numpy-1.18.1-cp36-cp36m-win_amd64.whl", hash = "sha256:9acdf933c1fd263c513a2df3dceecea6f3ff4419d80bf238510976bf9bcb26cd"}, + {file = "numpy-1.18.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:56bc8ded6fcd9adea90f65377438f9fea8c05fcf7c5ba766bef258d0da1554aa"}, + {file = "numpy-1.18.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:e422c3152921cece8b6a2fb6b0b4d73b6579bd20ae075e7d15143e711f3ca2ca"}, + {file = "numpy-1.18.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:b3af02ecc999c8003e538e60c89a2b37646b39b688d4e44d7373e11c2debabec"}, + {file = "numpy-1.18.1-cp37-cp37m-win32.whl", hash = "sha256:d92350c22b150c1cae7ebb0ee8b5670cc84848f6359cf6b5d8f86617098a9b73"}, + {file = "numpy-1.18.1-cp37-cp37m-win_amd64.whl", hash = "sha256:77c3bfe65d8560487052ad55c6998a04b654c2fbc36d546aef2b2e511e760971"}, + {file = "numpy-1.18.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c98c5ffd7d41611407a1103ae11c8b634ad6a43606eca3e2a5a269e5d6e8eb07"}, + {file = "numpy-1.18.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:9537eecf179f566fd1c160a2e912ca0b8e02d773af0a7a1120ad4f7507cd0d26"}, + {file = "numpy-1.18.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:e840f552a509e3380b0f0ec977e8124d0dc34dc0e68289ca28f4d7c1d0d79474"}, + {file = "numpy-1.18.1-cp38-cp38-win32.whl", hash = "sha256:590355aeade1a2eaba17617c19edccb7db8d78760175256e3cf94590a1a964f3"}, + {file = "numpy-1.18.1-cp38-cp38-win_amd64.whl", hash = "sha256:39d2c685af15d3ce682c99ce5925cc66efc824652e10990d2462dfe9b8918c6a"}, + {file = "numpy-1.18.1.zip", hash = "sha256:b6ff59cee96b454516e47e7721098e6ceebef435e3e21ac2d6c3b8b02628eb77"}, +] +pandocfilters = [ + {file = "pandocfilters-1.4.2.tar.gz", hash = "sha256:b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"}, +] +parso = [ + {file = "parso-0.6.0-py2.py3-none-any.whl", hash = "sha256:1376bdc8cb81377ca481976933773295218a2df47d3e1182ba76d372b1acb128"}, + {file = "parso-0.6.0.tar.gz", hash = "sha256:597f36de5102a8db05ffdf7ecdc761838b86565a4a111604c6e78beaedf1b045"}, +] +pathspec = [ + {file = "pathspec-0.7.0-py2.py3-none-any.whl", hash = "sha256:163b0632d4e31cef212976cf57b43d9fd6b0bac6e67c26015d611a647d5e7424"}, + {file = "pathspec-0.7.0.tar.gz", hash = "sha256:562aa70af2e0d434367d9790ad37aed893de47f1693e4201fd1d3dca15d19b96"}, +] +pexpect = [ + {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, + {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, +] +pickleshare = [ + {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, + {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, +] +prometheus-client = [ + {file = "prometheus_client-0.7.1.tar.gz", hash = "sha256:71cd24a2b3eb335cb800c7159f423df1bd4dcd5171b234be15e3f31ec9f622da"}, +] +prompt-toolkit = [ + {file = "prompt_toolkit-3.0.3-py3-none-any.whl", hash = "sha256:c93e53af97f630f12f5f62a3274e79527936ed466f038953dfa379d4941f651a"}, + {file = "prompt_toolkit-3.0.3.tar.gz", hash = "sha256:a402e9bf468b63314e37460b68ba68243d55b2f8c4d0192f85a019af3945050e"}, +] +ptyprocess = [ + {file = "ptyprocess-0.6.0-py2.py3-none-any.whl", hash = "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"}, + {file = "ptyprocess-0.6.0.tar.gz", hash = "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0"}, +] +pygments = [ + {file = "Pygments-2.5.2-py2.py3-none-any.whl", hash = "sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b"}, + {file = "Pygments-2.5.2.tar.gz", hash = "sha256:98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe"}, +] +pyrsistent = [ + {file = "pyrsistent-0.15.7.tar.gz", hash = "sha256:cdc7b5e3ed77bed61270a47d35434a30617b9becdf2478af76ad2c6ade307280"}, +] +python-dateutil = [ + {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, + {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, +] +pywin32 = [ + {file = "pywin32-227-cp27-cp27m-win32.whl", hash = "sha256:371fcc39416d736401f0274dd64c2302728c9e034808e37381b5e1b22be4a6b0"}, + {file = "pywin32-227-cp27-cp27m-win_amd64.whl", hash = "sha256:4cdad3e84191194ea6d0dd1b1b9bdda574ff563177d2adf2b4efec2a244fa116"}, + {file = "pywin32-227-cp35-cp35m-win32.whl", hash = "sha256:f4c5be1a293bae0076d93c88f37ee8da68136744588bc5e2be2f299a34ceb7aa"}, + {file = "pywin32-227-cp35-cp35m-win_amd64.whl", hash = "sha256:a929a4af626e530383a579431b70e512e736e9588106715215bf685a3ea508d4"}, + {file = "pywin32-227-cp36-cp36m-win32.whl", hash = "sha256:300a2db938e98c3e7e2093e4491439e62287d0d493fe07cce110db070b54c0be"}, + {file = "pywin32-227-cp36-cp36m-win_amd64.whl", hash = "sha256:9b31e009564fb95db160f154e2aa195ed66bcc4c058ed72850d047141b36f3a2"}, + {file = "pywin32-227-cp37-cp37m-win32.whl", hash = "sha256:47a3c7551376a865dd8d095a98deba954a98f326c6fe3c72d8726ca6e6b15507"}, + {file = "pywin32-227-cp37-cp37m-win_amd64.whl", hash = "sha256:31f88a89139cb2adc40f8f0e65ee56a8c585f629974f9e07622ba80199057511"}, + {file = "pywin32-227-cp38-cp38-win32.whl", hash = "sha256:7f18199fbf29ca99dff10e1f09451582ae9e372a892ff03a28528a24d55875bc"}, + {file = "pywin32-227-cp38-cp38-win_amd64.whl", hash = "sha256:7c1ae32c489dc012930787f06244426f8356e129184a02c25aef163917ce158e"}, + {file = "pywin32-227-cp39-cp39-win32.whl", hash = "sha256:c054c52ba46e7eb6b7d7dfae4dbd987a1bb48ee86debe3f245a2884ece46e295"}, + {file = "pywin32-227-cp39-cp39-win_amd64.whl", hash = "sha256:f27cec5e7f588c3d1051651830ecc00294f90728d19c3bf6916e6dba93ea357c"}, +] +pywinpty = [ + {file = "pywinpty-0.5.7-cp27-cp27m-win32.whl", hash = "sha256:b358cb552c0f6baf790de375fab96524a0498c9df83489b8c23f7f08795e966b"}, + {file = "pywinpty-0.5.7-cp27-cp27m-win_amd64.whl", hash = "sha256:1e525a4de05e72016a7af27836d512db67d06a015aeaf2fa0180f8e6a039b3c2"}, + {file = "pywinpty-0.5.7-cp35-cp35m-win32.whl", hash = "sha256:2740eeeb59297593a0d3f762269b01d0285c1b829d6827445fcd348fb47f7e70"}, + {file = "pywinpty-0.5.7-cp35-cp35m-win_amd64.whl", hash = "sha256:33df97f79843b2b8b8bc5c7aaf54adec08cc1bae94ee99dfb1a93c7a67704d95"}, + {file = "pywinpty-0.5.7-cp36-cp36m-win32.whl", hash = "sha256:e854211df55d107f0edfda8a80b39dfc87015bef52a8fe6594eb379240d81df2"}, + {file = "pywinpty-0.5.7-cp36-cp36m-win_amd64.whl", hash = "sha256:dbd838de92de1d4ebf0dce9d4d5e4fc38d0b7b1de837947a18b57a882f219139"}, + {file = "pywinpty-0.5.7-cp37-cp37m-win32.whl", hash = "sha256:5fb2c6c6819491b216f78acc2c521b9df21e0f53b9a399d58a5c151a3c4e2a2d"}, + {file = "pywinpty-0.5.7-cp37-cp37m-win_amd64.whl", hash = "sha256:dd22c8efacf600730abe4a46c1388355ce0d4ab75dc79b15d23a7bd87bf05b48"}, + {file = "pywinpty-0.5.7-cp38-cp38-win_amd64.whl", hash = "sha256:8fc5019ff3efb4f13708bd3b5ad327589c1a554cb516d792527361525a7cb78c"}, + {file = "pywinpty-0.5.7.tar.gz", hash = "sha256:2d7e9c881638a72ffdca3f5417dd1563b60f603e1b43e5895674c2a1b01f95a0"}, +] +pyzmq = [ + {file = "pyzmq-18.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:0573b9790aa26faff33fba40f25763657271d26f64bffb55a957a3d4165d6098"}, + {file = "pyzmq-18.1.1-cp27-cp27m-win32.whl", hash = "sha256:972d723a36ab6a60b7806faa5c18aa3c080b7d046c407e816a1d8673989e2485"}, + {file = "pyzmq-18.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:0fa82b9fc3334478be95a5566f35f23109f763d1669bb762e3871a8fa2a4a037"}, + {file = "pyzmq-18.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:80c928d5adcfa12346b08d31360988d843b54b94154575cccd628f1fe91446bc"}, + {file = "pyzmq-18.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:efdde21febb9b5d7a8e0b87ea2549d7e00fda1936459cfb27fb6fca0c36af6c1"}, + {file = "pyzmq-18.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:aa3872f2ebfc5f9692ef8957fe69abe92d905a029c0608e45ebfcd451ad30ab5"}, + {file = "pyzmq-18.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:01b588911714a6696283de3904f564c550c9e12e8b4995e173f1011755e01086"}, + {file = "pyzmq-18.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:8ff946b20d13a99dc5c21cb76f4b8b253eeddf3eceab4218df8825b0c65ab23d"}, + {file = "pyzmq-18.1.1-cp35-cp35m-win32.whl", hash = "sha256:2a294b4f44201bb21acc2c1a17ff87fbe57b82060b10ddb00ac03e57f3d7fcfa"}, + {file = "pyzmq-18.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:6fca7d11310430e751f9832257866a122edf9d7b635305c5d8c51f74a5174d3d"}, + {file = "pyzmq-18.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:f4e72646bfe79ff3adbf1314906bbd2d67ef9ccc71a3a98b8b2ccbcca0ab7bec"}, + {file = "pyzmq-18.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:1e59b7b19396f26e360f41411a5d4603356d18871049cd7790f1a7d18f65fb2c"}, + {file = "pyzmq-18.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:cf08435b14684f7f2ca2df32c9df38a79cdc17c20dc461927789216cb43d8363"}, + {file = "pyzmq-18.1.1-cp36-cp36m-win32.whl", hash = "sha256:75d73ee7ca4b289a2a2dfe0e6bd8f854979fc13b3fe4ebc19381be3b04e37a4a"}, + {file = "pyzmq-18.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:7369656f89878455a5bcd5d56ca961884f5d096268f71c0750fc33d6732a25e5"}, + {file = "pyzmq-18.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4ec47f2b50bdb97df58f1697470e5c58c3c5109289a623e30baf293481ff0166"}, + {file = "pyzmq-18.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:5541dc8cad3a8486d58bbed076cb113b65b5dd6b91eb94fb3e38a3d1d3022f20"}, + {file = "pyzmq-18.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ed6205ca0de035f252baa0fd26fdd2bc8a8f633f92f89ca866fd423ff26c6f25"}, + {file = "pyzmq-18.1.1-cp37-cp37m-win32.whl", hash = "sha256:8b8498ceee33a7023deb2f3db907ca41d6940321e282297327a9be41e3983792"}, + {file = "pyzmq-18.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:e37f22eb4bfbf69cd462c7000616e03b0cdc1b65f2d99334acad36ea0e4ddf6b"}, + {file = "pyzmq-18.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:355b38d7dd6f884b8ee9771f59036bcd178d98539680c4f87e7ceb2c6fd057b6"}, + {file = "pyzmq-18.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:4b73d20aec63933bbda7957e30add233289d86d92a0bb9feb3f4746376f33527"}, + {file = "pyzmq-18.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:d30db4566177a6205ed1badb8dbbac3c043e91b12a2db5ef9171b318c5641b75"}, + {file = "pyzmq-18.1.1-cp38-cp38-win32.whl", hash = "sha256:83ce18b133dc7e6789f64cb994e7376c5aa6b4aeced993048bf1d7f9a0fe6d3a"}, + {file = "pyzmq-18.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:d5ac84f38575a601ab20c1878818ffe0d09eb51d6cb8511b636da46d0fd8949a"}, + {file = "pyzmq-18.1.1-pp271-pypy_41-macosx_10_15_x86_64.whl", hash = "sha256:e6549dd80de7b23b637f586217a4280facd14ac01e9410a037a13854a6977299"}, + {file = "pyzmq-18.1.1-pp371-pypy3_71-macosx_10_15_x86_64.whl", hash = "sha256:a6c9c42bbdba3f9c73aedbb7671815af1943ae8073e532c2b66efb72f39f4165"}, + {file = "pyzmq-18.1.1.tar.gz", hash = "sha256:8c69a6cbfa94da29a34f6b16193e7c15f5d3220cb772d6d17425ff3faa063a6d"}, +] +regex = [ + {file = "regex-2020.1.8-cp27-cp27m-win32.whl", hash = "sha256:4e8f02d3d72ca94efc8396f8036c0d3bcc812aefc28ec70f35bb888c74a25161"}, + {file = "regex-2020.1.8-cp27-cp27m-win_amd64.whl", hash = "sha256:e6c02171d62ed6972ca8631f6f34fa3281d51db8b326ee397b9c83093a6b7242"}, + {file = "regex-2020.1.8-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:4eae742636aec40cf7ab98171ab9400393360b97e8f9da67b1867a9ee0889b26"}, + {file = "regex-2020.1.8-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:bd25bb7980917e4e70ccccd7e3b5740614f1c408a642c245019cff9d7d1b6149"}, + {file = "regex-2020.1.8-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:3e77409b678b21a056415da3a56abfd7c3ad03da71f3051bbcdb68cf44d3c34d"}, + {file = "regex-2020.1.8-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:07b39bf943d3d2fe63d46281d8504f8df0ff3fe4c57e13d1656737950e53e525"}, + {file = "regex-2020.1.8-cp36-cp36m-win32.whl", hash = "sha256:23e2c2c0ff50f44877f64780b815b8fd2e003cda9ce817a7fd00dea5600c84a0"}, + {file = "regex-2020.1.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27429b8d74ba683484a06b260b7bb00f312e7c757792628ea251afdbf1434003"}, + {file = "regex-2020.1.8-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0e182d2f097ea8549a249040922fa2b92ae28be4be4895933e369a525ba36576"}, + {file = "regex-2020.1.8-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e3cd21cc2840ca67de0bbe4071f79f031c81418deb544ceda93ad75ca1ee9f7b"}, + {file = "regex-2020.1.8-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ecc6de77df3ef68fee966bb8cb4e067e84d4d1f397d0ef6fce46913663540d77"}, + {file = "regex-2020.1.8-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:26ff99c980f53b3191d8931b199b29d6787c059f2e029b2b0c694343b1708c35"}, + {file = "regex-2020.1.8-cp37-cp37m-win32.whl", hash = "sha256:7bcd322935377abcc79bfe5b63c44abd0b29387f267791d566bbb566edfdd146"}, + {file = "regex-2020.1.8-cp37-cp37m-win_amd64.whl", hash = "sha256:10671601ee06cf4dc1bc0b4805309040bb34c9af423c12c379c83d7895622bb5"}, + {file = "regex-2020.1.8-cp38-cp38-manylinux1_i686.whl", hash = "sha256:98b8ed7bb2155e2cbb8b76f627b2fd12cf4b22ab6e14873e8641f266e0fb6d8f"}, + {file = "regex-2020.1.8-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6a6ba91b94427cd49cd27764679024b14a96874e0dc638ae6bdd4b1a3ce97be1"}, + {file = "regex-2020.1.8-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:6a6ae17bf8f2d82d1e8858a47757ce389b880083c4ff2498dba17c56e6c103b9"}, + {file = "regex-2020.1.8-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:0932941cdfb3afcbc26cc3bcf7c3f3d73d5a9b9c56955d432dbf8bbc147d4c5b"}, + {file = "regex-2020.1.8-cp38-cp38-win32.whl", hash = "sha256:d58e4606da2a41659c84baeb3cfa2e4c87a74cec89a1e7c56bee4b956f9d7461"}, + {file = "regex-2020.1.8-cp38-cp38-win_amd64.whl", hash = "sha256:e7c7661f7276507bce416eaae22040fd91ca471b5b33c13f8ff21137ed6f248c"}, + {file = "regex-2020.1.8.tar.gz", hash = "sha256:d0f424328f9822b0323b3b6f2e4b9c90960b24743d220763c7f07071e0778351"}, +] +rise = [ + {file = "rise-5.6.0-py2.py3-none-any.whl", hash = "sha256:24d22dabe4d1ddfab83c42de113353b377dd6d212a1d6fd667a29d00b2ede581"}, + {file = "rise-5.6.0.tar.gz", hash = "sha256:2be45c3dbe27e63aae047c6a90be19798b6e17e9b6b0e25408b3c4f645658e26"}, +] +send2trash = [ + {file = "Send2Trash-1.5.0-py3-none-any.whl", hash = "sha256:f1691922577b6fa12821234aeb57599d887c4900b9ca537948d2dac34aea888b"}, + {file = "Send2Trash-1.5.0.tar.gz", hash = "sha256:60001cc07d707fe247c94f74ca6ac0d3255aabcb930529690897ca2a39db28b2"}, +] +six = [ + {file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"}, + {file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"}, +] +terminado = [ + {file = "terminado-0.8.3-py2.py3-none-any.whl", hash = "sha256:a43dcb3e353bc680dd0783b1d9c3fc28d529f190bc54ba9a229f72fe6e7a54d7"}, + {file = "terminado-0.8.3.tar.gz", hash = "sha256:4804a774f802306a7d9af7322193c5390f1da0abb429e082a10ef1d46e6fb2c2"}, +] +testpath = [ + {file = "testpath-0.4.4-py2.py3-none-any.whl", hash = "sha256:bfcf9411ef4bf3db7579063e0546938b1edda3d69f4e1fb8756991f5951f85d4"}, + {file = "testpath-0.4.4.tar.gz", hash = "sha256:60e0a3261c149755f4399a1fff7d37523179a70fdc3abdf78de9fc2604aeec7e"}, +] +toml = [ + {file = "toml-0.10.0-py2.7.egg", hash = "sha256:f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"}, + {file = "toml-0.10.0-py2.py3-none-any.whl", hash = "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"}, + {file = "toml-0.10.0.tar.gz", hash = "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c"}, +] +tornado = [ + {file = "tornado-6.0.3-cp35-cp35m-win32.whl", hash = "sha256:c9399267c926a4e7c418baa5cbe91c7d1cf362d505a1ef898fde44a07c9dd8a5"}, + {file = "tornado-6.0.3-cp35-cp35m-win_amd64.whl", hash = "sha256:398e0d35e086ba38a0427c3b37f4337327231942e731edaa6e9fd1865bbd6f60"}, + {file = "tornado-6.0.3-cp36-cp36m-win32.whl", hash = "sha256:4e73ef678b1a859f0cb29e1d895526a20ea64b5ffd510a2307b5998c7df24281"}, + {file = "tornado-6.0.3-cp36-cp36m-win_amd64.whl", hash = "sha256:349884248c36801afa19e342a77cc4458caca694b0eda633f5878e458a44cb2c"}, + {file = "tornado-6.0.3-cp37-cp37m-win32.whl", hash = "sha256:559bce3d31484b665259f50cd94c5c28b961b09315ccd838f284687245f416e5"}, + {file = "tornado-6.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:abbe53a39734ef4aba061fca54e30c6b4639d3e1f59653f0da37a0003de148c7"}, + {file = "tornado-6.0.3.tar.gz", hash = "sha256:c845db36ba616912074c5b1ee897f8e0124df269468f25e4fe21fe72f6edd7a9"}, +] +traitlets = [ + {file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"}, + {file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"}, +] +typed-ast = [ + {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, + {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"}, + {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"}, + {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"}, + {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"}, + {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"}, + {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"}, + {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"}, + {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"}, + {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"}, + {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"}, + {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"}, + {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"}, + {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"}, + {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"}, + {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"}, + {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"}, + {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"}, + {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"}, + {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"}, + {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, +] +wcwidth = [ + {file = "wcwidth-0.1.8-py2.py3-none-any.whl", hash = "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603"}, + {file = "wcwidth-0.1.8.tar.gz", hash = "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8"}, +] +webencodings = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] +zipp = [ + {file = "zipp-2.1.0-py3-none-any.whl", hash = "sha256:ccc94ed0909b58ffe34430ea5451f07bc0c76467d7081619a454bf5c98b89e28"}, + {file = "zipp-2.1.0.tar.gz", hash = "sha256:feae2f18633c32fc71f2de629bfb3bd3c9325cd4419642b1f1da42ee488d9b98"}, +] diff --git a/pyproject.toml b/pyproject.toml index f25a893..c30f700 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,20 +1,19 @@ [tool.poetry] name = "intro-to-python" -version = "0.6.0" +version = "0.6.1" description = "An introduction to Python and programming for wanna-be data scientists" authors = ["Alexander Hess "] license = "MIT" [tool.poetry.dependencies] -python = "^3.6" -jupyter = "^1.0" -numpy = "^1.17" +python = "^3.7" +jupyterlab = "^1.2.6" +numpy = "^1.18.1" [tool.poetry.dev-dependencies] -black = {version = "^18.3-alpha.0", allows-prereleases = true} +black = "^19.10b0" blackcellmagic = "^0.0.2" -jupyter-contrib-nbextensions = "^0.5" -rise = "^5.5" +RISE = "^5.6.0" [build-system] requires = ["poetry>=0.12"] diff --git a/requirements.txt b/requirements.txt index 28405c8..f9ab033 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,57 +1,53 @@ appdirs==1.4.3 -attrs==19.1.0 +attrs==19.3.0 backcall==0.1.0 -black==18.9b0 +black==19.10b0 blackcellmagic==0.0.2 bleach==3.1.0 Click==7.0 -decorator==4.4.0 +decorator==4.4.1 defusedxml==0.6.0 entrypoints==0.3 -ipykernel==5.1.2 -ipython==7.8.0 +importlib-metadata==1.4.0 +ipykernel==5.1.4 +ipython==7.11.1 ipython-genutils==0.2.0 -ipywidgets==7.5.1 -jedi==0.15.1 -Jinja2==2.10.1 -jsonschema==3.0.2 -jupyter==1.0.0 -jupyter-client==5.3.2 -jupyter-console==6.0.0 -jupyter-contrib-core==0.3.3 -jupyter-contrib-nbextensions==0.5.1 -jupyter-core==4.5.0 -jupyter-highlight-selected-word==0.2.0 -jupyter-latex-envs==1.4.6 -jupyter-nbextensions-configurator==0.4.1 -lxml==4.4.1 +jedi==0.16.0 +Jinja2==2.11.0 +json5==0.8.5 +jsonschema==3.2.0 +jupyter-client==5.3.4 +jupyter-core==4.6.1 +jupyterlab==1.2.6 +jupyterlab-server==1.0.6 MarkupSafe==1.1.1 mistune==0.8.4 -nbconvert==5.6.0 -nbformat==4.4.0 -notebook==6.0.1 -numpy==1.17.2 +nbconvert==5.6.1 +nbformat==5.0.4 +notebook==6.0.3 +numpy==1.18.1 pandocfilters==1.4.2 -parso==0.5.1 -pexpect==4.7.0 +parso==0.6.0 +pathspec==0.7.0 +pexpect==4.8.0 pickleshare==0.7.5 prometheus-client==0.7.1 -prompt-toolkit==2.0.9 +prompt-toolkit==3.0.3 ptyprocess==0.6.0 -Pygments==2.4.2 -pyrsistent==0.15.4 -python-dateutil==2.8.0 -PyYAML==5.1.2 -pyzmq==18.1.0 -qtconsole==4.5.5 -rise==5.5.1 +Pygments==2.5.2 +pyrsistent==0.15.7 +python-dateutil==2.8.1 +pyzmq==18.1.1 +regex==2020.1.8 +rise==5.6.0 Send2Trash==1.5.0 -six==1.12.0 -terminado==0.8.2 -testpath==0.4.2 +six==1.14.0 +terminado==0.8.3 +testpath==0.4.4 toml==0.10.0 tornado==6.0.3 -traitlets==4.3.2 -wcwidth==0.1.7 +traitlets==4.3.3 +typed-ast==1.4.1 +wcwidth==0.1.8 webencodings==0.5.1 -widgetsnbextension==3.5.1 +zipp==2.1.0 From e8cd8a7e6303fbbb759d1563f0af6d8a5a369738 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 28 Jan 2020 18:23:55 +0100 Subject: [PATCH 056/242] Update the installation notes to JupyterLab --- 00_start_up_00_content.ipynb | 14 ++++---- README.md | 59 +++++++++++++++++++++++---------- static/anaconda.png | Bin 142513 -> 0 bytes static/anaconda_navigator.png | Bin 0 -> 317986 bytes static/anaconda_start_menu.png | Bin 0 -> 187769 bytes static/jupyter_lab.png | Bin 0 -> 129745 bytes 6 files changed, 49 insertions(+), 24 deletions(-) delete mode 100644 static/anaconda.png create mode 100644 static/anaconda_navigator.png create mode 100644 static/anaconda_start_menu.png create mode 100644 static/jupyter_lab.png diff --git a/00_start_up_00_content.ipynb b/00_start_up_00_content.ipynb index 6e3d84a..3ca98d4 100644 --- a/00_start_up_00_content.ipynb +++ b/00_start_up_00_content.ipynb @@ -156,7 +156,7 @@ "\n", "A popular and beginner-friendly way is to install the [Anaconda Distribution](https://www.anaconda.com/distribution/) that not only ships Python and the standard library but comes pre-packaged with a lot of third-party libraries from the so-called \"scientific stack.\" Just go to the [download](https://www.anaconda.com/download/) page and install the latest version (i.e., *2019-10* with Python 3.7 at the time of this writing) for your operating system.\n", "\n", - "Then, among others, you find an entry \"Jupyter Notebook\" in your start menu like below. Click on it to open a new tab in your web browser where you can switch between folders as you could in your computer's default file browser." + "Then, among others, you will find an entry \"Anaconda Navigator\" in your start menu like below. Click on it." ] }, { @@ -167,7 +167,7 @@ } }, "source": [ - "" + "" ] }, { @@ -178,7 +178,7 @@ } }, "source": [ - "To download the materials accompanying this book as a ZIP file, open this [GitHub repository](https://github.com/webartifex/intro-to-python) in a web browser and click on the green \"Clone or download\" button on the right. Then, unpack the ZIP file into a folder of your choosing (ideally somewhere within your user folder so that the files show up right away)." + "A window opens showing you several applications that come with the Anaconda Distribution. Now, click on \"JupyterLab.\"" ] }, { @@ -189,7 +189,7 @@ } }, "source": [ - "### Alternative Installation" + "" ] }, { @@ -200,7 +200,7 @@ } }, "source": [ - "Python can also be installed in a \"pure\" way as obtained from its core development team. However, this is somewhat too \"advanced\" for a beginner who would then also be responsible for setting up all the third-party libraries needed to view this document. Plus, many of the involved steps are typed in a [terminal](https://en.wikipedia.org/wiki/Terminal_emulator) window, which tends to be a bit intimidating for most beginners." + "A new tab in your web browser opens with the website being \"localhost\" and some number (e.g., 8888). This is the [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) application that is used to display and run the Jupyter notebooks mentioned above. On the left, you see the files and folders in your local user folder. This file browser works like any other. In the center, you have several options to launch a new notebook file." ] }, { @@ -211,7 +211,7 @@ } }, "source": [ - "" + "" ] }, { @@ -222,7 +222,7 @@ } }, "source": [ - "For more \"courageous\" beginners wanting to learn how to accomplish this, here is a rough sketch of the aspects to know. First, all Python releases are available for free on the official [download](https://www.python.org/downloads/) page for any supported operating system. Choose the latest one applicable, then download and install it (cf., the [instruction notes](https://wiki.python.org/moin/BeginnersGuide/Download)). As this only includes core Python and the standard library, the beginner then needs to learn about the [pip](https://pip.pypa.io/en/stable/) module. With the command `python -m pip install jupyter`, all necessary third-party libraries are installed (cf., more background [here](https://jupyter.readthedocs.io/en/latest/install.html)). However, this would be done in a *system-wide* fashion and is not recommended. Instead, the best practice is to create a so-called **virtual environment** with the [venv](https://docs.python.org/3/library/venv.html) module with which the installed third-party packages are *isolated* on a per-project basis (the command `python -m venv env-name` creates a virtual environment called \"env-name\"). This tactic is employed to avoid a situation known as **[dependency hell](https://en.wikipedia.org/wiki/Dependency_hell)**. Once created, the virtual environment must then be activated each time before resuming work in each terminal (with the command `source env-name/bin/activate`). While there exist convenience tools that automate parts of this (e.g., [poetry](https://poetry.eustace.io/docs/) or [virtualenvwrapper](https://virtualenvwrapper.readthedocs.io/en/latest/)), it only distracts a beginner from studying the Python language. Yet, it is still worthwhile to have heard about these terms and concepts as many online resources often implicitly assume the user to know about them." + "Next, to download the materials accompanying this book as a ZIP file, open this [GitHub repository](https://github.com/webartifex/intro-to-python) in a web browser, and click on the green \"Clone or download\" button on the top right. Then, unpack the ZIP file into a folder of your choosing, ideally somewhere within your personal user folder so that the files show up right away in JupyterLab." ] }, { diff --git a/README.md b/README.md index 0da3f5e..8c1cba2 100644 --- a/README.md +++ b/README.md @@ -91,33 +91,52 @@ Just go to the [download](https://www.anaconda.com/distribution/#download-sectio section and install the latest version (i.e., *2019-10* with Python 3.7 at the time of this writing) for your operating system. -Then, among others, you will find an entry "Jupyter Notebook" in your start +Then, among others, you will find an entry "Anaconda Navigator" in your start menu like below. -Click on it and a new tab in your web browser will open where you can switch -between folders as you could in your computer's default file browser. +Click on it. - + -To download the course's materials as a ZIP file, click on the green "Clone or -download" button on the top right on this website. -Then, unpack the ZIP file into a folder of your choosing (ideally somewhere -within your personal user folder so that the files show up right away). +A window opens showing you several applications that come with the Anaconda +Distribution. +Now, click on "JupyterLab." + + + +A new tab in your web browser opens with the website being "localhost" and some +number (e.g., 8888). +This is the [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) +application that is used to display and run the Jupyter notebooks mentioned +above. +On the left, you see the files and folders in your local user folder. +This file browser works like any other. +In the center, you have several options to launch a new notebook file. + + + +Next, to download the course's materials as a ZIP file, click on the green +"Clone or download" button on the top right on this website. +Then, unpack the ZIP file into a folder of your choosing, ideally somewhere +within your personal user folder so that the files show up right away in +JupyterLab. -### Alternative Installation +### Alternative Installation (for Instructors) Python can also be installed in a "pure" way as obtained from its core development team (i.e., without any third-party packages installed). -However, this is somewhat too "advanced" for a beginner as it involves working -with a terminal emulator, which looks similar to the picture below and is -used without a mouse by typing commands into it. +However, this may be too "advanced" for a beginner as it involves working +with a [terminal emulator](https://en.wikipedia.org/wiki/Terminal_emulator), +which looks like the one in the picture below and is used *without* a mouse by +typing commands into it. Assuming that you already have a working version of Python 3.7 or higher installed (cf., the official [download page](https://www.python.org/downloads/)), the following summarizes the commands to be typed into a terminal emulator to -get the course materials up and running on a local machine without Anaconda. +get the course materials up and running on a local machine without the +Anaconda Distribution. You are then responsible for understanding the concepts behind them. First, the [git](https://git-scm.com/) command line tool is a more professional @@ -156,17 +175,23 @@ are popular tools to automate the described management of virtual environments. After activation for the first time, you must install the project's **dependencies** (= the third-party packages needed to run the code), most -notably [Jupyter](https://pypi.org/project/jupyter/) in this project (the -"python -m" is often left out [but should not be](https://snarky.ca/why-you-should-use-python-m-pip/); +notably [JupyterLab](https://pypi.org/project/jupyterlab/) in this project +(the "python -m" is often left out [but should not be](https://snarky.ca/why-you-should-use-python-m-pip/); if you have poetry installed, you may just type `poetry install` instead). - `python -m pip install -r requirements.txt` +The *requirements.txt* file also installs the [black](https://github.com/psf/black) +tool (incl. the [blackcellmagic](https://github.com/csurfer/blackcellmagic) +extension) and the [RISE](https://github.com/damianavila/RISE) extension. +With them, the instructor can easily re-format code in a class session and +execute code in presentation mode (currently RISE only works with the +older `jupyter notebook` command). With everything installed, you can now do the equivalent of clicking the -"Jupyter Notebook" entry in your start menu. +"JupyterLab" entry in the Anaconda Navigator. -- `jupyter notebook` +- `jupyter lab` This opens a new tab in your web browser just as above. diff --git a/static/anaconda.png b/static/anaconda.png deleted file mode 100644 index f82cbfa6c4db758358f17de439b31d3f45e49711..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 142513 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�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

}LV`DPVoZ1er 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 literal 0 HcmV?d00001 diff --git a/static/anaconda_start_menu.png b/static/anaconda_start_menu.png new file mode 100644 index 0000000000000000000000000000000000000000..e7120648b32887ba0ce80304df27e9cd2f989855 GIT binary patch literal 187769 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 diff --git a/static/anaconda_navigator.png b/static/anaconda_navigator.png new file mode 100644 index 0000000000000000000000000000000000000000..5b35574836f666e5c6b34503f51348d25ac2fe46 GIT binary patch literal 317986 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%0Dp+^aytqg4qOusMLs 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

r8M@(i6>G`_3 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 literal 0 HcmV?d00001 diff --git a/static/logo.png b/static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b3b5b2220a609b23acf42fcbba221cafea22f043 GIT binary patch literal 7821 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�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 L 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 z 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�IRnwc*Yksek&Ee3Q^}5>|7DD#GgNAkqsU=NMI+ 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μ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�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 literal 0 HcmV?d00001 diff --git a/static/terminal.png b/static/terminal.png new file mode 100644 index 0000000000000000000000000000000000000000..d515d6d74875a5b03f2b14b8bf78a2d40a6bb2b2 GIT binary patch literal 29931 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