From 9fbfb9de8d71c21da45648e8b5e56edd6dc7a050 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 30 Sep 2020 16:21:56 +0200 Subject: [PATCH 001/142] Add jupyterlab to the dependencies --- poetry.lock | 1075 +++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 2 + 2 files changed, 1075 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index b9e1c09..0024db9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,8 +1,1079 @@ -package = [] +[[package]] +category = "main" +description = "Disable App Nap on OS X 10.9" +marker = "sys_platform == \"darwin\" or platform_system == \"Darwin\"" +name = "appnope" +optional = false +python-versions = "*" +version = "0.1.0" + +[[package]] +category = "main" +description = "The secure Argon2 password hashing algorithm." +name = "argon2-cffi" +optional = false +python-versions = "*" +version = "20.1.0" + +[package.dependencies] +cffi = ">=1.0.0" +six = "*" + +[package.extras] +dev = ["coverage (>=5.0.2)", "hypothesis", "pytest", "sphinx", "wheel", "pre-commit"] +docs = ["sphinx"] +tests = ["coverage (>=5.0.2)", "hypothesis", "pytest"] + +[[package]] +category = "main" +description = "Async generators and context managers for Python 3.5+" +name = "async-generator" +optional = false +python-versions = ">=3.5" +version = "1.10" + +[[package]] +category = "main" +description = "Classes Without Boilerplate" +name = "attrs" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "20.2.0" + +[package.extras] +dev = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "sphinx-rtd-theme", "pre-commit"] +docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +tests_no_zope = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] + +[[package]] +category = "main" +description = "Specifications for callback functions passed in to an API" +name = "backcall" +optional = false +python-versions = "*" +version = "0.2.0" + +[[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.*, !=3.4.*" +version = "3.2.1" + +[package.dependencies] +packaging = "*" +six = ">=1.9.0" +webencodings = "*" + +[[package]] +category = "main" +description = "Python package for providing Mozilla's CA Bundle." +name = "certifi" +optional = false +python-versions = "*" +version = "2020.6.20" + +[[package]] +category = "main" +description = "Foreign Function Interface for Python calling C code." +name = "cffi" +optional = false +python-versions = "*" +version = "1.14.3" + +[package.dependencies] +pycparser = "*" + +[[package]] +category = "main" +description = "Universal encoding detector for Python 2 and 3" +name = "chardet" +optional = false +python-versions = "*" +version = "3.0.4" + +[[package]] +category = "main" +description = "Cross-platform colored terminal text." +marker = "sys_platform == \"win32\"" +name = "colorama" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "0.4.3" + +[[package]] +category = "main" +description = "Decorators for Humans" +name = "decorator" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*" +version = "4.4.2" + +[[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 = "Internationalized Domain Names in Applications (IDNA)" +name = "idna" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.10" + +[[package]] +category = "main" +description = "IPython Kernel for Jupyter" +name = "ipykernel" +optional = false +python-versions = ">=3.5" +version = "5.3.4" + +[package.dependencies] +appnope = "*" +ipython = ">=5.0.0" +jupyter-client = "*" +tornado = ">=4.2" +traitlets = ">=4.1.0" + +[package.extras] +test = ["pytest (!=5.3.4)", "pytest-cov", "flaky", "nose"] + +[[package]] +category = "main" +description = "IPython: Productive Interactive Computing" +name = "ipython" +optional = false +python-versions = ">=3.7" +version = "7.18.1" + +[package.dependencies] +appnope = "*" +backcall = "*" +colorama = "*" +decorator = "*" +jedi = ">=0.10" +pexpect = ">4.3" +pickleshare = "*" +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 = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.14)", "pygments", "qtconsole", "requests", "testpath"] +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" +name = "ipython-genutils" +optional = false +python-versions = "*" +version = "0.2.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.*, !=3.4.*" +version = "0.17.2" + +[package.dependencies] +parso = ">=0.7.0,<0.8.0" + +[package.extras] +qa = ["flake8 (3.7.9)"] +testing = ["Django (<3.1)", "colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"] + +[[package]] +category = "main" +description = "A very fast and expressive template engine." +name = "jinja2" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "2.11.2" + +[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.9.5" + +[package.extras] +dev = ["hypothesis"] + +[[package]] +category = "main" +description = "An implementation of JSON Schema validation for Python" +name = "jsonschema" +optional = false +python-versions = "*" +version = "3.2.0" + +[package.dependencies] +attrs = ">=17.4.0" +pyrsistent = ">=0.14.0" +setuptools = "*" +six = ">=1.11.0" + +[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" +description = "Jupyter protocol implementation and client libraries" +name = "jupyter-client" +optional = false +python-versions = ">=3.5" +version = "6.1.7" + +[package.dependencies] +jupyter-core = ">=4.6.0" +python-dateutil = ">=2.1" +pyzmq = ">=13" +tornado = ">=4.1" +traitlets = "*" + +[package.extras] +test = ["ipykernel", "ipython", "mock", "pytest", "pytest-asyncio", "async-generator", "pytest-timeout"] + +[[package]] +category = "main" +description = "Jupyter core package. A base package on which Jupyter projects rely." +name = "jupyter-core" +optional = false +python-versions = "!=3.0,!=3.1,!=3.2,!=3.3,!=3.4,>=2.7" +version = "4.6.3" + +[package.dependencies] +pywin32 = ">=1.0" +traitlets = "*" + +[[package]] +category = "main" +description = "The JupyterLab notebook server extension." +name = "jupyterlab" +optional = false +python-versions = ">=3.5" +version = "2.2.8" + +[package.dependencies] +jinja2 = ">=2.10" +jupyterlab-server = ">=1.1.5,<2.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 = ["jsx-lexer", "recommonmark", "sphinx", "sphinx-rtd-theme", "sphinx-copybutton"] +test = ["pytest", "pytest-check-links", "requests", "wheel", "virtualenv"] + +[[package]] +category = "main" +description = "Pygments theme using JupyterLab CSS variables" +name = "jupyterlab-pygments" +optional = false +python-versions = "*" +version = "0.1.2" + +[package.dependencies] +pygments = ">=2.4.1,<3" + +[[package]] +category = "main" +description = "JupyterLab Server" +name = "jupyterlab-server" +optional = false +python-versions = ">=3.5" +version = "1.2.0" + +[package.dependencies] +jinja2 = ">=2.10" +json5 = "*" +jsonschema = ">=3.0.1" +notebook = ">=4.2.0" +requests = "*" + +[package.extras] +test = ["pytest", "requests"] + +[[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 = "A client library for executing notebooks. Formally nbconvert's ExecutePreprocessor." +name = "nbclient" +optional = false +python-versions = ">=3.6" +version = "0.5.0" + +[package.dependencies] +async-generator = "*" +jupyter-client = ">=6.1.5" +nbformat = ">=5.0" +nest-asyncio = "*" +traitlets = ">=4.2" + +[package.extras] +dev = ["codecov", "coverage", "ipython", "ipykernel", "ipywidgets", "pytest (>=4.1)", "pytest-cov (>=2.6.1)", "check-manifest", "flake8", "mypy", "tox", "bumpversion", "xmltodict", "pip (>=18.1)", "wheel (>=0.31.0)", "setuptools (>=38.6.0)", "twine (>=1.11.0)", "black"] +sphinx = ["Sphinx (>=1.7)", "sphinx-book-theme", "mock", "moto", "myst-parser"] +test = ["codecov", "coverage", "ipython", "ipykernel", "ipywidgets", "pytest (>=4.1)", "pytest-cov (>=2.6.1)", "check-manifest", "flake8", "mypy", "tox", "bumpversion", "xmltodict", "pip (>=18.1)", "wheel (>=0.31.0)", "setuptools (>=38.6.0)", "twine (>=1.11.0)", "black"] + +[[package]] +category = "main" +description = "Converting Jupyter Notebooks" +name = "nbconvert" +optional = false +python-versions = ">=3.6" +version = "6.0.6" + +[package.dependencies] +bleach = "*" +defusedxml = "*" +entrypoints = ">=0.2.2" +jinja2 = ">=2.4" +jupyter-core = "*" +jupyterlab-pygments = "*" +mistune = ">=0.8.1,<2" +nbclient = ">=0.5.0,<0.6.0" +nbformat = ">=4.4" +pandocfilters = ">=1.4.1" +pygments = ">=2.4.1" +testpath = "*" +traitlets = ">=4.2" + +[package.extras] +all = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (0.2.2)", "tornado (>=4.0)", "sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"] +docs = ["sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"] +serve = ["tornado (>=4.0)"] +test = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (0.2.2)"] +webpdf = ["pyppeteer (0.2.2)"] + +[[package]] +category = "main" +description = "The Jupyter Notebook format" +name = "nbformat" +optional = false +python-versions = ">=3.5" +version = "5.0.7" + +[package.dependencies] +ipython-genutils = "*" +jsonschema = ">=2.4,<2.5.0 || >2.5.0" +jupyter-core = "*" +traitlets = ">=4.1" + +[package.extras] +test = ["pytest", "pytest-cov", "testpath"] + +[[package]] +category = "main" +description = "Patch asyncio to allow nested event loops" +name = "nest-asyncio" +optional = false +python-versions = ">=3.5" +version = "1.4.1" + +[[package]] +category = "main" +description = "A web-based notebook environment for interactive computing" +name = "notebook" +optional = false +python-versions = ">=3.5" +version = "6.1.4" + +[package.dependencies] +Send2Trash = "*" +argon2-cffi = "*" +ipykernel = "*" +ipython-genutils = "*" +jinja2 = "*" +jupyter-client = ">=5.3.4" +jupyter-core = ">=4.6.1" +nbconvert = "*" +nbformat = "*" +prometheus-client = "*" +pyzmq = ">=17" +terminado = ">=0.8.3" +tornado = ">=5.0" +traitlets = ">=4.2.1" + +[package.extras] +docs = ["sphinx", "nbsphinx", "sphinxcontrib-github-alt"] +test = ["nose", "coverage", "requests", "nose-warnings-filters", "nbval", "nose-exclude", "selenium", "pytest", "pytest-cov", "requests-unixsocket"] + +[[package]] +category = "main" +description = "Core utilities for Python packages" +name = "packaging" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "20.4" + +[package.dependencies] +pyparsing = ">=2.0.2" +six = "*" + +[[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 = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.7.1" + +[package.extras] +testing = ["docopt", "pytest (>=3.0.7)"] + +[[package]] +category = "main" +description = "Pexpect allows easy control of interactive console applications." +marker = "sys_platform != \"win32\"" +name = "pexpect" +optional = false +python-versions = "*" +version = "4.8.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.8.0" + +[package.extras] +twisted = ["twisted"] + +[[package]] +category = "main" +description = "Library for building powerful interactive command lines in Python" +name = "prompt-toolkit" +optional = false +python-versions = ">=3.6.1" +version = "3.0.7" + +[package.dependencies] +wcwidth = "*" + +[[package]] +category = "main" +description = "Run a subprocess in a pseudo terminal" +marker = "sys_platform != \"win32\" or os_name != \"nt\"" +name = "ptyprocess" +optional = false +python-versions = "*" +version = "0.6.0" + +[[package]] +category = "main" +description = "C parser in Python" +name = "pycparser" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.20" + +[[package]] +category = "main" +description = "Pygments is a syntax highlighting package written in Python." +name = "pygments" +optional = false +python-versions = ">=3.5" +version = "2.7.1" + +[[package]] +category = "main" +description = "Python parsing module" +name = "pyparsing" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +version = "2.4.7" + +[[package]] +category = "main" +description = "Persistent/Functional/Immutable data structures" +name = "pyrsistent" +optional = false +python-versions = ">=3.5" +version = "0.17.3" + +[[package]] +category = "main" +description = "Extensions to the standard Python datetime module" +name = "python-dateutil" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +version = "2.8.1" + +[package.dependencies] +six = ">=1.5" + +[[package]] +category = "main" +description = "Python for Window Extensions" +marker = "sys_platform == \"win32\"" +name = "pywin32" +optional = false +python-versions = "*" +version = "228" + +[[package]] +category = "main" +description = "Python bindings for the winpty library" +marker = "os_name == \"nt\"" +name = "pywinpty" +optional = false +python-versions = "*" +version = "0.5.7" + +[[package]] +category = "main" +description = "Python bindings for 0MQ" +name = "pyzmq" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*" +version = "19.0.2" + +[[package]] +category = "main" +description = "Python HTTP for Humans." +name = "requests" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "2.24.0" + +[package.dependencies] +certifi = ">=2017.4.17" +chardet = ">=3.0.2,<4" +idna = ">=2.5,<3" +urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" + +[package.extras] +security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] + +[[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.7, !=3.0.*, !=3.1.*, !=3.2.*" +version = "1.15.0" + +[[package]] +category = "main" +description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library." +name = "terminado" +optional = false +python-versions = ">=3.6" +version = "0.9.1" + +[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.4" + +[package.extras] +test = ["pathlib2"] + +[[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.4" + +[[package]] +category = "main" +description = "Traitlets Python configuration system" +name = "traitlets" +optional = false +python-versions = ">=3.7" +version = "5.0.4" + +[package.dependencies] +ipython-genutils = "*" + +[package.extras] +test = ["pytest"] + +[[package]] +category = "main" +description = "HTTP library with thread-safe connection pooling, file post, and more." +name = "urllib3" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +version = "1.25.10" + +[package.extras] +brotli = ["brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)", "ipaddress"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] + +[[package]] +category = "main" +description = "Measures the displayed width of unicode strings in a terminal" +name = "wcwidth" +optional = false +python-versions = "*" +version = "0.2.5" + +[[package]] +category = "main" +description = "Character encoding aliases for legacy web content" +name = "webencodings" +optional = false +python-versions = "*" +version = "0.5.1" [metadata] -content-hash = "fafb334cb038533f851c23d0b63254223abf72ce4f02987e7064b0c95566699a" +content-hash = "69a81b3b0db5478e6ec02790481e00bdcd688f9fc50bd86ba79be2028f9dc195" lock-version = "1.0" python-versions = "^3.8" [metadata.files] +appnope = [ + {file = "appnope-0.1.0-py2.py3-none-any.whl", hash = "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0"}, + {file = "appnope-0.1.0.tar.gz", hash = "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"}, +] +argon2-cffi = [ + {file = "argon2-cffi-20.1.0.tar.gz", hash = "sha256:d8029b2d3e4b4cea770e9e5a0104dd8fa185c1724a0f01528ae4826a6d25f97d"}, + {file = "argon2_cffi-20.1.0-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:6ea92c980586931a816d61e4faf6c192b4abce89aa767ff6581e6ddc985ed003"}, + {file = "argon2_cffi-20.1.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:05a8ac07c7026542377e38389638a8a1e9b78f1cd8439cd7493b39f08dd75fbf"}, + {file = "argon2_cffi-20.1.0-cp27-cp27m-win32.whl", hash = "sha256:0bf066bc049332489bb2d75f69216416329d9dc65deee127152caeb16e5ce7d5"}, + {file = "argon2_cffi-20.1.0-cp27-cp27m-win_amd64.whl", hash = "sha256:57358570592c46c420300ec94f2ff3b32cbccd10d38bdc12dc6979c4a8484fbc"}, + {file = "argon2_cffi-20.1.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7d455c802727710e9dfa69b74ccaab04568386ca17b0ad36350b622cd34606fe"}, + {file = "argon2_cffi-20.1.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:b160416adc0f012fb1f12588a5e6954889510f82f698e23ed4f4fa57f12a0647"}, + {file = "argon2_cffi-20.1.0-cp35-cp35m-win32.whl", hash = "sha256:9bee3212ba4f560af397b6d7146848c32a800652301843df06b9e8f68f0f7361"}, + {file = "argon2_cffi-20.1.0-cp35-cp35m-win_amd64.whl", hash = "sha256:392c3c2ef91d12da510cfb6f9bae52512a4552573a9e27600bdb800e05905d2b"}, + {file = "argon2_cffi-20.1.0-cp36-cp36m-win32.whl", hash = "sha256:ba7209b608945b889457f949cc04c8e762bed4fe3fec88ae9a6b7765ae82e496"}, + {file = "argon2_cffi-20.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:da7f0445b71db6d3a72462e04f36544b0de871289b0bc8a7cc87c0f5ec7079fa"}, + {file = "argon2_cffi-20.1.0-cp37-abi3-macosx_10_6_intel.whl", hash = "sha256:cc0e028b209a5483b6846053d5fd7165f460a1f14774d79e632e75e7ae64b82b"}, + {file = "argon2_cffi-20.1.0-cp37-cp37m-win32.whl", hash = "sha256:18dee20e25e4be86680b178b35ccfc5d495ebd5792cd00781548d50880fee5c5"}, + {file = "argon2_cffi-20.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:6678bb047373f52bcff02db8afab0d2a77d83bde61cfecea7c5c62e2335cb203"}, + {file = "argon2_cffi-20.1.0-cp38-cp38-win32.whl", hash = "sha256:77e909cc756ef81d6abb60524d259d959bab384832f0c651ed7dcb6e5ccdbb78"}, + {file = "argon2_cffi-20.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:9dfd5197852530294ecb5795c97a823839258dfd5eb9420233c7cfedec2058f2"}, +] +async-generator = [ + {file = "async_generator-1.10-py3-none-any.whl", hash = "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b"}, + {file = "async_generator-1.10.tar.gz", hash = "sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144"}, +] +attrs = [ + {file = "attrs-20.2.0-py2.py3-none-any.whl", hash = "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"}, + {file = "attrs-20.2.0.tar.gz", hash = "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594"}, +] +backcall = [ + {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, + {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, +] +bleach = [ + {file = "bleach-3.2.1-py2.py3-none-any.whl", hash = "sha256:9f8ccbeb6183c6e6cddea37592dfb0167485c1e3b13b3363bc325aa8bda3adbd"}, + {file = "bleach-3.2.1.tar.gz", hash = "sha256:52b5919b81842b1854196eaae5ca29679a2f2e378905c346d3ca8227c2c66080"}, +] +certifi = [ + {file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"}, + {file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"}, +] +cffi = [ + {file = "cffi-1.14.3-2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3eeeb0405fd145e714f7633a5173318bd88d8bbfc3dd0a5751f8c4f70ae629bc"}, + {file = "cffi-1.14.3-2-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:cb763ceceae04803adcc4e2d80d611ef201c73da32d8f2722e9d0ab0c7f10768"}, + {file = "cffi-1.14.3-2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:44f60519595eaca110f248e5017363d751b12782a6f2bd6a7041cba275215f5d"}, + {file = "cffi-1.14.3-2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c53af463f4a40de78c58b8b2710ade243c81cbca641e34debf3396a9640d6ec1"}, + {file = "cffi-1.14.3-2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:33c6cdc071ba5cd6d96769c8969a0531be2d08c2628a0143a10a7dcffa9719ca"}, + {file = "cffi-1.14.3-2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c11579638288e53fc94ad60022ff1b67865363e730ee41ad5e6f0a17188b327a"}, + {file = "cffi-1.14.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:3cb3e1b9ec43256c4e0f8d2837267a70b0e1ca8c4f456685508ae6106b1f504c"}, + {file = "cffi-1.14.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f0620511387790860b249b9241c2f13c3a80e21a73e0b861a2df24e9d6f56730"}, + {file = "cffi-1.14.3-cp27-cp27m-win32.whl", hash = "sha256:005f2bfe11b6745d726dbb07ace4d53f057de66e336ff92d61b8c7e9c8f4777d"}, + {file = "cffi-1.14.3-cp27-cp27m-win_amd64.whl", hash = "sha256:2f9674623ca39c9ebe38afa3da402e9326c245f0f5ceff0623dccdac15023e05"}, + {file = "cffi-1.14.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:09e96138280241bd355cd585148dec04dbbedb4f46128f340d696eaafc82dd7b"}, + {file = "cffi-1.14.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:3363e77a6176afb8823b6e06db78c46dbc4c7813b00a41300a4873b6ba63b171"}, + {file = "cffi-1.14.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0ef488305fdce2580c8b2708f22d7785ae222d9825d3094ab073e22e93dfe51f"}, + {file = "cffi-1.14.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:0b1ad452cc824665ddc682400b62c9e4f5b64736a2ba99110712fdee5f2505c4"}, + {file = "cffi-1.14.3-cp35-cp35m-win32.whl", hash = "sha256:85ba797e1de5b48aa5a8427b6ba62cf69607c18c5d4eb747604b7302f1ec382d"}, + {file = "cffi-1.14.3-cp35-cp35m-win_amd64.whl", hash = "sha256:e66399cf0fc07de4dce4f588fc25bfe84a6d1285cc544e67987d22663393926d"}, + {file = "cffi-1.14.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:15f351bed09897fbda218e4db5a3d5c06328862f6198d4fb385f3e14e19decb3"}, + {file = "cffi-1.14.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4d7c26bfc1ea9f92084a1d75e11999e97b62d63128bcc90c3624d07813c52808"}, + {file = "cffi-1.14.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:23e5d2040367322824605bc29ae8ee9175200b92cb5483ac7d466927a9b3d537"}, + {file = "cffi-1.14.3-cp36-cp36m-win32.whl", hash = "sha256:a624fae282e81ad2e4871bdb767e2c914d0539708c0f078b5b355258293c98b0"}, + {file = "cffi-1.14.3-cp36-cp36m-win_amd64.whl", hash = "sha256:de31b5164d44ef4943db155b3e8e17929707cac1e5bd2f363e67a56e3af4af6e"}, + {file = "cffi-1.14.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f92cdecb618e5fa4658aeb97d5eb3d2f47aa94ac6477c6daf0f306c5a3b9e6b1"}, + {file = "cffi-1.14.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:22399ff4870fb4c7ef19fff6eeb20a8bbf15571913c181c78cb361024d574579"}, + {file = "cffi-1.14.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:f4eae045e6ab2bb54ca279733fe4eb85f1effda392666308250714e01907f394"}, + {file = "cffi-1.14.3-cp37-cp37m-win32.whl", hash = "sha256:b0358e6fefc74a16f745afa366acc89f979040e0cbc4eec55ab26ad1f6a9bfbc"}, + {file = "cffi-1.14.3-cp37-cp37m-win_amd64.whl", hash = "sha256:6642f15ad963b5092d65aed022d033c77763515fdc07095208f15d3563003869"}, + {file = "cffi-1.14.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:2791f68edc5749024b4722500e86303a10d342527e1e3bcac47f35fbd25b764e"}, + {file = "cffi-1.14.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:529c4ed2e10437c205f38f3691a68be66c39197d01062618c55f74294a4a4828"}, + {file = "cffi-1.14.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f0f1e499e4000c4c347a124fa6a27d37608ced4fe9f7d45070563b7c4c370c9"}, + {file = "cffi-1.14.3-cp38-cp38-win32.whl", hash = "sha256:3b8eaf915ddc0709779889c472e553f0d3e8b7bdf62dab764c8921b09bf94522"}, + {file = "cffi-1.14.3-cp38-cp38-win_amd64.whl", hash = "sha256:bbd2f4dfee1079f76943767fce837ade3087b578aeb9f69aec7857d5bf25db15"}, + {file = "cffi-1.14.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:cc75f58cdaf043fe6a7a6c04b3b5a0e694c6a9e24050967747251fb80d7bce0d"}, + {file = "cffi-1.14.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:bf39a9e19ce7298f1bd6a9758fa99707e9e5b1ebe5e90f2c3913a47bc548747c"}, + {file = "cffi-1.14.3-cp39-cp39-win32.whl", hash = "sha256:d80998ed59176e8cba74028762fbd9b9153b9afc71ea118e63bbf5d4d0f9552b"}, + {file = "cffi-1.14.3-cp39-cp39-win_amd64.whl", hash = "sha256:c150eaa3dadbb2b5339675b88d4573c1be3cb6f2c33a6c83387e10cc0bf05bd3"}, + {file = "cffi-1.14.3.tar.gz", hash = "sha256:f92f789e4f9241cd262ad7a555ca2c648a98178a953af117ef7fad46aa1d5591"}, +] +chardet = [ + {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, + {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, +] +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.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"}, + {file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"}, +] +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"}, +] +idna = [ + {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, + {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, +] +ipykernel = [ + {file = "ipykernel-5.3.4-py3-none-any.whl", hash = "sha256:d6fbba26dba3cebd411382bc484f7bc2caa98427ae0ddb4ab37fe8bfeb5c7dd3"}, + {file = "ipykernel-5.3.4.tar.gz", hash = "sha256:9b2652af1607986a1b231c62302d070bc0534f564c393a5d9d130db9abbbe89d"}, +] +ipython = [ + {file = "ipython-7.18.1-py3-none-any.whl", hash = "sha256:2e22c1f74477b5106a6fb301c342ab8c64bb75d702e350f05a649e8cb40a0fb8"}, + {file = "ipython-7.18.1.tar.gz", hash = "sha256:a331e78086001931de9424940699691ad49dfb457cea31f5471eae7b78222d5e"}, +] +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.17.2-py2.py3-none-any.whl", hash = "sha256:98cc583fa0f2f8304968199b01b6b4b94f469a1f4a74c1560506ca2a211378b5"}, + {file = "jedi-0.17.2.tar.gz", hash = "sha256:86ed7d9b750603e4ba582ea8edc678657fb4007894a12bcf6f4bb97892f31d20"}, +] +jinja2 = [ + {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"}, + {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"}, +] +json5 = [ + {file = "json5-0.9.5-py2.py3-none-any.whl", hash = "sha256:af1a1b9a2850c7f62c23fde18be4749b3599fd302f494eebf957e2ada6b9e42c"}, + {file = "json5-0.9.5.tar.gz", hash = "sha256:703cfee540790576b56a92e1c6aaa6c4b0d98971dc358ead83812aa4d06bdb96"}, +] +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-6.1.7-py3-none-any.whl", hash = "sha256:c958d24d6eacb975c1acebb68ac9077da61b5f5c040f22f6849928ad7393b950"}, + {file = "jupyter_client-6.1.7.tar.gz", hash = "sha256:49e390b36fe4b4226724704ea28d9fb903f1a3601b6882ce3105221cd09377a1"}, +] +jupyter-core = [ + {file = "jupyter_core-4.6.3-py2.py3-none-any.whl", hash = "sha256:a4ee613c060fe5697d913416fc9d553599c05e4492d58fac1192c9a6844abb21"}, + {file = "jupyter_core-4.6.3.tar.gz", hash = "sha256:394fd5dd787e7c8861741880bdf8a00ce39f95de5d18e579c74b882522219e7e"}, +] +jupyterlab = [ + {file = "jupyterlab-2.2.8-py3-none-any.whl", hash = "sha256:95d0509557881cfa8a5fcdf225f2fca46faf1bc52fc56a28e0b72fcc594c90ab"}, + {file = "jupyterlab-2.2.8.tar.gz", hash = "sha256:c8377bee30504919c1e79949f9fe35443ab7f5c4be622c95307e8108410c8b8c"}, +] +jupyterlab-pygments = [ + {file = "jupyterlab_pygments-0.1.2-py2.py3-none-any.whl", hash = "sha256:abfb880fd1561987efaefcb2d2ac75145d2a5d0139b1876d5be806e32f630008"}, + {file = "jupyterlab_pygments-0.1.2.tar.gz", hash = "sha256:cfcda0873626150932f438eccf0f8bf22bfa92345b814890ab360d666b254146"}, +] +jupyterlab-server = [ + {file = "jupyterlab_server-1.2.0-py3-none-any.whl", hash = "sha256:55d256077bf13e5bc9e8fbd5aac51bef82f6315111cec6b712b9a5ededbba924"}, + {file = "jupyterlab_server-1.2.0.tar.gz", hash = "sha256:5431d9dde96659364b7cc877693d5d21e7b80cea7ae3959ecc2b87518e5f5d8c"}, +] +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-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, + {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"}, +] +nbclient = [ + {file = "nbclient-0.5.0-py3-none-any.whl", hash = "sha256:8a6e27ff581cee50895f44c41936ce02369674e85e2ad58643d8d4a6c36771b0"}, + {file = "nbclient-0.5.0.tar.gz", hash = "sha256:8ad52d27ba144fca1402db014857e53c5a864a2f407be66ca9d74c3a56d6591d"}, +] +nbconvert = [ + {file = "nbconvert-6.0.6-py3-none-any.whl", hash = "sha256:d8549f62e739a4d51f275c2932b1783ee5039dde07a2b71de70c0296a42c8394"}, + {file = "nbconvert-6.0.6.tar.gz", hash = "sha256:68335477288aab8a9b9ec03002dce59b4eb1ca967116741ec218a4e78c129efd"}, +] +nbformat = [ + {file = "nbformat-5.0.7-py3-none-any.whl", hash = "sha256:ea55c9b817855e2dfcd3f66d74857342612a60b1f09653440f4a5845e6e3523f"}, + {file = "nbformat-5.0.7.tar.gz", hash = "sha256:54d4d6354835a936bad7e8182dcd003ca3dc0cedfee5a306090e04854343b340"}, +] +nest-asyncio = [ + {file = "nest_asyncio-1.4.1-py3-none-any.whl", hash = "sha256:a4487c4f49f2d11a7bb89a512a6886b6a5045f47097f49815b2851aaa8599cf0"}, + {file = "nest_asyncio-1.4.1.tar.gz", hash = "sha256:b86c3193abda5b2eeccf8c79894bc71c680369a178f4b068514ac00720b14e01"}, +] +notebook = [ + {file = "notebook-6.1.4-py3-none-any.whl", hash = "sha256:07b6e8b8a61aa2f780fe9a97430470485bc71262bc5cae8521f1441b910d2c88"}, + {file = "notebook-6.1.4.tar.gz", hash = "sha256:687d01f963ea20360c0b904ee7a37c3d8cda553858c8d6e33fd0afd13e89de32"}, +] +packaging = [ + {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, + {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, +] +pandocfilters = [ + {file = "pandocfilters-1.4.2.tar.gz", hash = "sha256:b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"}, +] +parso = [ + {file = "parso-0.7.1-py2.py3-none-any.whl", hash = "sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea"}, + {file = "parso-0.7.1.tar.gz", hash = "sha256:caba44724b994a8a5e086460bb212abc5a8bc46951bf4a9a1210745953622eb9"}, +] +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.8.0-py2.py3-none-any.whl", hash = "sha256:983c7ac4b47478720db338f1491ef67a100b474e3bc7dafcbaefb7d0b8f9b01c"}, + {file = "prometheus_client-0.8.0.tar.gz", hash = "sha256:c6e6b706833a6bd1fd51711299edee907857be10ece535126a158f911ee80915"}, +] +prompt-toolkit = [ + {file = "prompt_toolkit-3.0.7-py3-none-any.whl", hash = "sha256:83074ee28ad4ba6af190593d4d4c607ff525272a504eb159199b6dd9f950c950"}, + {file = "prompt_toolkit-3.0.7.tar.gz", hash = "sha256:822f4605f28f7d2ba6b0b09a31e25e140871e96364d1d377667b547bb3bf4489"}, +] +ptyprocess = [ + {file = "ptyprocess-0.6.0-py2.py3-none-any.whl", hash = "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"}, + {file = "ptyprocess-0.6.0.tar.gz", hash = "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0"}, +] +pycparser = [ + {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, + {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, +] +pygments = [ + {file = "Pygments-2.7.1-py3-none-any.whl", hash = "sha256:307543fe65c0947b126e83dd5a61bd8acbd84abec11f43caebaf5534cbc17998"}, + {file = "Pygments-2.7.1.tar.gz", hash = "sha256:926c3f319eda178d1bd90851e4317e6d8cdb5e292a3386aac9bd75eca29cf9c7"}, +] +pyparsing = [ + {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, + {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, +] +pyrsistent = [ + {file = "pyrsistent-0.17.3.tar.gz", hash = "sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e"}, +] +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-228-cp27-cp27m-win32.whl", hash = "sha256:37dc9935f6a383cc744315ae0c2882ba1768d9b06700a70f35dc1ce73cd4ba9c"}, + {file = "pywin32-228-cp27-cp27m-win_amd64.whl", hash = "sha256:11cb6610efc2f078c9e6d8f5d0f957620c333f4b23466931a247fb945ed35e89"}, + {file = "pywin32-228-cp35-cp35m-win32.whl", hash = "sha256:1f45db18af5d36195447b2cffacd182fe2d296849ba0aecdab24d3852fbf3f80"}, + {file = "pywin32-228-cp35-cp35m-win_amd64.whl", hash = "sha256:6e38c44097a834a4707c1b63efa9c2435f5a42afabff634a17f563bc478dfcc8"}, + {file = "pywin32-228-cp36-cp36m-win32.whl", hash = "sha256:ec16d44b49b5f34e99eb97cf270806fdc560dff6f84d281eb2fcb89a014a56a9"}, + {file = "pywin32-228-cp36-cp36m-win_amd64.whl", hash = "sha256:a60d795c6590a5b6baeacd16c583d91cce8038f959bd80c53bd9a68f40130f2d"}, + {file = "pywin32-228-cp37-cp37m-win32.whl", hash = "sha256:af40887b6fc200eafe4d7742c48417529a8702dcc1a60bf89eee152d1d11209f"}, + {file = "pywin32-228-cp37-cp37m-win_amd64.whl", hash = "sha256:00eaf43dbd05ba6a9b0080c77e161e0b7a601f9a3f660727a952e40140537de7"}, + {file = "pywin32-228-cp38-cp38-win32.whl", hash = "sha256:fa6ba028909cfc64ce9e24bcf22f588b14871980d9787f1e2002c99af8f1850c"}, + {file = "pywin32-228-cp38-cp38-win_amd64.whl", hash = "sha256:9b3466083f8271e1a5eb0329f4e0d61925d46b40b195a33413e0905dccb285e8"}, + {file = "pywin32-228-cp39-cp39-win32.whl", hash = "sha256:ed74b72d8059a6606f64842e7917aeee99159ebd6b8d6261c518d002837be298"}, + {file = "pywin32-228-cp39-cp39-win_amd64.whl", hash = "sha256:8319bafdcd90b7202c50d6014efdfe4fde9311b3ff15fd6f893a45c0868de203"}, +] +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-19.0.2-cp27-cp27m-macosx_10_9_intel.whl", hash = "sha256:59f1e54627483dcf61c663941d94c4af9bf4163aec334171686cdaee67974fe5"}, + {file = "pyzmq-19.0.2-cp27-cp27m-win32.whl", hash = "sha256:c36ffe1e5aa35a1af6a96640d723d0d211c5f48841735c2aa8d034204e87eb87"}, + {file = "pyzmq-19.0.2-cp27-cp27m-win_amd64.whl", hash = "sha256:0a422fc290d03958899743db091f8154958410fc76ce7ee0ceb66150f72c2c97"}, + {file = "pyzmq-19.0.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:c20dd60b9428f532bc59f2ef6d3b1029a28fc790d408af82f871a7db03e722ff"}, + {file = "pyzmq-19.0.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d46fb17f5693244de83e434648b3dbb4f4b0fec88415d6cbab1c1452b6f2ae17"}, + {file = "pyzmq-19.0.2-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:f1a25a61495b6f7bb986accc5b597a3541d9bd3ef0016f50be16dbb32025b302"}, + {file = "pyzmq-19.0.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:ab0d01148d13854de716786ca73701012e07dff4dfbbd68c4e06d8888743526e"}, + {file = "pyzmq-19.0.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:720d2b6083498a9281eaee3f2927486e9fe02cd16d13a844f2e95217f243efea"}, + {file = "pyzmq-19.0.2-cp35-cp35m-win32.whl", hash = "sha256:29d51279060d0a70f551663bc592418bcad7f4be4eea7b324f6dd81de05cb4c1"}, + {file = "pyzmq-19.0.2-cp35-cp35m-win_amd64.whl", hash = "sha256:5120c64646e75f6db20cc16b9a94203926ead5d633de9feba4f137004241221d"}, + {file = "pyzmq-19.0.2-cp36-cp36m-macosx_10_9_intel.whl", hash = "sha256:8a6ada5a3f719bf46a04ba38595073df8d6b067316c011180102ba2a1925f5b5"}, + {file = "pyzmq-19.0.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:fa411b1d8f371d3a49d31b0789eb6da2537dadbb2aef74a43aa99a78195c3f76"}, + {file = "pyzmq-19.0.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:00dca814469436455399660247d74045172955459c0bd49b54a540ce4d652185"}, + {file = "pyzmq-19.0.2-cp36-cp36m-win32.whl", hash = "sha256:046b92e860914e39612e84fa760fc3f16054d268c11e0e25dcb011fb1bc6a075"}, + {file = "pyzmq-19.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99cc0e339a731c6a34109e5c4072aaa06d8e32c0b93dc2c2d90345dd45fa196c"}, + {file = "pyzmq-19.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e36f12f503511d72d9bdfae11cadbadca22ff632ff67c1b5459f69756a029c19"}, + {file = "pyzmq-19.0.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c40fbb2b9933369e994b837ee72193d6a4c35dfb9a7c573257ef7ff28961272c"}, + {file = "pyzmq-19.0.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5d9fc809aa8d636e757e4ced2302569d6e60e9b9c26114a83f0d9d6519c40493"}, + {file = "pyzmq-19.0.2-cp37-cp37m-win32.whl", hash = "sha256:3fa6debf4bf9412e59353defad1f8035a1e68b66095a94ead8f7a61ae90b2675"}, + {file = "pyzmq-19.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:73483a2caaa0264ac717af33d6fb3f143d8379e60a422730ee8d010526ce1913"}, + {file = "pyzmq-19.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:36ab114021c0cab1a423fe6689355e8f813979f2c750968833b318c1fa10a0fd"}, + {file = "pyzmq-19.0.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:8b66b94fe6243d2d1d89bca336b2424399aac57932858b9a30309803ffc28112"}, + {file = "pyzmq-19.0.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:654d3e06a4edc566b416c10293064732516cf8871a4522e0a2ba00cc2a2e600c"}, + {file = "pyzmq-19.0.2-cp38-cp38-win32.whl", hash = "sha256:276ad604bffd70992a386a84bea34883e696a6b22e7378053e5d3227321d9702"}, + {file = "pyzmq-19.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:09d24a80ccb8cbda1af6ed8eb26b005b6743e58e9290566d2a6841f4e31fa8e0"}, + {file = "pyzmq-19.0.2-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:c1a31cd42905b405530e92bdb70a8a56f048c8a371728b8acf9d746ecd4482c0"}, + {file = "pyzmq-19.0.2-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a7e7f930039ee0c4c26e4dfee015f20bd6919cd8b97c9cd7afbde2923a5167b6"}, + {file = "pyzmq-19.0.2.tar.gz", hash = "sha256:296540a065c8c21b26d63e3cea2d1d57902373b16e4256afe46422691903a438"}, +] +requests = [ + {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"}, + {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"}, +] +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.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, + {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, +] +terminado = [ + {file = "terminado-0.9.1-py3-none-any.whl", hash = "sha256:c55f025beb06c2e2669f7ba5a04f47bb3304c30c05842d4981d8f0fc9ab3b4e3"}, + {file = "terminado-0.9.1.tar.gz", hash = "sha256:3da72a155b807b01c9e8a5babd214e052a0a45a975751da3521a1c3381ce6d76"}, +] +testpath = [ + {file = "testpath-0.4.4-py2.py3-none-any.whl", hash = "sha256:bfcf9411ef4bf3db7579063e0546938b1edda3d69f4e1fb8756991f5951f85d4"}, + {file = "testpath-0.4.4.tar.gz", hash = "sha256:60e0a3261c149755f4399a1fff7d37523179a70fdc3abdf78de9fc2604aeec7e"}, +] +tornado = [ + {file = "tornado-6.0.4-cp35-cp35m-win32.whl", hash = "sha256:5217e601700f24e966ddab689f90b7ea4bd91ff3357c3600fa1045e26d68e55d"}, + {file = "tornado-6.0.4-cp35-cp35m-win_amd64.whl", hash = "sha256:c98232a3ac391f5faea6821b53db8db461157baa788f5d6222a193e9456e1740"}, + {file = "tornado-6.0.4-cp36-cp36m-win32.whl", hash = "sha256:5f6a07e62e799be5d2330e68d808c8ac41d4a259b9cea61da4101b83cb5dc673"}, + {file = "tornado-6.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:c952975c8ba74f546ae6de2e226ab3cc3cc11ae47baf607459a6728585bb542a"}, + {file = "tornado-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:2c027eb2a393d964b22b5c154d1a23a5f8727db6fda837118a776b29e2b8ebc6"}, + {file = "tornado-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:5618f72e947533832cbc3dec54e1dffc1747a5cb17d1fd91577ed14fa0dc081b"}, + {file = "tornado-6.0.4-cp38-cp38-win32.whl", hash = "sha256:22aed82c2ea340c3771e3babc5ef220272f6fd06b5108a53b4976d0d722bcd52"}, + {file = "tornado-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:c58d56003daf1b616336781b26d184023ea4af13ae143d9dda65e31e534940b9"}, + {file = "tornado-6.0.4.tar.gz", hash = "sha256:0fe2d45ba43b00a41cd73f8be321a44936dc1aba233dee979f17a042b83eb6dc"}, +] +traitlets = [ + {file = "traitlets-5.0.4-py3-none-any.whl", hash = "sha256:9664ec0c526e48e7b47b7d14cd6b252efa03e0129011de0a9c1d70315d4309c3"}, + {file = "traitlets-5.0.4.tar.gz", hash = "sha256:86c9351f94f95de9db8a04ad8e892da299a088a64fd283f9f6f18770ae5eae1b"}, +] +urllib3 = [ + {file = "urllib3-1.25.10-py2.py3-none-any.whl", hash = "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"}, + {file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"}, +] +wcwidth = [ + {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, + {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, +] +webencodings = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] diff --git a/pyproject.toml b/pyproject.toml index 6f05059..35e025a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,4 +13,6 @@ license = "MIT" [tool.poetry.dependencies] python = "^3.8" +jupyterlab = "^2.2.8" + [tool.poetry.dev-dependencies] From 1351adaa621d01312d398aa00afc85fc332710e5 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 30 Sep 2020 16:33:33 +0200 Subject: [PATCH 002/142] Ignore Jupyter's checkpoint files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 35008af..79eb637 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +**/.ipynb_checkpoints/ .python-version .venv/ From fc87305922264f826bb9e75780d5b280adb6efeb Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Fri, 2 Oct 2020 15:18:40 +0200 Subject: [PATCH 003/142] Add nox to the develop dependencies --- poetry.lock | 123 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 2 + 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 0024db9..2ac41a5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,3 +1,11 @@ +[[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.4" + [[package]] category = "main" description = "Disable App Nap on OS X 10.9" @@ -7,6 +15,17 @@ optional = false python-versions = "*" version = "0.1.0" +[[package]] +category = "dev" +description = "Bash tab completion for argparse" +name = "argcomplete" +optional = false +python-versions = "*" +version = "1.12.1" + +[package.extras] +test = ["coverage", "flake8", "pexpect", "wheel"] + [[package]] category = "main" description = "The secure Argon2 password hashing algorithm." @@ -103,6 +122,17 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "0.4.3" +[[package]] +category = "dev" +description = "Log formatting with colors!" +name = "colorlog" +optional = false +python-versions = "*" +version = "4.2.1" + +[package.dependencies] +colorama = "*" + [[package]] category = "main" description = "Decorators for Humans" @@ -119,6 +149,14 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "0.6.0" +[[package]] +category = "dev" +description = "Distribution utilities" +name = "distlib" +optional = false +python-versions = "*" +version = "0.3.1" + [[package]] category = "main" description = "Discover and load entry points from installed packages." @@ -127,6 +165,14 @@ optional = false python-versions = ">=2.7" version = "0.3" +[[package]] +category = "dev" +description = "A platform independent file lock." +name = "filelock" +optional = false +python-versions = "*" +version = "3.0.12" + [[package]] category = "main" description = "Internationalized Domain Names in Applications (IDNA)" @@ -447,6 +493,23 @@ traitlets = ">=4.2.1" docs = ["sphinx", "nbsphinx", "sphinxcontrib-github-alt"] test = ["nose", "coverage", "requests", "nose-warnings-filters", "nbval", "nose-exclude", "selenium", "pytest", "pytest-cov", "requests-unixsocket"] +[[package]] +category = "dev" +description = "Flexible test automation." +name = "nox" +optional = false +python-versions = ">=3.5" +version = "2020.8.22" + +[package.dependencies] +argcomplete = ">=1.9.4,<2.0" +colorlog = ">=2.6.1,<5.0.0" +py = ">=1.4.0,<2.0.0" +virtualenv = ">=14.0.0" + +[package.extras] +tox_to_nox = ["jinja2", "tox"] + [[package]] category = "main" description = "Core utilities for Python packages" @@ -529,6 +592,14 @@ optional = false python-versions = "*" version = "0.6.0" +[[package]] +category = "dev" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +name = "py" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.9.0" + [[package]] category = "main" description = "C parser in Python" @@ -691,6 +762,24 @@ brotli = ["brotlipy (>=0.6.0)"] secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)", "ipaddress"] socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] +[[package]] +category = "dev" +description = "Virtual Python Environment builder" +name = "virtualenv" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +version = "20.0.32" + +[package.dependencies] +appdirs = ">=1.4.3,<2" +distlib = ">=0.3.1,<1" +filelock = ">=3.0.0,<4" +six = ">=1.9.0,<2" + +[package.extras] +docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"] +testing = ["coverage (>=5)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "pytest-xdist (>=1.31.0)", "packaging (>=20.0)", "xonsh (>=0.9.16)"] + [[package]] category = "main" description = "Measures the displayed width of unicode strings in a terminal" @@ -708,15 +797,23 @@ python-versions = "*" version = "0.5.1" [metadata] -content-hash = "69a81b3b0db5478e6ec02790481e00bdcd688f9fc50bd86ba79be2028f9dc195" +content-hash = "ff4ef02eb98bcdc61e34672f3684f54493b3e5522a84fc678bbf9847b10a3102" lock-version = "1.0" python-versions = "^3.8" [metadata.files] +appdirs = [ + {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, + {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, +] appnope = [ {file = "appnope-0.1.0-py2.py3-none-any.whl", hash = "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0"}, {file = "appnope-0.1.0.tar.gz", hash = "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"}, ] +argcomplete = [ + {file = "argcomplete-1.12.1-py2.py3-none-any.whl", hash = "sha256:5cd1ac4fc49c29d6016fc2cc4b19a3c08c3624544503495bf25989834c443898"}, + {file = "argcomplete-1.12.1.tar.gz", hash = "sha256:849c2444c35bb2175aea74100ca5f644c29bf716429399c0f2203bb5d9a8e4e6"}, +] argon2-cffi = [ {file = "argon2-cffi-20.1.0.tar.gz", hash = "sha256:d8029b2d3e4b4cea770e9e5a0104dd8fa185c1724a0f01528ae4826a6d25f97d"}, {file = "argon2_cffi-20.1.0-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:6ea92c980586931a816d61e4faf6c192b4abce89aa767ff6581e6ddc985ed003"}, @@ -801,6 +898,10 @@ colorama = [ {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, ] +colorlog = [ + {file = "colorlog-4.2.1-py2.py3-none-any.whl", hash = "sha256:43597fd822ce705190fc997519342fdaaf44b9b47f896ece7aa153ed4b909c74"}, + {file = "colorlog-4.2.1.tar.gz", hash = "sha256:75e55822c3a3387d721579241e776de2cf089c9ef9528b1f09e8b04d403ad118"}, +] decorator = [ {file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"}, {file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"}, @@ -809,10 +910,18 @@ defusedxml = [ {file = "defusedxml-0.6.0-py2.py3-none-any.whl", hash = "sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93"}, {file = "defusedxml-0.6.0.tar.gz", hash = "sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"}, ] +distlib = [ + {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, + {file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"}, +] entrypoints = [ {file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"}, {file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"}, ] +filelock = [ + {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, + {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, +] idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, @@ -924,6 +1033,10 @@ notebook = [ {file = "notebook-6.1.4-py3-none-any.whl", hash = "sha256:07b6e8b8a61aa2f780fe9a97430470485bc71262bc5cae8521f1441b910d2c88"}, {file = "notebook-6.1.4.tar.gz", hash = "sha256:687d01f963ea20360c0b904ee7a37c3d8cda553858c8d6e33fd0afd13e89de32"}, ] +nox = [ + {file = "nox-2020.8.22-py3-none-any.whl", hash = "sha256:55f8cab16bcfaaea08b141c83bf2b7c779e943518d0de6cd9c38cd8da95d11ea"}, + {file = "nox-2020.8.22.tar.gz", hash = "sha256:efa5adcf1134012f96bcd0a496ccebd4c9e9da53a831888a2a779462440eebcf"}, +] packaging = [ {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, @@ -955,6 +1068,10 @@ ptyprocess = [ {file = "ptyprocess-0.6.0-py2.py3-none-any.whl", hash = "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"}, {file = "ptyprocess-0.6.0.tar.gz", hash = "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0"}, ] +py = [ + {file = "py-1.9.0-py2.py3-none-any.whl", hash = "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2"}, + {file = "py-1.9.0.tar.gz", hash = "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"}, +] pycparser = [ {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, @@ -1069,6 +1186,10 @@ urllib3 = [ {file = "urllib3-1.25.10-py2.py3-none-any.whl", hash = "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"}, {file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"}, ] +virtualenv = [ + {file = "virtualenv-20.0.32-py2.py3-none-any.whl", hash = "sha256:9160a8f6196afcb8bb91405b5362651f302ee8e810fc471f5f9ce9a06b070298"}, + {file = "virtualenv-20.0.32.tar.gz", hash = "sha256:3d427459dfe5ec3241a6bad046b1d10c0e445940e013c81946458987c7c7e255"}, +] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, diff --git a/pyproject.toml b/pyproject.toml index 35e025a..c4fe109 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,3 +16,5 @@ python = "^3.8" jupyterlab = "^2.2.8" [tool.poetry.dev-dependencies] +# Task runners +nox = "^2020.8.22" From 3b9fc59cb613b34e1b62ca3b271d84a4dc60c8ed Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Fri, 2 Oct 2020 15:34:31 +0200 Subject: [PATCH 004/142] Add noxfile --- noxfile.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 noxfile.py diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 0000000..44afb33 --- /dev/null +++ b/noxfile.py @@ -0,0 +1,13 @@ +"""Configure nox as the task runner.""" + +import nox + + +PYTHON = "3.8" + +# Use a unified .cache/ folder for all develop tools. +nox.options.envdir = ".cache/nox" + +# All tools except git and poetry are project dependencies. +# Avoid accidental successes if the environment is not set up properly. +nox.options.error_on_external_run = True From d88dc0f91c5de32283954473d0ac5d3397d00ddb Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Fri, 2 Oct 2020 16:15:30 +0200 Subject: [PATCH 005/142] Add fix-branch-references task - add nox session "fix-branch-references" + the session is a task executed in the normal develop environment + it rewrites all branch labels in links in *.ipynb and *.md files --- noxfile.py | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 2 deletions(-) diff --git a/noxfile.py b/noxfile.py index 44afb33..e083aa7 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,9 +1,24 @@ -"""Configure nox as the task runner.""" +"""Configure nox as the task runner. + +Nox provides the following tasks: + +- "fix-branch-references": adjusts links with git branch references in + various files (e.g., Mardown or notebooks) + +""" + +import contextlib +import glob +import os +import re +import shutil +import subprocess +import tempfile import nox -PYTHON = "3.8" +REPOSITORY = "webartifex/intro-to-python" # Use a unified .cache/ folder for all develop tools. nox.options.envdir = ".cache/nox" @@ -11,3 +26,103 @@ nox.options.envdir = ".cache/nox" # All tools except git and poetry are project dependencies. # Avoid accidental successes if the environment is not set up properly. nox.options.error_on_external_run = True + + +@nox.session(name="fix-branch-references", venv_backend="none") +def fix_branch_references(_session): + """Change git branch references. + + Intended to be run as a pre-commit hook. + + Many files in the project (e.g., README.md) contain links to resources on + github.com, nbviewer.jupyter.org, or mybinder.org that contain git branch + labels. + + This task rewrites branch labels into either "main" or "develop". + """ + # Glob patterns that expand into the files whose links are re-written. + paths = ["*.md", "**/*.ipynb"] + + branch = ( + subprocess.check_output( + ("git", "rev-parse", "--abbrev-ref", "HEAD"), + ) + .decode() + .strip() + ) + # If the current branch is only temporary and will be merged into "main", ... + if branch.startswith("release-") or branch.startswith("hotfix-"): + branch = "main" + # If the branch is not "main", we assume it is a feature branch. + elif branch != "main": + branch = "develop" + + rewrites = [ + { + "name": "github", + "pattern": re.compile( + fr"((((http)|(https))://github\.com/{REPOSITORY}/((blob)|(tree))/)([\w-]+)/)" + ), + "replacement": fr"\2{branch}/", + }, + { + "name": "nbviewer", + "pattern": re.compile( + fr"((((http)|(https))://nbviewer\.jupyter\.org/github/{REPOSITORY}/((blob)|(tree))/)([\w-]+)/)", + ), + "replacement": fr"\2{branch}/", + }, + { + "name": "mybinder", + "pattern": re.compile( + fr"((((http)|(https))://mybinder\.org/v2/gh/{REPOSITORY}/)([\w-]+)\?)", + ), + "replacement": fr"\2{branch}?", + }, + ] + + for expanded in _expand(*paths): + with _line_by_line_replace(expanded) as (old_file, new_file): + for line in old_file: + for rewrite in rewrites: + line = re.sub(rewrite["pattern"], rewrite["replacement"], line) + new_file.write(line) + + +def _expand(*patterns): + """Expand glob patterns into paths. + + Args: + *patterns: the patterns to be expanded + + Yields: + path: a single expanded path + """ + for pattern in patterns: + yield from glob.glob(pattern.strip()) + + +@contextlib.contextmanager +def _line_by_line_replace(path): + """Replace/change the lines in a file one by one. + + This generator function yields two file handles, one to the current file + (i.e., `old_file`) and one to its replacement (i.e., `new_file`). + + Usage: loop over the lines in `old_file` and write the files to be kept + to `new_file`. Files not written to `new_file` are removed! + + Args: + path: the file whose lines are to be replaced + + Yields: + old_file, new_file: handles to a file and its replacement + """ + file_handle, new_file_path = tempfile.mkstemp() + with os.fdopen(file_handle, "w") as new_file: + with open(path) as old_file: + yield old_file, new_file + + shutil.copymode(path, new_file_path) + os.remove(path) + shutil.move(new_file_path, path) From b3b8a0d5ab1de3cb8bd8fe8df9399410f2c25e2e Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Fri, 2 Oct 2020 16:19:54 +0200 Subject: [PATCH 006/142] Add pre-commit to the develop dependencies --- poetry.lock | 94 +++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 2ac41a5..e2a50ce 100644 --- a/poetry.lock +++ b/poetry.lock @@ -105,6 +105,14 @@ version = "1.14.3" [package.dependencies] pycparser = "*" +[[package]] +category = "dev" +description = "Validate configuration and produce human readable error messages." +name = "cfgv" +optional = false +python-versions = ">=3.6.1" +version = "3.2.0" + [[package]] category = "main" description = "Universal encoding detector for Python 2 and 3" @@ -173,6 +181,17 @@ optional = false python-versions = "*" version = "3.0.12" +[[package]] +category = "dev" +description = "File identification library for Python" +name = "identify" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +version = "1.5.5" + +[package.extras] +license = ["editdistance"] + [[package]] category = "main" description = "Internationalized Domain Names in Applications (IDNA)" @@ -465,6 +484,14 @@ optional = false python-versions = ">=3.5" version = "1.4.1" +[[package]] +category = "dev" +description = "Node.js virtual environment builder" +name = "nodeenv" +optional = false +python-versions = "*" +version = "1.5.0" + [[package]] category = "main" description = "A web-based notebook environment for interactive computing" @@ -561,6 +588,22 @@ optional = false python-versions = "*" version = "0.7.5" +[[package]] +category = "dev" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +name = "pre-commit" +optional = false +python-versions = ">=3.6.1" +version = "2.7.1" + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +toml = "*" +virtualenv = ">=20.0.8" + [[package]] category = "main" description = "Python client for the Prometheus monitoring system." @@ -661,6 +704,14 @@ optional = false python-versions = "*" version = "0.5.7" +[[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.*, !=3.4.*" +version = "5.3.1" + [[package]] category = "main" description = "Python bindings for 0MQ" @@ -727,6 +778,14 @@ version = "0.4.4" [package.extras] test = ["pathlib2"] +[[package]] +category = "dev" +description = "Python Library for Tom's Obvious, Minimal Language" +name = "toml" +optional = false +python-versions = "*" +version = "0.10.1" + [[package]] category = "main" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." @@ -797,7 +856,7 @@ python-versions = "*" version = "0.5.1" [metadata] -content-hash = "ff4ef02eb98bcdc61e34672f3684f54493b3e5522a84fc678bbf9847b10a3102" +content-hash = "cbabc76963596b4cb08138b7b42feef3ba509e9b10ea821a1a5d02675fd6c761" lock-version = "1.0" python-versions = "^3.8" @@ -890,6 +949,10 @@ cffi = [ {file = "cffi-1.14.3-cp39-cp39-win_amd64.whl", hash = "sha256:c150eaa3dadbb2b5339675b88d4573c1be3cb6f2c33a6c83387e10cc0bf05bd3"}, {file = "cffi-1.14.3.tar.gz", hash = "sha256:f92f789e4f9241cd262ad7a555ca2c648a98178a953af117ef7fad46aa1d5591"}, ] +cfgv = [ + {file = "cfgv-3.2.0-py2.py3-none-any.whl", hash = "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d"}, + {file = "cfgv-3.2.0.tar.gz", hash = "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"}, +] chardet = [ {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, @@ -922,6 +985,10 @@ filelock = [ {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, ] +identify = [ + {file = "identify-1.5.5-py2.py3-none-any.whl", hash = "sha256:da683bfb7669fa749fc7731f378229e2dbf29a1d1337cbde04106f02236eb29d"}, + {file = "identify-1.5.5.tar.gz", hash = "sha256:7c22c384a2c9b32c5cc891d13f923f6b2653aa83e2d75d8f79be240d6c86c4f4"}, +] idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, @@ -1029,6 +1096,10 @@ nest-asyncio = [ {file = "nest_asyncio-1.4.1-py3-none-any.whl", hash = "sha256:a4487c4f49f2d11a7bb89a512a6886b6a5045f47097f49815b2851aaa8599cf0"}, {file = "nest_asyncio-1.4.1.tar.gz", hash = "sha256:b86c3193abda5b2eeccf8c79894bc71c680369a178f4b068514ac00720b14e01"}, ] +nodeenv = [ + {file = "nodeenv-1.5.0-py2.py3-none-any.whl", hash = "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9"}, + {file = "nodeenv-1.5.0.tar.gz", hash = "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c"}, +] notebook = [ {file = "notebook-6.1.4-py3-none-any.whl", hash = "sha256:07b6e8b8a61aa2f780fe9a97430470485bc71262bc5cae8521f1441b910d2c88"}, {file = "notebook-6.1.4.tar.gz", hash = "sha256:687d01f963ea20360c0b904ee7a37c3d8cda553858c8d6e33fd0afd13e89de32"}, @@ -1056,6 +1127,10 @@ pickleshare = [ {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, ] +pre-commit = [ + {file = "pre_commit-2.7.1-py2.py3-none-any.whl", hash = "sha256:810aef2a2ba4f31eed1941fc270e72696a1ad5590b9751839c90807d0fff6b9a"}, + {file = "pre_commit-2.7.1.tar.gz", hash = "sha256:c54fd3e574565fe128ecc5e7d2f91279772ddb03f8729645fa812fe809084a70"}, +] prometheus-client = [ {file = "prometheus_client-0.8.0-py2.py3-none-any.whl", hash = "sha256:983c7ac4b47478720db338f1491ef67a100b474e3bc7dafcbaefb7d0b8f9b01c"}, {file = "prometheus_client-0.8.0.tar.gz", hash = "sha256:c6e6b706833a6bd1fd51711299edee907857be10ece535126a158f911ee80915"}, @@ -1117,6 +1192,19 @@ pywinpty = [ {file = "pywinpty-0.5.7-cp38-cp38-win_amd64.whl", hash = "sha256:8fc5019ff3efb4f13708bd3b5ad327589c1a554cb516d792527361525a7cb78c"}, {file = "pywinpty-0.5.7.tar.gz", hash = "sha256:2d7e9c881638a72ffdca3f5417dd1563b60f603e1b43e5895674c2a1b01f95a0"}, ] +pyyaml = [ + {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, + {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"}, + {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"}, + {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"}, + {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"}, + {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"}, + {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"}, + {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, + {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, + {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, + {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, +] pyzmq = [ {file = "pyzmq-19.0.2-cp27-cp27m-macosx_10_9_intel.whl", hash = "sha256:59f1e54627483dcf61c663941d94c4af9bf4163aec334171686cdaee67974fe5"}, {file = "pyzmq-19.0.2-cp27-cp27m-win32.whl", hash = "sha256:c36ffe1e5aa35a1af6a96640d723d0d211c5f48841735c2aa8d034204e87eb87"}, @@ -1167,6 +1255,10 @@ 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.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"}, + {file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"}, +] tornado = [ {file = "tornado-6.0.4-cp35-cp35m-win32.whl", hash = "sha256:5217e601700f24e966ddab689f90b7ea4bd91ff3357c3600fa1045e26d68e55d"}, {file = "tornado-6.0.4-cp35-cp35m-win_amd64.whl", hash = "sha256:c98232a3ac391f5faea6821b53db8db461157baa788f5d6222a193e9456e1740"}, diff --git a/pyproject.toml b/pyproject.toml index c4fe109..c06108c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,3 +18,4 @@ jupyterlab = "^2.2.8" [tool.poetry.dev-dependencies] # Task runners nox = "^2020.8.22" +pre-commit = "^2.7.1" From b2b29d9ec0774670149f43010affc8e5376b915d Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Fri, 2 Oct 2020 16:27:48 +0200 Subject: [PATCH 007/142] Configure pre-commit hooks - run "fix-branch-references" before every commit - add common pre-commit hooks from the pre-commit framework --- .pre-commit-config.yaml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..40d4f46 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,22 @@ +default_stages: [commit] +fail_fast: true +repos: +- repo: local + hooks: + - id: fix-branch-references + name: Check for wrong branch references + entry: poetry run nox -s fix-branch-references -- + language: system + stages: [commit, merge-commit] + types: [text] +# Enable hooks provided by the pre-commit project to +# enforce rules that local tools could not that easily. +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: check-added-large-files + args: [--maxkb=250] + - id: check-merge-conflict + - id: no-commit-to-branch + args: [--branch, main] + - id: trailing-whitespace From b0a5b496640503f63eb8447867d1daedc98cfbaf Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Fri, 2 Oct 2020 16:31:36 +0200 Subject: [PATCH 008/142] Add init-project task - add a nox session "init-project" + it installs the pre-commit hooks + being a task, the session is run in the develop environment --- noxfile.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/noxfile.py b/noxfile.py index e083aa7..184e1b5 100644 --- a/noxfile.py +++ b/noxfile.py @@ -2,6 +2,8 @@ Nox provides the following tasks: +- "init-project": install the pre-commit hooks + - "fix-branch-references": adjusts links with git branch references in various files (e.g., Mardown or notebooks) @@ -28,6 +30,16 @@ nox.options.envdir = ".cache/nox" nox.options.error_on_external_run = True +@nox.session(name="init-project", venv_backend="none") +def init_project(session): + """Install the pre-commit hooks.""" + for type_ in ( + "pre-commit", + "pre-merge-commit", + ): + session.run("poetry", "run", "pre-commit", "install", f"--hook-type={type_}") + + @nox.session(name="fix-branch-references", venv_backend="none") def fix_branch_references(_session): """Change git branch references. From c67d26b78744ef2a0945447d91424f818ce4467b Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 12 Oct 2020 11:28:27 +0200 Subject: [PATCH 009/142] Pin the dependencies - use Poetry 1.1 - re-oder the keys in poetry.lock - upgrade transient dependencies: + colorlog + identify + nbconvert + virtualenv --- poetry.lock | 522 ++++++++++++++++++++++++++-------------------------- 1 file changed, 257 insertions(+), 265 deletions(-) diff --git a/poetry.lock b/poetry.lock index e2a50ce..6b2b916 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,85 +1,84 @@ [[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.4" - -[[package]] -category = "main" -description = "Disable App Nap on OS X 10.9" -marker = "sys_platform == \"darwin\" or platform_system == \"Darwin\"" -name = "appnope" -optional = false -python-versions = "*" -version = "0.1.0" - -[[package]] +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" -description = "Bash tab completion for argparse" -name = "argcomplete" optional = false python-versions = "*" + +[[package]] +name = "appnope" +version = "0.1.0" +description = "Disable App Nap on OS X 10.9" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "argcomplete" version = "1.12.1" +description = "Bash tab completion for argparse" +category = "dev" +optional = false +python-versions = "*" [package.extras] test = ["coverage", "flake8", "pexpect", "wheel"] [[package]] -category = "main" -description = "The secure Argon2 password hashing algorithm." name = "argon2-cffi" +version = "20.1.0" +description = "The secure Argon2 password hashing algorithm." +category = "main" optional = false python-versions = "*" -version = "20.1.0" [package.dependencies] cffi = ">=1.0.0" six = "*" [package.extras] -dev = ["coverage (>=5.0.2)", "hypothesis", "pytest", "sphinx", "wheel", "pre-commit"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest", "sphinx", "wheel", "pre-commit"] docs = ["sphinx"] -tests = ["coverage (>=5.0.2)", "hypothesis", "pytest"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"] [[package]] -category = "main" -description = "Async generators and context managers for Python 3.5+" name = "async-generator" +version = "1.10" +description = "Async generators and context managers for Python 3.5+" +category = "main" optional = false python-versions = ">=3.5" -version = "1.10" [[package]] -category = "main" -description = "Classes Without Boilerplate" name = "attrs" +version = "20.2.0" +description = "Classes Without Boilerplate" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "20.2.0" [package.extras] -dev = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "sphinx-rtd-theme", "pre-commit"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "sphinx-rtd-theme", "pre-commit"] docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] -tests = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] -tests_no_zope = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] [[package]] -category = "main" -description = "Specifications for callback functions passed in to an API" name = "backcall" +version = "0.2.0" +description = "Specifications for callback functions passed in to an API" +category = "main" optional = false python-versions = "*" -version = "0.2.0" [[package]] -category = "main" -description = "An easy safelist-based HTML-sanitizing tool." name = "bleach" +version = "3.2.1" +description = "An easy safelist-based HTML-sanitizing tool." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "3.2.1" [package.dependencies] packaging = "*" @@ -87,129 +86,128 @@ six = ">=1.9.0" webencodings = "*" [[package]] -category = "main" -description = "Python package for providing Mozilla's CA Bundle." name = "certifi" +version = "2020.6.20" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" optional = false python-versions = "*" -version = "2020.6.20" [[package]] -category = "main" -description = "Foreign Function Interface for Python calling C code." name = "cffi" +version = "1.14.3" +description = "Foreign Function Interface for Python calling C code." +category = "main" optional = false python-versions = "*" -version = "1.14.3" [package.dependencies] pycparser = "*" [[package]] -category = "dev" -description = "Validate configuration and produce human readable error messages." name = "cfgv" +version = "3.2.0" +description = "Validate configuration and produce human readable error messages." +category = "dev" optional = false python-versions = ">=3.6.1" -version = "3.2.0" [[package]] -category = "main" -description = "Universal encoding detector for Python 2 and 3" name = "chardet" +version = "3.0.4" +description = "Universal encoding detector for Python 2 and 3" +category = "main" optional = false python-versions = "*" -version = "3.0.4" [[package]] -category = "main" -description = "Cross-platform colored terminal text." -marker = "sys_platform == \"win32\"" name = "colorama" +version = "0.4.3" +description = "Cross-platform colored terminal text." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.4.3" [[package]] -category = "dev" -description = "Log formatting with colors!" name = "colorlog" +version = "4.4.0" +description = "Log formatting with colors!" +category = "dev" optional = false python-versions = "*" -version = "4.2.1" [package.dependencies] -colorama = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} [[package]] -category = "main" -description = "Decorators for Humans" name = "decorator" +version = "4.4.2" +description = "Decorators for Humans" +category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "4.4.2" [[package]] -category = "main" -description = "XML bomb protection for Python stdlib modules" name = "defusedxml" +version = "0.6.0" +description = "XML bomb protection for Python stdlib modules" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.6.0" [[package]] -category = "dev" -description = "Distribution utilities" name = "distlib" +version = "0.3.1" +description = "Distribution utilities" +category = "dev" optional = false python-versions = "*" -version = "0.3.1" [[package]] -category = "main" -description = "Discover and load entry points from installed packages." name = "entrypoints" +version = "0.3" +description = "Discover and load entry points from installed packages." +category = "main" optional = false python-versions = ">=2.7" -version = "0.3" [[package]] -category = "dev" -description = "A platform independent file lock." name = "filelock" +version = "3.0.12" +description = "A platform independent file lock." +category = "dev" optional = false python-versions = "*" -version = "3.0.12" [[package]] -category = "dev" -description = "File identification library for Python" name = "identify" +version = "1.5.6" +description = "File identification library for Python" +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -version = "1.5.5" [package.extras] license = ["editdistance"] [[package]] -category = "main" -description = "Internationalized Domain Names in Applications (IDNA)" name = "idna" +version = "2.10" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.10" [[package]] -category = "main" -description = "IPython Kernel for Jupyter" name = "ipykernel" +version = "5.3.4" +description = "IPython Kernel for Jupyter" +category = "main" optional = false python-versions = ">=3.5" -version = "5.3.4" [package.dependencies] -appnope = "*" +appnope = {version = "*", markers = "platform_system == \"Darwin\""} ipython = ">=5.0.0" jupyter-client = "*" tornado = ">=4.2" @@ -219,24 +217,23 @@ traitlets = ">=4.1.0" test = ["pytest (!=5.3.4)", "pytest-cov", "flaky", "nose"] [[package]] -category = "main" -description = "IPython: Productive Interactive Computing" name = "ipython" +version = "7.18.1" +description = "IPython: Productive Interactive Computing" +category = "main" optional = false python-versions = ">=3.7" -version = "7.18.1" [package.dependencies] -appnope = "*" +appnope = {version = "*", markers = "sys_platform == \"darwin\""} backcall = "*" -colorama = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} decorator = "*" jedi = ">=0.10" -pexpect = ">4.3" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} pickleshare = "*" 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] @@ -251,20 +248,20 @@ qtconsole = ["qtconsole"] test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.14)"] [[package]] -category = "main" -description = "Vestigial utilities from IPython" name = "ipython-genutils" +version = "0.2.0" +description = "Vestigial utilities from IPython" +category = "main" optional = false python-versions = "*" -version = "0.2.0" [[package]] -category = "main" -description = "An autocompletion tool for Python that can be used for text editors." name = "jedi" +version = "0.17.2" +description = "An autocompletion tool for Python that can be used for text editors." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.17.2" [package.dependencies] parso = ">=0.7.0,<0.8.0" @@ -274,12 +271,12 @@ qa = ["flake8 (3.7.9)"] testing = ["Django (<3.1)", "colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"] [[package]] -category = "main" -description = "A very fast and expressive template engine." name = "jinja2" +version = "2.11.2" +description = "A very fast and expressive template engine." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.11.2" [package.dependencies] MarkupSafe = ">=0.23" @@ -288,28 +285,27 @@ MarkupSafe = ">=0.23" i18n = ["Babel (>=0.8)"] [[package]] -category = "main" -description = "A Python implementation of the JSON5 data format." name = "json5" +version = "0.9.5" +description = "A Python implementation of the JSON5 data format." +category = "main" optional = false python-versions = "*" -version = "0.9.5" [package.extras] dev = ["hypothesis"] [[package]] -category = "main" -description = "An implementation of JSON Schema validation for Python" name = "jsonschema" +version = "3.2.0" +description = "An implementation of JSON Schema validation for Python" +category = "main" optional = false python-versions = "*" -version = "3.2.0" [package.dependencies] attrs = ">=17.4.0" pyrsistent = ">=0.14.0" -setuptools = "*" six = ">=1.11.0" [package.extras] @@ -317,12 +313,12 @@ 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" -description = "Jupyter protocol implementation and client libraries" name = "jupyter-client" +version = "6.1.7" +description = "Jupyter protocol implementation and client libraries" +category = "main" optional = false python-versions = ">=3.5" -version = "6.1.7" [package.dependencies] jupyter-core = ">=4.6.0" @@ -335,24 +331,24 @@ traitlets = "*" test = ["ipykernel", "ipython", "mock", "pytest", "pytest-asyncio", "async-generator", "pytest-timeout"] [[package]] -category = "main" -description = "Jupyter core package. A base package on which Jupyter projects rely." name = "jupyter-core" +version = "4.6.3" +description = "Jupyter core package. A base package on which Jupyter projects rely." +category = "main" optional = false python-versions = "!=3.0,!=3.1,!=3.2,!=3.3,!=3.4,>=2.7" -version = "4.6.3" [package.dependencies] -pywin32 = ">=1.0" +pywin32 = {version = ">=1.0", markers = "sys_platform == \"win32\""} traitlets = "*" [[package]] -category = "main" -description = "The JupyterLab notebook server extension." name = "jupyterlab" +version = "2.2.8" +description = "The JupyterLab notebook server extension." +category = "main" optional = false python-versions = ">=3.5" -version = "2.2.8" [package.dependencies] jinja2 = ">=2.10" @@ -365,23 +361,23 @@ docs = ["jsx-lexer", "recommonmark", "sphinx", "sphinx-rtd-theme", "sphinx-copyb test = ["pytest", "pytest-check-links", "requests", "wheel", "virtualenv"] [[package]] -category = "main" -description = "Pygments theme using JupyterLab CSS variables" name = "jupyterlab-pygments" +version = "0.1.2" +description = "Pygments theme using JupyterLab CSS variables" +category = "main" optional = false python-versions = "*" -version = "0.1.2" [package.dependencies] pygments = ">=2.4.1,<3" [[package]] -category = "main" -description = "JupyterLab Server" name = "jupyterlab-server" +version = "1.2.0" +description = "JupyterLab Server" +category = "main" optional = false python-versions = ">=3.5" -version = "1.2.0" [package.dependencies] jinja2 = ">=2.10" @@ -394,28 +390,28 @@ requests = "*" test = ["pytest", "requests"] [[package]] -category = "main" -description = "Safely add untrusted strings to HTML/XML markup." name = "markupsafe" +version = "1.1.1" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" 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" +version = "0.8.4" +description = "The fastest markdown parser in pure Python" +category = "main" optional = false python-versions = "*" -version = "0.8.4" [[package]] -category = "main" -description = "A client library for executing notebooks. Formally nbconvert's ExecutePreprocessor." name = "nbclient" +version = "0.5.0" +description = "A client library for executing notebooks. Formally nbconvert's ExecutePreprocessor." +category = "main" optional = false python-versions = ">=3.6" -version = "0.5.0" [package.dependencies] async-generator = "*" @@ -430,12 +426,12 @@ sphinx = ["Sphinx (>=1.7)", "sphinx-book-theme", "mock", "moto", "myst-parser"] test = ["codecov", "coverage", "ipython", "ipykernel", "ipywidgets", "pytest (>=4.1)", "pytest-cov (>=2.6.1)", "check-manifest", "flake8", "mypy", "tox", "bumpversion", "xmltodict", "pip (>=18.1)", "wheel (>=0.31.0)", "setuptools (>=38.6.0)", "twine (>=1.11.0)", "black"] [[package]] -category = "main" -description = "Converting Jupyter Notebooks" name = "nbconvert" +version = "6.0.7" +description = "Converting Jupyter Notebooks" +category = "main" optional = false python-versions = ">=3.6" -version = "6.0.6" [package.dependencies] bleach = "*" @@ -460,12 +456,12 @@ test = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (> webpdf = ["pyppeteer (0.2.2)"] [[package]] -category = "main" -description = "The Jupyter Notebook format" name = "nbformat" +version = "5.0.7" +description = "The Jupyter Notebook format" +category = "main" optional = false python-versions = ">=3.5" -version = "5.0.7" [package.dependencies] ipython-genutils = "*" @@ -477,31 +473,30 @@ traitlets = ">=4.1" test = ["pytest", "pytest-cov", "testpath"] [[package]] -category = "main" -description = "Patch asyncio to allow nested event loops" name = "nest-asyncio" +version = "1.4.1" +description = "Patch asyncio to allow nested event loops" +category = "main" optional = false python-versions = ">=3.5" -version = "1.4.1" [[package]] -category = "dev" -description = "Node.js virtual environment builder" name = "nodeenv" +version = "1.5.0" +description = "Node.js virtual environment builder" +category = "dev" optional = false python-versions = "*" -version = "1.5.0" [[package]] -category = "main" -description = "A web-based notebook environment for interactive computing" name = "notebook" +version = "6.1.4" +description = "A web-based notebook environment for interactive computing" +category = "main" optional = false python-versions = ">=3.5" -version = "6.1.4" [package.dependencies] -Send2Trash = "*" argon2-cffi = "*" ipykernel = "*" ipython-genutils = "*" @@ -512,6 +507,7 @@ nbconvert = "*" nbformat = "*" prometheus-client = "*" pyzmq = ">=17" +Send2Trash = "*" terminado = ">=0.8.3" tornado = ">=5.0" traitlets = ">=4.2.1" @@ -521,12 +517,12 @@ docs = ["sphinx", "nbsphinx", "sphinxcontrib-github-alt"] test = ["nose", "coverage", "requests", "nose-warnings-filters", "nbval", "nose-exclude", "selenium", "pytest", "pytest-cov", "requests-unixsocket"] [[package]] -category = "dev" -description = "Flexible test automation." name = "nox" +version = "2020.8.22" +description = "Flexible test automation." +category = "dev" optional = false python-versions = ">=3.5" -version = "2020.8.22" [package.dependencies] argcomplete = ">=1.9.4,<2.0" @@ -538,63 +534,62 @@ virtualenv = ">=14.0.0" tox_to_nox = ["jinja2", "tox"] [[package]] -category = "main" -description = "Core utilities for Python packages" name = "packaging" +version = "20.4" +description = "Core utilities for Python packages" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "20.4" [package.dependencies] pyparsing = ">=2.0.2" six = "*" [[package]] -category = "main" -description = "Utilities for writing pandoc filters in python" name = "pandocfilters" +version = "1.4.2" +description = "Utilities for writing pandoc filters in python" +category = "main" optional = false python-versions = "*" -version = "1.4.2" [[package]] -category = "main" -description = "A Python Parser" name = "parso" +version = "0.7.1" +description = "A Python Parser" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.7.1" [package.extras] testing = ["docopt", "pytest (>=3.0.7)"] [[package]] -category = "main" -description = "Pexpect allows easy control of interactive console applications." -marker = "sys_platform != \"win32\"" name = "pexpect" +version = "4.8.0" +description = "Pexpect allows easy control of interactive console applications." +category = "main" optional = false python-versions = "*" -version = "4.8.0" [package.dependencies] ptyprocess = ">=0.5" [[package]] -category = "main" -description = "Tiny 'shelve'-like database with concurrency support" name = "pickleshare" +version = "0.7.5" +description = "Tiny 'shelve'-like database with concurrency support" +category = "main" optional = false python-versions = "*" -version = "0.7.5" [[package]] -category = "dev" -description = "A framework for managing and maintaining multi-language pre-commit hooks." name = "pre-commit" +version = "2.7.1" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" optional = false python-versions = ">=3.6.1" -version = "2.7.1" [package.dependencies] cfgv = ">=2.0.0" @@ -605,128 +600,125 @@ toml = "*" virtualenv = ">=20.0.8" [[package]] -category = "main" -description = "Python client for the Prometheus monitoring system." name = "prometheus-client" +version = "0.8.0" +description = "Python client for the Prometheus monitoring system." +category = "main" optional = false python-versions = "*" -version = "0.8.0" [package.extras] twisted = ["twisted"] [[package]] -category = "main" -description = "Library for building powerful interactive command lines in Python" name = "prompt-toolkit" +version = "3.0.7" +description = "Library for building powerful interactive command lines in Python" +category = "main" optional = false python-versions = ">=3.6.1" -version = "3.0.7" [package.dependencies] wcwidth = "*" [[package]] -category = "main" -description = "Run a subprocess in a pseudo terminal" -marker = "sys_platform != \"win32\" or os_name != \"nt\"" name = "ptyprocess" +version = "0.6.0" +description = "Run a subprocess in a pseudo terminal" +category = "main" optional = false python-versions = "*" -version = "0.6.0" [[package]] -category = "dev" -description = "library with cross-python path, ini-parsing, io, code, log facilities" name = "py" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "1.9.0" - -[[package]] -category = "main" -description = "C parser in Python" -name = "pycparser" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.20" [[package]] +name = "pycparser" +version = "2.20" +description = "C parser in Python" category = "main" -description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] name = "pygments" +version = "2.7.1" +description = "Pygments is a syntax highlighting package written in Python." +category = "main" optional = false python-versions = ">=3.5" -version = "2.7.1" [[package]] -category = "main" -description = "Python parsing module" name = "pyparsing" +version = "2.4.7" +description = "Python parsing module" +category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -version = "2.4.7" [[package]] -category = "main" -description = "Persistent/Functional/Immutable data structures" name = "pyrsistent" +version = "0.17.3" +description = "Persistent/Functional/Immutable data structures" +category = "main" optional = false python-versions = ">=3.5" -version = "0.17.3" [[package]] -category = "main" -description = "Extensions to the standard Python datetime module" name = "python-dateutil" +version = "2.8.1" +description = "Extensions to the standard Python datetime module" +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -version = "2.8.1" [package.dependencies] six = ">=1.5" [[package]] -category = "main" -description = "Python for Window Extensions" -marker = "sys_platform == \"win32\"" name = "pywin32" -optional = false -python-versions = "*" version = "228" - -[[package]] +description = "Python for Window Extensions" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "pywinpty" +version = "0.5.7" +description = "Python bindings for the winpty library" category = "main" -description = "Python bindings for the winpty library" -marker = "os_name == \"nt\"" -name = "pywinpty" optional = false python-versions = "*" -version = "0.5.7" [[package]] -category = "dev" -description = "YAML parser and emitter for Python" name = "pyyaml" +version = "5.3.1" +description = "YAML parser and emitter for Python" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "5.3.1" [[package]] -category = "main" -description = "Python bindings for 0MQ" name = "pyzmq" +version = "19.0.2" +description = "Python bindings for 0MQ" +category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*" -version = "19.0.2" [[package]] -category = "main" -description = "Python HTTP for Humans." name = "requests" +version = "2.24.0" +description = "Python HTTP for Humans." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.24.0" [package.dependencies] certifi = ">=2017.4.17" @@ -739,68 +731,68 @@ security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] [[package]] -category = "main" -description = "Send file to trash natively under Mac OS X, Windows and Linux." name = "send2trash" +version = "1.5.0" +description = "Send file to trash natively under Mac OS X, Windows and Linux." +category = "main" optional = false python-versions = "*" -version = "1.5.0" [[package]] -category = "main" -description = "Python 2 and 3 compatibility utilities" name = "six" +version = "1.15.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -version = "1.15.0" [[package]] -category = "main" -description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library." name = "terminado" +version = "0.9.1" +description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library." +category = "main" optional = false python-versions = ">=3.6" -version = "0.9.1" [package.dependencies] -ptyprocess = "*" -pywinpty = ">=0.5" +ptyprocess = {version = "*", markers = "os_name != \"nt\""} +pywinpty = {version = ">=0.5", markers = "os_name == \"nt\""} tornado = ">=4" [[package]] -category = "main" -description = "Test utilities for code working with files and commands" name = "testpath" +version = "0.4.4" +description = "Test utilities for code working with files and commands" +category = "main" optional = false python-versions = "*" -version = "0.4.4" [package.extras] test = ["pathlib2"] [[package]] -category = "dev" -description = "Python Library for Tom's Obvious, Minimal Language" name = "toml" +version = "0.10.1" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "dev" optional = false python-versions = "*" -version = "0.10.1" [[package]] -category = "main" -description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." name = "tornado" +version = "6.0.4" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +category = "main" optional = false python-versions = ">= 3.5" -version = "6.0.4" [[package]] -category = "main" -description = "Traitlets Python configuration system" name = "traitlets" +version = "5.0.4" +description = "Traitlets Python configuration system" +category = "main" optional = false python-versions = ">=3.7" -version = "5.0.4" [package.dependencies] ipython-genutils = "*" @@ -809,12 +801,12 @@ ipython-genutils = "*" test = ["pytest"] [[package]] -category = "main" -description = "HTTP library with thread-safe connection pooling, file post, and more." name = "urllib3" +version = "1.25.10" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" -version = "1.25.10" [package.extras] brotli = ["brotlipy (>=0.6.0)"] @@ -822,12 +814,12 @@ secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0 socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] [[package]] -category = "dev" -description = "Virtual Python Environment builder" name = "virtualenv" +version = "20.0.33" +description = "Virtual Python Environment builder" +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -version = "20.0.32" [package.dependencies] appdirs = ">=1.4.3,<2" @@ -837,28 +829,28 @@ six = ">=1.9.0,<2" [package.extras] docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"] -testing = ["coverage (>=5)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "pytest-xdist (>=1.31.0)", "packaging (>=20.0)", "xonsh (>=0.9.16)"] +testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "pytest-xdist (>=1.31.0)", "packaging (>=20.0)", "xonsh (>=0.9.16)"] [[package]] -category = "main" -description = "Measures the displayed width of unicode strings in a terminal" name = "wcwidth" +version = "0.2.5" +description = "Measures the displayed width of unicode strings in a terminal" +category = "main" optional = false python-versions = "*" -version = "0.2.5" [[package]] -category = "main" -description = "Character encoding aliases for legacy web content" name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +category = "main" optional = false python-versions = "*" -version = "0.5.1" [metadata] -content-hash = "cbabc76963596b4cb08138b7b42feef3ba509e9b10ea821a1a5d02675fd6c761" -lock-version = "1.0" +lock-version = "1.1" python-versions = "^3.8" +content-hash = "cbabc76963596b4cb08138b7b42feef3ba509e9b10ea821a1a5d02675fd6c761" [metadata.files] appdirs = [ @@ -962,8 +954,8 @@ colorama = [ {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, ] colorlog = [ - {file = "colorlog-4.2.1-py2.py3-none-any.whl", hash = "sha256:43597fd822ce705190fc997519342fdaaf44b9b47f896ece7aa153ed4b909c74"}, - {file = "colorlog-4.2.1.tar.gz", hash = "sha256:75e55822c3a3387d721579241e776de2cf089c9ef9528b1f09e8b04d403ad118"}, + {file = "colorlog-4.4.0-py2.py3-none-any.whl", hash = "sha256:f14f30f58e2ce6ef40b0088307cac7efb9ecff5605fa2267a2d29955f26aff23"}, + {file = "colorlog-4.4.0.tar.gz", hash = "sha256:0272c537469ab1e63b9915535874d15b671963c9325db0c4891a2aeff97ce3d1"}, ] decorator = [ {file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"}, @@ -986,8 +978,8 @@ filelock = [ {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, ] identify = [ - {file = "identify-1.5.5-py2.py3-none-any.whl", hash = "sha256:da683bfb7669fa749fc7731f378229e2dbf29a1d1337cbde04106f02236eb29d"}, - {file = "identify-1.5.5.tar.gz", hash = "sha256:7c22c384a2c9b32c5cc891d13f923f6b2653aa83e2d75d8f79be240d6c86c4f4"}, + {file = "identify-1.5.6-py2.py3-none-any.whl", hash = "sha256:3139bf72d81dfd785b0a464e2776bd59bdc725b4cc10e6cf46b56a0db931c82e"}, + {file = "identify-1.5.6.tar.gz", hash = "sha256:969d844b7a85d32a5f9ac4e163df6e846d73c87c8b75847494ee8f4bd2186421"}, ] idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, @@ -1085,8 +1077,8 @@ nbclient = [ {file = "nbclient-0.5.0.tar.gz", hash = "sha256:8ad52d27ba144fca1402db014857e53c5a864a2f407be66ca9d74c3a56d6591d"}, ] nbconvert = [ - {file = "nbconvert-6.0.6-py3-none-any.whl", hash = "sha256:d8549f62e739a4d51f275c2932b1783ee5039dde07a2b71de70c0296a42c8394"}, - {file = "nbconvert-6.0.6.tar.gz", hash = "sha256:68335477288aab8a9b9ec03002dce59b4eb1ca967116741ec218a4e78c129efd"}, + {file = "nbconvert-6.0.7-py3-none-any.whl", hash = "sha256:39e9f977920b203baea0be67eea59f7b37a761caa542abe80f5897ce3cf6311d"}, + {file = "nbconvert-6.0.7.tar.gz", hash = "sha256:cbbc13a86dfbd4d1b5dee106539de0795b4db156c894c2c5dc382062bbc29002"}, ] nbformat = [ {file = "nbformat-5.0.7-py3-none-any.whl", hash = "sha256:ea55c9b817855e2dfcd3f66d74857342612a60b1f09653440f4a5845e6e3523f"}, @@ -1279,8 +1271,8 @@ urllib3 = [ {file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"}, ] virtualenv = [ - {file = "virtualenv-20.0.32-py2.py3-none-any.whl", hash = "sha256:9160a8f6196afcb8bb91405b5362651f302ee8e810fc471f5f9ce9a06b070298"}, - {file = "virtualenv-20.0.32.tar.gz", hash = "sha256:3d427459dfe5ec3241a6bad046b1d10c0e445940e013c81946458987c7c7e255"}, + {file = "virtualenv-20.0.33-py2.py3-none-any.whl", hash = "sha256:35ecdeb58cfc2147bb0706f7cdef69a8f34f1b81b6d49568174e277932908b8f"}, + {file = "virtualenv-20.0.33.tar.gz", hash = "sha256:a5e0d253fe138097c6559c906c528647254f437d1019af9d5a477b09bfa7300f"}, ] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, From cd783a6ac7a044e5ab1bc54132f1d046f6fa3c6a Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 12 Oct 2020 12:02:40 +0200 Subject: [PATCH 010/142] Add prerequisites section --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 4d95cf0..084a280 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,13 @@ in programming with **[Python Date: Mon, 12 Oct 2020 15:21:39 +0200 Subject: [PATCH 011/142] Add installation sections - add section "Installation" + explain how Anaconda is set up + include many screenshots + explain how the repo is downloaded as a zip archive - add a section "Alternative Installation" + explain how git, poetry, and pyenv can be used to set up the project as well + explain what a CLI is (with screenshots) --- README.md | 159 ++++++++++++++++++++++++++++ static/anaconda_download.png | Bin 0 -> 81049 bytes static/anaconda_navigator.png | Bin 0 -> 75999 bytes static/anaconda_start_menu.png | Bin 0 -> 718473 bytes static/cli_install.png | Bin 0 -> 61546 bytes static/cli_jupyter_lab.png | Bin 0 -> 112032 bytes static/jupyter_lab.png | Bin 0 -> 53263 bytes static/jupyter_notebook_blank.png | Bin 0 -> 44093 bytes static/jupyter_notebook_example.png | Bin 0 -> 44702 bytes static/link/to_wiki.png | Bin 0 -> 503 bytes static/repo_download.png | Bin 0 -> 59439 bytes 11 files changed, 159 insertions(+) create mode 100644 static/anaconda_download.png create mode 100644 static/anaconda_navigator.png create mode 100644 static/anaconda_start_menu.png create mode 100644 static/cli_install.png create mode 100644 static/cli_jupyter_lab.png create mode 100644 static/jupyter_lab.png create mode 100644 static/jupyter_notebook_blank.png create mode 100644 static/jupyter_notebook_example.png create mode 100644 static/link/to_wiki.png create mode 100644 static/repo_download.png diff --git a/README.md b/README.md index 084a280..dfbf6a3 100644 --- a/README.md +++ b/README.md @@ -15,3 +15,162 @@ It is only expected that the student has: - knowledge of **basic mathematics** from high school, - the ability to **think conceptually** and **reason logically**, and - the willingness to **invest** around **90-120 hours** on this course. + + +## Getting started + +If you are a total beginner, + follow the instructions in the "Installation" section next. +If you are familiar with + the [git](https://git-scm.com/) + and [poetry](https://python-poetry.org/docs/) command-line tools, + you may want to look at the "Alternative Installation" section further below. + + +### Installation + +To follow this course, an installation of **Python 3.8** or higher is expected. + +A popular and beginner friendly way is + to install the [Anaconda Distribution](https://www.anaconda.com/products/individual) + that not only ships Python itself + but also comes pre-packaged with a lot of third-party libraries. + + + +Scroll down to the [download](https://www.anaconda.com/products/individual#Downloads) section + and install the latest version for your operating system + (i.e., *2020-07* with Python 3.8 at the time of this writing). + +After installation, + you find an entry "[Anaconda Navigator](https://docs.anaconda.com/anaconda/navigator/)" + in your start menu. +Click on it. + + + +A window opens giving you several options to start various applications. +In the beginning, we will work mostly with [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/). +Click on "Launch". + + + +A new tab in your web browser opens: +The website is "localhost" and some number (e.g., 8888). + +This is the [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) application + that is used to display the course materials. +On the left, you see the files and folders on your computer. +This file browser works like any other. +In the center, you see several options to launch (i.e., "create") new files. + + + +To check if your Python installation works, + double-click on the "Python 3" tile under the "Notebook" section. +That opens a new [Jupyter notebook](https://jupyter-notebook.readthedocs.io/en/stable/) + named "Untitled.ipynb". + + + +Enter some basic Python in the **code cell**, for example, `1 + 2`. +Then, press the **Enter** key *while* holding down the **Control** key + (if that does not work, try with the **Shift** key) + to **execute** the snippet. +The result of the calculation, `3` in the example, shows up below the cell. + + + +After setting up Python, + click on the green "Code" button on the top right on this website + to download the course materials. +As a beginner, choosing "Download ZIP" is likely the easiest option. +Then, unpack the ZIP file into a folder of your choice, + ideally somewhere within your personal user folder + so that the files show up right away in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/). + + + + +### Alternative Installation (for Instructors) + +Python can also be installed in a "pure" way + obtained directly from its core development team [here](https://www.python.org/downloads/). +Then, it comes *without* any third-party packages, + which is *not* a problem at all. +Managing third-party packages can be automated to a large degree, + for example, with tools such as [poetry](https://python-poetry.org/docs/). + +However, this may be too "advanced" for a beginner + as it involves working with a [command-line interface ](https://en.wikipedia.org/wiki/Command-line_interface) (CLI), + also called a **terminal**, + which looks like the one below. +It is used *without* a mouse by typing commands into it. +The following instructions assume that + [git](https://git-scm.com/), [poetry](https://python-poetry.org/docs/), + and [pyenv](https://github.com/pyenv/pyenv) are installed. + + + +The screeshot above shows how this project can be set up in an alternative way + with the [zsh](https://en.wikipedia.org/wiki/Z_shell) CLI. + +First, the [git](https://git-scm.com/) tool is used + to **clone** the course materials as a **repository** + into a new folder called "*intro-to-python*" + that lives under a "*repos*" folder. + +- `git clone https://github.com/webartifex/intro-to-python.git` + +The `cd` command is used to "change directories". + +In the screenshot, the [pyenv](https://github.com/pyenv/pyenv) tool is used + to set the project's Python version. +[pyenv](https://github.com/pyenv/pyenv)'s purpose is + to manage *many* parallel Python installations on the same computer. +It is highly recommended for professional users; + however, any other way of installing Python works as well. + +- `pyenv local ...` + +On the contrary, the [poetry](https://python-poetry.org/docs/) tool is used + to manage third-party packages within the *same* Python installation + and, more importantly, on a per-project basis. +So, for example, +whereas "Project A" may depend on [numpy](https://numpy.org/) *v1.19* + from June 2020 be installed, + "Project B" may use *v1.14* from January 2018 instead + (cf., numpy's [release history](https://pypi.org/project/numpy/#history)). +To achieve this per-project **isolation**, +[poetry](https://python-poetry.org/docs/) uses so-called **virtual environments** + behind the scenes. +While one could do that manually, + for example, by using Python's built-in + [venv ](https://docs.python.org/3/library/venv.html) module, + it is more convenient and reliable to have [poetry](https://python-poetry.org/docs/) + automate this. +The following *one* command not only + creates a new virtual environment (manually: `python -m venv venv`) + and *activates* it (manually: `source venv/bin/activate`), + it also installs the versions of the project's third-party dependencies + as specified in the [poetry.lock](poetry.lock) file + (manually: `python -m pip install -r requirements.txt` + if a [requirements.txt](https://docs.python.org/3/tutorial/venv.html#managing-packages-with-pip) + file is used; + the `python -m` part is often left out [but should not be](https://snarky.ca/why-you-should-use-python-m-pip/)): + +- `poetry install` + +[poetry](https://python-poetry.org/docs/) is also used + to execute commands in the project's (virtual) environment. +The command is then prefixed with `poetry run ...`. +For example, to do the equivalent of clicking "Launch" in the Anaconda Navigator: + +- `poetry run jupyter lab` + +This opens a new tab in your web browser just as above. +The command-line interface stays open in the background, + like in the screenshot below, + and prints log messages as we work in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/). + + \ No newline at end of file diff --git a/static/anaconda_download.png b/static/anaconda_download.png new file mode 100644 index 0000000000000000000000000000000000000000..037678b069cb8e6307a5959a26ff6b06cb848e96 GIT binary patch literal 81049 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 z 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 literal 0 HcmV?d00001 diff --git a/static/anaconda_navigator.png b/static/anaconda_navigator.png new file mode 100644 index 0000000000000000000000000000000000000000..d2ba796f55b8d1343f6de7ac467f504f2a74333c GIT binary patch literal 75999 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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..f3d5d9a97300b7eaabb350bdf27ae91316409dca GIT binary patch literal 718473 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'LppYFTZ6c&-1+-w*b#oYS-oA5w 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

swImx-`1{v%=(UzE~k&ohMiy^l9&o83FiOD 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%0Dh+JP?-BrJZ*D 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

&eYUvqsd6YO#&tpgL^V=tn_9?_1a2361wl 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

czE~z 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 literal 0 HcmV?d00001 diff --git a/static/cli_install.png b/static/cli_install.png new file mode 100644 index 0000000000000000000000000000000000000000..08144dce05cad3f9a3c5b754eeb6ccb8f89f75b4 GIT binary patch literal 61546 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%0AY2#%60@j$ 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 literal 0 HcmV?d00001 diff --git a/static/jupyter_notebook_blank.png b/static/jupyter_notebook_blank.png new file mode 100644 index 0000000000000000000000000000000000000000..d19b8a97aaf7e15130f7d97d63925336ab2a2b03 GIT binary patch literal 44093 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 A7XSbN literal 0 HcmV?d00001 diff --git a/static/repo_download.png b/static/repo_download.png new file mode 100644 index 0000000000000000000000000000000000000000..afb219343a86a983623778ff9c29c6cff0b4528a GIT binary patch literal 59439 zcmb@ubySr>*Eb9*BGMort%s8CZaH*G9vW%s?i3_Ny1To(QRyz}lJ3p}9L{&){oLO_ z-@D%R{PE3N$ehDmd-lxi+40-^GW3&zB>HQj*9Zs*=+aVPWdwv5z6c1!@Bm3ZLFo1^D?#S%nd|`s*Xf z*?!3?5((F9EzW z6-W5iICYiWEQz<4?RhOiM+PR`ZZz$JH}fKyJ@_JHZ0ACVBo$Nq#W;<#&le+8Q`18& zfH?$&-Js-?P%NWaM3Lr?Mv#ABi3H1q>mvT^`q4bgVgA=c5EHpX`q%sXDx~8-U!ftQ zZ25(NLDR|1yd>8i`;d(yFJN^D2!BI^JO2CFc+ttw^z>DJBEEbI$3S0SaEQ%H8+3@d z)9d~yx}=0o5P~U!O78oBjSV}pJt1rb{i7}8MDAU#c`+)>+}^LZzVXy9CS{qu?~fY! zT__LAZ4egDE-yJ;@cxZf-KW0e^SX^{Y-}uX)%V!TmyV@wznsL;oqakQ1cVG7OfrE_ zEnd6h*)L1Geh_o_{va+q(%vsly{R`H;a^!9jJJ<#2v4mH#9V$ z5-@3X`UrZi-Jf)J+O2(bra^mRO5hinfZIjs+{r^!e|W%NCtWW~>C zbhlm4>JXtUF`J+``38?fAQU>*;eD@GUTeGRLw%gWVV+ZS^IY9pOQXTOgwla0{LmAiiWelCLJjyG_>yH>(&s3 zVT;MwNp2BgC4;>R%d0C_>vi{$nfWqcF$0(ZFAP*kB$Mi@&-|RUX10z5fzCCr=UpXLR618WeOHOIWfpD!#JNXz91F!3 zDrO4s{+S&K>h}m)40*41z66(7JG6MOPFeO62Z%U+Jh3PHo{< z?Yj+vAUP!zLsS8~3RH60oAg7+A&FqzW+s{DHYt~T{(Tu&-MX{{#bhn4s(tXFuzI=@ zyMW@M>dZ8H^n}y?8}YHrGSFBuV(aVI&rB6^`OQLdVpR%sHH4fYh)S0M~#)*&thF9@aDez&NM9w((uO_Y+7Eq`S$S#D0xI%4THyQazA>VdD!Nw zoJ3~2p_H7%C`lhqlR^GBuz`5=p&Zxt+_|91lspE8MGogjTGNM}Gc^;3qppgikS{Z< zjrR1y$*oXO4LyX*;3@-K-TyD>$2cQHDNss^Otqi*u#Nc*v_oA8;*~zV|!`dmea(43=Hz!amwh{{D ztiQ@%cT6$)d~Mo>PQ^LTGH4FC8Jt?aT@}wjbaU|z)6SHhXepC=3?MrC((}vL@4_N3 z+oBwz3&zm0fb-t_o&4A5jqbcIew&DvhYdfbt}bhiB#tWcA)*-oOC)nDe?fzANe zy6IfV+Cy?d{;lBDML$Hw1HE`3xKyAV6|;(%!o(Pi&We;5ZtlvEzTCX?l9a%Z2MN#d zD|iMCC}**c?K?@PM_9knXjop}bSb3$OyctGna79;l$$tI%C0fU*1$mY&z~g=0}};s zw~|M9OtYSY<4-_d5D+-b%DRaULuA$P!6U_o_k$xaDz0l@kjGIy#CC;2iNtqzf4{|4 z-rv}SO8iY+!URVp0;pY4@AYX+>Z3}P)72OfJ{qXt+q({ zv1>=$=kJHel9!s=Y;bc63o;7>^M#2~Y-gALa!QnT%I6r#yq{CKZD$&epV&X8WFJJ3 z>KSFCMws&Mh1zxs%7FnTniv=70~fTtzqXD^{tW2x#YWrvN6!r776_nt2M2lL9lqh` ze{ib(QhulqpSNmGUD`L`d?#m09bbZC8uofrCu=V?!Cj9>MFh!=$xVXkhO(fsD61|! zLyt+=SI&SSBco#2ZV)(M&nQma0ll19?(qUz^2wlzpw~&f?T7T`lN2QjN|Y*wKTEb0 zU1x>F8wbtTGc%|_IZCKG4`JtVNPoawip^-$pc+yO+h04}IN0@qK60k`)UOM1E_9e( zcBzt|H+Vs7$jAtumWk$s`Q~jtQAtkO%B3H0Omwug(Cfu6=Jv~V$1!`TS4U)|qKOIB zg}RA}iQ8>$3}+|ox~e2P7^yWQQ~7Qno<5Nq3tMSA8iH$FNM@qz?JG98B@Pizi^(d8 zL26a5(3`h|4!+DOg=ltRmkvriZVcic1j`WznN_E@>Sh;3~!}{1Q+29#G54D()1s#)rIybc_YRlxuElm5c2NYpAuU<$-k}Wr@Y-;uz_oolxm*_5p0&&Bn zNa4qjIFpU1`4DzdTS7ZRzKo=}wvg@Pfi(NU`l~*kZhaAoms-LsAuHng+YJE>3*SZ} z#p!TtFOwQ3{++9c{tT@AD1tN-@OliGxD(p#;E&8>>HL*!)9(dn%YvJDwd=SAJ>HF* zFu6ZXyqQ5}JQK{&5dwY3+&{oZbALB=E(;^5?D2A72DhA^ot7%IM~qvt*xHg+Ma+Hr#VlG5o}~& zU~cG7X!5S-n;sVz7cdkbAHTN(1E40Lsim!5T2hjdnu`5IG`wSBVL?lqytSp}2NCDw z(o)-^|M19&MEKj|{e6e~>-3Tm8Ch9+c6K?s=OFdU#L`krM@RO|F>V`gfZAVaBMXcD z{r$T>a$hWupFfM@kfp4x%dM@CHu{kH`1ro4r8PGEn~j%+dxNOnArF4v~S|pA8M_AO$I@s)p>uZ@IZ*c6KbmUFN{H>0SXNf%fthbhotp zsR@4gFut(R!o9UHrx8gm+)-ZM4m9qq|BORJW1Ap|H8py;wFeg8&g?OsCA!gzB;d33 zJFhU92A6*Ny#pC=z<&0jClXO%gyG`UcNW&(hihwVf5*EJgTqKjNd*N2fK53l5&1{b z&qpT3IX5>qAcWoUTcM3Bi{UpjKqW~^_TgTUu+Y<&;?b|X%NY}f=j7-2%uVl+3kV7> zFE1Y+9)^UB?Ib2bmfAgM=i>hf={TYJ<@C(V{vNOLS+<@70|P_Tk_PDRrZdvA<~gvn zii%^ct|uue7J|`M54UGvRSk`~iHYo^h7t`;&A>oGcAF(0S66P(9qdNfX6Zx!$cTwy z-nVbl;JCE5+Qe3k>UH#j2Y??Dgc(MD{?@aNK34lg;L=LCgys2mV0w^)7T_^ZzN*VW}BXl&UUHEes59{w&kNuVhL!N>uVQ+M1%cV z(~p(eV!<=hg5zG~OiWCTHp}~(u$SmHXE!&qY6x65IzN90q5?-mDcc)C%Ir`uYIZGk!tjUhD{+c7*#s&?7dVJXL>Us(TuqI`v5t+12mz=ULP$^%; zq>7N-%7}>TY_aJcM~7^PWvL71IT4@J?sBVZ{f`dddOG3xX=-YYqz_T$P)J5d3&jJB z5_7)>#7d*?i-Fx~-tFye1|HKq691*4VL33}GsGkh8yg$2s*Vl=_wOp;(ls^R`abUX zE-f!}eDAP={SKhr@ro7^##8I1qI9`!zf7~(Vm1L=ZI-+2lzTJggOAZqw)89Vt;2@Y zLg)HXTf1TJTKnRUX`LktefTT-1obLr7HMf|lZY*}Q%1bH8+_K*^jx}`1zi<}B9{P? zJe`Bn+RA@^8htwfuG1w!m^Tj+5|V+z*tc)rE_v<@$p96o30?8@b#!tnslWUU%$BfpE39mL9_8f5AERFZU8ZacS!W_xMz#vY|ZBIfE?C2jq zI-u6UgL0C56h$xfJh7RNgwht1uSPWUgf=px4aYy#~1x;n2%C;=W` zyc~k5$@GOTnksPoU{(FqtXq8c*U^IHWx+cB&F`7$^skf3D>E&p+b!^zw~T#%Zn*?% zU=zC&Q#gG>6jSzfy6bwZDsgldum>9)5FAqVuboZSqqlb>D>E?4bCv$D|XHn2^kqz2~bm_qH6&2e5>^~ z(z(pCva

NvWwfY?mOAtn3h(FhrCF@vWnfiOIl?12tvYN{9Es^^ta81nE@meza8z zdJhKRh2~c9etz|{ZrZHYW)8zOdvYD%A2~_L&B;qt(Zl8L?^i&W-$KO1#Kgt(>+A7w zdoO!EPhi&f*X1YeH{9u};f(Lzy`w2iRSG0v)|-?@D~7B`2dAf}%SC2`m=gJ&c0Yj} z9Ek2Z;l!04O~{yJA4USd0ET2cd1#|AlAJ>-cZZ)Han80ARxmJVCHJM9EUe^pETb5G z%=q_^xNBz_d^W_S$C{B#$sa$w04*XZ8GL_ZxwT^0(vw8S@}v2iotRE&NJ+(+LusfL zEaS&%4TZy&L-D?rM|8mOP@09QjtG8*D-WUWw z7@Hx5E#5`yFtb2LpYg)=K7>k_;^k1{$?7Pn1V?nA_SMj6vo6Ies#Kj-S~9f{{J%f> zv}GH^i12a=8|`~|osynu%38Qt>cdLFZ3|j_4;P!$O9tS}A*N&Gyie%KxsE8ISGXaP zck_3G#B0jw-W3`mV5VP7Yg=t;7V7uxBc->^Qrby?5M>Xyc+I%|{!CZmf^t=pTk@d1 zC_+W9UM=R$u3nn$Mtd9JXVEKrP^{G zy{m`jRj29_#_KnO?hfV_DbfT^k(ayJ?O8};lM$*Qn zqLrWf-Mg&Bi-OCEgC09q*Vv^2{gz8y{(<1U(fIiIcE#+%xZ_%zU71c#o2H6q?@g_; zH}f}fW9}(EuolqWfFak{LLD6)S6AqVHcag7?0~Wdl@t|?H?iN%%yyo4D7;wmU#c^kbvQdSXK z>fT+Z2;9C!?v^f(=@E!~y`yIwb`m~yORUh5 zRR%S>93xY0wtK-{TsT`I6znSLoSGP;{ZNcX#nG^eGm<75UE=ejwP;4`< zaQjpTwUYXKM59Jz36O2yciePG`=ux9*SqmfkU}xPhrAHqnLL3C5`S#o341ZU zVqKOgYaj8GyE~unUZ;NPYyafmy6DQJa(&>O-vzrypcH2Octw+MANP?r#YQSGXZcty zFzBx34eLa7d1j`in$jI?-;kS5;LZ<9*Jd;O6F*Z|SV->4pF{E1n@5Z0MlG=F$EJICg;c{Nkc~ zI$wU%t*Ma_1vc>M{_c(iR(*4_I@`qJW8QGfeZQZh?9-2>T{838DaDDtgo$Y@7}M2& ztJe8*LuEw;HAwV~SFhTbIB~5)O?>ZJPvVL}Y_P5{!|@L~>G|H&v^i@e;DYshRk$`- z`5v0v7W!xVZ&Zcie6TyS4eIwSEAz^^?@I==t{rqfx-sW!p*J-ICJ9zM@_PIu_oXp7 zpprFfqu*7BU2WoIb?L=!13j6R8q-l^y3X!uMZu_QkVTKCS7F~j7&ig^Ry5c0mc*`Z zuKf9Ro8)~@wf8<<*5AWa>+Cqy2%PK9o}NL*?Z;M9i!S*BCfwr3QzeMM3%tEU(J65p zrH3XNfALt5qBlKQGlV#o(-+~s*-B`6f!WA-?1PcwLQH4PZT_4sACERNCXO_&rTylx z^*P`yfB}7N+ld!WPL73x;{|wU?X)tA_7HJ#Do2`5r#)p$%hI&8B}E}=>Ks2mKkgMb z-49m09+yRc=bF*N=j4XT9MQo;G74zacnN)LYwNwez3S>}1qHF@rMV2G&wwYrZO0>6 znxnHjU0W9nm{q_{i;j-g_k}r+l_1{5#>V2};27H4-U!3lMbDPU$FT&39W}?_ML|2i z2QaD;WDXUahG;NDv7N#z+{DiEgZrvd&K=sP<3mE|Y@`EfW3;z8-0QeaYS%fNUtc!J z90uUMx~=HamHPYlqyPDS5S=ixHV#^K{P3@ui?F{Q(hZx5VU5Z&zn=m6_=IRlTGhGI zlA?Uak#@N_i>L5z9&1pLhEe`lWeUI4u%W?au#}ecd9jyjHn4%Vjh@pFA!|&ky4L(l zfim@kpdyD8z(%Ql`o!YnC1Pi1o69Un`{BbY6qF9Pv&q{tL~veG&3pPG`-QqHfB$Dj ze-uzBcT1a_HK%0&Z?+xnXG%(n2fp_nRMAU*Fis&2;!oyv;sgE4-#*u)gNg-eVY-uSw zE&dFI2z76YLRH%dTU3A2b~wsUSGG;B=;BI z8SNwTYfGDv$~t3Iwwk@SsLnoTSNWu>({p9_o~Xj=tx4oH5Fm;GchzyHx23g}mxqUg zwGLpiwK^Ydd3l>9BS?(M)`oh~gwS4{uC{4LQjj@+6d)S^^XKqa0J6o5(X%Qb zkQEEY*m5ue-08Xnd2jFb;^G;{VJAEfYyh*A)zad_X4ILY%w7-zgfG@lT^ zaE5@F2>LfYEn0f|R)?*@y(!@g!PNBFSQSmp`IVInAcUOjZ>+6FMMr04pE~2{VRF&) zSr651@cE^us--HwiL(^nq5bZ2jnCX&kYnamEtUO(z@8iPeBykf4`Hvo&apfgyfUPp zHaDde5BVeKrR2`r)x;czJLAc$KSZ^%W(7Ji(LcXtzDX!W01N282=~v8OJe*N(jB=Bsl-k1j5cJYq zDX4-q?6+9a2X30e1*+~@_4%)tGS2q2tDGF)oIgE74bCn;S_d$j;f(LKkYmY${IN{urQ6O*l-Oy9FbTcnTKpFX^K z1mcpf&-GWEl&TDSh|SISJUp5zDCJv9e23Y~INx`qbF#C0UhXOZ+99P%rUF^c+(s-P z2>QtcJ&Y|&uT)ZjqhU>As%&;N>Wjqu+`>Th?c>@+Zl4p*7Ja(b%u(~J5*9=aThE^* zL!}?Z^7j62>M2jxdy?tcyu!v-)7`*B4Uu|R^&@19EfunLS`tdxc+)^uGk{^DuY8Sc zDe2wpO&lo;=4?xEOCiA9kM-V5oGvwXB%}MMwHauGUDKDSY;{-NX~X-psUC%I=;x*A z?w>PRZ?Y2OOrOpa#2I(C45c^9v`M4e9+eUamuf!lCWQ zhnwsX&CT2NsI55AM!Q4T+a4Jie`8YsI1B4X*h>JO0W>k#>7l7$VipL`PFC72P4WeD z+lc^WO#@2*riY`hu0B-|i1gxxlmia_n>RXbZpAlW&kN+qiv}0QK3&b7eEo5i#vPrR z*?GsLt~NY8oP-cQwxi$SB_2F&WI{IU^*lLl^_=kMR=-);iCGJ8%VRD%S;zwW6jjmz z&8Kt*Yy^b3w|q`tl$72_yD`Z{s^DCz$nD5EoGbLeKj_LYo|}z?pgS$>R-bxSz2{5GME%#lFwmxhdaLG!gbde$Ch0b3VAXk0Q zl~l~MJ5TKr_aE`(o*}$l#U=AbOvTP+HqSD#uozskJ6>TVW|t5Ol(JoF*X~`jd+D_+ z4)%SpKR`KLV0%l=9$alH3Z5SJzLxIGbFng5Ep&5&{qf_)azF`1ccv;CTzh{B z1eCkMdEWZqKlAgnZ@2(^xH2)JM3c|W!LbRfQ+u(Tfpe(C-78}@yN zu|EhG7Z(+E2ne137-!DvsHjZYb}m=d)qVU2-~kzMa)RX4r?$1V#m$ZCsbb^k=;-rk zyS;7dF@*C3%K?Z?=7+s_HtxoEI9uD>*9?Thp%=*yA1T?mXOF8tvz%77TupRh0^@oE z4T3)#5Aqibk1&StK%wq7HpjUVKRCF#XRDfkBML8EIi;o^78Wag*#F}X7(z17XTCicyMs=*p7#UI8`+1 z04eNvncmgeS?vOB`^2dCQ(#t7!uCW(5};yeXlP=xJTYN^V>5rW*cgmPxDG%`z&r*9 zHrCc+;Ex6X(y6T?9^k(F-&%m!zks!fB;y2ZssPH^l|PxKjST>OtmaE&Ju!;r-}PS3 z->)O<{@2%nr#1tME^2Ou0%7U@5q=GLi9YFTovpua$5YmJ5%YEC)o*~xPlU|?Cb_$3 z6fy2A1b&Yp<2(P*)Sz+HaMaq`pCWeEfKP?6@q78zThd7!|FtS+_ubb|Up*J;M)H2$ zH#0Lj=L~!V+S}V-k}ptT{q2;Q_&ypLhRZ6YWKQpnD{l;G_la<^PJ!U&C|%sW3p;O?W`DnWiB5F+3z_`S%*t3lk26re zU<4@)21*$k{{|9Ry+mJVvNuvuiET(t#NXEK^wGVz>I)~#4cN8Z(zfmHV^kt{tp?^o zR>nMjJp8VA{FLapV(IY9CAJ3@{xQ9x!Qi&*jR zz{8T`*!0W_`C9;EA{BHgYc8e*2`nMp@4H`a?Ho8B%Tcp851pn0LMze<^0x-)`%Vvy(x<<9Qem*@ViYpI*U!6w zXCjGsYv7|-%KN~M1Pzc*#yW~KSZgIgflpt{hLF`c&UB}In7z_csmDYl;FIav9%{W!*JFNn*TJ`^ z(O77&dcO6$t&Xy5`c)RPAq2+mu2xrViKtjJ@5#J-xxN})(cU6d=OfixMjRG^<~e@W z!vky(V=DZYUQQPcbER+w!AV@ZhsVbtVX}_fu-@XXys1ho3o=n1%j!+SRVyR758& zo4JnK88Q|PX|ZpN$mLG=y~UY&op@VI@MiB^d%TZTfAS;LmIT|`+ax1zW<2U49$o%L z5J8ihm`;|_Z0TKFKxzkcB=$xMjFSVd~f zc^%UbcMhW0m-bxUOOryD!z%93*OgnFGUuc8_$$Pzj$s=cOVn7>>pzlqMa)1uQDD%3 zyjWH%JD%zOF4;$i)oo^4yXzIyN*3q22d^lOYbic0BFUE_5;M%ML#Jlf{GKO)<@}K} zrn|79z01_8p4HN;zKiwZ%9R{*;_pP;6pJNlnVvsR!bofF|o4- z9_h9BM8eX?bM$zZs?i7(r;_q=iq6k_AByun`_gq3q_$>u*Zn))nnsGk)ij%7jLeZS z*=rv@icST4Bp^koqT)>Bm$I3Fa97ZIF_m02V;k{%x2b#9^pin!jP2|G1upJ-jcqLp zfu<0__lA8Km19GoSfL;)hyiP#_S&}6M};2 z@1?Jk{2YFmp-VI{OBg-E3&00{r`b}~Wp(@etvCfU9sShJ>v+J@$Ip$e@ zj*g4do7#ITa|1ndgA8N3@4v%Qw=T*IUB=ETM^-BNqGID}AKmoK)0m@e1uU7DrR8GN zC9`=j!PVPrIWea!~oJF{Bx9 z7GV#wKhsWh9mB_blhE;p)iiQ&43UaFd9uquArf4WVJa?|T4lf5)W1x9S0cuHO10;!?Z%pFHuXw3R85wof6*tKX2i!rD(3|{H zjhb|wFB`vN0 zbO8Sav$=VevAxr8mx1#}BdYI4D+()*E>^>f1B>wFhZVzu;jF~i7%KYO0?+D%S{Xl@ znA%!L6~|aT8H}{t!*vGNuHti#qWxoWvipT0oub?%;laD6T&$_X@j2fQ$(`kF+-u9^ zT9ZmW#Ik4D=4U(8S#{q#HgWKjPkc1vP*>ido&-)2LY+4^sUKNl{9%?C>hPV z^9g^k;K=g zBcR$872`M7#};Dfk>b8PJKJ$qDQB-sJ!)?xp!lZL4}*HNbi8#Aq>em##XXPqhvcGO z8)6Ra4*_kPK~>bG%e(X~=x(nX;K2`T!FF*2edMk~2`!$xUAhM1Q*Sj<$Yq z)?8PSSPosF7quBxg-ke0D>)OXJXs8?QM1{B^nsc*kn0Qm3&?{7O+j^Ic^U=#S$NMU zK$r?HbqS97UiQ+Xeti-se|4aWMSn?f8U#$FIr9gWl6dDjX z>fpT)wlx_V2w9qBN>U+es6-=dh6KcDSvVu7`asoL8O9c){U6^qTC$b>wf zV!JCTiqgj6tqnF`$GaGMQuBm`=a%Wujgqtnu=GnxhqIog`s0s0A^|m z8L!{c*Q}q!4riVCIw~&w0o}-gK2Mw$THne)T=}=mSH=-i^s_~F<>5he{(WMoxxIp> z<)1)z$q3keb5fCLz)R_bwx(0&zDMtz+FF$jA}0#d8xnX4jM`=%+ zQCe!VMwFNl%2X>TM7?}THZTIUoJaCdb>V&1CCT`QE56Ns0eV7U4+Z?BY>br9}> zRD79w5-o~q9S^>DdScuwHJwg@-^tF~RrF)( zNej+j4LPydvp&~4EfOM!sm}Bx05Z~`G9ZUVHH(hd zyf=+B&NI9_x#^!U_*PdJq(7E!7(ae$xu4-Q5J8zgy>rpMLF{|acS43v{^PikW=2Jk zcePW$G_$601P@+TTw_~!e~@kJ%3QNg=^Mzd{+M+*=5gZe`{hkM$p)magFGoki=p$D3Wp4rBr05D^7Nx zrf0&4)FO#(LZ_Q-e>XA=k=bZS!0!ObUtt52C_M6?%eYplt9lDBFy?D=a|!#h)Y(ZF zlj9Y1Kjbj*)^6uB#579@-*ZK zZVXS+_Emhy1T4;a07(n}s!=(pC^45|vL?!(s zu-R6}SAe;Lg;{m2((z_iY~)yPvmqKzUqHT(`NMx{^Q3p{a&vt1`}pZt>3eemZgouQ^aTDH&q@U#^>PjJru(!!E|=cr99+8EF$Pm$soei z6(<>mP`o2s`VB6r6rGRfTv|*yLtqLq>bPE4|S;WVu1XG}tdJ>P+qO6)OB z{G*m;q1U#Fa+NS1IZJmyzmxRV+Lw0{)l+Kq;qmNk53Ri?Ch9Z$CZGMRu3Rd=m+|k$ zYn9o&YMPvnlqpGkyHCRof*cJlPjx}vfveHdzZJ^by2Df3HxWt|bXOfr?^JjkTx?lm z-mAVRx862L7I~@SlTmcnQS@bOC9SB%t|g}e5@2LPUwPd!EAC6cx~p=&kF*~B?S^Qd zG+6Nyi3tw7$BX45b)0zGPioH)u(9jw>p4+Vk#tj1Qnjzb%;w%S&gc1L7yT&%BhqP5W6x^c>JtcvvjBV{4VSB1l`tU$~%Fa{~rxd1vau^6Lg z((hL#5{JKyn9(`}n`&a^?fvGcJJQ02N=g^ZR>r2{JUK+OK4vHKVNrsml*tDiAgtZ3 z3aSlNpK`};50`y1)4^Oh70QAE`T9eCV_~-V;M_Eo&4>yqYC3lqntZPc5bx=*^8aqI z6z|3eGjt)Jw*NV1943rBjEC>7{^@Z>iFt7^s@!xMoa934xW71+4w$2 z&ZwxmyqFfFd(a4WfKBX&FQ>(fRJ5%-E8+?2cK>WLgj8QxhwBq@q#ZBI(BwQkv|eH0 zX2hEQ!K!qx%+9bP?O|1O`gm&>FTO*5ca_ogjYk?ffzn@r__F)1RYdNQrrBxf&-a6D zdvk{`!%q5p9SxiKF}C4N-lk8RWt~B`6ttHP1X7`2Zp<>rGW)1<^6kGT zGUzt&*vx1Dsr2&p0;-hOHxd9ahql9={dx>_v*FHbY&02rpYO4rw6!SNdNwSf`+nM_ zI=7&0Q0v@TUEukjxfpGYCd!)Kti*i{UZbsWG-~n>mJFYc=AC7Hc*f-VH8Kb~DJq?U z3j!*W=6U#0FI;e@Gjv_EzE}zzEHBg|)918Ymu^HW@Wmvv#K$n7%xP&g0SVFlnwjCx z3APbJtttZW!dnrG>P`a*GCoO`9|OBSP6Zp~+s=6eRif*TqV9ld>%Aj`(H7bQBa|aF z48ubnGT&adXtWn-eLprjzgKW+@sq>D~NApCt~q%if`&OsM| zv#EW3y3PRfG}}P*l2T!*EGZvBB)w>Ji2s?dZtmG`ea9C7LeQq!ya_Fx_T$7F#cjte zJULl(923zdYxlJHAd;!9r!)_fX&INS2=0T!PIlnu1f&Eh!0tsjaSiF-3s(yA6af%1 zP>*fd@tDMFRCjz2%25=)7jQ6bs*H>TK&in-^|t$!m&XqT+heJ+V7&PFFyN)35iA zIp@HF;Fq;y^&>)KJE-`5j0c*eXeQSyALfG>0f@_BdbG#3%dmL@BK+R~X=ZQn zn*lB-$%ilLIRS&cCC`92pN!RKjz0^QmD!GdK|)~h`|2eq02tnQPQfy!gMqi-R%6}u zaR3-iZV>(YBQyM6K7AtoV4e?Shr6TwQcVZR(@Q>nkDpfv&?sM~-b_pGnye&N6Pm6- zFeS;@h@cO1!p!4swX%jcX8PTa?uh?Nhk;plM$;EJdVjFHFggLMVK<$>5xpu`Ph3Cz zX{!a;_77|^&Y;hF%vfbvPBM4~P$R?l56rm$tWRW&PaFi)gNtzU@$vuAMH1t8ef;QD zXG1PvVLUOx3P7i50HGfOaYnXgX12D4B^%RS%jMP4k&jHk)9*lLW@fUejz+W3M3BiE z8-QwFqE`H%2BZ6i%*rhi{(|#lWmvyI5-N3YP?=`x9IT`UEzVvqUZSJr@4hrK##3Rs z1yJd&sfET0K-5lI#l-`2YApWn#oHTSuhSa@xS!D~DgB2JEH2I-AKtzD{0kM;%G&zT zc4HO2R#I2uUx_=Bg^djv#}}-Y&a6()rG=TLB`W|<6a`XGDfOY)YipGcwh#T$x#7C$ zWG#+6E)u=PhmJdF4U+!~ z?Z@7Ui3v?@ZL|L~?!Os{X#H2_2^5u}zzGFXIT^#|>8|oP>%sa$vFIrOR&*&0)L2fd zO&6FJh@}SscnV=F-`#m%`67^iW&0VSrKLqgMD)VzQKc(m-L9om_IUQlwn|-XKh(k^ zRLGN>nC#yH?)sLLmK-53F3!&bmEa0M!e@H#rmUsR zAiiP5(#@L{^z^>Qr`$8_)jz|3MrYK&r7ER6044pLoaPodMf&JRtDavh+9#{re?0pS zr|i!Vefy){HxW-G0}Zh=)lb;WV1`~kW%dIawzqf7%@Duq&`!^&(w)uO z$m(}lA8rv&;8BF1O>DKJ*h;F#P1W?zK9dB6bcESF3NgEkdf9|ex+9S*CZ2$hQ8Aa1wg}>@hB+Ws``_|=) zmzbd&ylw?Ja766dwHL&eS(!5f%Q zX?RF)#5Ee)DQfatW9wyo6}KBnxjur6%pTNj!bY~|ii5L~fsDWHeS`?$_qD;Pmb-a? z)xMTp>485AjE?QxEV?Rp2(_@9$5)p^^TcM*NiCMzk7hPwU#T>)RZL0XtU-^Enuu(Y;s5gO4vQv3d5^&9fJ9ayO<) z!3_>7ygOH0j1CtBY4?fAwsgjT#l2x;{jlSq=iNnNH2WpX-0jxxa5q}1HiIe$`RZ!; z!TRKm`oKY=jUdRj6CmFlzP5GV*(GD_U(7}Uj;IDuF|!-z-Dbb?=i7i zj}*bHJ~x?`v%WBxdh(~DLy3(-O8C#v$FI1s=dnzLO}#P?uJyiHkaMUS$!3X_+B_$^ zdaODV^pHSs~50N;v?w z3p1t$;W3&IZ%^Ze3ve)GcbsfPB|G_6`Pd$(MPSD-pE9vcfWS1arjzXK)Vlo=Xci4J zl8n@W9;6v~{kImt<00FKrW1BdiOt$o_ts!Zv+{!J#LbirTdC)0QPtsI$7BhI8&uNd z*vD;`SEH+|hkI-OX*-(76-N4l2rek7=}kSp@i^<8O;XAN8(W*yGuW)nO;Ge4sF9?= zsgH&_$t1vd!OFflRc7JyZ3MGz$)cMyK3A*L(Tfr%LmPn;J)ELL+sGOG)!kyylpro) zE}0KrX^w)n%O!X*=FY&Co-Nlpi}XE4OMc4%{Bho8MOuR2R!w;!rq^0ecaMBxHu7F( zXFJuq#rjVv*E$t9CY*JxaI~{+`$~Q`pY7z7ToG;z(%dmHa9%R-QEv|kVIVDE@trU! z?+mmmPB%%UBQSn-0R@2~8WWd~Avy)p_knyidL?1Fow5lo36;IgHV1TT+iSwq55^gH zXMZi_9?_T!RZG6p1Fvgl(v|swKvRuyF4I&=mGz)7L9z62rKF+z&9>7f*w{)l+*C8G zSnjMtp+w}?@Yw)N*+p{P=wwaf+i_KU!|-6{$`-h-W$Ki+KuhljuJ+DsL&;p{g^IP> zxCYf8xGe0)vsJi0)oRu`U1lWIwFl3rUFU$zy?(pCG}3Y*v}`1?^RR=z-=C6@dJfOD zMqY~B=}MvW!P1-t%8R<{@-8Rgd;y|DJVnn(hKp_gl3%OB`>eSBalda$HZ^b`io3Fo ztF$?^(pFeph$J%?7cy>J(U+BNS=83ekQo*i&tDz<@FGMdSoaAskJiha?^F0&1OS^ZAUZQ zKyPu`1$a@yvetZz=Qxua%rp{gxe0#;d{PUoHRY7UTA+vj6I#DH?r1ZLej=N$sLH2l}HbrQ$>Uqxpf zjkb_+Ro9#_$GzN{^5%5k;Qk*)=T<}`i+iSd&y_b6`CUWc+}0~r9)fWEXk-|zP=w~T zkYmQ!c5LI&o}$MXPw)Ef-*+J3KbC+t^KLx%nm6a#?voga{83`-cBw6T=Gy@m1%o7! zk@pfBed(_Wg|&2UdL%H==rC-?F3&EGQpoIS83Rmx@g%?;N9o{Bsg>j~so8wKb@8kmsxFw;aH{6}MK`BUv0SC0hWq5fvo zfNWg68oL8IJDqF)abh_GbR?9fr;Cu>baho}3_3)AJSfJT)8?DM+TeCQjpRbaT^@2% z>FMZeVa>XuffHTDXyQ9-pp}Z&upL6qVqnTPw_sdVKRoYnv>NZrcti=ZAi$pJDs|Nr zvbl8??=xCQYv8J;oXEXPK+jxGeK5i;?jp3ZW}6GYf>$I8GJe{hm_E;<7~u}3{iLe= zhecGNe%L3p69rpv(AF6JEf(3ZEe+`8=P$Br>VR_IVP)9@nQM~0Fzeq<=$xK|;_5Rb zAP=3@w$t2_OsW&E8OK@m|A)Q542vr4!bfooL`6YBKw1Y0>Fy92y1To(y9|(_1*99K zOB#j&q>-V!k?tB8dYH3$-{1TH{Qq*U^WmI*%?0x8y?y5CweGdny~kCcs^4C7-c2LRB5M*FANXykAx^gP^A|P*=MGQ}%Qd*;W=ZW>F+!d-eC?2my8hx72H93z8jQ_b2!pVc?~zv4 z9de8UrfgkoFoQLGMm3{Xm2WA!yl+PQnR~pD%2x-1>tuYoma{<(PE9+!tP4K? z8Gv8lh2fPi3^;Xgt%f)sd?q)EI1c690*)T7R!Y|RhbJVC6N40t`Q`7IVbxK?WxuTl zp%T7t3bb8}H|j@k;B z&A3AK#k<_io-@+tk+oExz=KGaP`y6*%8;!UZSg<2Nqra@!^N*jM{Lq5WS(Yk`=_+h zc_~OUX-9fe4D7%+9X%I=BRX6S0&#E}Arj$91{nsd%|Dy{r~HrO{&f;NH1AFZt@q$m#;PoQ$ie;QF~%UI)}^aR~+M5 z1Uy`b)R5DTV zh{OY!oadGoRFQ|ywk)GAXGPUdO4f5N)a6{{R8nKuPIf`LsxT8|L=N}Ub?l6`^O9dS z2w?!z$>| zTmS61*%C_u2k^7z>UxKBL&NywRk2P~pTk5^Tzo^=!qK|QzTlyn)Zo>bCuYdvrp3?- zM~1wKUL~<3jpZMc-4bd+#KjMHYkiAd?BR^`n}8L3Pgb43i3UFtf)-JLq{S=RQfn@k z?1-D-Vq(vdN8`|FTT%IO)=0?ql0w zDJw)QX%*KkCuHf2s<#((vDvF-V-STif=J6v74nRGNbWS17ntlVX}P{DKCiB-kKOb8 zsZmX1yGjHC71Xg-hRw0t9w7;aUjd>IRUN&?!hRf4tA8A%hyxkYU~`1*uyXf*Dyvr= z!&RyHrszoSJ^o-sM&4Og<%Rak=f5K;WMbqn<(0ZSVVnMTaEBVEV#DOS&kzVguOfC9 z+e8z5cD$Qc<{3|%o1t^H;E;5graEYLKKw3cMR8xnP#i>lXDWFW;(xMdJk2^wO^9;% z1ZbZ#xC1;Hgy!Ww*!0s2zYEp5yyH6IV=GRyVoLKb^3EexW1WatVI1!xb@9@*_=KdLzWqiHboFHHSE_c;zuSWQsN&y&bzKXvIb$E{#76_Y@26MS@&r^pL zaiU5ZZ)*2d-JZo-WCvWd$D|xsNT2x?BV|pO8J>dv z?P5KqD`b02W459^_M2K~^7f)Tf+-CT49=iIrjnCjU|7GmovH!hAIt`QtTy*q0ENE$ z222IZLc!%4G{8C~U%(f}c339AWN{sq#BcjkaCG~lfKP?09$qm{V+jq!D||ub9x~#KI1`tL|o5BCUM0cUQW_f zh!e>bp`9)9CE)#m)goh{RHFech~*P_k$E?;UexCdI7dd~i06Vj3q?6~ z4#p>8@f?-Jn?|eyZLMo0mbq(XKJxM#t$ZemGD{98w);dPP1jBFZf(msdn0l_a_9M* z2f~k#0fU)W_D7lZ_IDlcPunPi(hU{Ih2~ms@*SN2jo5r)&N*Wfu5@q+zKZdQ`8&$v zNsp(Zpg^ax)owt8MJU^(smKZub{+JGql64Y=L@Cf5G z(Sm^iApZs2EdKpDK^KTm!wo1x%hu{-o2Eu+d;$Wv7q>j%?|KVfp`1Z)pGO(3Ho=8YC z_!Llfnl0&z2g~+by)U3=(n}U8&Hm!tcir)YVb&>Vo0C1}QA0X{;SB9ny!yIYXGTD` z6FW?twd>qoB_*`mt#n<)d#(onILYYSpz}i^mJ(ky)g_q@_!Y1EPch8bUp{;}OO*T< z{QyL7J?FuAgv;Re>|K}YjbReFW8r|oK(@E}rp&@P`cFa^Hr3h0wQ& z4h^u~Jmj=`5+F=<%kDO`<$wR-AMhErB4NHQS|rTP>Ar3Mk@~)7h<9$s09lVkHkvd( z@{Ud^3;Qffh}FBw*~EYUTom!#acSK^lhPLt59!S9#*g@SdlY3p{zKpKA9pcXN{W0H zw5{adyC~kj^maQ#+{8Y#JgJoC>@px2h?;n`N;}RjB_sn%fX7X-#@)ZK7GC;Bc^mJ{1yppeIZ^RYvgD2Sc=R6O=_H5z zJ(w)UF^e()*y=_U2jsttl8e-v4PT5GcsC4{NV(V5-dei~5cq~EdiV8`@Fv`fKqyK7 za#cF-?~f5njQ(1bx2H}g`kLgk&mnVX&t|ot-m8#CkmxPT6J+y})x_j=K&Fc{(^>!Z z25d`jpvzz@CgW|P(t(9jnFk?E4?IyS71OnTcG{-@7L*lcCe_9rr^6k|$W8gdF9=4# zIV-ub^NUgOK|Oe*+>TJIbu)75r%^{1Joq9Y+i6ujY$*+PL>wd$TbvgrEh9@YQ?=!{ zRL-#3T_`82ZYCFzso59l+-)z3gWk09SiVW2ZNiV;At%n$NI}xs zWo8D=*!0r}E{wKBJQ#ITOA~P;M3o)azzK){O0KIk=iIFd@9vK^QQ>J!lri~=jC5YR z?$K8^-?XZERAX_w?@WB#hQ{vbD_x1HEw=dFkp9i@oh!L1T`k!xJ79Iwg^i;XD(chm zg$z$E)Vq}##Tk6gvtcU&9%K{H%ayC=pxi)B`S-I9C%KLh7LJt(FS0h0!ZKH(k-(iV zJRN;S^W=vaPOL&oLnUeANM6YI=v{7AO-b9|9QC<9CA|v7cGk7|&O4{4G)iF*D2Z+z zCqUS!yi7)|?}yxPMUPZb_?^36>ovhv<-wRi#3zm#$oYZJIsmGA|-=fWm#u+9<$JUFuvtH*i#-uMrK5HC=0eJ9LgNL(`b~9 z3pHqkJF}e5`AMyi+kxz2cM~f7c~FRFUMs`kU(KWLvIfk|TZ<*Bn_CKCDYdc6#6);g zpM~r4Pa>l2&I$BLU+p8r6MA3C!k>f3YqTL2ILV;hp@k)RELb3Vn}aQH#aY$ui^^8rSMAcQ!r75XXlJok8|j?X z;hPV+k!8E{dDqVcNQ6YuYKsya$wR@;4OvTX5w$i4@m}ZwYQn;iCY5pUFGS1-Ju70v%`%&N6x0(j*dXV(&s=}ssyx{vj;nf_ox2SjSmYn_^P z0Zbf1_x{xZer3KwWVw~QqsgA&o=>RTR#+jiJZ8J|vigtaFz_-I?pyw@qt!W=ubOE0 z;dsljMnzuCG+#kyPRw*;ZREhR>*>as(9a@a(8+E>i9fS?@tYyPu&bjJqqe$ywx9^v z4<6rBTG6XiLE)_l;+m5#mUKh|iCOZOx;+Nj{2X?wtR)Q@+Nc)R%4cQ|Q2}A}C*HPX zJYHv9WXY)G!iR`d#y9C^W+G5{XXn-M-wMCI&-C!t6G~)WwcU-Bh#^;TL&BX@TRtANyla)ZGOS8AC*_6_Tn;jFaj;coB zBGm7RcvuDf2V<*V!=EbdPKPO*G8d_bF{@0PcZ6z5$4x|4?6~5Z`hxo=?43QIi}JI7 z-Fb)XoJ^u9!Vaw#pR>Fh)kbWGZ10?64T8asgs;Erh5!0pt8cE? z@atBF2ya&RR#dL4oWf6~M;Pk;3npbtSAF0S6k8n~2L^8%G5_WxIX&)g2{jY-nQ4-v zQ){m%*Z1u$eG>v0e~H|8Oa*$(*5AaOpSynMIX)qsFSSw{4Wsf!?u*=H6pI+H9ZpW| z{x)VUF%)h_yY)->#V&sxlpm36$te0Lp)PAS;0V??g;87_FdJ*wq?<(;@^s(yYH=b6p>J} z`?{&P)V2%@A~s#aML%`|!Ah`S-)H9gUtj>EQlS{br`!oF=Da&rj$Jg!BGTq@28Xq8 z$=KOycH0n>+s*peL~tZp&93B%m?Ve=jOoTu-`_TCHoQhBX%JVB^FuW2Jz(BV0?3B| zF98bTuY^EMvN*+)8rb;QSoMQPSPar5qkBelbqwR!Mw6eeeP|l8s$xw53X$C%fJ4o{(tz)#bC;Nyy>T77Pr5R|IBjlHHsM;(C+Q(_|!!I}9<` z(@)jwG>cVR1lo5SSw|j5q};e!?LuEsOrSdY6KKHZw^!;EiW5MDq%9}!(e=OfM`~)f ze?m7WCnr6)wK#&_>>5!kPwM8&B6lCpwf?hk_m+}rJC3^(By)V#^!N;0@)#3iB*(pu z%_(qBKjIk~S2++%*>shXQf;L<5pg(~a#u2bQ zehi(Mxxcj0MXftKY6eLX5s@!uw~Ni~?d>fcEltOOIQ*d}Po9+6kLB!TW#}gCSd`kn zYiWs_uMz~DaMAbaeKy~~n`wxXN28R9r2#^0f`5%Yg|67aL#ylSUlbE_( z|2tj?1UL?-W^u#ijm@orPPH~Q9h>b+-l+a!DU)uOt^;%_h7F|kBO~DbAOZmG=eH@V z=D-_iS=_uLKBhB&f0s6;Jj^^cdmW7SxA zlM@SVhV9h3TIm!EMpPARh`-a{N910^V|Mu4HWKw2dD(eVxR$>8^p_R4WVx;m-0FNO zOUPMM-P5n3`s}fg1__vE^xl~E+2FlzH2&yYGMt(p5PA7X2dtSHoK-e#F?#l^uBQ!J zST8@;0ox8NGU^2o?X00B>V0fn8?cktwN3!EI<&bJQF1ymL`&PTp_H0>#{i8>Lth-s zdp#IS3UFCVh?)=0A z7@Q<0n{srA?nCb_uo4G}#9sX>4$J6p=X2mwD9f0|2_UFC>aXL#@|(XpdJ>I%!|(pP z{CAH)TJj4Dd%-EQ^;@q;6#EX9`i$ca{DKv3ZTj-^A~|~i3HxWmk@0OtArL}(bu`uq z#sYO-4GxpSk12WcS;L>$n72#ik~q+C(m?C}kN0mYJ}rXmdh)Knw?)W(Z*JuPs&O_M z>ee6a?aKM)a6c?wE@3dM+3=%>`yh1@<3St$vG-o7Q4xDRk$PTY`v+a@YqWQJh4W)J zS;9d*!5`-~`MQ?6X83?nM?TN&G}Z&T2AMT}YhlL298M$Q+c*GvX4*-UVQ5`HIIl0E z;H;mYO62`n`b-3lOq3h**9=a^Q`)%fGg@1XDz8)l^hXF}D0KHYNP1pdJ2|2?;WCRl z<74#cN{64xwc()FN31e!d@knmI8px!W^`2oK!3Av7Oka)fx-GHAhI=igwc7fmQ}O- zwL$AnY0)2>arLKvhC*>&&h(wos7biRfKhs-CU%(^pzjcjmJW$a5HFRs1XZ55ZsXHF zh}RC{;)VPFZVk9ZE0D)4uIVM+o*!-S5~I9#^*6tN5p3gQqQ1*5D73HT^Wf&iMqJQ> zu>ABccrWul_xMKocmMqYNj5Q-zezTA%*rCtw>h`#^_-#0Jk2Q)eOTgOK~v>yneUVv zZ&jV2$3*Cq>o(s?PQLB;UP5OOGPSflXrUEPG*hHy-vUC3N?~qwhCVhTSishaKUK87 zm3&pM?ZKiowSJqJcABO=(I0Gxb6#&IENSbibkNjuR=taXVMysq9PmuMQo%?!-;8`W&!;q@a5Q6-t7Ab-D{v=%K2)fs%R)h*4W+enr( z1sZhCKMx*|@jg*PY)gSF{bHImy~UAbTrnv(imQGBChrUCM@>o>!s9swtS|GQXbjht zklDtaDY&2le_j2bFTkCFI>JlkFHrhC$(jamfczXS-jMA%esDQ5`kZi+-BsD_(wuC(YhM^n>&|0xFQ}}fxiSjSgBU2OHfkvC^ zq`qZ7N9b=V6(;7VkWytkH_YonZQc*4#W1s;^6V%^qhCkCjIB4`4w*+kJ-KU%Ditc)b^e3Tg!m0Hs(BUa-!!KeL259}%c%pwP0R4mz^G{c8o29dHVHO-SQ^+4j;oc~vCz8VfE5FkdcR8-!&L9@1R|H(S$hPUY9V^*DQ+lcvu z-F}e=>K{BsiKJ3l&cZ~E4oNEcGMExww;1rvKPi?DAy^aGVnF+uaC5xG)lg&xy zWdHJ{kTH~#*vld|wk5?aPK%W>agp|$R2GzHAsLwFhJhtoZ}GyG@DPzWdK-zU15V6> z84F8JA15>ejxw#YyCW$Ay8q@VP~c3$*j}T=e%N;YwUL~S7Vt(I85s!!JQlL8c`kzpEEUDWi#nt9 zqeb!Va4}d;1`e3Nh@t$Oe5u&Z@T|PXu~zU5_oPHBn$`wqvoK(r6;5+Ou#rkr&WacHZke$85Kcct6*>tp#h%3%qM2%_fr<(GJoD{c39r2 z1ZGjiV#u3@34Jw89avV`+|#&9BO@t36NjR(a2iwZ;LIQp^hcoQQ?JkNqy%O>foqp4 z0iChy?jWdZxx8th17@i<&FO0h!=T#Ksq(>TUm@F<@GV_ZY%9%cTGBoCxD-Q)pVeta ztLwwvVlJSDpmf$clfx*O5aHc0L>YBHkuLU#4^@7Bd(@zBf<5!gg)+X!hpp1i_6U8{ zuyCgtFmb74lJ-n(;qmg<4=vxh+c#0av%B)Pwr>-WT-|#T>4)Oo*y0I6PaWWZF^vmM zmB)(z*qDx!15BJb(5H;=i}ws__^XvpSXG*AEs-x0hU-)!5$eyP8UlQZj9c&B+OqA)`8+biJ~*Q5zrs^T7$XdgazYKX;X3vaeX9u1^Tl z6kEb=YQ{&M=1a+zF1etU=BC1Xu`7l5i(eFGS`=WijIHyGyzj{<>iZSg+(Su-tf>mD zX+P#~N!S5`NiLBrzsvV$f1bSj$5y4OaDe(rU{w?X+)#~^=w5POvCwo8;2*XLPl|dz z&8GI{pv_29>6yGjG@tK#Ws{d)%FkI|G@)P3WpDz9%Fw&$irF)b*Rk5!8pm5y3=TG& z=Idh@PZA@yEL zxnyrTYJX1Kl0QB$&@V<7^U&!z0^Kcu z$tAG4Z#5pw-y5)5uW&-CArg~Uh{^o4UhY?RBb>cok*gSx=6Fch5|KICT7`NCs1(PH$A!9OzV z$PZif+bT~(`IEU#;M8k6Yhd`m?1uy#u9d&$Vv8?{KE9fOjX%KnSo0z^)AL_(i;*!u zv0Dmlk{D0=QlfBJ5g;S^V#YuWU0g@jArBQwE1j&Z?d?xDEei@ZWwTDXLW`|7uf|mU zO=U1k&!oGTY_dkqv$Qi-6r{`D1`#Rw(f$NG{eUhdinzOOZVa}`wYT-X4v+5y6u*SK zhdG9PRJIL+%n{vwE7GplEpu25w1>d?XHERXfuG7Yi6bgR5dk<~v+-~Y3~HD``T;^H zynvlbDdbB)E?+KzcKZLh*qv=@(=bZXO8*)3tlZ84=g&;g$D?C+(tz0hEnO|E`n4sn zz*$l2%7rLvO7a`8qVN((n#emJgbi~Pn~i~zJ?9$^RHx!bZPopXzOAjlrT3GBZ%mgdFt zP4m?N^&fYL&l7JG6A+jQ`M14yt^gdsF@BDXlWg7p1CZL;{+DBF=6`7mKEM3GrO5c# zEINeY1_3r>8MF2XDzbuReTRs|&OZxUv}^@oVlnnvRT(y0ILCGbOelcEXfy)>ynUixr^aH*ck;yB}aQO+>ZU&JU>S-DJn-C6c&^PNN-T|fXee%hm^LgOF>S` z$!X%2ePT(T-xQ)OKIL!B)rb3MtRp@1DuEFJvU*MJvKX=fzXNB#+daZ?cfb5?Mt}FT zS<@{ea+YZ*q9a-MXmP~j9mYp9w)T5|xZe*baON=*o*aMuKkRD&-(%YL?jO|CjJFTw z{u_9MalCMzCTzwihEp7i`lsJ2V0AX{s1CaCks$243tH!dMSU&osNmDZqV@Px5Az-)D6mP=vIRJJscshzVUVOZ$XL1?2<{sHsd@I13(4r*uxfR)`N8t zE2&Umn>BJuLVbI;)u~U>__gh~kvYwWIf^^+)+t#Q=Zp0o2f8Ec0Nzh0qIX=@pB0op zS8gw7G*8;`I&oP^}QF=@sGo z{X}1ox9i0K9f6V8Kd6ASaDmgM!h4}o5j3)G$eT0 z{LTiwj`Rh&+f@rGevD!CbU!1+ERL%I{LOA}NErF@Wn~kNpwF(o<`AZ(iRMs9eBqFQ zz>8uh>lvt9O#OKV#b1wHdP zl}|3|7n?2JK|3UDDfu0{tiZI8ee^s0(u-Uq8*S;oJK(y4@h_xPF1g!-gyj}nzGwo0 z!?Cs);#4KfL3XM=~>`6G}9% zaY_!4FJ{4tOJ4$@3Yk2SMYj`TVOu$HcCo!fMQu{T>-v`N&f5UAlOj`mHVaMHmeKNE zVA84QKP0&nHxTSA}``ARJV;FbU2u&F8TdY&&O`^HfYG&+S~sw$%wb&3Z~>F zXK9kPlt?!1-?~1ok+)3Bjn3kHy`_xThO~0M^?xWebe+4nB1-fM4~YZO<|t;xTNG|O zkxIRtt6a8Hk5!W%5H&zwudE%VoWDKcI`T`9;G7o->AD?H$XZ#9Cu^Wvv@jG5>GBRc zlHkgBe%AzR*!-py264=qt>>vreXmxpvQBJ-uQJglN-c&_PrJKUN5)&JOUWSoWWaJM zC(Z{93x1=c^X$3itxIEeEk}vOZMJ_)NnqZOjZD;>*K5F-v>x2VBzwA~qWy2k=+8V= zV_mxR#t29-&TpZOZO*_)_JLtXHGMNhNa$i1M6*ge`JPNH`ekywuZESwS=a5B$J0D*k>tGK3g3e5h@UH7B#WT6);2>-ibVck7g3q_T{}&5XkkKtHwDxeTO#uA{glWR9wh*C6!3@U3)myu{s?}zSMMz%|!ez z<3XwE-E54^sX-#PXnK)i_+9)g54w5Mua$A`s+CjpZ1?7HXk!TMej~4$ko!|q^NMAO zzZF+y^zhh$jyh33w@kRWMcg#85ny7FIG~E30^NOMkbT64c^zd`U2}v>g+8Afy0~&M zt1gi^%RbCpXl(fmn+te_F%oMoaX%dIhy($D0!h1oHbs_t-R01`6M$x^X6jKNfvlkBVBfvwRWbN116C2|??UYqvld^M2HvB9De zX;k(W6vHbul)-H=-#&j+P1mzq%brT!mkpH>rb-s!cPv`GVaWTrlTx&!W#BNi(dS`9qJIR=> zbW#~~qSYwx=uqWC!Rn3LwPW#e(v)%76PShXDk1^claYF-LJjg(WQTWYTtiG2@tkBi z6aqtu-g{*DQNlMl&e7Hkas9~V9 zujPH0;U&U?K(5Bnh9I}=k-q%rspXdjkDFLJn>f$q&_gw&q&~A>0FpRU> zg9wQ4#i0ziVk z;S&^q)K8x6I9$c7RBT#W_BWjAUpI}1A$vI(H)R?=BkOnPQ}Z>9t^op!hsgRp#<*R@ zsnqQkVj~+|vL}bvFO<308`G+IU`Q{ivL|>*q&|h1U9J{g*qMa-^q&XmfbUGu3$OU; zS{nNY^EaI<$23&+7E~{GY@6I_WRaa%pNW-b^G$m{xC;8JS=_!5H6a!=>|m-S`T6ed zS$B$C_K0TfR?e70*%rl6#t$|-_J9n&-c))z`5r~$1c_g-IqVE9y9+6j=^BE|NIQK{ z;z>H_4}8wi7ip^@)gD{Ulm^mCDDJrtrAN726Ao?)0^;CK$$gop+59r-7O=SJOKnp< z@|EjeLJ)$=%buAAdki}%W|`Y>xDfYKYuqX3qI+n=w=36y()H8Bpe-T;>+}6EGZ~NZ zhsm3j(B;4_yjj7Q)F3}Wx&Dd)#vS+gKc6|RO=fmmZ%?>`~?qqraBkZ z$u9-CT(M8v*IJoOtw{m~{llGm6kKE()?x)i``m`?19fqOvO;?0!uas{e@XZNdDXVY z*b0>{_5OB&b#fmhD}G`H*=nV~)$*9600m}yH0rronLOK!MX1&{Zn+6-FmO*!>2AFCdR&4!!i@YsXi`RTn5u^2GX zjR!pQSm)Ap_?;BVwi-stmP=T!CVT?7Ipi9HpS4s>p9GddCo0OAg?l{)4HCMXmYU9_ zhkJjm2xEyD zo6DF5R^UU>aFzt%w=1ML@EG74JRxhiSn(u!EpkiNsCk$9j(Kiap+O zDQ@1+LMVu%R;i{(#uMx56Q7j5wSu*CFd!FCno2gUKjYC7eUSq%6I9p`JczhTtVmjS$Id}q1PZ!U$Y z5dBU6w?q|Qe!Sl4&r|~a)Bkafp-zRDl%fOb0+){_iK1)jspk5q@Oyk%*}40Bt2mFq z16!qLV+=R2yTDJ9XV#V*!Q3uxp*ma%XqXSuaY=6(42?KgjmWPcQqO4d2=I)m51J$S zNVV-ZueF`~ig`1W;;(^pGq$U67{s+hKe|Cw&ZMh5a2qi0cWzY&(d72AwnkoGSmC=l z^>tU}(Hn)*-!=bjK5g*sm*0AgxXz*qMfU|!P}_O7YPCEJTpDu6t3@DPbJCuB?ca(G zG&gqwFtIEc901ddDrC@5<=`Ygr%)zrxi+(GXFNKzR~r@wFkTM5KfyWEyL}cVngt`J zl}-NWJmVqpl*!dwvEb=c>&(v1xF0fxZ)p~m9qG#)yplb_R#?pJZ@UN0=> zeuq6S*eDm~w|+_Rxty}eS(z6HuNV(VSD~P}nYWEA^+NbAv`n(3f?N}PVFkC$H~@aywq57ZUR zi{qERgMVhm@~?6y?WmzN{ZBUTbve=Ivab#GuQ#1k;(D&Wy|Dt#ecQdj)2?OF7I;}P5Fh@4{~cqn}>#BF_@LDx8C5)}9}2=!y) zf?J*!_n4z9vw>zmIfkmiI5ZNkn^F!$feQW>UOPB~Y(BwsKs;PaxG<`vGSPT154iy zVp|>L;}-VTnCyDz_evMoD1WvM)?~g0VKPejl1_e2?)3lze#Uh@=k+7Xh3R;*N8&e32rEnBp3Z2C9fJk^vx9i!5h2}xGh}8))yDu zEJrA{8^MPd0UaqB+H5R=->If08m+C(=N{nT#Uf#yf(Qd5mz2YxXcLl9i@~e)m7^MG zJl+g{&kaX}vIBv{0x#kIQ@4{fgjGvcZSz}M&Sh5{MR7N?bylMW3wSE}`f&J~>#aag z{Xe_bd+8DEzES$i!LugmI7w7Cvyl#1=F*5GMs)plpaWHF$5WFI zan(97%KMTD@TH8g{kTk_9)zm84h{{~za$&NHoQ&U`~BzhUqI+q$XKP=7S*Z290&4E z-h0?Jm~qWgwI z&qX>67kABOnmOScKI}rJfr#m`b6iC%jB+6LiiYqB0K6h=58%~Wm2euZ7)m9YT#**P zJt!Z=w}X#q2kd+92H`l7HVdjB$M-lCD7B^QtaRVeWG_u)!>LE+qz7S`l!*Gz6&hz@`@6cj*Nfc~tN z09b?auCJ4hJEO0j?wQ)&U&hASd?!GH%xFUqpI`KO`*#YS;nQNX$TzRn_czQ0d#bfq zsi}oCa!4_xQ9w1+*PvXh;Y{Ix>z^3`9$>(25pYVhMn1&Y9swXr0C2zEpM`TnffSgE z83G=vBfB&?#l^)dCu>Q+W0jd{fvcxyb8}PnkuJsvvqv6lVygl>7Z=zX0LWSEfmc$J zleeL-uZOzsuVARxw1*hr{D{$Zai7xxFeLmx=Cqhi^5I#MJ-;x3tbd!b(gXYw+BM#?mwo2) za*)X9sH)05reR2yTI>4ZA%bZ$AG!Za4NrfO2y0GZZ*=-JF>&6@_JttH`R{F8d(*Om z#d`~aySq|a=9F+rrQcbRkr7<+$rGl-Chx^*esa6LGP9lac_4M2lTvFI!;Y$`>(j!) zkmaJE+BqFBMIIFP*0D3$L^X~PJ0}SS|LDLd^Iq&qDWoMAYVIg@kZ}0+^T&qH?I)tj zG(NYLCqXc*RL5|wL$99G~O^(=)4+??Re5mrxgszjN zAfpa?@7em9I7UM26lhBfDP>K4Z--fmPz;9X7Q{KGU#&M>PwTZr%*J&YqpZ;%5085Q z?;e>;O!>C0Ouy-|@`MWOPLnGCX;SDGpiMs~E(l!zgQBNFQe##0{bt`}KBeK zRXn(1D0U6{>`T^fYSo*AfaR`w5YR-k1UvvHnEjdd;Xq45*F3M<>Ty2qkJrTF8~yKH zU26)<%;WMCRg`mu*Fs9*&T(OMKI^`R3m-^#>{&uSa!rH3I%X28DsRwZg(<0rJKtG%z zR)fR)u5_#uMXciX_>oxiUfVhbe#Z#~8iV0rW^X#M^vgm%xm{@a=83neqG`#D@9-P3 zU+c~8t##gO$wE^#UMG(T3lre4y$)(JsTj!J5-4jb$2e&n@;T8h%b8JQ8?pK2WJD4% zFNaqK40ffu45}t-I5<^a;pCIAu_me6reGDp-jrxq0#kpWg-dX!&mKqP(16K)L9V}= zl6afR4tvBe`M>GE-*>z?`iN_i#rwUkCF43}uDM8vDum(8h;BEuy31 zW|MQ*z~MjHsB`uXBIct%Q_)E~VY5>Nh5cFEpEe&buzqJq(1AIwBF37q?mIVwdWhqt zHL#jfIJw)Y;;B59nFjgpv+iJAm(mN+rg_hDQk;jeEJ7>#{5G%`dO%i*K5fN^IYv~h z`YT-u3jGLe(!zLBW6O_tDJvsRN+KbRPn<6Znw*xtrQixO)c%Ld47UF~#sZN%s#YFE zCM;8T7(%+WZ^}Arz$VO|wr`LSLhi7n{Pk$;>Su~J_(QMjV*-OBakcJjgwV-}abQjj ze{Ks=&ZAHNN&Ipg$EYY|@L@7#P|&~P@|J+KE6Z4U$@b{DX-a3MeEd2;m}$5=lY+%= z=vhT6vqoYyd%L23;L4;z)}B37?AyetLrr-eBDIT zYvkeoadFQbGdtJtWr_Gxc9`w*=D%mjpCtTM(o}`?CahsoX+rA<|Hrr~(k6VBJWNrG z^?&{^jQ@X4wfy&W*H%C`4FPEt$|BMG0|~FZ!KLBh5=w0HOl=#k{gC{W97BNNKi?_! zr}MG2=MZKMi$P)_IfpeyD5PR}-2pOtomD;xWzp^Z;e6be{oqkLMW}QoG_YoM`=oX^ z)vp~$qta%KfpJjiC5&4G`tD}Xg|^(^Qn9#=)Hx9xfEp74|P1j$m z{w%>H8jKH#E0sLxz%Ewiv|TyCO|LQvxm}+&^n9;%7S|VT-?9X9<>RlILNv<$pCu63PfE z&UALAaP+f0BuEEq=zW={C%^1L-)iK|?#2S!^ZiCYWuAC%jS-a{HIJrh6Lp#LP01(ZP`cMF_ONL}5U{z_3hi|KUH zivc(ZA-XD}00F<=&;lX+?Ox-Bz(hkK)qzbT&03W=Z z?yOC7X%J!LoMDRH9GD@7bLp6h#E<4$KgTqPI-B^I&*4xR5dRWa)%Jr0Tc~)(gEe)A z1(_mG%eR;_etPPQrnX^ZKO#jlOcyNaVn&1%JJ-A!r5(w_@?$Wq7n&9s4?(X`E zJdP)glgNShgO1!!7!>F#@v-H}OQOGy1Qek~oQcV|cWL*L?{KwANiQJ5f=+Hr2+>oUiZK;p4cwVVLUgIM zm0#9B_{6lz`&-qE;c*%G3vwazVGos;R zZP~|KtGIsny(pr(V_=^heY_1)S3W3ObxaL5a4yIRO&W(xGOA_}ALQLQ)eKh3iS zG&*2lb#G_n84%D@2Z>P8cGJ;Le?`0*TB@*lFPXqytu3e4@~$BG=||K8y^eTXRdQ*T z@h*pH!G^QkoO$E8zEbsDrQ%Wayk)yj{NHf4RCi%=O4x6@NNgBhZNF!()j3+^6~cZ? z%803ud%$VuGMx`*ot@{577^F0?dtLkPA`mBC})4E)Y)Yb7KW6oA(8*F!D=Htc})y1 zv8R7sv2zkslv6&p2As`+F9e$8D_X@yCaqn*o<2CRwBt%lc0PBUUD$PTC^{nGO$D>4 ztQ;rK>DS(d;L<}&4HS9{?fq_FcRd%rK)CT}`ok24qBws!!m0esD+&%WLr4VWr7i<9 z9ZowLn;w^DUHci^8?o|#zFVu&h_+e*1ZW!*jhd@roudEsQ-#zB<9+?l?a_t0II44_ zaiz&CsL6QA+b4+0QdHU+r4Wn!Pmj0=^OYi+A- zk-&Pk|AaaGI55>RB8>0-sE2ZNr1bS=v+nzDf_Ucjd0nb4q@GbCX+m_@hoefnnzE2~ z+tB4|zm}m5@tn8dlqR~})9eMWpP!OBD|r>Dvv*%Bertef?AOy3Zw}2~ALD5wHq${K za8qgjp66eh045%+qayKrk$#6u+-HI^`9^+4P2J#(ng9Jxjmn z#;JdA;BipJ9I=B0wxvgUAIU6v(TGHgiQ0p>hLl2)RY$K_`tHtm_h6e6EFbt!SIX^& z`ku(UimP0?5oAmwCdOvn{+AnUOy4;!sdnDqFzA1aj_CVT%dO(1U{~~U@3PF{8WD1j zbxVVzOTmmzl3A!G0Xj((=SHM8f7tB?CXkdML9#DbdzXb)3s*%5&H{CJ1Fu2B_GQ+odQyLW>ExtZ#@uP+aXKKu@@t_0&vJb`0ArUM6X+jbnEEpD9qLzKVKudsAq zEj-p!aKh&@S=qB&8p`M-4CQz&*?29RA8ZFnRJiuXm*zPKH{?Dy*3JD{$A4Yc_;$t` z7jIBj#iW8LSWS?;q2~vKbaEnal?T`dUB+2rW)v-gH;}Il@hYBWii>7`;wNPmj42a&%lU;j)C_SWb(BTvmSuwbKc%h!)bKfb=Lb?Pa-7C6IWN*E?BWB{3 zlo~I2St13JuOsf!jIR`y$~U@>XmTYsr;l)IJXORLqMSt-N^w!Fi|i3`yfb)v+OxDD zflkd)@-I9kN08DouG@#9C2wb~va9)+pdnG$t<=OMzWRT~GN0_X-D|Q9Sud4_FWb%ae|SE$x&y_AR_*j@jbS*>pD#7CP#Ud*zIG zMQ9GX%!)5W1qjLz6s?uA&10{%t5XRUxg>oY<_Rqy#SNbVCgl^CaF47vEetNZfly5D z6!IBFOtN<~M_LC0NzugBi#HEQMKU*~eoj#K?a$~y5ZkWZT6Wz*W@O3+g5UFCV*;hk zTEi?y;vQQZ4~NypSG=&o8qc9W)NpUkOV$SETwm;3(sWR6p`fBzKK`qzxi~>-qrxDn zHDrsvHHuoi4}&ndyB>?PR7wb{6PjE-nY+s`nWAT` zfSPW*Za~YKeM@KV?~VWlaKC;XM7sOZGpt2WJ{43|6bJ9$L&)=F`$O01F$UcL4UCO? z-fN4fzBsV}yPJ!a2K(&~vDV6sO#+(8nSP4kf_UEom5SbL2<6VHC_|j&Zy{s)jR<4- zO>2LEi7V0;)%lqyx4TnVGiOuKS7_oS7f_hll;`cgo@Sa;>uV79dYOD=h+oW({w+!5 zn^=pu1rq%w9T7tT&_fejr!!+#5}1Dci>07(+fmWwp5}uF&m94Ov?8n5(j?g-c!rK7 zhA>+{zrj8zzh(Kt9CzD7$m1}fHYm2!g6JzqzKr==X(p++n1xq=kAjKn_0&Y{WlRhj zj6R)e4;rDz+1vw)q}26&Ka8!PaO7Vd3OON34nBP z#b`-@U+2gtV6HbPv8e5Y?E?i~)O~qv2vQXN0f1cQ=E4Ub9ik-|#O9R1*~_LjYD&Th zR3#wu4R*Uzv{)gTX{Lmc&}BR~&EIU~2W*6od~xh_Qu!F`^;%6oAA1ptgY91M#DchL zkQY|SX@e@%GR+o|>5NPXsC(P~Ecr=UZ07yg3^J76N0GM=eEqyRMS`_hzaueGVpS49 zv+&^5NDUNo$e@J3dZ9*KLsOwPq;YHnZgfj^=S86nZR5jF^LukH7^M7?dSMVNYqbd^ za>(R+i@4wDwcEWcnnFB-%&ckFw?)2YR*2Z6z)14OzKj8d#~~_KGd_{M}2P*57>2@*+1T93zye0A#j*{LgUcjfwXHI%b5ey)ii+p0C=*FnZ zQ^+OP2(M4+7kl*+uzkq$8yKY9GLsQR1#`jN_V{^B%;o;Fp&O1;7Zgh{L%sd&nXBz% zF{yG1*OUNSRb3sX6`ZJrB`%6!ZU3e>x%;TMPUge0l~VQz_Y15|L_r3hd3MCHqVky2 zND=Bl!ajSXoDg!a)J&5Dp{2%8=33xCy{^7zYc59wK1?J@+V#RcD%^_sxR+EfYC$5 zwjS#d&NR;CwHJv3V}qP}2ix4_RF%_FqHK)TOFrj8Kp=T=!Sq~9lN{}FZ}0XxJbfl{ zd-g&kGyArrdF_d8oWg`TLwNF727SVo>gJ0;4a-DcEEuGslKNU3Neoo|4fW3ly3|}V zzFrD%Rc`3chg6#^G)Yh|+$gO+#85AQ68pN!+qk_GB>au0N2kM4*H2GypEiCcW8Yp$ z%Qp(EB%D`5%bibn41TIv@t{XFvYD)xOgpu)R4hp90G_(#qS*trek;r%zoy(Zv+nbw z-H=_Z%Fdu%)=tOqs7F$9s)pyv%MshsnC3CATJ0WcHZ6Olwe`B|REwFP;!?GhJ7AB* zI}Bas>C00@McT_BldfIiHne^`Y_~niYwHHmE@3%QrM1escQEuaRH!ZFel58GL@ENd zym%gA0Ek#S-@mG;Xh()L%Ey$_r)caOd+1W%R1=Cj>$1&AD`Yec&Uj>AJdReJkD6Vz z^0u=ilE=o#k+;kd=TPVa7@`~#H+{`l;N$>sq`dUViCheTfN@3G){hhS$s{BwW3*xJ zmwawX?pB0EK0_obLsGKLRby&!z?K5DeGhP@g)It}2OB61^~L@w`rE%f<{=`mr)CH=1I*zwfu+G%ax03WaKjxK8=txxTy{acxt3lt-7)7sHSZ z1ks%=srJn$@8s_cVrXXa35AJ2QkOUABM(9LA8jEQ?_j(&6oEV04o7%Pk>rCU!2O}a>v#H(fBVun>P;jZzxGlgE=``>~g7dR!c<0 zNz!0eI6j4ikmt#p7p+JUbj{Q*r7jJHKgIs~vz1H=9vHw_-*4vZc*>iRfsQBpNd1~&D)2vmj^d8W9|?dHO?wuUbO+9qA}4*5RX`Zig= zR5OkCt&Y2JyBO7 ztH?VLqn-B~b##9udu$sN*!#(Gv zt2c*NC40>lG-^tMY03ISL5mj9rTrMUNW&UCo1brC%)|d8$esb$R^J|ITVjIp7t94} z+Qy<*qpvEfUG_0OMVFfyp0Un7{fxV2m~^?|rlX=f9&=z(9PnvvApXyyjWXCDf76J- zz{Gm)|Kn6!(f$<<1Ec)1;%^x+FyddHM+*38BXs}GmC>rLua-vC^-(exg)59Gf+!TD7{^kI%`2CiJ z*NDmVnec((op^vp0sdnLsAsjAfTM&s9LqycB!h$JRVwLu4Ysu_BVS+9jVj!9V%KY~ z#d}aY=xor+Vc2ZE`VLe}3G%(>!b#sJr%OT2#ohMCL458=d;?Ew{j|9-BhigVUuTf1 z)Vi;E%C%w+A*ri$_mtWUhZg<~7k%u@&xJ2I{~A1W$W3ci+g1zjm#RIppReWD(n9a)%FWQ7S^Rkjiuq~ZZLr^kc?NHJ_@=evN=#^4_ySdCtmB( znvf7zT#VRwc(kbg5zstW9=13=qNuZhajSBjVsplnNNJtqH>{8&Pz*87 z1Sl<+WbBys>QZr2Rv#W)gfM#!C>eKAFk8VN|MliZFFXB&Yx<m zU_8nHwE|dz^8fSkh54TggGW3c5hJUAIPT=VyF)pk9VP&3;wiZ|m1n=*iWSxcJdz(_ zv~>-Jnh)8lqbFuLxz2S^a$kgQ9s;{4!?bQLm{t@$R1SqeA$-M??#S^4u*0_T4^LYa zs!-yvX(W$#1%DacuW%frB8Ir*OC=J%PJ%GJfligBMYV)nT+>L0mXlJ?&3FD7C)~Zb zC?)$;27UDcCW_JW(!Qw8z57e$<@4cTy?W%#2JU#LH^2?9p0eqMPG97wl?<3Pir zd$lckZ2tE7%>TKwv;*?!09gf$r@66tPA-;-MMkWmiSfaqQEUJHLHA-P{(jF$>b_N8 z2IoI}=E(+uj9-T*mJPdE{$8IRpc@xipZ2@G`ig8o|IZ+PDXI54WVLsAKMYAElpPbq zT^2#E6Y6{ z3w29#cS|O$-sAV*4`+Y2mGLl@bv&Mml62!OnBpH3gIPB}gU3hh?o>5qcEod99?#Ck z_m4s`QmCkJ4}z7?)y7iPl*HZZHPw&1F8aWbYu@L&4=WJlX)+mG?dNDPMO~>AjqMl= zV9JJR??!pA&;Kg6*xy_6=C0n7UTm_x$`EWBA8N4z>s0f(T8V@6P8J`SJ4jBfPWBUY z!cYk9wo3MA>#46!evWq)Km~!!ATVV*1f|8Tx?2e6W+h<(kfgH59(`JLxCt3yf?q#= znDPcANJ()6{aqVIg||OYY`R}`(Uwx`V7a@eeZ)9Q>NH2mzL)*5FRJdkxtMkla&`D! zd~6FR&3xukRPW6DZUBWWvgmc!MF1UtoFh&x~_mC%ccI*sN;dn zNLCoU&j@b3t@B4g@y^sH}=q(cc^N(*8( z7*$o>+7@hei`-fckpWQ?Oe+PBl{o~TqVTyPpNo!e^&A3>Mrnr{)9)C|Srb6f@-^<6 za}aV2bTwggsx$Q^Z!c7a?(8%1kZ&SM<|w)vHPfB2uOi00$o zu)hLk+H~PAkgSr5#|_eOyV~)VwxI+O(+?2`KRA=bNu~OEwBU~?3R^>_ z9&d2V<-TM`a-*^;1fMrnXYN`Bz)iL@#};0c$qQAg#~wjTQ|tfP zK6wy{6bnCON7lT1@1)W}JEZ3Icz}}q=Peh1DR%d97>vY_ssW+qb(7;$^PerTULqbl zeBQ~p0m2ooUBlS?8k+TMxsnuYd59388mg-ditI3piXJ%~ldT+#WYJk&;TZ zN^qvHP#c17p5F{?JDPu*N>K=B>#XJ4P+mhIEX=fa2Fm$BGIrqS5#(tZy1##jT((uo zeu!t*l|ADf;LqWGbH1Dzn?r!VWflJ-*7q$gM;y+RPWhCS8FdV54=%Mjkyalu2`=jT z@2Rn{{T5*p_!0%8%F*In2sXsJH@mG|fAIOML*Q6c-Ux@a)n^o1sh626QPzaA$Eey8zeecc{ zHZv%od~|Min#IimQc~XPewbKsgnbVMHJ4$ihol`shFd-Z_1{$|hmV))>Woh6m$nz( zT$uLaw+rmu=kpeM^#gOs?zA@eE_N4jJMOd=H~2QX+u zQvRjzL+k43{z{Y`yJsVC)5YOOq^act>*^5v)j={m!QDeAa0^q;ifm(dyaVme=|Jx;bAi< z;$Ls3$393yoO&7T=7bQ1=3Xr2pI0FL8VtP44y1O@nuP%PM3 zEUP1<_VpB9USct#j!fKhbr?yvAe01VkXbi1hF?tMPeIjA@wmr=L%WiAx7!ZYv3w&%ZA(F7Bcb}V zjwrFBb}^V1r;jF?c)^i&2!ydu`jzlZJ}lPhy7cN#`bW^1PDL&v(nto~jA~Ly(+9ko zpCKI13L3Vd=DMpR`FhtUh0Apm1d#KPCdralH(0oLyPwFALc5zKx=N}ig3&MCo5c9w zcmy&&?kzuEOWi+SmzzeK(n0LbjRrSEh*%$0j`mlXj6BC4d|fo9`J5hIXR-t}2yup6 zd{jNS>~H7xkMPv?Df#i{t*$g7>!;|1gBdyuwvUu`f12H$;-Eb2WIi}Xg)@C9ck8+$ zyOu=Kg}Z#db7|iDJ3%L- z(qKSd5$bfd>9WTk&*Nm%zREt4l>a;yzc4(a=i~-j6V@hhD*+>Gm&kk*$C42o4@$xC zE9MdR9gpzH-LHv~h8BgnZ)Hd8ew|( z2Jn)T@#(N52B_%i*&0u`N3tNg`=&E;AT}uK$mDPUFgv11a*UIb_%`%L%E3uh_7B#B zg}uY(UQK;@&6?!b>#Ok-U3`-f;BJj+ED8xun%Vwe=k(I73brl>NlV{KoOVAPv4?zf zFIdIrhE9a|19dRzSlrsciKggzR1&Op%6+`NkS8gesjU@j`q5UbBpTtVF-?)KXDHOK z;zsSTUuZUBzz!VNo`mLMFn*BD^Im?Du0%t{^JkU(^Heu^o<123HSrR1iB(QlSoh{9 z+0sajkzQln{tc!WjMNpl-WD}O+r0120W9m};JG)Lb5X#YK`mGCgUG*y3fR#G<+|q2 z_yZ$-zI%Lq9ycw~Z|F}MrnQ?8jzup02fT4h@#`AS2ArcG)0o|jAL>sz5TGk=yH=^( zH_CR6CKDiHYmsHZy$?n3k2GaW6Q2o zuyHZAC|n$~Aiw4QZnzcdnc>did>33~6X8~*Eg~Y)B+*CRiJF$g`ZRvi=Kv&5K5GdX zl=9BfRI{tDI30YTFl2N2+?R7HV#iHV+qHOLeCP3B_w#L<<6}e`Lu3MQJIJaRUPra%bZ$IXpL4O24N3Ub!lM4grql_UI}q4hSgv96go_0 zuZL<>3o6NGvw#(9T&qmP_IDX$V&E40SvFXO`sAG2usamEyzjeef{3XC@4F7Fr}D;= zIVMYfbWwBN>V=SeaB7u{>k1)2ul44nv=}oif;A8Ug7B1VS)B42vrJ(8@!nDn8AIR2 z65NEVbP4Fu{faCm-M8B44nJbr^eq}cM{$}kHYNwPU;YSq3uHHAwJ42K?4@{J7?xyA z$8xG|vz9eVB{mnHB7Ytf%&b)2=721H^8h^(53B+bD@GQ}ih-I}Y?W6tBWnm=t?%SZ z^d`U6Dlu>`%t-;KRA{N@Zp%~O3U#XFif~Rd2n}Y8k>{#ety&0rs|Ivgg#`w_B~AR) z!!=6(`@NPMW925TUT6w8O>)QEupgoE2iytdG1SO?fL-0@K2ZEU7z(#cGDnN+VEzM3 ziZ;o^V!7>VW|OF!MPKB);4^2@hr73_12mAx$4Ao@wR;B&{x)ab-bzFRx?=OjB?)kg zl18(NN9D@nLq-@1uDcT3uIORTE`QDA*&Y>hu8X7dcRe>P7wB5vkjAG{$Cdgmq}_h= z+hWEvGj8Wh11q!wu^X+UB8lsWok zLt(C%@RUWVWe_(P4X1;IdMJ)kw{|XT@6xaSQ90XphS|GwVkbh?XfQB+x=y1fNFyz< zop}s;ar_5eLBMS;PWklPtO^dWSV>l4-=mhhy;2c=DlP zLAWCh+7E9>p?dtYP1;M`j663oQIM(~d;+*6K>P)`Bl2{1U@)JIl>jXoo$_Zbr>m(i zl$2R&NVw(6=0jZ5QDy&mo1ISvF(;>X6Vg~^ z;J#^h)~gaF#b7)(yX&m5@1{$EiN3ymS7A@wes}L2X3ym4H9z5PPd}mNJ^t}U;iW0$ zQKQD;b{~Di;IRW~;J2K|W4{!QLv{13b9sFqmZsBG&(H6&U-RQ6jIxm4w~^N;SN07Z ze~p9gh7V(99u#%reZl;7_PBdWQ(`amX3HH`zgY9f7Wf_(<9LH95UYz4xz-zGgdVw+ z86(xaNuCP_F-{uUMMM4hL*OX`O*Yu+o?LPUoPV7Z^WcKgxsrBxKzq{yqqJ^B&?1`k za;xbN-op6mRjO1}o$uz4gS1FS+1{<-aRkW6)&X-^pwKvSyOL!l8AVP@7u|L*v~dQo zSS{K4?>pXpSFaHdqcr=JT+G%dWtt%ayu#V!ee?2m2)LpYfDJBjb7GiI$#i9fW!KFL zPyD06cBSORn^pj#6c!AQ=5`_h=_>N z4>q^ktJE{VCh8>Yq=tUWP5Ec=i5(H5^}hvIFl z9I@KW)Mv`j5+q}_#bOkWi=+EgEcA%01FAGx`S!cYe!>Dc$4j4X)bbVzqC zmY&v9*I>3}lzxObs3jkceJQ0xO8K_O=63m^kPCeNJ3fFkAc0ZY>3q85*)pO>%$Zuk zkNKFt6l$@v){gXDZq1yF2i4Seu(lU)!`Ei_{hGdhS9oou-*Si!?enEmp(5Fi` zRuB6ImC=uik2Xz^iq{=vfAZ}nJoMZ#R_uNrrlihDX$8lk&IZS>L*TM_MKum-*zDQt zNyHb<6j)y4$2!KXT5#^1*i9rP`{ErkHfCn7YX0G^K5cDn1;WhLC9R>xCa1GpWirA; zM)j;03$sE}qigl#6oE5F1gT(}HEn6{Tip<-=D~?b<^}!w?6=&Q?Q*pY z=S2qi8v%ut)=S@TT_mTG>_5Bf<~0h8Rd7o$o??V6;+5QsDw<>BI^0~^XAA z^S!r!pJbI*T(2U%Cgyg%Z-99mzS-X&uT^sCLgyTnM3KWX(!buL*4(;L^i+oRd1NC| z5!gYD=&RAg$^^-}aC32yUy1|C24r?3B<5%}1q2K&9Fury z_Rn|5`0t@#?0p0!Z}>4j$AlOsLCNS&D?d@#kYF*akk^z_>9YA-E@xMrcYjL6h-m;l z8P|$|!RzZH4{8MkK8b4iIWemJbpmY4uU)t+YDwW;Y^o@^Y25uSP_!{=2GK{n`7IwW z-7d?zG>RVYmix5aky0=ZIe#3t@_C$_HmBTaD2f-k>8*L=FsI@%hq};SJvDRMG(Kt~ zy42+jT?d4r*dOe%Z0`i5qPu-dIQr7+tKH!Q1H;)Na7KZ*yrZ4)ILL84(1!k+5&!{^ zs&D+ne0+Rd9xo=&amKLe<1K%?oBQi4Cc}FUmiJ(81$0|4>uC|*Z-)3-E>_p3)qia7 zu?IHIaC>S5+t2=@)I>&T`c7(XZ%cGDtv8_%cT@Vyr}0`{q58b2-_rfwBCWEh%dsVo zl0JT9K^Q>5v=y?u!u(Z!uH+`EQQk_}lMR5d=;J+3Ry%l~Zg)2!YRm=_M9VB+>URKt zv8%a{o|fm}aAna{jsa#{r*u8~x`YvSKrg|aeE>e68e$oANw{fc{Jdy`r-I4?Z zAy`UN{5;IAr&6h>0;^yYctF83S`GPhS!@P;H#=`iDhFE1Z+s>aJF zGLF}GoL0_C<;6MFTXdw~9WrYhF|UZi0{v2R0JImFQ0k42GB%O|puxdGbuEiwL}uT2 zIHL<0_RRe}uw@1{Txa1UrIsya0n+&N`M6Bo<#lD-wPXBY7xil~J}Pa2MXzaALvFo%#x@Q2Pm_DB71JA%4Rb*U+o`B!tP! zk9!GD`QxRQ9_}LH>$j|i4sm6#O9KaElkXqA@b*sNQ4aYXhi{{o#(-EX&BHx-E&YWX zJpZH@!e7#hVq#V-1f^g{$Dy+|8~=ZkAdH8)O>-z2G$#%&$D3mvVbz9Z4f{d<-Ghq@ z=D(nG$D5n*q&&PHyc*M6Bh9s?<5Kwi*28*XSJpPWSKV2VZYWS~7zs#y$8KuP{CRjH zA@0esmMJ%E)@p6d@<7*gSanxf@4oopU|U97-`aygGrkxILj*J~F+NqUC5_|u%uJV& zNTL*E)I+_8S2WQ%Im7kRkPB4~h(DmUOu$+4!MPXniZY0^J<}Oqk~XnSTh1+wayPNu zFpKZspLbbqv?WRV5t*WR|E0jwYfa6oLl7*Ty{ZBPhcPoGVJ-QZcBdo$Qts!#?gz}f zqv>m63yR2JP48>a8gI65m0bw`L*3w3*40CdOo#{aCT53;jXEs_+mga2!>WY^{xayp zMG0G@EqoN0qx1=!s*`9|U49><;F9PF^$LVc*KgR@0?fNs4U6Nl)Bn**&a7h)C+&>( zFU-rKC_g&}(Ru2FCpuCkZ9Pp!RCjLJ2XL4L7*t$d{bdHfluqiJGB>lls(}c82Nd33 z>}i@dg#Xz5a}zlAi^;Bb%~|qTxUN{vuGrUG>}afqv&vYcie}ha_KS}8)@G6vtPp8x z+s9%}O?tV79E||mVwAh0p^0lfb#Hm-}Od6&%vypSnlnucs6 zD~mgzHmzZvNvFMAVZKSQdn``P=gW0-Jg7A>nXi^P@cvFC!znWxDnBQjJCb`F`Yky& zwKp-X=9z1UQOq=ckfBDs4h|Rfjw(4=<*7Hj{IaFB=U@YeK7!J+xG{7&(^K%5A4*Lq zQCzACx-LB1s^*6cas0Wf7irN&Wph zG`Y+7%YyxxC7xL4(xY7w<(E~7iA@oxgc7BEzT(2qhoDLwL8{KcqR+T$C z_;v|S;f9SOOfSyxN@R-aon*G8Tr|W@__M>&_C2U&ZCM`)vn0%8@E;q%uSm%N4Bg{> z36JAI(|8R)1^nfbka#|QIC)uglx@q}cJpu=A1rdYL|Xl7%48ta4!_`5_!};r(TvPe z&&a*93)z1rZem1%_?pm7Ra2X(;Y3r+(zcL2rnF1&XK6Fl1j;*Erh6E8LX3RmG2@iw zJfD%7IK}^<1! z-=%fKMVue(c&F_^^CDzKWC?x2f2;^Iv`lO7#eCmg1K~|_x_Mb$<_yZ8TJsd2* z|gJs^(n_niBN^X`*oKGb~8dQjbDU`UcM4P zz2rKC|A|+ulnZ<|$kRyO_HRtKR8CC_cZgY8)N+dkt!zGa@ez!dZwhXIb~i;{bbWWD z3`rFLt6i4TG$pfA;bV9yhm*P4**Xd|Uu{(~X+^hR4C{T?_@}0d1-H z2R(z=e#hWDVmtCj3zS9IhU@ancmELrq@)mB90_7 za!+#<-64Nvy&%c#G`OvareE4^_N<~_AVR0?E;>k${Y1*wbUx0#HhB*MQBFCAsFew4 z<~95nQh&fqH;nv2dHkBhjWuxW8WrL99$Z{s04%Dp5XGA^Q?55?FkiwN8tLxl9^7ex z;;;$$YRUsY40*Ct0N(ln;#)^|9|F?oYn_!g$W&C^o@WDU(GK=R%~Zu8emH`+7+6S&#zqanG>jBU5XNc84P>a$$I&K4=|{;Blv8-AoxrjwP`-{nMfXo>W1OZ4s9hxi74r*- z3=M<#i-+9oz&7K`~vDQzQm&m~F+&ZHa4tbsE4SflWS4`<^ zGAA2L7}s+zu>K3K8q*ZmMqj@jF-@6}|4$A?;PMz{QY!0m3Sn0(+oURMrl8nAwj`yK z!o(qB8SVUMeL_(e3^DKbYN7;z{$P4kQ=W%B#xB1!Bvj@0)AL8D$#Ou2O|S|aX5b-Qm@1!TjI zgU&Lg1Kd?R9_FYuJ8BBd@yJ~ag%u$~*mU1EC-8cZ8yh#|MFe;w0$3Cn?M5aYWJ%4nOc~9%T5>t82G^~3*d%EB z?MD<&FLQ6YQt+0WT%?hs?^D<_NwSD#RHWW3ucA9mb2)7 zq#(rS$T1|aaO?~wUr5vI8Cw)Rdqe4Na!EiR25qpnvdtX#|DmrV*2C(LU(UK<3g;F) zNwW4L$}3UL&C3~e)k(RM6%y-AWehR?E-ycbFm|?Qzklm`<2Y4GqjV>)s3~Vr-{=4y z9;v@s`gpMmQAT8E6#RzqIc*E9VY19c`$8wsu8;rocU6_FB2DYb7gUBhBu~~ThzND` zw*?xa>jGSEn++uK%DF0sG-t{dXCw*4tPW2-)IV`#;bB^{O{6J0^+$C#>Y{dYPsi*R zc{;4H*l9(akV-eg?tXnt6~EaTts$W7J0cl#NJ)PNn;T$-KXF0m_!kaIehy_-u7<|$ z?_$Bx>dO$JyBNnci)SL!r8>_XT{>IAGv%KH55_)k-d}BcF^iy z@)Yn)=&v=zqU34bQ9RG0OB@oCXa6jUv{dvzQlvINOuAt zEr;D@Tzu8*&hNomq7{|r8=8+lf6A_0n%yMgMefg&JRSR(oq#)Db&<{N!29~_X;i&} zNry%w*!t2>JUPZAxZ_h}_dd~2Z4}Ul7C(eRht^Lv@|Xt$yWo4a7{QJy=`&K$pcgSx zl&~l2y$&>~;uLh<7!7d733SG<;EpirPI?%Po5Dh2MIi$J(G@*QJs=*9tF{}#?YV&w zgtf4wn%`&6e8~A~VS%s3y{)~klsasg{@+PdXJ+d9?#!9TgYwL05|)23RieY$_iN*R zblB=?fIlW~dxptP=)vx%q_Y5t&AhIh_VxFu5#qz}=)FGcwN>IRYI*L7EzaQY`AUs>x!FYwc-utH$J)B;zry|JH&0#`$X-3pPX~*91(7@fBCv9&9-V#9 zfe0m%(Hqpj$N>X2OhMCQ7#O(1CgQBq1?v-Pp+r-(TTlst*nIE324SxnetRzk35kZs zd_dicMd($uEfzy$%an6UdUg{5K}w|fv8ubix`71#8U40*b;_g7WaIvU1Xm)NX@2@o z(}b?qWz(jVu>`TG<^J8)$ayW!`p*ijBVgMNy~sA+X}vbVG8HfcVt@~AqK*i$Vz>Bx z=2G8jUryCtce|O}>`DhX7Rt=e$Ws!La-n_1`ruysM07bIg{%t|YU(r-te&Fw0n~$| z-qnGOB}9BQ-1k*OQlT|XA}W>EQ=O(&*>2nooYP}4!ejW{U$8N;vGFvW&bLQuc+bkx zCXg0lQXZSk3&?@Hlv&t2&1@9ISdJ|uD#SMDC8KNS=&3hwd~o2Mw)8TUqhc2SuAWv< zpYm6@!hFW0KT5X+H@IP>s9VFwu{Jvu&k=x zx(qH4%~(+oI{az1!gPHzF-Jjvf`2!o@k12Dwn@xdqFvP}`z2dF@;aXFlrL!oLz#*q z``NQn6(*Y2mUUwI$B7}mLu?i=r_L;~a>yd+R9#V)Y@JumjmJqP}TeSR+WJu2sA`C?nni_rDyJg7I!Hq+i?;Iq^f zh88&gKW^`@SAAw*q`m$XGdn#K5k-1o`M-6rjB9UaW@qLing}?jqq2BsW>gv+V9vh& zbFX*z_wy}@c7uA{VJLinl?8u4v~Bx(T1wWKqr8~S-EiBY zpZ~QC=Ks_3g8v77^1rV5e_N|ywS5Rv#jRm?8Gd}{+xcxm0EPV$4{^(}4r#9>|6hf< z2E-~$1%DqcEKKWt3zLssUw?9he7iL^>Es?k6>0r@QtPJjPX8a@&Q8WGVPBvA?~kM; zgN{dvCJ!vEYpMhG%$II9Ayaj)UC7|#UfG3^asOm^zf%Ml@i3A1CXeHL)N$wtB%^^6 z@3p7w-jl(2652bdUDn<`0QQuEUh|P5t9T6#cqv+NQUSs?7?^LBqvZ?`p807V;xUKp z$FW9I?XeogWTi&F`c9f4rE>E^XHHM@8)1EePny_yCmzWC!5f8fYIGVvDuQjL27Uu+ z&WNkgn#_hqGu6c8ZhcKlQBelT;N)SAlNT^{zA>S0#tPQ;V(O}Vo1#NNqe_wScMh$9 zhPLtmwK%MAQ~R@3MPrYk3H2{>CD^2+XVSh#glSEj?w_8*D;?zg)L(Edjd^yd8X~%h zn(S<-ZpVg->eB?hwZY0EbYJk6YGRh+xVoQ|(V~xkJb9g|UV21Dt8u==;&fBVkNZ+6qSX2KaZoTm<|nb?HV_8V;QCBju-xQc^T#d%jK%pJW=X zz)FLw!&l3Rp()(ozmkcR+S6Zz%>UE7et(-jDSvJTbWut&hg75|NAi?F@&VJM$UE(oc{YB{+u_a{@n~r%(1YjCV+P$taO~q zYY#L0zgj!*pr+PtjfbwH2m&IY9t=%Dx*R|R6avya2_2*aLY3Z&C=ddubVKimbdcVW zfIz6BNL5M#N|OL0f^au@zB}K!b7t=SX6{V>$xLRyZ}z+QUeE7YYrVU=UL#5~Ls2iO ztO@n*JLOtr_H=DRevgev8?9vaLnjQe<1Uz`&zPQdT_q>lXMw(d$+rHT{)d~#9n%Os zwjZ}wDCy{Y1=Y>+WX#G(Ucd{hz($Q;EU8H5T>1&Rv+zKa1Fg0=avi2z|12)q{?a!+ z-S}1fiB)ncaC%q+59ZQP;&bTr+o{lE=kGo{sTVX8(GipzKDT(3eqek@3L&zki^ook z)#cVhv%?%iW2pDKk*m|m)CEJF3Z!1Q5LD?Bfm>sEVr zXSj=lnnR&be(5dU>q(WGz>Ng+LATBs zcjP^WaG%6Nigob=8tI3`u=lbSkuS}VHBab8tsx{kzRr{Rl*hE>&v{1qPud?KY+}m% zFosllX^B9=hV&hd&yA~vUYxPiUv|-JC<{w8yrCRx)i4lc9X>Yui~FyFjrh0H=xfQ! zQHe_$Z&Uj;Cw+Gq-W$$Vuq9bidxwKJ(13g@PNEz6oA7PARcvGb(=Ny~@89LG=}OC8 zxgd0VM#svCp{o-U&2^At=0J2HdfvtrEpD4Y;WY*eE-C*SPSh-Tc?dbs=r0cdnU<}T z`DwmUYLs~DXI~{fH@#tCm#qGY^ioez9Jh2tXgj>|2Au zOd9Q4kF}ZAzr`kHIigmD~ z?%tHx*w~wI2sZXa`Fd~kGb(4eI5cDzn^uDqb^cQo%F9heh%>}h z)Z^QThpqLcW46Q}L>#j%X6f1T&NbEjLQ!`*#G68bla0~L>9Y2JDxLarwyw??_uf1T zwA>^ce>G=mdlT!Q+w3{|C;5TfHghqlp5o5Vs>-mL-OX_Ixf_BZm(~WX%ynf4%%!Dn zkF8mOS(;=5YldiIc!9qfJAx9s95YpUV;>WttJZ5(dQAX$Mrt&LQ3N%nnmRZX9~iZr3ehd>^Oe{2KZdhb-pj%5=K4x}>qR9i9Y$)r zb=HXCgjqTuv^Az%X8XnuTm}p%-Q_ynlWXb1_X5RiB!VEje9gnY3!;gf`mplSnTkl{ zpTJA&+#oZibP-jphtSvY=vey)#X1X(U3@#}@)?G;H7}bfV{3DZL{wDNrSkiJyLM|g z4&T0Nm4d zBc#Zp_ld^cj5hc13D)L|g+wJAQ$Eh=f~RDZuL~c?vVqeZR2!TG{9IXN!y4jW)I7ma zVs3ok6#P|H4jyBb<<^cxp zKIQ-$C4)k0J}Yu;-c?r>=y0HarjFCoxcuG7ep$;hsz%;sQ*)J1 z4z|d8R~3#diwcT!yV~cV(;kq~hvPA%wnG1$g%K)e2?`?U#HJJLx;351O*3He0nKw} zJ(pS)95J5Hsr!RrJ3-Z1DGYnBJ?`Jh;>*na3Rwo5xSdfr%53CMvg-U- zi)%EgB|91WCcYtejCPB3BH|hY0&Qb2$QWbtbX0>L`VV3MQ3H;`1 z+f|66t5vf51>I2;5^ZpMHltMdvX(HGvv)9V*THTt{b4P>}%Z|@W^ zI&L5NH1W|z?@w{{^CjD$kFPRz-r1b4L5C;Dd5o!;J)ZKIf7+*q`C4m%($PJa_JP&tZ@01g1q!9{9x_R%9q+V*_u*eM|Z zFJ?1E909ru9?UaxXV=IRDDJ%uJ9gQecYr{4alFpP3lf`B+>uIEwgQt5vniQ+-ZBk} z`8B#**T!geb>h~^TOZ_=D+0GF_~Dxa-ST1+4M0`#i98H3#XWolv_6jEou)o*?ppz3 zsov+HExPIj>l94M3MOP~gisv5zA!VFwK3p7ac{!!BQKiXE3Q3By>n zgXUD-?As?EDQ?NLZ8e(ndMT*Zl8<;u{f|#qer?dk8W9WBBN(%|w_o;QKek&b21Mbe zb;QQqiu3sZ6!`^M?_K%l*7Tx&o9Byz%U$Wc{k}EmFZpwf*Fg`sr*yvmPnZxkeJWikYy>nFeed|fj%{@9Xurl7}LZm+~-FIc1FoD@DxO5drN%Rx} z|7?ZQd-q16)S*d|g-nA!GsQJa@3n-w#CiEsaZ3g-J4=6j{!j6${VYC+(eI z-U3u!rv0+B?$Z3Po?X1Zx!%AO^23}vLtxwyerBi@VK?Eq(NBSsZGDiiwkI8yAGOHK zq&Acicse^Q*T@Wa{fSm-mMlMcDwwfMweaINzO8(wD|D_n7!m=Dq3w|-4xUykdm;}-OI=`bNIsvm1UzA&*Pmq@rZi9XK2}jLWake z=nfR%-EnMk{6g|yS1-)JE2IJWU7bX1_u`O!qn@zXiFpfYVZE88s@-}u0uz|~?witj zk6>(u8~Fc18Bl`%y_u19Jt#zVQp1X#ZXEBV9sRk1JdjP0JY>EfURB;p5)@B%^m=lt zfkN^?Hl?_*e^F#>O7QDoVxJ7n-p|**%x5VD-ahI1*-9C{y9di_8@TMEOhS3a4Ti6K zu1$S@GY18o?lG{d<(4yg;2gxKfLn`PnE}6W+WxuK@JxyOfUX+GOWD@b#@=QR67QuK z(%$%XIgY6^@{LEZTDS7xG-4LCHRn%M5Pg00RzR>RjlLFX>MtT}yE?jTAV8OxgcwYk zaEzEMJHrc0-$DRfbe@+Tr8@*7m5kVa*iqjiAzQ^eA_&=uS`FsKrhh6dxB4h3d08Lg z#gQL=?d7g0m^Y^#*14LDkIQ(}Xj#^yShzrAHqC-Vm%dvD%Gn071HGGH;?1> zNk#K$Kq1QG8EVn}d6+&|?s0W|;aamVYT=Hv0j+9fn3R0%ceQUzWBUC_J2~2+{38#< z%!_z#Au;C<_UIIu6mup7qR_NZpo0#j;m{B8h+xdO$Mco8-+VowCNp7VRG=AKQ7iK# zQPh&IK&#pq`o8V-grFILEBN4hb;eiYj5)5i1`Xa`TB}kS-9wBX!O~&@5ASYBp!~ZZ zB(F6T&(rh93201pStNNK!jkl*K}aBv zb7F??j2^?Dev|@>lA(UxeSo`<6wP-MQClP?PzGR<+QbU)$G9R9X1jkEaM z;sEwn=c^4$5jp(kcCge58sq6M=a@1K^Lu+hMX3@tQ;m0UR;qH30z!QiW`>cvL$wNb z`66}Qcl~vetYlt#!bN?JWt?;99}#_F6j%|aQeozEi-BH!t<6%fz(Qp2k+|{ZioJMYU*azkAIHr;P$bX_E-vU z`%2bWrX~dj&JRfbEbv>x5?Pgr1^1QngA5v#8Wx-Vqy*pG6^>!L$~K9rcyt*Xj2%#kgN$Q8D^u==kxwd>9aMjJ80OfbUg3*-)4W|G**6NV|6vb zBy^IT#zh&Q=i zpA&d$nWLH7_@mXap_(V>hobRU0o9r5WM1jSX(p-c)#-(@_DbN>SStK*&uELO-RNUY zZEZ^VUh$QwbNhp8J})ApE?@GKxnmtZQHo88kcy{ox_Vm6vFnElBdFIhJO%^-xGnu> z&ua9@mtvmWk{HcKBmT^%q8`W{_aU|8woFZWx}C-D0b^_dx1vu#+P93SP9jqYso;Jc zAXg7$gBuXm=BN$cA>ENI&O_2wCb!IBW3Dx%fNL-zfXISlNwCr3Zag6vI=Vhf%Z3c@N)cn3waKflfE^o*nmSAaVcuEcUk z)rAA!zrY9>+wjUrAS+Csee_*u>goOk7(B0A1jkO1gEbH^9hwV5)`(cigXkEeP|KI> z=Wb-}R1j_fGkH)B^%?$L{iKe$;s zF-LN5NTuiZ4n86@Ha1d{d%==ElyvxI#_N53X5S}LDV@bY{-dHLWU^6~zYIH{Ew&$M zZ$b!up+$=ad`@|CaCM9D8N0bd8tSRqNN5XF-~-RN?1d(>Ym|SLAb!Uc;m9Z@ZVbt~ z?wdUW+IJ6#QQ@mJEZlM%(0>8Fi^l8DG5A}2;8mi6dK~&QrJOH2F0R}SuTD3bfaAe`tvs= zWoSH-w4=1v`hx)}4lwN+6B0aJG+Ah{k)SDUkGeplbW{s{Q}o z_~joGE7u#PKpPrN>CzlWt=W~@HN#6MnkwKw!CHzcZdoQ`7Zf{gU!%yiV2BRY7W)0g anheCxza9R=jmP$MipmNakTN-o(EkDkJKur; literal 0 HcmV?d00001 From 772f7f59d7e2098719536814fce9e8bfaac906bc Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 12 Oct 2020 15:29:31 +0200 Subject: [PATCH 012/142] Add author infos --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dfbf6a3..58bbc94 100644 --- a/README.md +++ b/README.md @@ -173,4 +173,13 @@ The command-line interface stays open in the background, like in the screenshot below, and prints log messages as we work in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/). - \ No newline at end of file + + + +## About the Author + +Alexander Hess is a PhD student + at the Chair of Logistics Management at [WHU - Otto Beisheim School of Management](https://www.whu.edu) + where he conducts research on urban delivery platforms + and teaches coding courses based on Python in the BSc and MBA programs. +Connect him on [LinkedIn](https://www.linkedin.com/in/webartifex). \ No newline at end of file From 3b730fb3d586b0d95ac5593f9610c5339ab9c27d Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 12 Oct 2020 21:32:57 +0200 Subject: [PATCH 013/142] Streamline text --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 58bbc94..7746018 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # An Introduction to Python and Programming -This project is a thorough introductory course -in programming with **[Python ](https://www.python.org/)**. +This project is a *thorough* introductory course + in programming with **[Python ](https://www.python.org/)**. The **main goal** is to **prepare** students for **further studies** in the "field" of **data science**. From 05df4c085785345471b1c92ec981526343dd18e5 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 12 Oct 2020 21:38:27 +0200 Subject: [PATCH 014/142] Make course objectives more elaborate --- README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7746018..1752053 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,25 @@ This project is a *thorough* introductory course in programming with **[Python ](https://www.python.org/)**. + +### Objective + The **main goal** is to **prepare** students -for **further studies** in the "field" of **data science**. + for **further studies** in the "field" of **data science**, + including but not limited to topics such as: +- algorithms & data structures +- data cleaning & wrangling +- data visualization +- data engineering (incl. SQL databases) +- data mining (incl. web scraping) +- linear algebra +- machine learning (incl. feature generation & deep learning) +- optimization & (meta-)heuristics (incl. management science & operations research) +- statistics & econometrics +- quantitative finance (e.g., option valuation) +- quantitative marketing (e.g., customer segmentation) +- quantitative supply chain management (e.g., forecasting) +- web development (incl. APIs) ### Prerequisites From dff18c86a8b116ed96d1bdda19a8450b0e7e2191 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 12 Oct 2020 21:40:46 +0200 Subject: [PATCH 015/142] Add contributing section --- README.md | 15 +++++++++++++++ static/link/to_gh.png | Bin 0 -> 1236 bytes static/link/to_hn.png | Bin 0 -> 632 bytes 3 files changed, 15 insertions(+) create mode 100644 static/link/to_gh.png create mode 100644 static/link/to_hn.png diff --git a/README.md b/README.md index 1752053..595e7a3 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,21 @@ The command-line interface stays open in the background, +## Contributing + +Feedback **is highly 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. +Simple issues that *anyone* can **help fix** are, for example, + **spelling mistakes** or **broken links**. +If you feel that some topic is missing entirely, you may also mention that. +The materials here are considered a **permanent work-in-progress**. + +A "Show HN" post about this course was made on [Hacker News ](https://news.ycombinator.com/item?id=22669084) + and some ideas for improvement were discussed there. + + ## About the Author Alexander Hess is a PhD student diff --git a/static/link/to_gh.png b/static/link/to_gh.png new file mode 100644 index 0000000000000000000000000000000000000000..01f1a8a25c1065be403c7573aab98981cfc0147f GIT binary patch literal 1236 zcmZWp3v5$W7(Tu2?Y6G%ef2U@g3iQl8pn3mJs{N(hikU!*340YK_R!c-2{e1DbeX7 zL^MJ)(LH$9sGAJLO~?`+naq(Q10{wC1m^?ec*wS{9SE4P(cSsoUNe*V=k%Qa{eS=0 zrrWALWfqg&L7Z0Egxnk#16%;aC=#$U!$uK{?O!@XrWLI1xgLnqb2!>W=ET zq*KL^RQ}x><6uH&DapMM(g;!E!IQj!1UOZSOeQdZwm2$rJa4sFDV@$p)B2^^5uwQ;IT`=%*H4jQxHqabLN0Id<`Z6L(sRH+|85c$2gB&WSaX!z+xGo?10B zc~bc)(=n)=j4P)s=5LkwgmV12G7&vLfByNgZ>H|2axJs ze5R~a&zUQ4!$=8LRJl{G8+42=??_kUVYtRCN=v+{gML|V`>3sDNAvdP9W8BgK$crt zmsfjhRvqg2a$j!VnNy#&AL#t*^RE33@=JmGjsp>SP3_CR)xmZCwRJa#MgnJtVppT5 zW8;yb(SfV+YhynQ4L`_!ca~7~C6R~P&AuyKb4*@b<_%TmJoQ2om900dcVsyV_STnI zT{CWIyiibF>kI^kuiyBK>TQ~!-*pd|9i72H#qG-ymx_*T3`gh9wTJgSzv8!FX_sqZ z!)SEeHxeKFRC?t?AoA$aW1jG0f7X^CyMNhm<6JnePrh>0{r=9alC(8<<14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>$t4sbLnEF418LAzqxY_XkjlKW-oc~|m`oF~(swIu_|JIiOPp^PTARA(PlfnO| zm;X2EgVX?BQl<3&#)1D8av(N@dlQLUEcSmx_5UJakQCHMU~?es|NpCsv~+=9W-JNv z3;v(`#QZggljQC0;`2RU;4_fJUgGKN%Kn6fkzY-6!Id}9~oCwRwsTHU}m_S!c$fLD}M*jF{&l55hW>!C8<`)MX5lF!N|bKK-a)T z*T68u(A3JFVdQ&MBb@ E03 Date: Mon, 12 Oct 2020 21:42:29 +0200 Subject: [PATCH 016/142] Add ToC section --- README.md | 11 +++++++++++ static/link/to_mb.png | Bin 0 -> 1222 bytes static/link/to_nb.png | Bin 0 -> 1138 bytes static/link/to_yt.png | Bin 0 -> 912 bytes 4 files changed, 11 insertions(+) create mode 100644 static/link/to_mb.png create mode 100644 static/link/to_nb.png create mode 100644 static/link/to_yt.png diff --git a/README.md b/README.md index 595e7a3..aad3e4c 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,17 @@ This project is a *thorough* introductory course in programming with **[Python ](https://www.python.org/)**. +### Table of Contents + +The materials are designed to resemble a book. +They can be viewed in a web browser + either statically (i.e., read-only) on [nbviewer ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/tree/develop/) + or interactively (i.e., code can be executed) on [Binder ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab). +**Video** presentations on the content are available + either via the individual links to [YouTube ](https://www.youtube.com) below + or this [playlist ](https://www.youtube.com/playlist?list=PL-2JV1G3J10lQ2xokyQowcRJI5jjNfW7f). + + ### Objective The **main goal** is to **prepare** students diff --git a/static/link/to_mb.png b/static/link/to_mb.png new file mode 100644 index 0000000000000000000000000000000000000000..ae37d5079424f1bb9f44ab166e08985acb1e3f57 GIT binary patch literal 1222 zcmeAS@N?(olHy`uVBq!ia0vp^{214Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>z4wzW^aRNe4g5Hq-av%guTsOTdrq! zzD#L&lU(ON_Z(0q1i{S*8VF>+O0G9sb{kn1sO4IA=Z>-&yGy4(N^kxVR{@s=GJxS9 zIrC)1tW!aA&IHami!4P3`*lSzFmT?rWCC3hH~T2igvePZQ)VA{lGb!5v+Yq@^Z)<< zZF@In0TaaHk|4ieAPoo1z6U!a{dHw$W&qUn^S0xzYp8uH43o?SqV(#w;5nLd1CCGuoQeqS?V^4k01^%^sVXC-2>KsPcbdAqx;+nGIiIgrC%;_2(k{)B~*PhWZ4jWA#y zV0h%|;uyklJvo7a$xSZKu8z@BG0~09V0xfSh);}DkeAe<^hpz^P7V(U2?`5jonLCFrah=${ZR6I>RaRC0 z{_*Q4I|~~t^H0TviEADTo(^9VBPw~6%|x#%(kCaT#wjVN$SX_Anz^{jEiJ6fFE6gn zF)`3^LVQrvwyLwYK6Ra1^=ejB*@CuRzuJy1dnVR*ZQD1#vI)N}tatCKuKN8;-DeKV zv*JSkc}AtTzDOFUH}2%&d9(TXnYrfm2M@Wv;bCx?V149HR_q+0Z&gcNBT7;dOH!?p zi&B9UgAvdVx&|h?28JPqCRQfqRwicJ21Zr}2Aq3dT}084o1c=IR*9*>*c76{`HbUB ypaw~h4Z-hC=3Q8BO@d48RX~Z zXJuuu~^VZr_=d-K9k9W zOHj|w&W1{1As0p^CnrM!0WFZ^7qG5g-6qVOAJbClMTOiH=JyOb4U)9%Tt><^$tz&d zXvxq5?f*OygL4AuY>8OG@0}Wb zxTu_6+OSwv`t^j`3DMAkbb47Rd&KXbDQO=|7f9y&O~X>CKlvaX(A8P(|JGX|r{Y%!ZRpFDwI5)Me70uqa5Td`Oy z|2qzvH|`&RM~X-d3;h&!JZ87sb^;VZ4tNZ{@NG=?207xf3F&#qiE%t;I*eh5cFGmQ z-rA`*G3+2LJci-%SSi_KH;md96HE*wz5LRv`(HUwl2GzfVR3W}=eltfx5w);-GShS)ATFP zUnE>dBL>g74*PnXe8=zT>43;=G{V>Cy-D9A$B#{qj(!|`1C4M$hEc8!>SN7&glo`Wiy8>`!IwbbA3X=)mfm7)q> zM^$S>Mt3i%sOnrxa}1}Y?6~@hM0T-BC%bt3#`%*I<6r|m zh!5h4_<17U;og4!eu%%{srL~de*`(`kqQ3=|1wZ6$9@}6f7ampw;^7HXrTd+2%&+( IfT*m$09O=nl>h($ literal 0 HcmV?d00001 diff --git a/static/link/to_yt.png b/static/link/to_yt.png new file mode 100644 index 0000000000000000000000000000000000000000..b3955f6d9a4345276692c15c7902294261f1e2bf GIT binary patch literal 912 zcmeAS@N?(olHy`uVBq!ia0vp^{6Ngj!3-p)&s5I?QY`6?zK#qG8~eHcB(ehe3dtTp zz6=aiY77hwEes65fIk}QdCpzj+ zbk(225Ri?+1HBmfar|8A`l&ukq4p!QgT4F zUQBwQge(vN8M(Xy(}ctqiAXLJkz64nxl&XLh`NmR_>ac``62tpG}-} z+1eH&cv4mYh)&4JpVrj5U~P9&M*f7f+(}t^i1<+{`J+;DU<6_uk(7mTL4pS*5a^JE z92~*Lfw3mO)wd8x8I}b31*5`A1 zN_Zpt<}Y8far35iE9Os(<=teFCJfZfnB?v5(*FA<_h}%9y~NYkmHi0|BabYTi0iSV zK%pI;E{-7_*OL?*-=T4qIt$*T-&XlwXnp%3Ay4vek=xA!L zTs19q!sPWU*RIab$jQpf%nc4m2?z=c3=O`1;mW0J7q4D6XJA;rNc&f8vAQJC52_`u z5hW>!C8<`)MX5lF!N|bKK-a)T*T68u(8$Wj(#pU{+rY@mz(8N&sVItu-29Zxv`X9> U;%Z}80yQvry85}Sb4q9e0LtlFKmY&$ literal 0 HcmV?d00001 From 1899a814c0d7026e9b9bff5697a76966e95e9c9b Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 12 Oct 2020 21:56:04 +0200 Subject: [PATCH 017/142] Add initial version of chapter 00 --- README.md | 7 + chapter_00_intro/00_content.ipynb | 979 ++++++++++++++++++ chapter_00_intro/static/cli_example.png | Bin 0 -> 29502 bytes .../static/example_python_users.png | Bin 0 -> 364253 bytes .../growth_of_major_programming_languages.png | Bin 0 -> 210392 bytes ...rowth_of_smaller_programming_languages.png | Bin 0 -> 175532 bytes chapter_00_intro/static/logo.png | Bin 0 -> 7821 bytes chapter_00_intro/static/xkcd.png | Bin 0 -> 90835 bytes static/link/to_so.png | Bin 0 -> 991 bytes 9 files changed, 986 insertions(+) create mode 100644 chapter_00_intro/00_content.ipynb create mode 100644 chapter_00_intro/static/cli_example.png create mode 100644 chapter_00_intro/static/example_python_users.png create mode 100644 chapter_00_intro/static/growth_of_major_programming_languages.png create mode 100644 chapter_00_intro/static/growth_of_smaller_programming_languages.png create mode 100644 chapter_00_intro/static/logo.png create mode 100644 chapter_00_intro/static/xkcd.png create mode 100644 static/link/to_so.png diff --git a/README.md b/README.md index aad3e4c..458d934 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,13 @@ They can be viewed in a web browser either via the individual links to [YouTube ](https://www.youtube.com) below or this [playlist ](https://www.youtube.com/playlist?list=PL-2JV1G3J10lQ2xokyQowcRJI5jjNfW7f). +- *Chapter 0*: Introduction + ( + [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/00_content.ipynb) + [](https://www.youtube.com/watch?v=YTU8jaG27Xk&list=PL-2JV1G3J10lQ2xokyQowcRJI5jjNfW7f) + ) + ### Objective diff --git a/chapter_00_intro/00_content.ipynb b/chapter_00_intro/00_content.ipynb new file mode 100644 index 0000000..b07b0c7 --- /dev/null +++ b/chapter_00_intro/00_content.ipynb @@ -0,0 +1,979 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/00_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# An Introduction to Python and Programming" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This book is a *thorough* introduction to programming in [Python ](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). 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." + ] + }, + { + "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": [ + "There are *no* prerequisites for reading this book." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Objective" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The **main goal** of this introduction is to **prepare** the student **for further studies** in the \"field\" of **data science**." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "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 *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 [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 and the other half in domains such as biology, business, chemistry, politics, (art) history, or linguistics (cf., the [official Q&As](http://computing.mit.edu/faq/) or this [NYT article](https://www.nytimes.com/2018/10/15/technology/mit-college-artificial-intelligence.html)). Their strategists see a future where programming skills are just as naturally embedded into students' curricula as are nowadays subjects like calculus, statistics, or academic writing. Then, programming literacy is not just another \"nice to have\" skill but a prerequisite, or an enabler, to understanding more advanced topics in the actual domains studied." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## Installation" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To \"read\" this book in the most meaningful way, a working installation of **Python 3.8** with [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) is needed.\n", + "\n", + "For a tutorial on how to install Python on your computer, follow the instructions in the [README.md](https://github.com/webartifex/intro-to-python/blob/develop/README.md#installation) file in the project's [GitHub repository ](https://github.com/webartifex/intro-to-python). If you cannot install Python on your own machine, you may open the book interactively in the cloud with [Binder ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Jupyter Notebooks" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The document you are viewing is a so-called [Jupyter notebook](https://jupyter-notebook.readthedocs.io/en/stable/notebook.html), a file format introduced by the [Jupyter Project](https://jupyter.org/).\n", + "\n", + "\"Jupyter\" is an [acronym ](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.\n", + "\n", + "Jupyter notebooks have become a de-facto standard for communicating and exchanging results in the data science community - both in academia and business - and provide an alternative to command-line interface (CLI or \"terminal\") based ways of running Python code. As an example for the latter case, we could start the default [Python interpreter ](https://docs.python.org/3/tutorial/interpreter.html) that comes with every installation by typing the `python` command into a CLI (or `poetry run python` if the project is managed with the [poetry](https://python-poetry.org/docs/) CLI tool as explained in the [README.md](https://github.com/webartifex/intro-to-python/blob/develop/README.md#alternative-installation-for-instructors) file). Then, as the screenshot below shows, we could execute Python code like `1 + 2` or `print(\"Hello World\")` line by line simply by typing it following the `>>>` **prompt** and pressing the **Enter** key. For an introductory course, however, this would be rather tedious and probably scare off many beginners." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "One reason for the popularity of Jupyter notebooks is that they allow mixing text with code in the same document. Text may be formatted with the [Markdown ](https://guides.github.com/features/mastering-markdown/) language and mathematical formulas typeset with [LaTeX](https://www.overleaf.com/learn/latex/Free_online_introduction_to_LaTeX_%28part_1%29). Moreover, we may include pictures, plots, and even videos. Because of these features, the notebooks developed for this book come in a self-contained \"tutorial\" style enabling students to simply read them from top to bottom while executing the code snippets.\n", + "\n", + "Other ways of running Python code are to use the [IPython ](https://ipython.org/) CLI tool instead of the default interpreter or 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) that comes with the Anaconda Distribution)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### Markdown Cells vs. Code Cells" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A Jupyter notebook consists of cells that have a type associated with them. So far, only cells of type \"Markdown\" have been used, which is the default way to present formatted text.\n", + "\n", + "The cells below are examples of \"Code\" cells containing actual Python code: They calculate the sum of `1` and `2` and print out `\"Hello World\"` when executed, respectively. To edit an existing code cell, enter into it with a mouse click. You are \"in\" a code cell if its frame is highlighted in blue. We call that the **edit mode**.\n", + "\n", + "There is also a **command mode** that you reach by hitting the **Escape** key. That un-highlights the frame. You are now \"out\" of but still \"on\" the cell. If you were already in command mode, hitting the Escape key does *nothing*.\n", + "\n", + "Using the **Enter** and **Escape** keys, you can now switch between the two modes.\n", + "\n", + "To **execute**, or \"run,\" a code cell, hold down the **Control** key and press **Enter**. Note how you do *not* go to the subsequent cell if you keep re-executing the cell you are on. Alternatively, you can hold the **Shift** key and press **Enter**, which executes a cell *and* places your focus on the subsequent cell or creates a new one if there is none." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 + 2" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello World\n" + ] + } + ], + "source": [ + "print(\"Hello World\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Similarly, a Markdown cell is also in either edit or command mode. For example, double-click on the text you are reading: This puts you into edit mode. Now, you could change the formatting (e.g., print a word in *italics* or **bold**) and \"execute\" the cell to render the text as specified.\n", + "\n", + "To change a cell's type, choose either \"Code\" or \"Markdown\" in the navigation bar at the top. Alternatively, you can press either the **Y** or **M** key in command mode.\n", + "\n", + "Sometimes, a code cell starts with an exclamation mark `!`. Then, the Jupyter notebook behaves as if the following command were typed directly into a terminal. The cell below asks the `python` CLI to show its version number and is *not* Python code but a command in the [Shell ](https://en.wikipedia.org/wiki/Shell_%28computing%29) language. The `!` is useful to execute short CLI commands without leaving a Jupyter notebook." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.8.6\n" + ] + } + ], + "source": [ + "!python --version" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## Programming vs. Computer Science vs. IT" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In this book, **programming** is defined as\n", + "- a *structured* way of *problem-solving*\n", + "- by *expressing* the steps of a *computation* or *process*\n", + "- and thereby *documenting* the process in a formal way.\n", + "\n", + "Programming is always *concrete* and based on a *particular case*. It exhibits elements of an *art* or a *craft* as we hear programmers call code \"beautiful\" or \"ugly\" or talk about the \"expressive\" power of an application." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "That is different from **computer science**, which is\n", + "- a field of study comparable to applied *mathematics* that\n", + "- asks *abstract* questions (e.g., \"Is something computable at all?\"),\n", + "- develops and analyses *algorithms* and *data structures*,\n", + "- and *proves* the *correctness* of a program.\n", + "\n", + "In a sense, a computer scientist does not need to know a programming language to work, and many computer scientists only know how to produce \"ugly\" looking code in the eyes of professional programmers." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**IT** or **information technology** is a term that has many meanings to different people. Often, it has something to do with hardware or physical devices, both of which are out of scope for programmers and computer scientists. Sometimes, it refers to a [support function](https://en.wikipedia.org/wiki/Value_chain#Support_activities) within a company. Many computer scientists and programmers are more than happy if their printer and internet connection work as they often do not know a lot more about that than \"non-technical\" people." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## Why Python?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### What is Python?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Here is a brief history of and some background on Python (cf., also this [TechRepublic article](https://www.techrepublic.com/article/python-is-eating-the-world-how-one-developers-side-project-became-the-hottest-programming-language-on-the-planet/) for a more elaborate story):\n", + "\n", + "- [Guido van Rossum ](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 and still in heavy use as of today - was **open-source** from the get-go which attracted a *large and global community of programmers* that *contributed* their expertise and best practices in their free time to make Python even better\n", + "- **Python 3** resulted from a significant overhaul of the language in 2008 taking into account the *learnings from almost two decades*, streamlining the language, and getting ready for the age of **big data**\n", + "- the language is named after the sketch comedy group [Monty Python ](https://en.wikipedia.org/wiki/Monty_Python)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### Summary" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Python is a **general-purpose** programming **language** that allows for *fast development*, is *easy to read*, **open-source**, long-established, unifies the knowledge of *hundreds of thousands of experts* around the world, runs on basically every machine, and can handle the complexities of applications involving **big data**." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### Why open-source?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Couldn't a company like Google, Facebook, or Microsoft come up with a better programming language? The following is an argument on why this can likely not be the case.\n", + "\n", + "Wouldn't it be weird if professors and scholars of English literature and language studies dictated how we'd have to speak in day-to-day casual conversations or how authors of poesy and novels should use language constructs to achieve a particular type of mood? If you agree with that premise, it makes sense to assume that even programming languages should evolve in a \"natural\" way as users *use* the language over time and in *new* and *unpredictable* contexts creating new conventions." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Loose *communities* are the primary building block around which open-source software projects are built. Someone - like Guido - starts a project and makes it free to use for anybody (e.g., on a code-sharing platform like [GitHub ](https://github.com/)). People find it useful enough to solve one of their daily problems and start using it. They see how a project could be improved and provide new use cases (e.g., via the popularized concept of a [pull request ](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." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While these communities are somewhat loose and continuously changing, smaller in-groups, often democratically organized and elected (e.g., the [Python Software Foundation ](https://www.python.org/psf/)), take care of, for example, the development of the \"core\" Python language itself.\n", + "\n", + "Python itself is just a specification (i.e., a set of rules) as to what is allowed and what not: It must first be implemented (c.f., next section below). The current version of Python can always be looked up in the [Python Language Reference ](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, just a lot more transparent. Many of the contributors even held or hold positions in academia, one more indicator of the high quality standards in the Python community. To learn more about PEPs, check out [PEP 1 ](https://www.python.org/dev/peps/pep-0001/) that describes the entire process.\n", + "\n", + "In total, no one single entity can control how the language evolves, and the users' needs and ideas always feed back to the language specification via a quality controlled and \"democratic\" process." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Besides being **free** as in \"free beer,\" a major benefit of open-source is that one can always *look up how something works in detail*: That is the literal meaning of *open* source and a difference to commercial languages (e.g., [MATLAB](https://www.mathworks.com/products/matlab.html)) as a programmer can always continue to *study best practices* or find out how things are implemented. Along this way, many *errors are uncovered*, as well. Furthermore, if one runs an open-source application, one can be reasonably sure that no bad people built in a \"backdoor.\" [Free software ](https://en.wikipedia.org/wiki/Free_software) is consequently free of charge but brings *many other freedoms* with it, most notably the freedom to change the code." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### Isn't C a lot faster?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The default Python implementation is written in the C language.\n", + "\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.\n", + "\n", + "In contrast, Python automatically manages the memory for the programmer. So, speed here is a trade-off between application run time and engineering/development time. Often, the program's run time is not that important: For example, what if C needs 0.001 seconds in a case where Python needs 0.1 seconds to do the same thing? When the requirements change and computing speed becomes an issue, the Python community offers many third-party libraries - usually also written in C - where specific problems can be solved in near-C time." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### Summary" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While it is true that a language like C is a lot faster than Python when it comes to *pure* **computation time**, this does not matter in many cases as the *significantly shorter* **development cycles** are the more significant cost factor in a rapidly changing world." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Who uses it?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While ad-hominem arguments are usually not the best kind of reasoning, we briefly look at some examples of who uses Python and leave it up to the reader to decide if this is convincing or not:\n", + "\n", + "- **[Massachusetts Institute of Technology](https://www.mit.edu/)**\n", + " - teaches Python in its [introductory course](https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-0001-introduction-to-computer-science-and-programming-in-python-fall-2016/) to computer science independent of the student's major\n", + " - replaced the infamous course on the [Scheme](https://groups.csail.mit.edu/mac/projects/scheme/) language (cf., [source ](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 rewritten in Python right away (cf., [source](https://www.amazon.com/Plex-Google-Thinks-Works-Shapes/dp/1416596585/ref=sr_1_1?ie=UTF8&qid=1539101827&sr=8-1&keywords=in+the+plex))\n", + " - Guido van Rossom was hired by Google from 2005 to 2012 to advance the language there\n", + "- **[NASA](https://www.nasa.gov/)** open-sources many of its projects, often written in Python and regarding analyses with big data (cf., [source](https://code.nasa.gov/language/python/))\n", + "- **[Facebook](https://facebook.com/)** uses Python besides C++ and its legacy PHP (a language for building websites; the \"cool kid\" from the early 2000s)\n", + "- **[Instagram](https://instagram.com/)** operates the largest installation of the popular **web framework [Django](https://www.djangoproject.com/)** (cf., [source](https://instagram-engineering.com/web-service-efficiency-at-instagram-with-python-4976d078e366))\n", + "- **[Spotify](https://spotify.com/)** bases its data science on Python (cf., [source](https://labs.spotify.com/2013/03/20/how-we-use-python-at-spotify/))\n", + "- **[Netflix](https://netflix.com/)** also runs its predictive models on Python (cf., [source](https://medium.com/netflix-techblog/python-at-netflix-86b6028b3b3e))\n", + "- **[Dropbox](https://dropbox.com/)** \"stole\" Guido van Rossom from Google to help scale the platform (cf., [source](https://medium.com/dropbox-makers/guido-van-rossum-on-finding-his-way-e018e8b5f6b1))\n", + "- **[JPMorgan Chase](https://www.jpmorganchase.com/)** requires new employees to learn Python as part of the onboarding process starting with the 2018 intake (cf., [source](https://www.ft.com/content/4c17d6ce-c8b2-11e8-ba8f-ee390057b8c9?segmentId=a7371401-027d-d8bf-8a7f-2a746e767d56))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As images tell more than words, here are two plots of popular languages' \"market shares\" based on the number of questions asked on [Stack Overflow ](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": "skip" + } + }, + "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 domain-specific language in the niche of statistics, can compete with Python's year-to-year growth." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[IEEE Sprectrum](https://spectrum.ieee.org/computing/software/the-top-programming-languages-2019) provides a more recent comparison of programming language's popularity. Even news and media outlets notice the recent popularity of Python: [Economist](https://www.economist.com/graphic-detail/2018/07/26/python-is-becoming-the-worlds-most-popular-coding-language), [Huffington Post](https://www.huffingtonpost.com/entry/why-python-is-the-best-programming-language-with-which_us_59ef8f62e4b04809c05011b9), [TechRepublic](https://www.techrepublic.com/article/why-python-is-so-popular-with-developers-3-reasons-the-language-has-exploded/), and [QZ](https://qz.com/1408660/the-rise-of-python-as-seen-through-a-decade-of-stack-overflow/)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## How to learn Programming" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "### ABC Rule" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**A**lways **b**e **c**oding.\n", + "\n", + "Programming is more than just writing code into a text file. It means reading through parts of the [documentation ](https://docs.python.org/), blogs with best practices, and tutorials, or researching problems on [Stack Overflow ](https://stackoverflow.com/) while trying to implement features in the application at hand. Also, it means using command-line tools to automate some part of the work or manage different versions of a program, for example, with **[git](https://git-scm.com/)**. In short, programming involves a lot of \"muscle memory,\" which can only be built and kept up through near-daily usage.\n", + "\n", + "Further, many aspects of software architecture and best practices can only be understood after having implemented some requirements for the very first time. Coding also means \"breaking\" things to find out what makes them work in the first place.\n", + "\n", + "Therefore, coding is learned best by just doing it for some time on a daily or at least a regular basis and not right before some task is due, just like learning a \"real\" language." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "### The Maker's Schedule" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[Y Combinator ](https://www.ycombinator.com/) 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 (e.g., a \"boss\" or manager). Their schedule is usually organized by the hour or even 30-minute intervals.\n", + "- **Makers**: People that create things (e.g., programmers, artists, or writers). Such people think in half days or full days.\n", + "\n", + "Have you ever wondered why so many tech people work during nights and sleep at \"weird\" times? The reason is that many programming-related tasks require a \"flow\" state in one's mind that is hard to achieve when one can get interrupted, even if it is only for one short question. Graham describes that only knowing that one has an appointment in three hours can cause a programmer to not get into a flow state.\n", + "\n", + "As a result, do not set aside a certain amount of time for learning something but rather plan in an *entire evening* or a *rainy Sunday* where you can work on a problem in an *open end* setting. And do not be surprised anymore to hear \"I looked at it over the weekend\" from a programmer." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "### Phase Iteration" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When being asked the above question, most programmers answer something that can be classified into one of two broader groups.\n", + "\n", + "**1) Toy Problem, Case Study, or Prototype**: Pick some problem, break it down into smaller sub-problems, and solve them with an end in mind.\n", + "\n", + "**2) Books, Video Tutorials, and Courses**: Research the best book, blog, video, or tutorial for something and work it through from start to end.\n", + "\n", + "The truth is that you need to iterate between these two phases.\n", + "\n", + "Building a prototype always reveals issues no book or tutorial can think of before. Data is never as clean as it should be. An algorithm from a textbook must be adapted to a peculiar aspect of a case study. It is essential to learn to \"ship a product\" because only then will one have looked at all the aspects.\n", + "\n", + "The major downside of this approach is that one likely learns bad \"patterns\" overfitted to the case at hand, and one does not get the big picture or mental concepts behind a solution. This gap can be filled in by well-written books: For example, check the Python/programming books offered by [Packt](https://www.packtpub.com/packt/offers/free-learning/) or [O’Reilly](https://www.oreilly.com/)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## Contents" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Part A: Expressing Logic**\n", + "\n", + "- What is a programming language? What kind of words exist?\n", + " - *Chapter 1*: Elements of a Program\n", + " - *Chapter 2*: Functions & Modularization\n", + "- What is the flow of execution? How can we form sentences from words?\n", + " - *Chapter 3*: Conditionals & Exceptions\n", + " - *Chapter 4*: Recursion & Looping" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Part B: Managing Data and Memory**\n", + "\n", + "- How is data stored in memory?\n", + " - *Chapter 5*: Numbers & Bits\n", + " - *Chapter 6*: Text & Bytes\n", + " - *Chapter 7*: Sequential Data\n", + " - *Chapter 8*: Map, Filter, & Reduce\n", + " - *Chapter 9*: Mappings & Sets\n", + " - *Chapter 10*: Arrays & Dataframes\n", + "- How can we create custom data types?\n", + " - *Chapter 11*: 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 book, there has to be a [xkcd](https://xkcd.com/353/) comic somewhere." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import antigravity" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## Further Resources" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A lecture-style **video presentation** of this chapter is integrated below (cf., the [video ](https://www.youtube.com/watch?v=YTU8jaG27Xk&list=PL-2JV1G3J10lQ2xokyQowcRJI5jjNfW7f) or the entire [playlist ](https://www.youtube.com/playlist?list=PL-2JV1G3J10lQ2xokyQowcRJI5jjNfW7f))." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAUDBAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChALCAgOCggIDRYNDhERExMTCAsWGBYSGBASExIBBQUFCAcIDwkJDxgVERUWFxcYExMYGBgVFRgWFRYWGBcVGxIaEhMXFRoYGBISFRcVFRUVFRUVFRUVGBUSFxIVFf/AABEIAWgB4AMBIgACEQEDEQH/xAAdAAEAAgIDAQEAAAAAAAAAAAAABgcFCAIDBAEJ/8QAURAAAQQBAgMBCQgRAgUEAgMAAQACAwQRBRIGEyExBxQYIjJBVZTVCBUXUVJhk9QjMzVCU1RxcnN0dYGSsbKz05G0FiQ2YrU0gqGiY3YlQ0X/xAAcAQEAAgMBAQEAAAAAAAAAAAAAAgQBAwUHBgj/xAA9EQABAwIDBQUGBAYBBQEAAAABAAIRAyEEEjEFQVFhcRMVU4GRBiIyocHwFjRysRQ1QlLR4fEjM0NigpL/2gAMAwEAAhEDEQA/ANMkREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREWzPgVcVfj/D/AK1qPs5PAq4q/H+H/WtR9nIi1mRbM+BVxV+P8P8ArWo+zk8Crir8f4f9a1H2ciLWZFsz4FXFX4/w/wCtaj7OTwKuKvx/h/1rUfZyItZkWzPgVcVfj/D/AK1qPs5PAq4q/H+H/WtR9nIi1mRbM+BVxV+P8P8ArWo+zk8Crir8f4f9a1H2ciLWZFsz4FXFX4/w/wCtaj7OTwKuKvx/h/1rUfZyItZkWzPgVcVfj/D/AK1qPs5PAq4q/H+H/WtR9nIi1mRbM+BVxV+P8P8ArWo+zk8Crir8f4f9a1H2ciLWZFsz4FXFX4/w/wCtaj7OTwKuKvx/h/1rUfZyItZkWzPgVcVfj/D/AK1qPs5PAq4q/H+H/WtR9nIi1mRbM+BVxV+P8P8ArWo+zk8Crir8f4f9a1H2ciLWZFsz4FXF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hodQ0EBkkco21dQ8qJ4e0HOodmWhZBgrB0X6DItAPDV4q9H8P+q6j7RTw1eKvR/D/quo+0VhZW/6LQDw1eKvR/D/AKrqPtFPDV4q9H8P+q6j7RRFv+i0A8NXir0fw/6rqPtFPDV4q9H8P+q6j7RRFv8AotAPDV4q9H8P+q6j7RTw1eKvR/D/AKrqPtFEW/6LQDw1eKvR/D/quo+0U8NXir0fw/6rqPtFEW/6LQDw1eKvR/D/AKrqPtFPDV4q9H8P+q6j7RRFv+i0A8NXir0fw/6rqPtFPDV4q9H8P+q6j7RRFv8AotAPDV4q9H8P+q6j7RTw1eKvR/D/AKrqPtFEW/6LQDw1eKvR/D/quo+0U8NXir0fw/6rqPtFEW/6LQDw1eKvR/D/AKrqPtFPDV4q9H8P+q6j7RRFv+i0A8NXir0fw/6rqPtFPDV4q9H8P+q6j7RRFv8AotAPDV4q9H8P+q6j7RTw1eKvR/D/AKrqPtFEW/6LQDw1eKvR/D/quo+0U8NXir0fw/6rqPtFEW/682q/aJ/0Mn9DloT4avFXo/h/1XUfaK4WPdo8UvY9hoaBh7XNOKuo5w4EHGdR7eqItaURERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERf/2Q==\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from IPython.display import YouTubeVideo\n", + "YouTubeVideo(\"YTU8jaG27Xk\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Also, to see some common shortcuts in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/), find a **video tutorial** below." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAUDBAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChALCAgOCggIDRYNDhERExMTCAsWGBYSGBASExIBBQUFCAcIDwkJDxgVERUWFxcYExMYGBgVFRgWFRYWGBcVGxIaEhMXFRoYGBISFRcVFRUVFRUVFRUVGBUSFxIVFf/AABEIAWgB4AMBIgACEQEDEQH/xAAdAAEAAgIDAQEAAAAAAAAAAAAABgcFCAIDBAEJ/8QAURAAAQQBAgMBCQgRAgUEAgMAAQACAwQRBRIGEyExBxQYIjJBVZTVCBUXUVJhk9QjMzVCU1RxcnN0dYGSsbKz05G0FiQ2YrU0gqGiY3YlQ0X/xAAcAQEAAgMBAQEAAAAAAAAAAAAAAgQBAwUHBgj/xAA9EQABAwIDBQUGBAYBBQEAAAABAAIRAyEEEjEFQVFhcRMVU4GRBiIyocHwFjRysRQ1QlLR4fEjM0NigpL/2gAMAwEAAhEDEQA/ANMkREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREWzPgVcVfj/D/AK1qPs5PAq4q/H+H/WtR9nIi1mRbM+BVxV+P8P8ArWo+zk8Crir8f4f9a1H2ciLWZFsz4FXFX4/w/wCtaj7OTwKuKvx/h/1rUfZyItZkWzPgVcVfj/D/AK1qPs5PAq4q/H+H/WtR9nIi1mRbM+BVxV+P8P8ArWo+zk8Crir8f4f9a1H2ciLWZFsz4FXFX4/w/wCtaj7OTwKuKvx/h/1rUfZyItZkWzPgVcVfj/D/AK1qPs5PAq4q/H+H/WtR9nIi1mRbM+BVxV+P8P8ArWo+zk8Crir8f4f9a1H2ciLWZFsz4FXFX4/w/wCtaj7OTwKuKvx/h/1rUfZyItZkWzPgVcVfj/D/AK1qPs5PAq4q/H+H/WtR9nIi1mRbM+BVxV+P8P8ArWo+zk8Crir8f4f9a1H2ciLWZFsz4FXF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hodQ0EBkkco21dQ8qJ4e0HOodmWhZBgrB0X6DItAPDV4q9H8P+q6j7RTw1eKvR/D/quo+0VhZW/6LQDw1eKvR/D/AKrqPtFPDV4q9H8P+q6j7RRFv+i0A8NXir0fw/6rqPtFPDV4q9H8P+q6j7RRFv8AotAPDV4q9H8P+q6j7RTw1eKvR/D/AKrqPtFEW/6LQDw1eKvR/D/quo+0U8NXir0fw/6rqPtFEW/6LQDw1eKvR/D/AKrqPtFPDV4q9H8P+q6j7RRFv+i0A8NXir0fw/6rqPtFPDV4q9H8P+q6j7RRFv8AotAPDV4q9H8P+q6j7RTw1eKvR/D/AKrqPtFEW/6LQDw1eKvR/D/quo+0U8NXir0fw/6rqPtFEW/6LQDw1eKvR/D/AKrqPtFPDV4q9H8P+q6j7RRFv+i0A8NXir0fw/6rqPtFPDV4q9H8P+q6j7RRFv8AotAPDV4q9H8P+q6j7RTw1eKvR/D/AKrqPtFEW/6LQDw1eKvR/D/quo+0U8NXir0fw/6rqPtFEW/682q/aJ/0Mn9DloT4avFXo/h/1XUfaK4WPdo8UvY9hoaBh7XNOKuo5w4EHGdR7eqItaURERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERf/2Q==\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"5GGyKq1F_5c\", width=\"60%\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/chapter_00_intro/static/cli_example.png b/chapter_00_intro/static/cli_example.png new file mode 100644 index 0000000000000000000000000000000000000000..0e40e0491cd739cc1ee52c726bffe0588deab082 GIT binary patch literal 29502 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 oeaNdgu4nCRd=l}o! literal 0 HcmV?d00001 diff --git a/chapter_00_intro/static/example_python_users.png b/chapter_00_intro/static/example_python_users.png new file mode 100644 index 0000000000000000000000000000000000000000..b123bfbc79c70973dddec53ce8bb9039a2af149e GIT binary patch literal 364253 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�Eo$%FgFlU(8Vz1oC+UTO-cgM$5y!fEtykTyCu3k*jN^p`1zRJ z0NsA9)1^~117^Of8x8QIXVTdS{5vQU`&%GIVzK3HgBL0Ov z4MD&3I3gW1Ubl zv~C>C=V3P#X1LWT;5iVW?ya8bzenY@r@buK-6-~Y#hkx|LSJehE44-4*+3|sFsY7Z zUK(C4W7YfuS7F(79t+Cc8!n552$&2Q4n<1nLINJGKEpw$5~he3SFk`MV(x?MC&rUD zD3V0LNx*q%NMlJHoQ<{mC|%~rEX!7Gko8cDFSgxF_qOPw4MYubD@OCqrJ2f-<2EE) zhaj?}Tvvq2Msw^HwAeUnQLqG`@U`%CYqs)OUm;C%v%zCAN!)85zPrs$_KJ3UnP}lnB)E(=STui6DWLjWs-*{YX_2KP;g8v(Avi_TOI)*LOW-G=~Z_v!pX~sG5 zx)>Zlrr>TU;Q8b-MPhNp8ugt4MfuH|xx_Y5tOS9pPdH5@0VOpCWf(E;?uDjEu>Dw&K{H8aqf4=kCr93uYV{?Z}(~ocD zt*CdtQs-$~7(9UedF?`E&fDz&z_cspd=oz~+9*ovAWM>9v+zi(AkRdtObREG$Gf~G z7SKvkCxJTesNdK}6l;^^#3@7!B->upcL_Ai)S+~4Z%Rtw4KB7bzN{z7W9B4NpkmJl zrY;avS3hmf?W!k;q2lUCKB+i#5lYmgY@YZTAG##^>TmeO%5;JdzV9^S%#zu`xO%T0 zWrw!P4nh^eR1zT6{~3lGTcOMS4g>?#>JPmN6oxFN>_Gtd%vo~y8*C*hafn#LEMr_m z&=IkDAdvalXT`^o>avf{Vya#T!_uB^1zm%G^G|JD$|QAKmiqOTMmQ|?PDQ;6WG%lr zvYN=_7ySi9&nntM(GLw_(+FV_H5JG%2W?cW+D7=~)y>Az^yzCA3fIV)=}qY^CZPo; zs>g+mgpupAKxAF>?>2ejC!f|)pbD6@nau^nu{Y@?au5H?avML?uvooiq>9&ayzR4-j8?!y+wZmI4%jM#6k-ZUxZRN^ zjK^2~#$-1tj&5GeNL+=?_Ss-f`wGeW}!e3ETTC>1URVV``NOs-%`p3oVNHJY;CW9r_D90>j4~IA9?cp zu9&)AcrT@IU8m)I{Bj>=UkH0M-gahz|J!{V!v8Wo3zZ0y<+HIY+SNJUxN9f#m>2aK zlLfBDF-!u1v^zv}r8Ic|r*GAtdj_z-A4@)6g&4EVrQqk}+CLgoE zR{j`H3tJ2a`xL+kKrqlXTsG-4I)Mt9Y{fvUf(PD1J5F93CDwvU6aI=O|1O=9MqCg+FfefP=)6*(Dwl8p#)2?1UFbTM2== zILU}(qw6K~#cv+MRV9NyL0%embI)G6*8s1b?i^H36KGDE4e_g=3E$qIu~Q%WJ$7~t zd|$pQ>RUhl-12ZL2LC)_gX|h~l3&r8ADh2hB3nGRZGV_cZbQJ`F5(zyS&R-*MXre- zg-`>?B-wxnY;7Xt2ZXjt+c5>LiJhv5{iGrz3r2wg6FUo((UYz80xfGZa%bCwAP~YJ z@pfuaZ6Z&H4h~VAcWMFp_>WuBZSQdU!{Llax9$6zOKVe(N|4Tp`NmIWU%d8OTy)p0 z!~G-l$#>}q5DP=wevFwkiWBa7Q~ExnaB}x`{qrcFZ{*80y&CYjnZaPgvyJAcoDI<=LWP(Mpg;3uxod9k?b9(v8X? z{xReg#^DBc!680;!c#zs!*R+F<*X-<_Dw?vXimB;yNmTL}HL;JrG@XB5FKgwYH z5Ki^C6bvS~Hg%ldl>Mo~jk$IAT(1jt2tChP;2e1zWxn1}& zHy5Bu+nniUrb!pG_sc3&L9N%m9Xsm`rGtQFsNq#>0p!Z=MC*@}CV5#j9Bur}`JOoJ zfl*XcnTYeAiNbryHZcS5s`rN zk6)jrHh15P0-$3V7GWjgiS$*KnvQFWC{#N!*z!cn8#t1ABEaCdK*DW0%@w7ykTZqB z)>zPLPTMBtY%xe9Vc8q;7fIt=8eVg=^K1#R9&GZ&PDsNAr9pnBZzVhUEE3%hK{#Ra z8_5|Zv0=-#cEeDXnj{uu?Z8)PbtT&sWoSYo7&o@;DkW7hzVcE(>!IyRQFQXLhi?Qv zcTD;`E+N>n?7HhGi)H)J4!ZjptSgV>PIGm9t+O9tV~5mJ@ujwT|FIb~RpWZjXx+kg zdR?=cj5+@g?*5aOT`4I2*9NM%7~fYLMEsVmXT`zt0YDi`jqzF5`$y{&uVa>vIgGg$ zc5N<1fvBn^m0rUX?BL~OER7Zuj|pQ$X52UmiOesBE?K{y@8>Mp!rfgVoo%v=y)f@c zw!GzZ4fB~1{IME=vM)j8qnScCkq*ucjvI1jUFR=L2ldLz#TY%?^m5vI{B~LMd1ZFI z=RI9LAax!h_pIC6+vz_s3Uq{x?hvBgMT$-EyzCCQiz+LEz{o4fw4!ms z;bA7fNP^iq+IK$$qcl3!ptBO0=W<#FHQ0TzKppTA^xR_GnWFOTou$s=68-7xppsaV zL#lak@C_CG_u!V~ef`4^z|$@JpBs7y#~bauJn!-$gZDcgk2)YOOqt7!U#v+`7X3c+ z`t_I7r!3TK2lw8;^nDPR|DR0Qv8=I4g5T%%V?dd`*%1J^esA7tKro~oMdo{NK@E$r z49M+T_Pi@F@ysmfnFuC(DGpq)A6bsrkZq-IAmE6~&pakqXk1;IH7ZMyrWesVXIJE@ zZC2;uKkySOM9YG)FTOR=ZB8Fp!t8PBZo8WOYUTF3*{<}BZ|?E7`=2GYcdvGW(9}D< zc|s=G-|2Qv<(zYv_cYnr7OD@oy!;v|a)soUUpyPu+M3HOpN)$IeG)rcC`+7| zh>HJ@`a2WjUlm~+?%&juq7_)csC1Jv#_a9;;-zjJyRIpR@6+ZLZQp14*`*D0E-8)$ z`(=jwtbe3g8+RZx6IswX&~HjB9VIS9PFI(=#*wY+z+|iyN7Ke=DH0)&tFZ)LI(Ymb zvgFH#ok^0q1vM(U-3Cqtiq#NUfoS|Py#M6u=-RhtI=A{k9|YDSP7YYAU23aa;-SsD?aNE6L2pg`=Qn?1&|5#&1ouj->Wp7+_k$r1OlPippAATG z<2bLYCGp;n>vLMBNTQjcj{$}v#=uoMtd5`O=>mH$w@Xl*|1QK_uT##>>w?$&?VRoP zhi%O&;JS9Q{z_pVTBXRp4AxxfG2<+h)gWz%FXScT@Q(5?gD+{PG-*pW`_m?)tLCs4f96=VRr2NYb;`PAxQk`$wq{W zFF&lsiuR!iF;dSftxs#B@f zsu{iGPRGXkyr1RYPm@MdYj3XfUQU0vw)=}dJ>&E1_g>KnJ|ztKJ`NM9f2kx1$D3ut z2N#}nwX9obqc$5xLL}5RNrZpvxEDCFTy@Wth^^!2w@Vv3acA0K&uFbfepthLa{HMw zZ!S6>{wZExxA_+g}_Ff3>fNGm>Q=5WD@;nAP+t9a_3 zo_^+20TvW`i0M#Vi$>t7(uP7{l7r(viAyXeI_m}O~CbF0vcatL~gvKhJ z%S|ag>y)2D;R$|(dt*f0qe*q{kAi(3 z1#993e|rHUWbv7=qv8nndV^k6EhduZd|t0>e2J;;y2^ho*t>XF)h+%l6YshI8wQ7P zl6|Hfw~^Ir6*k>vu6@)C{1@nEwM;gn$64lHOSKai9yhDKicvJ<=JPR!PhixzHg{(}Q*D{oI_v zW>maKx~i3$U%yz?YT+^ISm3;qnDH{Vw{hUlV+yM;FsA?PU6ik#&ebfcv=zhOHk1Yi zQ{E!RLczhw{(ykE=X=bYK>Of;y|^0gA^LD>fhy#1G!AN5nb=;@yfKSnftTbe&o8=`)R0E{O>3G&~OqdTd2*0>seh}rhb?hE^rKVPItGva0g;s|BuQ1Ws^Zx{G(%OH6ZoitlzgYb7l<9_e5_@&D|5ebkVe)bTAQ}c_2l(b1?E8lf?n_e)XJK-&ocC&I%RuKH~eci@(C< z!SDFzSpW%m4Pl+lEr%JWxGV@ol`GMoeSQ2}KqFUR=)nMVvld5iJU^s|bJAqUw~EMnQIY|QO2B751{NCk_L2uvS^Um_nX!c*lCdH2eyUwEc( z;a3*9{F?4>FJZ7DkI65wDcNYWAcZ32+!|e?fQ)&C8BRxtV1rj_pBCkI z_*!~UQO4}_Y)tG#M2$G>p-eDuoh&c_W-RMEVlspEK%{N_p+#QDfN~BU-uud)rs5p_ zVF>0Ws=(*1RbYoW58m;c0@K1#{YJXD>0fN?#;5tOo;6UMa&~%6rY;+`{*`gi$AM@l zrLzVYemmG;B3WJ;5AR{Wg1O!ln#U&?ma*>}#xvOVu|4{3zq_ zFQqPjXKcw)*b;AuV3%I^sJ$RzrItpc&n;6U*8S;P64}|ejyio`MBEk_WgL|MxLa|X zf~S|8byQ0UfL4&Tdl#M7lLuz!n8c5vn4D=k&Z3$SbUawtP|E6Io%(hJY@v0aSIl_g z@UrqU=BzHiHorXufbl--W{RRwn*K~kvDXqU5E<9gwidHfNt66#1U|M{XK*mG$X{y< z+uJ1aC8#tmvaq(L?Un>dY~Y!}M^f;ftFdu7?)yGz?`@Fk>lQWB?PLj2-`yjuI?pe) z&z-LSRO$bVQf*=XRfjeA^X*5K>4D2{Exul6(K9|iG9F7+c^-ke0;a4hyGd(g zZSW=QL1!!|;{z-{x{3~Q{r|kg#sc@|$$i##BFgDnV2I-A*cw7ti|N%>^#i>WT}9Z! zDwMeQoSAQ8G&s3kt|{{TEpy^FnxXk@??Q@jErkW%kWH3DGt~SIe zg4`54O)M*twi2QwXXL4S%7xkF`zA5d>L9%&)hXPW z$0bb`Mh%O5tc%7dg^N&0OKl9`zg`CQB#J9LAP7Do&7@W z{eBF^4`y!#JC7Hq@{I_Rqb1A(MY}*^@ft7CV*-nw1OlLheVTr=2EEMPzPT5nF?T!J z*E3xwsDJy|6Xr}cy4o!}_K(Q@A1`*C`d9E%S~r|)?dPa#`B_{j!@F&`nMGqLmo?VB zzh5(L(iV91$kBz(P5Bh1d81NCH+D05Q$OxdV>Z4-N)^NN+iM5w&vmx%ErO!&Ib^=9 zEyYV#5SL`M8!QTv);GI|D(O}b;v>gsNy!@ z1sMofb?zStJ%MN<+78k_6NeK}vLRl?Yk)bsPK^4^UA&fIgr^KRIdEDFJ$LUp`FUpm?NO`x)zcUkMWdI#IMpUYZaV)yW=(EDy=5Oo|4KMIun~ z7H(i3#QC`E$P}W<3rI8+Y1FsQghIuIK{4m}))--nz|Y~S7di6n!1 zl}iIQfF1zQ?;C)pW_B@?EiVZQ5voCj=E5%jV64G(Yw86ertmSS31;zdtfbaKNl*-A z#}(7czq0t8%Vk>tI0oOZ3HqK|5dDuN4&jOUM2@y<`^f)4Gt}e!D|$uisuN@QEr6}& z=r1p-yUe0@^Lh5I)&9DlzgpZH&21@kIp#CBWu0UTFnZ?uGyz$`aL6THW!d!&+A63- zRd!<94`gatXB=b!1*Em&qV9fuHe=H5{d`e`2h^R1$^+(os{U=))(pnQ?3?Rd%^Q`n zIQLME+1(_NwkIHiT77ldKfQYL`)SBxdpFPjgVNn!jqib1d*D;tvjpVg5L&dSUo0-c zQlU>h9O5f(^@VfzC*yz`V50z_uzS164mXi6|UB9STQw_ zyvrj-Am7XB?kki@ArTB$NLzqLCKFAfQa~Y$g}THy8yqVxQo>ii;X)Yf56P|pWKpiw9xTCHGEUzU+|%+ad@!2_G-ff9Y7xGX?$yJYUQs{HA)s&-`+W+ zd(#Rj)*GAUkd@|;b|(nj>nU2ip4bmcKex4C>98dhOv7w)G?JS2GQ4WQy;@>rZu$n_ zE7M9z7pQsukh^WBWhSLMJ2GhnTOaM~-{tBp=RL=_lTZP0t>wprb3laBU0vPJ`+kn` zXXU}B^5VO z_?IT(QPD~lKW7`dowVh2Yu*Jf>F>Q<(RuHfs`}~_w=9P z2cZKlb$W-V8cZ(aQ2Psb$}ehns~b*BZ+MQL1|Wlh<&V6`*s~3uAjGS|3a~Wl@9UG3L8ZtV5nh zbq&$+<2#cq)_i+}2a!XlYEd2Bg7k=;kh6!zq!CKyMU^>B3!b$M%0LMRrMA}3BiM2X zH3DOH`d?@(FZjN1N9Wnynzs?qD^KI1KAM9~(M64Tb6*sp5+4mMTYd2nMHM7ZC92aX z0NfPVAms*QH3DH558d*U10`tw zkR(9cm3p|E7!c?Z0F5&WV9 zLWvw+`RKdvE?{JEv#Tma=pp$Mj#&hbenj6LV_H&>zTYiy>-YF(_C2o=5qG_iYh?YdNx|y$VCg4+6kM@ zuBaq>?-#WE&m*F`TRSEbezPM1VO?908{(wtG@{Uz!l*;qvbeJAezvD{CaJW;9fO^L zr%z=T8Ka?M1}CTd)UPzRA}AIaZuoUq-uYsMpucnCv^SyJ?QI`2M#SGA0ZxCB@MA?v z62j%ldlu4HyX*2fIVOXHFaT;I6A)H7so|KG@MSUuyJ{`CB6mf~fXX>Qjw-ri>8v#Q6H zq0g13>uaO&`yWp?RDzLZLFodSymX*tM^{g$E@M$*f~-SkW*Fr`$zl3-C=vuQP za23S5((ZYLN8D+_oacXMfA13~c?F)tnW(1}J)vTGxUME|bVn9iX@1L&jFg!^nEh>l zHz4~d{-O{sUV(=ytxoor?MU)NCdfx?A$VY?1|}24{bYbTc7>YWWgksPN7F6m$~AqSFVC;oQE9fPvrr?{ParE4hM0g1-AufZFasX1{w^UULYSA=fShJ51>UbiEjzPFgB*Qzw| ztyuvgf$qU=6sI>L1!opLFyjg}yOv{LFkwBWjxK`LOFk@2hPfT4kIUzqd zRV;jWgmm%lk5(%HWc8>~P|DMM@Jzi7Galf3{eY~6{t+uO@uA7Xh0$7ab^g^qybboO8rrCS;#c2ce|?e)4$6|b;S@1NHwkRRh^5belhbKpRbY*aSX1XKb$ z2fFy%LSl~lPB=s(+>J9yC0up^I4N`xwOI!en#W5Y9oTO{QQh8`Pz=B_<_R*HY*e+y zr*T|F0nao4m=UR?kk0Ml2qn(Zn}yuFC+lJEypgoqJP1&PlMMwL5KDtDTHWXjp zW8=65-mA~emf2ZOfByx-Jz(*_6@$bmgSA*+z*O6Siq(o8HHNzV#wvPijv7f$igW{S zD%=xQeqzqI1#1K9On5U|PxWM`E8G?1C9thaqq=AQx9d zyTwc1y}Ec>1tpRCnYxKRsk^T?Kzc7TKEQqU_b}$XgD#8C>Y9JLq0s_}7CvsxTV$8l z#%7*Hb|G-~pR6B>FkLN*l#)u*LNZk#+cCTr1c8EV zBv@8we^pX8dwraNC~#XqPOwJLfQo7jfvlY5NGg37O{P{Wx@dY%jY{yQ>+%qETZE@f zpg`dUA6$%OS@igGz(+bzLIApm1zB2ZUag;VgyCqj*u*|=*;1_^+PkAkM66wX#rL6w zmG93Pr9GSMa6R$~36rt52_Ec#NrUjPk&xa4v>wGa?=%_NPaso4=B3^Cw;xn{V0pY6 z$v9a{zdl#s7XuYlWv{uOw$lZuAzh@SQHG3XA~giAf9f#+cMjun{bF_=|0rDB2rd8? zABYKGFEz!Y?fRhqSJ-(;_%~k7>S*1x=Xjxf)XdsE%mD7^@f{p_q9-g?;$g7M3U)&0 zt$+`t%k^qhu{WC*CjzzhT?3<&jjo320gp8fo4X@ZK3uJ>Y2-fq3Y@4`jskk}l~yA? zF0rMxPX2?D_XBR}8VOy=4Jbuq-_orJma)opSn3&zk#i*nxQAgs4?Sn?(iJi|jesfU zo?Wk}a6TT2+W3Bq{Hb>D9M+F3p;N*YqKH(XUs@Zb!GU0%SECb`t7&>$I5$DI8UxV((5x_@F&rxOO z6JhpUPY&mUd7F*lvVeI{-CrQ|Ec;h}+N1Eu)tS<1*Ap2eGufH-l51*eFKLKDoiQvP zGu)$%jY8(oblSQk!<^pEA)Di6j~mn(0@iEeeFy27wXnV4O7tB+YM_?#7P@=SY@!Skr~;fCx3+!>}!##_DcOOaFu zx}>gXYB(hpu z;o_(+A8mJmr)ioiHE_e0?MXLSYVGHh&oFADq!E^yxTC-v$( zjkqCH=)lV#De%IN*l;K%hQgXF`wsY&RWE~Bg<>Y*nXvcqgAS#a%z29QFolva^qF@F zN<&4R#N-H8@H{_`7x*5$0NUG+%HPoT>9S#FJxB)C`@$^qO5#mA{ouOXx8q#H5ybP# zBXP=0r9kBgm2k8WyzrFUWiIEpIApQO4s2v+9}fA%o@UpooiGBv&c=m(F!b{!J8 zImcx_&z^ov%dOkS2=S-@K;~BEyER zk6|shQ?E&y{bf67IJD7{0)EWbCi`>32r`07J21AtM{Cnp(ADX74}O10QRVZf9ZE<6 zC)ijiFlNO}%GqVCxp#O@IB7cBC14q)ATzBZT=nOt9xG61i_cEjRS63o&j%-&F)L0o zI$U1wB{G3Xiv*g7qJw+W1lIACL*h~A_n7uHyXBOiD1pt~e4fp^F8R3M=5+$z_c%e+ z_v8_7sqHk+ zG0T}$+n+rNJaqQuj%R072G85H`h93tJT{tR2PMKN&u`-kXr9VE1AA9$V%1jhBqXt; z2*#q6L~0X=NE2YRo`Ei!)mqwTNP_gi<)|)02Qdw#BN`L|G|gba#d8wHe$+p}!DBK9 zD@e^0xZ*73F$tFIv`Y3|;s_4M3qIQJHgR*`-dJB-=70wcJ}aC0Z{#G78Py>Z>|4T*q@wKaydHTH*3mH=D8Gd`ENNQ&`NWAy1 zhd!I9ZJ>-uA26J$BpDVr>I^5JpU~|6{R{F0maI8f9uFo3JX|SUsu&Dh<8Y{EcELC;*nT_; zQS8fUp^a!Y;=<)1`C;bzH|@2+TXY1mA!el$RFlw|rHU}0zYTTy_B9toSD8HI zE}49#qoF4W@@YX2GtLi{f~&M2>cbq!K*bOvn-wQoUo`uDAo+B&Sa<#K=Marq@8X(X z&>PvO>(?+t(*MZpo7{ie4+fIpJ=hV-ECA>XcIo7R@FHc> z&`5#73VOfZ_h~PEZod%uwrE%N#BswK7v0G$$AX|jwQUo&51mVa%KdfQ}^=Z>e^Zi)*)NA)?o$Z zvT-?2$sv0K5epF--1=@$ta+VT)cW1i#IJuYdVJFLzWM$6qiqB1naj{I|2kp%HADAD zdhjL;1&YvRAcRy#8_X7qxZ%_1!HHt6401r#+mA}gJo2{$fQQ*mxFbYru zEPn_|g~O71pgAj2x#YRbZrjxC5Zi$G0n^zT(}#s94_OxD89gid=S?fU)6;++0no$p z=h!>Yp5koY_yuiD1)F{-0xXBa_Vn4WDsJD$E9p~*_tz&~&%F>f4vyHTbaKhqtGh+T zCZC7PBs9^$&Q6|E`K-}m=_HuIA6AQ{^Xu#0Dq|-=pxOCy4Mx<66Jc5M{_E|WU`0bT zq_&NVC)DBJUH~2*p4DEzm-IN;z#kQ=Wedy8?r`K&@uZ@m2)O>d*eb2<>_atfI-#lAXiAf@Ata zG=wzsuCFiUQA+RASM0Z9UH$ZgK_v@EQ4bnGB^N=}3ZUkr0HdVfLG$#F9ml#BlcI&# zW3NAw8c*m&LUM&*$ly;X%^tK2uz)s@R3FB~Z%=vS$Svzn@?=R+fniVhoPc8_YeA^@ z6%5_;PHSqNQPRuHhcib!AlYq}nG7zXx3X!vCu4+R#l z<61{IghbE_k(WHf)8P1*Q%?_6_aXtb*&v@e3UFd4vj+^4)cp+MG6bR3T zO-&StrKP1W7PADi{13XR3|fOFY5Fqq@-t3x4Q}V^@6UCAGC8+BO?1bOZoMvh9`sY0 z^fJ|(-l%@AtxbR<^Yv@=-lVV)FD@^Sh6%nJ2|UlojhF(TSMGB=cvF=P8cP+!1aD|ofj-0~M_jJrQbQ&>_Rx*|)#wcjdmp3I}JS25H z-74z)7NQ@UikChgUq*F<^qjZbXCT>?4J4%PaHI&3L0D# z8kia7V4wvg4m?^kX7H!nxKt9kV8-A>KICS~EB@wRnu?C?6q-#gKL}rZv^d&QylS}d z5`#PI4TZeyA^Q>)E+^1BtYJvg)6;?l5Rwr>XviADVhUj}_$FfC-l~1gh@rj7$odk{ z;O0~f5hs&88^n}xQh%z)-}=$qCr$z8IfUTDqe&M&?)xDR85D~xSEggx2&G}z^GO^Y z6DC7dF>0<#ma0w2a^~h*p~|p`XNib^O>6%AMZG8)THVli(UvrvGLu2!Zi4&^STV^B z0#z&UIyx+x-k`f1Om)8z_cLP)uzJ%wT=8wr|NSn|zgA~eLXU~7bZN)3SK?PJ;i*fD zz}LZpnV{BI4yN8m<5I;u{f3^VrX+(u&nvbYtu<#(X?pIstmf1G4{wpcC@jXp)>e02 z7B)7vdfT-|qcPw{TimZI4VSx<`O2{^*G2h{FKV_MO%}*u$i$^}b#Xz^$P2%IeQPj~ znW(Dkon)Qk6E!g*|M`Ewn5u*aW z;Mo;p_1UVv{7xx}h*=j*4zGTYvIi$UH`jG^Z{Z&4gVA?g57$c63)QGkZC%15M*$uAX4$HP9rF`)SbUzRIjhj9B*yiVPJ|p;?S#f(Bc}<6LN!F zqxOkFlHIR_&&`R*$Ge!%ag-uJNXF(|pmxQ4{f$mp2t1y|4tF#?{-f`pR_@ZHW#3~KwAM(U-)p@845RIcaw+rJef#ic zscMd!{sYy{Yw~@e(W9)sK$NFe_V&fXVAd)kf0oD)l|>tdR#Lcx!lUdwYA% ztAnN>SgBUcT>u)D$;H7UJs!3Ofz$Gb3#Xg?Mqcmpy}H0ZT4p9D{wi4lHE%~UHRcz6 zPDc!#`UrO}Iu7G*GT;8&Suq_5&3h5kmM8mdxF)`;u(Z5#8-c6UB)z?Hh!08W{^vWX zy6@$_4)!vNZTcq`y)LC|)Ef4FWg{=_t}VDA^j-Ze%TSYG1vbLxuQR&-1i79$-)fLW zq|oZ2#k6*1V~NBh{OxuM`#Po@rPGkLx`xh&@W_lnxOrrOZ!G8)8L~lql6> zIP$5OE;Wp+C;FJf7spRjfQl7?CQ4P7`(p5mUM@?+%%A`*&19^9Jh<$xIZp}(Sw&`k zY44jw)!dj%x(RWG^I~Wj6*jpu*WvneR3fuvzasN0DKHHKCoSIQsAyS@{c9 zO}*$YZP$EFbha>QMb?DT+I%c%pxJnd6kYaW*dx*#5y9Br!l^ufCoEu!0qUr0e;{=z z)}{3_pPqKLrmAd)jCfHk)#WAr977w;Q&w8c@BMB?_>5lk$?Luz18OWRte!H|PHpiR zrnDbmIA;sFlu^BUv*FxqO88`@jM%w(A$(vYS2PQBVwWEsXsD=78;MFk>urh2U%uNo z9-!VX5x=u;5yzBr-}~7`?$FkiE9(9G_wSVthu+lt`}^=n(Y!I2nb($B^Gh=`0u^LM z6#XRb`*VGfgltx}wjVn>e3jR8Am{t@1K=yN3{rT?d15(V#>8%t2a^7W3ED{Gak2l! ziJ3i{Kjn-+h`TOZE$7KotnYY>@Tgjgi4J}-P^cXjNSKDG1jip^7`_r@6u2{9(70;T zOMCRisV|sOflio|fV_Hv|6RepUeqhpSJgBkYbgy-Av&qn`(P)Afbxd;lryXb=kXz1 z8>b>D?AT;I$hA$EiGI!5OVw9KQK;$$USvBZ_0gi+ zmSjM|MKwnEVPhfN=A;Qp!oh0@$DvZxV`gQLd>WI0YbB*B%$gk#@Yv?WtAdKbYFIah zIXQzHbrS_rNB^S#AEHFDlBS~8hEN&^7gOXQWSc&|Ev=%y3Q0=Ns&WwJvA!J1ME&#^Zm5Jbi)QyJ5|nm)ru|8z z&Lrm(cefPJ^Vyt=Q_<%~CS|9EbG_TE9q)lHO-s1G&k(oU1s&^q68O2O67 z`s-F)-%n~hTy{9ZGP&U*Hj`}4@=FpAZQ<&9F&7h4WHd@SPR%b50vXjAu&}dqEch-f zrdbd7Lfh+WX3-F46yM_^h)8+OC+rszUS367@&?k1kov-37^69&8T21;vF(26+rv6< zh^Biz)ZHdU%RRpx>By<5V7Y9MT8w53u^V?Fo*ol6Go#aS(SBI0ZLtCgciWvg9&zwZ zy1st@uH$Me30g++bot@_qWQR=TutaWzy9s^XwL2Jwzzol`u|`hrUQTWb!N~1ru@8& zem2cyHJpkkwqgC*HbE~fNrkav1Oqxt^2s`bQE(zsMVG$i$@9mg)$g^De#G0udX0xF zBMApFEKD^f>+zks)%#m$*N1oY3{`?E!>Jb(jc5(uO-bc{?pf2^tVtb+O7c$Z-uXE2 zd`EtDR1+g0Ss?exs?Nb$qEl(cE{W=f)*?0O(zT#9y6V%X`ea_q3-6@~YO24;dmtxp ztY+X~2Rx3Pp{bO4tTT`zhZp^W#ME#)?U5~(ysj5cC@oAekS6>C)rJX*HhUw(rb&v% ziD>WE6#ddo%3%e=YPOaLgMx$Yiy_S&PZ&m#BHqq%#MiEkxq`5Z2CQ)t=KRv$R%?J`{ z{3tPzCo!m)Ol+cTm-~V}qM(}wor|S@`-;kxAaQK_c&+HG%9te1rC3BY)cIhrIkkEw z4JIs(S;`VpVEd6k@wpskZ?L_74jw)MOrelPQ9-MC?c9Q!dbfQu{4I7+m;_x!eBqd&M`vP`9JA<&rz72DJy(N#>1yk32B$FQ#6El0#DV9ofsKssfXAOLS zm6G}2acJ&YP7BKJU-p3;%}na~Tvs;tM6e>fI1zbiQ~1nEHapTADMR9F_L-d}RhgZe zmK3W?XCfSA$LYSWv zt0YRGu@YYSV1;x`P<)NRKZsRN_Q$Xp@Bi2%Te0Ar9D|0zo+FwNV$aVPfwSY~DwjHf zhU$5pB5Eg$d{>6MizZK*h;8#(xK}ZNM$AESFKwj^C7N3rRfI93Fy3&qq_s6Gv}H~! zl$DX+Njmt@yL2I&USFP#wE|`NVq60qKdCKYg5~v_#jUMr!Vf~6n4e3%8XrZ^9J?jg zT*MY+8#+(>2VEHI?lJWBAtnhN}*TU*~)th%}K_$YsATljL9%C9jP>( z5#j`xMVCer|3@0EF_jY2No(&&2K#Kt?wMQ6T&&*T>D|S0#!5aeG{38?BeJ0Xq+BKw ziKIxXY_0({Jzf)v2$9%}r?LaPiM-uAxLz#mnwf4u3wKn8w3yW)Fw?g&9Bc=bER-hDop1ESwLr-{A zE)t!Po>(B;6x0B8?tA+f;Bcn|!jRp%&u>o1u1QB8F{u zGL7?xsJ~G4D6J)qB>f>rVF^fAt;MkCMeFj&!D8#q-d@E|zsn=jH@;UE4eu6* zn!!Cjp>R-JF`(;Y18#eIxgMu$f7*+$cEs#=!mOOZ6G8(~wQ93rA&Nqjp?0}W!q|3( zfIXQc&SxN{C5h?$9|_fw`mdfs8IITSSdJU;xSYRoIS1-m}(W@jV{>iNy$a| ztQz|&v-0&+Y0ve<@AB}W3+#Ol7JLg6kP3y{s9MsNv{CZP?HB@c=h>lXTy1D#bF&!7NaFE|Ttm$3=&j zwW6drHkOys@g$fS%t{Q!-B$bG&@gq#a3b-jvfSM?Sh^EN-w05%J4&ne(m;wNNcNw% z2D_SnQV+Tp)Jxvpn5S69oqDYEk%CtW=7n?Fwc>_cCve$8J;1~gf(vUJ8+x7RDQnY(;>@4e}D$E*uDh_x2vUe}8E9P+$t-X!XHxI4tAr+S1a zrT!RlwRF#K+o$Y?P4NJ0Cza&+wv-i3F134mUGJ1+ymX+_EKwCLI4)a_dAVeA_GcP& ztnl-5_ob^*kCP#`>g>Nx|EH!k(*2*Brm9=SMop@7qW*#wmdjq+&`#F><+?p{^h{4a zoV)L_9y${@dOa$-Cr@GhYbRL)N--i+!?mS+wBm@+zs5C-ibYPc(+$x@IC#mfiYt0g zpk^T?{g0z#|5VCUFwzCfKH~kvZ;C5ljw_X?z_+O z>Vhzx&zi@r!z>U<20IrEy{MB+20Tz`9dj#%*fd05w}_b`g8&ciD(nsu&5wy4iL}37 zUZH!>?dfOGxVF?UJ20(N`&8(6h)GzA|7-aOnS`qE7lDshQTnY!#(GYXP5(I-$uldE zD8ee{Ds+NU7Q>`TWLb=9KPM}q+~y0Y*4io)&`L{>&MMpIeD57pp`bW|WFr5S_0KQj z?>ITCIOWEH7Q$iLj|0LxA#$<(gfx(&WV9N|Ms9(e*D6j&1 zTsYrQ5`mj^a=FD#7aqBX+xVhiFxy)UvAT+!jHJxxD5pG=v{I-DDOTUW;8&}ML&yYQ zXIBs!_~)SlspU{IYh7dGX+vXs%-QZN=t!V1xd4@d*J3C+Iaq|0tN*iU4{EO07HKjJ ze51>L{p{QS$pq%#aOml&5S6s*DwFt&jP8mnQ^3^=?@G^pfT|zDwl?tdLT|3$4XyXb z6C(5(x{1+r(il3|Qr_KW8g=&UM82UA@Cr>0mn3E=2aUYpe0myz)-ZQuRr9H`1f) zS$2v74L5@n%USAlAhfImku00IHZjDPm;{mX1vt=E*1UgysEHl1%7)6z-~P1v^-JCG6c)eUw=Z{fR;9``O?rQu7-8uA zmiMeVzbx9MevH1JbSM4$I&F^uWfU>z+taNPS2s6pyF)C;v4crJs>;3X!(#gW(fJ?qz3A?7wo>_r2Sk!nNoXK1H1Mj&wGQ?+SAM z4xr>fNp^jKS0C@1Z8bj5mnvHuaVxj*m4)6bR0pQ@>S5I}1zjN3P7%+%TUg4D%LFgw zG+~`1?2Ai%VplPwhz-@r67JD_p#&4f zMuQ-&$53g8T%;M2=*Om*L{Ss(LhkcL;#JuE@Vtt9ze+-$9y?hig%C_KZo&h5PDKh? zT;C3(Du&?k^YiPGe-eZATLt=H6+<6C4o*n2j&%K9R*bwM8o|RjYM1x@2dcV;crM|F zH2U&;zAeHt-`tNzi`wkALe;D1eIh=UF{YL=2} z< zwBRx(3T%Rq&`?yd`{RE*mZq-m%Y&w}!GM5P)!gj@t&~2Ee?@yD+5hlTG~WbQVt?R3O)SpRBjBoAy1g4= zgcdHjTsSqlAu@&QIXCBr9&A3t_gribAGf8eu2velw3kSi7Cg%VBggTFC5pFL2@Job z+(g@L^>X$|2x5#o-x4O1k$-_ZNg`4@jvR~mSs>*?VIu+dH7uRNah_pLpZ!zdf+oxi zen`k_Hppu|gtrt`eTX@#4!5Jk%DSSYyfJUd487Eu zgrEQ=rc;W(M%pwyc`}ALi643mp=C*p{4w(bM+Sf)hDd>gBu*Gl%4ZCqin=PFgk$XB zd#Dgb1i1a_!|_&x&`tj}^eMc0F8c zt;Zzwv-tenUega-@@r;k32D*x$kD%(o6pr2F&Cud2E)mT(JPKvk#$sX9dqe4_rehf z-rE08uhTAU-*}X#MA^w3RotQcwlkk?rk^LnzGn+1u~oM@ORueK(O^m`7ULDeB7n+< zFn+3xS|{(I5GLjHPN3}{eV|^x7~DQB4I9@pjRw1M(N~Lw$#iMMG4g?_ij3V{%EXTt zYToC{;UEvVjp^A&6tXbZx3skUY_KQ)@ZkgD{@nb0hS&$;E1z5%$hS@B1ItlJ&Aa)A zm{*5wN#J^l=8Zp`X%n&Qb>3c`%H<3&Z1^ zM|J5E=8k^Fm+V*?Y95_n5?$J)Q*{C>nN>vit;mvGRW}Hx`PJb6JaWOsgsO0A18wO0 zpMrC`Y68^W7Poqd4AX1>%=vyB(v2t4dfr}ito~1vbqa$gtWuprJGeX>M@LZmxQGIp ztmH|OXjD3Ssposa;Xuti{2YUzLf$IeyvGqn&)uk^{BihG_b2I~XpddV3}V`NMV!}y z3)2ztL2sKj2Pcoo0xSosFu!6!IyEC@_LDBZGV_1FoPI{-S(!GAjmcG95IZyHFKj`B zfh+sTSr!JVnAWphE9!`wMrD-_8+`aL3qYVlUiX5Q0783QB*z`?n2Q>y$3K|x=w}m# z;xndB(hARfJHq#CjI_z&18TXvxcFg(-G7CDkTEhs{SUp<(~-x!d#qxr8Fa`AP$Nw9 zwemkzJ)^wo-Y}PUF?r2ZiJpfy1WB&@&DDJ^6ijWRSasb=rm~{I^pbzY?P6kjIV#mF z8sqa!4;@znL6!-5;kQry${3Lg#P?Z=0Um0HrI<>^=Q|ozbJZnYi6X*n?JJ7OcIVu zRP^^V3shsE;R)l+{!bZJ_TwcPq5GBW(mH{eK64j9_?GN|)3ljt@NV_~=CC@`b$@pk z4g_&_{krhR_I-P1ktGnC(EI8u3KAF=U>F1#BpNRESzmeNUz-YtvAmb^CjMg9f|Rqtzb7 z;E2FyuC_3ld9^BVcx$4InO1j|RhMi@?xEy286m7j-L zqF5_^+*lQz^H#hs3r2UcRXI^lXSc9>wA(W}sri;F3ItNp( z=i3>-L;{%9R(A^NfBA4SQSfze;-Xg6c=CkB@+l$>p;`lx+|Cg%A<&a*w~!$2lIh zUwqraQ@cijCcuSNjhW8;T2WA@jz!5~-galvyvrmns|=6yDIs8Or?@g}FAmvV%Q zKS)320ZkF4kG8e#9=eG;Iu|{bRpjL4$Xu=tqs6ZV)h_#Ac61@id->sR8DWS~x>VfX z`%-=QtI=^@UtRP5?2oIqm;^@}yg!;0{0MMvf$h7u)J|T>4ZO3+ zb&1~)BC?arsyW&5>eZ{)t?u^IPT>0uQo?us{4oQ~WZ}cbqR7qO7wh6qJ>u=X<*#`F zu%G1xL@b{4t}qm5_6b?wnI2?41_L3 zn1{-11NRxFa55h5if^vFtLxF;mod8F6_Dhi1stes$6Se)QeM;pyT@Fv(Ft`Y zV>Z?s56yG~npxN<)Z~$NVn4?=GAS(iJy{f4Xpn-B!l6-|Mcy9Y@N{IXHsSVIGwDCR z1pE~FMHZy)WP!t>k=ypD!WK1th##GHTz|c^>kwa|?qoUurYL9E>$lQ+$k;FFB{-%j zk1lHv%!8b3mai^hQd(JBTwOw}#+v*hK@)exEpTQ_7O6OaNg5+4qFY+)S%d_72*LAv zu`SM+a*}_l%O&4O##V_{r1>jq9Z4WOb8mdnb&cBzJS2`*V5b66;En1jkEjY%tJQ zNZMNBz2GXX4&7D?u8Aelj%0aG8xj_dPHjetg{x~95c?JH7emOi;;2C+NCm(%*%YZ~ z;}Q(WEJsDseS}U4{3M(v+XdnEubxfm?yb%+digqZxqedpN68{{=&;7-!iGOC?Bu3n z?(S9HSixP~d0X_kvetYa=#jDg{j%@cj{5-_%z5Q;(*2YZ5!`_@Ey#iU37G89)t8q3 z1pw!DljEw>#Y>$wAU*jUxa7e%hf-WUJn%zlXY>(aL2X^5zSgBP4iFSeWE#V_&Ld>g zIU1M3cKSU|3qYFfY}F=j-W8^r*cF4{Rhr)*2uKL8rNb0KplFmC1Sw2AC@3i@K?W^x zBzvU7Y6}>$bf8hOMIYw2p4{+aQ ziQhG|@9jFZ%Sva!kLALengm3}FE|ZQW_uqrTN4&=hk|!;0{rFZJMS3aFiHe6c0WG{ z02&+xF4b{6$HRFuMHi{6c2!RNT4TCqcXu~T+oHmtF&4Z&ER5P0k&-Ox4TePm8TS|g zPHn52L$6M^O9t+*XTNUM3{b5Xgi)CRPx7ZHE|e!OPV}7QmB+!xY2Jg#;rrQE+3YA} z@PNr@)K}l+1mFL)YT54$c!J1+fTQa7^EM@k`N@+fw*!8+XdsEgY0&r%BvYb2D--ir z^=%RU`SRW3a*fNj>Os@`GjLX2z6Y_phB~1N&Mwuh&POd0SN!GldpLrjdA<40h z)lhM39Pkw}&NMmI@vG_#F$BkUkwqQ$$u7>AREFU^o{_e~gp$QOg+#d3D#=IS&S=~J zgxDe{5P$ZQsIeZFJa=`ibu3%!QOXkXcyCKKHqLiH?tQ42=mP(h>V+UOPDd+Urf&yU zbPBgmuad;xfZ))f-^0z-+-yfb0Cu6GPc;aRL0}dUNXx@HMno}zdnsXTOue1wC#L(x zYqP2Tav+L1&nMCEGR)89e?Df>wmYEGjsuh)j>mn(Q{&?jVtLi_;V)mlbh+A0J3Wr` z%l5lz^jq&jquv03E!Z?&5%2CQ??I?>(cM5B--Z^M*K%~D`u@t8+vPxLg4Z`UaaX>I=0&-F_g)=WBMv8V50`P;^X>Q7yNJWSjwo*6 znOWERuJuMNfE3^D$nq`LREZk3yXvf=3zkwE$7YoAE#1YU2X)vpO1en8mey9UwFqVq z27U#O78M{-AO)!F`If=T%4)^v@I5g|8%y`22aKb5a@{Jw<#UhuK;Z403S61We z$+UE!fS;Y65pn}%rN(>^uL@w$j>|sIldlgqUkP0_SO^e++3(@PZ)|?)yaBGW%WlvR z4H#q5>EqA7ckgGajm4uqe}bf8PEL+hC2NYJ&sn+gCJ-$I0PqFeF_0#|zCI;2l?a5E zWh)BD^`{mWM?5-CDV>`ssBeA_^lMcnBC@Jj{}-sbjWw;v#rpO9e2Rbw$cC%x1GpH) zkbt~jVwQsYE8$31wiW9SEn3ItIdVEO4vg>#Qyx*{mq%&nFcA%j&RW`qGcBRQXkPGk#xal6~oETgrV`ACYmpRm?ngw z+L?vhf0}HEag)s7$rpX-^s|KhC_V9|e8CPmiy1WPD9H2dpFa60UTgOyVvNTy7^aeE zM_9d}P+9FMS;eD>a!L_?`5z{@Fq#BCOqkbi^^b6DG~Ew0a8jYig~(Ryw9rkblxL{4 zb+(pfei#K^HX`&Atig$xR(+#APuiN=iKP>;vkk^tx77-ZI|O3*0ZJH%t2Af?8X}k7 z&BkDoS_DH>aw7Qrm+lTcP7fdM54HWajp}U(%XO>Y{;IY3F*{2=E6Ao>We%ng2^>)m zX@<^&5uswwbA3(}#BP3#)q=(tSr%kl-JYuEdy<4m!~FnZQqhKnhSa$^{miVat36Bc zsF}<#s`ufsvClwM4^cD^AiRvxDJOQ4Q1-zyS2w|2&GGVRwai=woJdd`CLm>3lKVaq zT+u@n4JDIWt}=lP0F2sBTL0D-j@H1c2^@SeptL&xCxTbAGR#KrmF z9_;OH0EV~?Vpb*P<>iI@q?XxTh`bd?(@J33r(ey6v4$K27HLNPz$QW@`MJDGV5Pkv z?Rs_I_;rN1<0z!eC6Oj?44eK8u)#7sxG72Pha2}%u^YRNvr32K#Y5jT%2)260dris z=(un~G?vN9z{495Uo4Oc^W3W$XaeJBXLpzU+{ew`-5KaH+?d$Ph zIIE=xYV%wqw?}m%a`2@RMafIlV$-sNFEq(TU%sH(!@R?}3o@nULopq67}TIrVq@B; zpSY{*!yX()BzrTBXp(d_W z5_)@Fs{n3qLXzcW84L~ubEH(*vl@`fFAE(2)spShPg-KW0Cq4ydBz=8I(#TEQ&t96 zJ7>owUDiv7w>y)?>jw`2JG9-N4DT(rrhOR|j@lef)8!`-BmF#63m=G|fo z#-u6W7HuF=wOKpNL7w2s5(bOhnJRGsExFcyzJYJ`&GQsbFqE1f?r$=LT$pr$q=i1P zI5pKXQ)#dR{DN}hJDcY>Uwp2d#r_sx7sI5;1?*OCmLJ3bSD)ni8XTPdPpHF(U4sg} z8Z(j|E{7!%M4R3k%kxwIxQ@86-RiHopx}P_HY zkWHOYvCUK&Mb)RGQTxRAgkhhqV*8~VIJBqqMG^B_PZmWsePl61NN^1e&LC>5eP>`Y zmMe;YnBddm^}D}KBC5r zp}$0(>aSytnY!)zuJrLx6??_7iGJ_xF#iw`$f2rI5zDnrV*I0utu>WOT{=?J5aU)c zah?YM=l;%fAQd{k^rO@G&Fg{5%9~~fBkZhmTP8I-Q?vHM(U&ac35upTEXYujXsuYj zBZ7wfsjCR>h zFzA{1H^WgCl`FB+1-RAPEss>t)dHsvG8&PB|8mJ!Xn|dnBOEduR5*Qw+cq4<$&OB- z+kF~Cv8i#O2;J?nnJm`z41Wj02^sGso=lt2#bX}H~X6vP^%5;$ykpY3KJva zFh_}uNwLZla7h@cP<5@GB|Etxb6c;6ldUtok=8@+_H4R_tTKG8=t>fskq_pVC&V=k z|3=$R))Her-|0E)~g)+gf#MDQQ?_wQ;|eZxILgGrJb5; zqJ03YurV0peb0Is2a#_Xd<(T-xEtg*iTUAq_5@&mIXlSU@S+hwO5=YB#IsyB+ zWW_^*C>aFu@jVKn=JGf&1Zt8K)G1AovB6<^bUjXd6p;P6ZLbC2oBajn;TPE+OI~1a zimrGAWEK$z%x9C$c(t@7mrQfnq3sw|{B{)^VasxvSx*$ofQiL8pnMWUZw8817_k2d zj5XRWL|?yIx)4LicELCs&O&o@bAo`pmx8YEIreU$DFLxtqX(XTHG)7j==e<0I}1RN z7D5pGcke!Z{P@w#m_UX?(9s{9;~LN7wWM?J3lOOFJpX0+3nW`~>=w~ktpq8E$&Lp#m>NtJl&Kp+ZpOo`_8F+ud zaq*@*)EM9k9WQ4^EJ!G&sz21$CrX{bruDZQ&&j|BdHZ;%8J9IwUtX6xS)QH|-ekH~BVAm>*Xyj*eJ9DW zPI#tTHux-S>ZCadR_GRtM^uUB8!tuw`542`fWoA|Nchu?H{T_h=H9?5K{pZ$CRm2( z#$do;W6;s7cvOmviUk$Mw@lP6P6|Y_blhQ!NKR$JNErG^oA>08;QKm!%G7V0CqTqP zN2tTo)3hOK1RyrwnS((6B9&8DdQut=l!8(!Z(~fmxpWh9LoqG_!7FgWw*;X|@NJb5$#Uo&b z7R9(9GLm4UbK_0CoavDLLoXGo83C|M$Ias5GXtS#90lG$%*u6i_T~c#HD|CLa0;5K z7TA3`SR4ime$88%w*A^(8tg<<4E9Dwl;A`NTMSNfVZLs5W<=EH&i;O-XQ)dv#c&#@ z_@^a5e$U(igcOUfCZbVrwG7s`hEmuN;JxYYd|r*k9gYd)Utz?;4|o3&Kn-aKsP!F) z1?dmH)4IkgfRh^;8G)1d$!r)Sl-8kAQE(#ZetZ1F?Gd?q69s9L@1me?LPQ7ZCNwmHY^z z0jSMX+*S8|olj5!q&T$u5|erI*2DVa?#K$^F{o zD*SLcO5s9=_Oq@!%bt?fs6a+3C;=&u0?Mizknh%JV=QdV$eK{|Bc32XxahW&%`Z3m zN1}pmA5*V1zhJGTF2&P#^%kXCy=h2esa9nv->n@!wX@3Qu+u7i*hucw3z9UgKw70O zI(cF#-tQP=uAQ;KKq4Az4!K zXr#2-xZzx6MMIiAnZBWso<8-f${Fpp#@C!QZR^#! zY8Ob&D}^umirDu6bF|w1*w~7Ivi5=BY!BE) zEU)s7D-0(#_DKMtO;f`=gx7{eE`Zo41SJG$;ZC~lmpEcjEw|f#_sqa(Al2dL`EczD zwqe{B5??8$vX}m;sqwyem+QHP4o-u&;aw`inE;8Hsimb+1dawkcn@QeRuY^G#P&_z zWiN9Ru&*;jy#)|d+z;q4hAEG{HWK8@n3kEVr9n{h;pRN)x>0#;+HpPRWv%PZB)4c9Oc6wO7I4@1_V$We zTW^Z`(gCAv6sV2-cM(?bg(;i(CmFQ$G`+H!cSY;6^pvsn`@M5>lGe0r*%e{(-$IMp zZyF8f%o(EHP~5NXYpPf%9hL{^4_D0|GdCqyj5Db)**pm`OX#7@@ekBMk(EvTKsM&~ zJ$`j0w1{WBo7X_l1tu=Om;8xSF#$8^Vu$A2#e3#FlSoW9EtWt)2KO3i_&uTxH zcSkutNp6IR(9#LXn1`i+q4<;&M`~%LpCQ5{B@@OxAgvM1iPG?6Ecw%WrfF6tWHQ2M zj}np;@bK)}73D4eu`^PBW5|e;qR4{6;|n+Iw0hdzizjEY=229`bgqQs32Q#(SwRm% zkNUxqml}Z&a8rp#zC^x=D@o!QiWPiZBV2K8WRRVe5nYMV(vUJs#ygi-iM!d0dxi$L z<{;@2qv15FQ=bA-))Te>DjddOOo>Y?+lkyl29%8xik(`!pDlSGL~}q4CO3{ft2-l! zT-~gRSx$!(_&~=!`bxm|S_Z*6geWg*X}Qg{lM%(y5}x(0C8F77J9knq7za4Dj*D)y zV7YasEaJ}#gNCF3Q*01elKboiDCsr!^8!W5hOQGb1B}yrL?m!Q&m$Ufz(dQfwZ4Ou znZYDxYL5p1;t@GeLk?ZZXN+4zU$3XDJs)nC{TA++|0yqD4<kAAayEJ)G#0`Vg6 z*B8q#0JD>klM4ZPFrC*@Ri?G~r8!Fh3YXg-txu{s!UrS<`ugCdOJm>N%8We8+yuS#PzU$NAd6z|i1j4%Nh1Cw7SM{+jRRcVJ+Rw<&M0nbS9NC>u zq;Dw3wrdi7;a<<=eDYQf7H81w7ko`~qu#onET+ZPZgrXp2Z~G?Wd4(e3SXPPgqq>i za;qA!6-$7vFVwbEI7bw{=4kYHEJHa0YAV@`Fg#6f^5CdB?TxXF^D$X#Q?-LEZqnS0tW&iyzE zhfR^Sg8O%G?<@XPR7OL_uyLYNWSBw)|EQ0CuefnCiZ!b#x`jk_fJ!x`$!r(Z966(h z5r2ZxSSHloE>Lt%)R`TxjqI^_>W7c^;xoKlxi=#8YCN}0rj^AYJP){63L<;%bGK`3 zdAQV0cu{b8lkInZ{1|1K>lNDWeF2v5`zMQ))G`dUfRYco79mW2Go9a-2rM;<9Nd3cE&=ivP((}> zZb9oh1~&l6BUmc{%K7}eR(oZ8ME66e`rr@0Sr+iVCT3@KQ=+Y{tn$GQ*8;a{u83Ff z^MA7!d%s+i*H!_=gah?kIg>A9Y3WTMO@go1?tfVTljF6%h4;Ia1B6o!WT)F>B{elp z-}fK<&X+_KWB;^0+%I^Sd$(B5S}l8^2TS0|LwO$CK}?>|nY7^rQqFrB#~RNIsh;#d zV;&toycBGR+h_0`*o|0+e|Ez8$)5VtK7f&ygchBl?r_&iM@7v;ce?$MT6=nGO0#`L zo+jctmtMtVx?N3FyYY|v#cBK41um4!ey)yJXPyBX4A~~8TcZVk{bXr-3V`LIgJcX%0`-TiUk}62%rNh|MMG{HrcQ2P`V^6<@kA=qyzODo(^-f z7$!$2xOGMLIn@Ux5?PV25}Oi1>7z$+6QN2r6(p>)CW9na0XV3^U6-dBy00Y7<^_)R zzSBR(ffP$v;!Y+hP4>&6TfO_IkL0&PF_)Fu(5nGG_N|=OLA!Ac zrf6EXn?x;UTE^d<=<6@#Emp{Fjsv3!r>*ODLs3;u)aHKX@pDi3hpfjk>yl_|Y}^~3 zK+=zyilc6(7dm>apZ7Y=E}YZv%myIkyXpsCtZ%$A!1P~Tr+yFw$H-0l@#yLrq8tYk87YLE8MLk~<{Rt*LJbAX zKP@HwHP8SZICBA%iU1g`!66Tu733 z#=+-oM8C^_xNG}Dg1D##X>RdKWm$q9+tf{B6L1KGHht3po0~)W3M$Je= z%G+<|2%QW~)w^ZM;vQj9v9dm@)DTPf9g&9~pQ;O_Od7eX;P$459vfISMFci7^^EEr;=Va(E`y7#pVRCXW+{tcU_IVJVXHhsx1D?4u zR@Q~Z&ku!36N8u!C%HsKD(?!anBKUav!*TLG`tw{3_nus1i7>B1bR8=L!;Rd!ijxg zyMs0j4Ero#S)KxcYif2D&2t%rec0@KI`{WX{wRb)80WJ=4?=5S{$aY!=mpA|FHofL z75}~K25>)WLEtA*rjT-A6(MVZbS|KDwLaj+KFId$+@mV2GVyGG? zcHMEU7agb;T0A%T6n}PifiFutXzncdZp$Qk6(j^OUl2VTUszBEDHv;^8FenL(vmNa z;RCUhFrvx7J5xY2c9a(R|J5DuMj#t%(UHz(YFtCwXWcRf6w=2b0XwUg8j2h!t7sE3 z#O7!T!~R^*oAO9DL0`W)WTEE5M@%Lp&g9P1jQ{X_&iyK&zp+FrvPRdd+AKKf&mKc_ z$-p&fKwp6)59exXf~eNDn%DZ8)FBVyLN|5xQ7uUl({WeXdtY&{)B5?v9}~pAHso_6 zFm5i5SauK^8mgq+TUb`8<&d2$qqS{?6o;3Ryw)`dmc&2<*)Y$pY>663oAMUkMb z4Z2fZ$Y*589C)hn(gH$i|BRYudjFW|GEs5r>)t?bh4l= zrnR*!tzn3DZ_C6~Oc*j(=v{-rajH7C$C>lL%$pwG->RfU_lx4OvMaTahe7ON??u6&CQLEpP$=(PmcrW zqX?~->%;ZmailQ^!go!psBcHJ2UmN-2H$=OIzI$kZGhc$FdTztmOg&tb1Va9H^PA* z0>=KB)s`%|4aa z5?YAq#*e&JD48mN!7TCUWur-sDs&@(hGlx!5QRhA{ptzsF%Dv+Zoh(dVC&GmIw5M( z(1)^^8&70DX{HaSz*@1N__pw7e;>a)ZRgwJ3sgOfAt}#Qej%>k4kF}lJjET1L)!ZD zT`;`(k8;jqQ-%Xs{?1~{qN}{=2%~1vy0?8mi>Cg~C+o6dh1Rf6vo^i+MTeIImoB>; z^1D^~`&t7EezT_!vG~$>KQ;DTQw{EtqK1XY^|$*T18c5;+nU=~nO9|y5s%tHoMB(vB&F#zUMZo>4uZKIGx8BaBpV~n8H_xo49@C(Ocbh!F=|$ z)V_bVWQAK=cMf)?dTrDtvAkI|pmarYk(N`h*Nz2;NRqiB_DP=^Vv|tl@7@z~q?UD= zd^QRKk~sZ8xK7bCznhdw?0?o)$FSBr9W=G24qMi#<42`@9|E5_)cWuF`LoG~JVB>5 ze-meRA~-OGK_3bN+98+sxg8>K2PWWX=5{48$3W!aGq9jQsP3iJ1K4!)0vV~M)6rK^ zL&T!jzdvRRIw2Jk%)as7CmnJBY@Y>oCMQAcyKba60za(1#c%ItjZ74}<(JpsR~~9% ztmrjYONj)4Un$F~Ggy~2WS4L?Msywm?9>bCWXan`*u#8`F+^MKTBWJ)sZek1yRW>OqmVSZ4RCRR2- z)DcOl^fB)0f^6}bLRlpx{*2#0`=OaRxJ(-O$dxY`(_d9(ydoxTaCcw*22xLgzX) zs@=eN-~J$33>o$1{<9keRvf&#bB1^uc%-N1llnn+d(jdu`6BhESV=u@yZPZ~r4h{S zd%&y3)0oURLSy{^l_C$W{U%%`cMeHb?v@e>LFp8WRzkWF=@68bMx~K1rQ<*Mx%ZCm za|{mSoP)gZ?!DKV^O?`{TVJpu4G#)*e$QLe>MT6Ghx1|nemwE7hU4+N=B{v@8<DEILU03}1XCyR_70^62yU zYxpbLg`|K!ar)Vq3L-Y*6oPD%y`!TQjA_6PfVUw+OfMJZR!dJ$4WEhT1E13{mN82O zGKclrwi7Z)sU|REe_F)2ilI3dxIaaE{#2a(0W}+qDe2HLS^vz&akq}?S)e`RIVUc$ zwqv#j`ToX(<8Rs52h1W#2!aW(n$Db$chFqA{5UdW$<&wrz!9S_=9fzKysfB-307Iy zONupP-#M5OuIhKy;;#lniKA*)y_i%W^n-;oPUqEqH}zX#5n4iai%$l#i0nc%cE{c{ z`L`3Et}+gI)X+###FAoKI2lqPRQ);@ zo1ipXzWVfHrY1a`n>Xs?WShF% zCk;~2E%;;X3K)iGTbpB~)<|D8CgsWI*H9Lf>}jh1wk-ItqFgg*FKDJnj*F}oP%iv- z5!SelShT=T4@O8~J6Bhl>JX@U5PKyu^V2jBfW{K3fz+5g5C`t}41wQ|)(U+2VfG%B z6y&y2JN`t;^XKKo8P?b57$9O;t^X(lQSGr(b7QG0bMic7^gkKaya0E|0eB1n1(oV} z6&UJ^l}t>BZ#f&>pg2A>zMZaw4gkFj&AL5h5;=ME`M5I)0CPIPQ&BB6`GK9?{OV3O$R1~U( zkCW7AZck=BGnm5LU^$qk{j}5Fm}E~xK@2Sr|C#`O{3iB$EhZb6X6*MS4;+*U6AVd$ zQOq1;HwGnf%X~CK`mr87R>q0mI9@;5h!NZe$DFAMs*9 zQL+TjcG$H8s4fwA5SVV=7tlXyyrZZNIzQfnxs{Vr@P|=*c&JanFW1TtFV4^UhVD4f z1uQ^dL=c8nO$Zqafc+ft!6Eh>#JVmk>hs@)c8-dA4i{WbWH8}+oNB0#x5n)PQg*f` z!{f#gUHP$Up0Qe;?7^mz`yb~p){Gh4@T~7ht3B(MA>YCn#8l$?cl5kz!=_2|&E0F^ za%d_jhV*Wl@0C8YyrWMwvums@*VEX}Gt*>Sm8P}LL3Kt~co;rH{wJVSz1~mWhL|>1 z>ILnAbOOeck;s8~Bxb@7JWIjU!$uo%v4$$BcZ*teB$+Q-I3 zqfV%vS%>Zl@Ls$9!SDN#_0Ec$KocwSUA>NE_)9 z{`_JeUO-2zN4}L*S@mOLY~>tno>=IbWt!Z~U3Z%VpV#3S4C~1;cAvVEFqvp0< L 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 z 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�IRnwc*Yksek&Ee3Q^}5>|7DD#GgNAkqsU=NMI+ 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μcpw=dZZfvv4o_5nn@QQz#=<_>B-inc8z)*V7fJLP)zNKNV8HQM3qaC zuyv7Wnc*-$pTW+P_`cC5PT_gN5e^qhQRa51CwZ>e73g&zbvfNsZ|`Z1OeZqx)Vt57 zDb6nc;!0IKvrpX$Zo+noFc>CZz1{pJ<|d8^|=CvtL;3+~|trQ387l^8qS! z>}(7JK`*_xs*D*{a8dR|jKrWI2y<4V|LEwAXHX0ce2}^)QHP=%b%B^F@(^HOH|jRw7k)k0Wy*ect^5>`ZJ`<0!dVcpBcExxi4IDvM8d<5s_Z z?+yRw%+Uza{bJ?7(2P)Xm7wO*W|Tq5c1q=q#7l zFYAOMSD+{Li>-2sdXALDmRZZGRliM zyqBqZg9&P(VGQcABSw=o^#dAm&kf#T!=O}xw6HkJfy1MqSSWFO$5+SSr6-Kq=+nWz zQ@?adgC^q~Sk(|r~x+d=l6THgdt@ypbUszwG@J_2yO zAfKT;XoW zrjRmc>{2-KZrK~_(&4wd)og_*{S+6RCt>@Uxqj$>p?hl?+pNZls%a9mixw-Lkij`wRT1=Z70x zFlN!op&tFNBwB_IBanB^o=>SX`$u2Oc@>SOdPJ%e`s1Cr@IQBOBp0{%zrVO~1pRid zY{uN{rpO9QJHNZ^XS`aN4!LB|VA5wxX16cbvz@Bf5dbN9_-+(UE$xk5#r-LN$GQ(< zJ)FL4uKl=3r5`QGr^&k8ygkoF2fzC1a*$U2+x`8vO4*?I%+buW9WW}I;t*iX?clk# zdU5_-(dS#SWfx!*LPZst`n-ixHLLb&t(}=I{RM}SLeK)_bj+(#W#x}aKjmEZ}Y@7z0Y;4=M?KDZ_hK<$O zw$Y%mttZ$2#&v(5^KxJA@64R}VrHho%!+J93Fj57>v!k@?ap}1)-2Tq4dqFIR>v-# z?r#xknY<1)aL(oBkWwaQ78}+a_T9>==!o9&25UW4bIie@m#4P1bqNJmyiwd=b2Qfl zW3?-{jLcT+l@tOMCgXP;s!c!S5>h5Z&QhaZ`M;y`toR+!V#>h8kHxOx0pdj+ z$vr@TKUgebRir=6TXjNO&3(y4-+{s5AC0}vXO!6TM%&+Lzi{jsukhzaYlvc4qifQ! zRhBhKOGkB)#zao(rv1r=RfWvjH2O|PgD56>2xDdfIx;uCFg-h>p>Z(lX8P%7j)H58 zo#}*)ksn_q{`0XD_fOc>$VK2cs^p~=K>DD}h<|pCFO9}X`}I@Tznj;4%OcRNzJH&B zmcRJkcMX!U-(UYzaa|F`aRfGZnI4&CZ5qD(ioLh};_M~tAdiK%$)M$i^`1Y~F*#3Yo`Trkj^{D7Tx_BfrZwb} zRL4qOS63I7eV))V)^W!C_{bmcXfx;L{@Fz8h$SjolHlFzMFyynJ26Sk@n{(Uey!Hw z^m7Td)9kDdzg^NOwYRWXm9_MN;wb{LRt<@`h!am8O9Ct#tDr^5(l4PrGSXr`>w zww8%8#Ol$`m-e95hRLVNPD?<7CA}f8lT?RkuwW;(5k5Q&k$(Cx(!R|yubC3Cd+B8A zTJp8CHr7eWKhbDfdwAQ>Y@XOLT)6(9=>MM#@V(_{Wcb&~&QFSW?CN@f)y{a_j*Cim z5CCV#N2SOpRQGt4lDlEs!C6&`_dq!DIDkfU_=lE$4g+J$^7C)`q!+EyY}7{&2&dO> zXYAN39)4|UG@(JvnC#iz(Au+~bB!!h%jd#*<+8RY9+X(?-EYhcIX90y>Y1zC9T!EG zSwuyY6D)N-)XKl}8T&NG(*x&B_b&Lqp1>D0y$Ig6mN(6rnVhn@aK);9O`N?wMa^8& z;C&w6nAbXMkQCw~--e;sdjeuppk9=C2!Np*5#;d6Nhu{2f(mVvL*>(Vc7GCWw$x5o z*XSSgK2_?qaG0`bG`UkNxRsjB^|A}wG;1WFeNKjhzAnPl!Ny_QJNDK^mvDa(~n3Z0gs7fjzPi3OVjwFnsV=#%)s;BgE-kr{gslfVtu#5FP^cyQ z*xpo~ysNL3wJ#oDRa@J7pFit4FEwZjE^!VjnuaB*jTN4`4UyS{qjo89U8g{I0t`9y$eo9#K2Z}rHKsxlIC3~+!G zosDlrAD%Y-q}Y*)d( z*qj*V8ltmxSdXH3G2OMdmr(EO+CJ%uaqk-u)#OKh^q*e9|2Z>Zx{LLGSF>D)%v1t* zQR0e-Ix_0KNN8{(zhYR8sP!3t2FV3aZWX!3^)UPS)LO-k4IU-&^N}QTT4-F; zbW3anJ~Uekv?E{I!Va`eEnFS99qv1czi6lM93OXkRVlp-K}+WQwNG>TzvL$yE-(7b zkt4v`#)uDyCe@yVjO*sG>$Q;koID}D-lgXqW}1U`VsF%%+!@>Fu)kSH z_Y8wlGWW~A*<5^QV?43;7Z7WWf7 zf)UCN_JR*0894zt!w;}uv31!FByNYurNZ9W6c|_SNP;KmV;_7?wC8CzmJB;-H9aq$ z6ELE0TV)7Bud3`$^1Fz8HC$}xRDW&#HU+M*eE+o+USM_HK<~a~s~L8`6cPZg<%A{%yiBtR#B+d0`C#z9}@WE&(mOs-b1`{ z(wiwhdnic9y5`u-q zr}M@fW7f4SuxnL%$zGzd+e%l_*JXqSI$DC<<*p&>@PAPye zo-#%F0>)1E>MOt;G<0S&5SZ6!<1tTBWoThu6BNW8-NYj&p57=Uh8(4O@Ba6jxAI9R z0yi(651SF=RX@{@QACVE@KM+@U-TrRUdZLsT+NE>IP=*5qP_DBy(7K$_y-BE3>`i` z&c*l&EY{HiktF4?c`4z8DB;n)jjam^P;L!2^u5ib*1bXgE!=Dkh#CtTu~rFI1L9m2 z^z3ZAQYI!+>kpp!hnea`HXdt({Q-rnbPt13&?7G`fi4-ZrI{=VGv`%EqjqVm6Igt8 z`9NykRMso>Y|enly`oZ))Q6E0(@x2L6%|a*0BE^6$zvJ9@!Ag6tNt|_&z-~4r)Bf= z{luxQv8(WdTB@>}GL#7D-92xfANMLV7<6Q_DrDLDmD#z?OE2EsEqREiQV1QnN#z9T z8%GaoO6gn2+Kr>(xJ=)FTg((nM&oAhq)`(4MCjc#bU06-Yt+Jm_5Ui6BJ#Rumibab z(?%unquxH9wU_ev{eC$ieiueMHJiOtc4eD8{|o5ee8W;lPS#HJNTf;GN+x{9S~f(=cs&K%RU{^&|J7q0 zSMDejHdzBR@)8{;JNotSI-KP1R=a&;JoZG$BGaKLI6NKN5exsD)_F!{@lJCu8tU$n zFbMz?yKGcUM1qZahF&2Ht!7Bd`vjjx*o=u=MpZ5})f(IWgSm@?F(2e0c8Eo0B6IM= zvjDe$nA*3TL+9=XU4iQ-?;}DcggUY4hoE5jB$)?c6sJmfRzYInzmkw%?VEqOX}k^azx(?p0xDE%s_UKZ1=Ezf2aZ=7stj7k zS9>*JD=XDqC99ySX&ohH&E3MvTY1gm2u+Wd9q+f?h3DMYm1eS^W0|K1Rpvl^(*8S4 z&(WoL)p~{Jpdw;sEMG-r@p;-o>}7CTp?ARM`x;$oJe%vbU&;NJ_{>IkwebZv$|l^T zp7z0QE6${z-rv^+A+2<{s{8QyE-4j0rHnHi!qu()#~HlyPG6D-65&7V@5GZU*#crP zV%2RN{zEg$GwRT++?HKctg3fQEnde=nv*%;qg&nibmoGw=-T@6cZ09SAp?GrwBFdx z;dJqYruWs}%kCfIr|pMAQAbBx|86=Yqje$L>9WowwJzZ39zpi)YoTjg{ufkk(pBQ( z4~R{4MSW%1!kRd=oEl|40I{+lwz8^>rhd#kE(JmHowU1zx{Nq(f1+yNo?fi%Nr~%;$_S-cgF-zs z6$6vho;bBi4z)fcrs|I0E)j7i`+oWW00UVTZHDYQYo9y)shwST|L$;ex6=QW&hBN{ z53Hl6;93?5eFsPZ^k^r;qg&p)Ls`;bb=wE#?!Ef}9!W<_VkrltQhSxUu-uAbGJLwI zNdJ4OY(jODJa}B)LZ5KtV<0FDZ3AnpDZdgSozxEg2w*}iOK9LW0Vz;#AT@e&Q{*HP0>am!Ad`3cLg6UhMi-v-QiidW5R)dEG5jzk<34pZ1Bh zp3_1DYxP`pe#wOy;eT$eECL3{ru8CSprmNkXb=kMI$rh*NP9sEd1<9f6AO$6`l=P( zfx7C4Z)lh=|AO*F2Z|>pvDDCticwe8*URgwt|q3|2ei5#Gbi8}qA=pOojb|)`O6g0 z(ThwbJNu6J<@LO8IMWP-MLqX1?-t}kLSek#I7{HHRZuWUR4!%!y#EkuQ77{PH#y#) z%v+l-zC;h%4Q)AoZ6JBEMfKe?;+wNQP>9CmMp|l+Y|RTYqS+W)`}Nlg4=uT7C026G z&5hPOb)j~Ji+e0FLhgK1dE=toS~&Z~g2u5Q6IiZ7pAZCS&k<^UFm ze1F2|zVVK~y!xELSXQY-iTSy1Lg6qSn7+WlTC;Ijm+hMVjFv>j{?vw6VbPg~Gpp3> zDo8cpRw=@w>K&{IaWO|-|-jVy1YR3)gerQ9jM`Bxd024IDNZuc*9+=>0E=7@r09txZ zdScEn#OMsjM*R|0lD@7x+;os%rrJ=PYT%XP|>5$Z>miz{Ov+ zG9%Ml94w#sDB@S-=t6$ z=zT!G!yUJfm?q0^_taPf9Lt$>jt8mWL1~)W2TN{VSSA#Crf%OBn8O96Y5Tb0yV;WJ zk+iT$lSo1V799cLWX*Rey7E0N7TbX*8Hi6$yp}e}E)oI&qGa@hdXnEjRnla!0N09; zyCJKp=`Td4A__@}g|Jvq$@Ajk1u-9HzwIVIxRx=C8Sr9 zd-s9Z5RK;lRcHOD;Bp+pKqI3+OlOMr4L2b%T8IE7uY)92LeZhf#md5bZn?$+>4I6$ zQ10MFFY`W0n3n57>y*;1!iP%Gj#6%+wm!tK$lXjfjEm%p>V(K}i&|hE<&?a||M3Di zo_bde7VTaESqF`v!QE0@`oYc@J6>}N8@}Au4AFYLe%#VR&p=c{-k{cz;{X7~e|{9W zp??``<}DNs>Pc?dpTlM=HkM%)hA)%dN5xPNr5$bQo;Hl#mWXtXtqa1D)aiMsLDy1Z z(-7(-IyXAKhM5Gt(Xxm8M~gRa#5xZsZb%29L)v#cM>c<3?C_d&+*KH9rGl!K{~5%c zMvK82IJhXIu0offAk?q79$zi4Xb2FrW=KO~7`46$RNmB|Zm#q>z0V3L!W_y{Y;x|G(@s$At%Pi_23#oYPMw|}Iz40^?W zcxK4hmz;-(po*Gxg}peD7^e*ZI9jn{mWze5hlxR>2z|AM^jnsm>`?T$(BJE4_%}qd zFW0T?`2jUfEGV~Pd$#>qU&*gWFwNNY=`?%4DfNPC%zK;!D}Uz3c(&i!nRB`dUicU* zJkL&Sb<+*&s9{C4nd}sW6~r0oU*3M({M)|enK_p3EsI~w$Phbcv<>V_j==GazdU8QB2+4H zI>sw_Rn+v&7?Ss|XoH4Yk()(cdZO#hKYM%)v7%Lr;lSG^ZO+8^JW5n4PYeka*|m9- zh!j}BjrLG0%7nH?(&~>o-dP_TDPpZvXg!9AZH_n_Ad1NNv6ftn8z~@W{`JsaaLmv( zb?7}heA)ehqmSi}yoim6c&tRYq)My=!L=t~Mdt9d#G^~Y@WyS-P7>9|T>dwfY2RJm z+VM@-oa=?l3%5M$!hg`j|Gk7R(@UjHqG~$eYBm`cIZg=Wdl~ z@L^ZqXwO?G=wbRp)_D1%!D~n0eZc(aZ7)1z$w0J%{;qXJ1{tToB?>c$=D?Ci3Obe>zjT3tH{G8Gx9O#n17mU zLeoJ758vKHdT5CErv)FTbFpVZOS+B@kR%R}1Te#9R~vIM!B0 z#1t0)!7T1e7esmhOl4yHvaYzUF=k}zO~l~Y$bp4QHtbjk6#NxZcr)mz6RwSP|4wMX zfs>x`h|uszWCNBHMBDsU`o$?G#~6{M#Ruj zN|6sE=FJYMK+{^jKR7VtCXqhpzr`X4biXRYru()cUUkOz+=M?F^2=%#te3kRZ3`;avkv!R3v#&DbLp}Ba zcQJXA3mgwn3TCyD8l&3)Rkb*qC|~rDOzE`gOZWrk)#Zf?x*z3VyGSS=-jpMJVwBu3 zrdAsL21bxLKX!)Fw7w|-ha#3`YB^I$z{i|Xg~4bLxtFt=xxZ7(B99@G+jJaL7SV$! zXO%)fbz~Eop30y{+tnfy5P;n&PTzE^heU=&l=~$A(E9F=TB(oueAIx zx@*BX+o&CN=LF}V=3!a0r?wq$%Ixo&jqe&!@UOw*ihjV-FK3D$^?scYiSSa>1p(rm zFbdXHD&=L?(JGgx-zKlkKaQZ^Qi~!0;1F7%zm#f^fve3z3jZC!05t`;;Imh7=$ri-*8@ay ze-Aax)0K0qhI)~Q;qSLPIr8|%k;K_ZvoF-?$uoQk;|MaDsh>nfrVZeTJ#?PR2=j_v zRl>xH;@G0xRsYJ-$3X)51ZDv1FzcWLHtWPSF|M9Aj1Q$P4jT-#r+b6&d&*sM9KgB`wWMKNhV13vyO5Hx+s@sWhHZs$u2*>C<&b;kJ*juEZYz*jTvMR zE@?i|O*m!8x;9*dZiLAz?|Qw=tg5f={$pNGoGRf)fss5?44OwzBf7+~WnYm?&qMnog(K?w9PZItCe#?7^xO-;ESU(a8}D zaj~*U4^zH{z)FZTq^ddRPUtFiTcesS5;_Rfos_TaydA@d%a9FaDlE$gu+*zq$vi}| zbxUQ@o;eQoQ9iL)I&)AHb43%$L5IZ|*#9Qua)EL*y7sdtF6d+yE+iBA4HVzI`2ozK z_A`@6;4m3@Hcm++*jOU{>zkve1fXNgaxdXk&5Bn}#z=)vcJY-Wmx4^{U=VG2LUW#BYFmxEitFl;((HV#?rK;oF%_jAok^I;Ax6Y?jznawtL!CK$$z)t0M!4AVMW3a>Fd?H=!92Uou=m}qTRNs9jgtJdlM zh)YDbj-jL=F3rpQ0p8;4Y3wd5Us~k3e=N;h>HdH^Glmazwii}xxzEp-@h_$@clw z?pXiAp@Y`Da&gS1@~P#uXZSYjz6mS_0DylF8tH|4)G^Rp+_yOW7>B?DMn4? zU{Sxjj4mBA`F@aa%OmH&oQIT0s)av0nv)60Ar+i5`YM<6Ll$4D!lrr0vj6 zBmCGa;uc&g{v_#$+Q|S-UN)t?DBM{C4(!qS-awA^sux`XZz71q+l?9va!s6^6HFXt z(5=s5P2S;aI|M~Tzm2ZNoGk>9%G_;5&qTSO3u`ItpuKa2Bo>2`g+9Z2RKI`>Gi$ab z?(9KoXY?p4bb>xu_AW5evDgX_s=z%(U`d&M(w7BM6iZHK&tN~tRorLsbXU8Y@|LX^ zIs6z!Z9Ge^e`dJbNjL-h^h1h_m5&&R)tazXc~BY?XUdmHhbgXh-XC@)`yMYkGzuw0 zHj`~gEZh(O4d0dlkZU%~ve?2D(z|;%HmsA*k z_+m^oxi=-d9`k4K*J5)nrX>SauUr09_Fe8}c5T;xY5Hc=&!WW0Q3Jjwh!$w2E>3S{ zJ!g#5AkkTZJ@q4Vp7p_K_l{fXs*}cmd=?MpvZRhv-*zHoxz;Z$ohFT9_)(+2ZjR+3 z$hIM`WXYDWrHrLc(&$_y-v35AzhsTBnm;46P$$yS-Sz0J?geQshj$R0*&S?7$-yQZ`aSLl3PHL<`2zh+h!eScvGl z!u;W&2Ys6y&&tp3zVTqfj{Iz`$NNFQf8FfRM>362dK96Ib(#U0Djq`$OF2UuMOGo| zG~`d;E>XoA zjY`sf6ZRPdmOrPGX>dKSe=*zZGvoCuk)P@baW{~;&PNPJa8f<0Bg<8PcIM7rP)n(+ zg@akBao&t4Pgo)iqpT7v9Kp3@VhUrhtaJgoy|mc)wv!hnyNQe}O;p6B?y_MRbV-`& z^|Y$ZRoiab*3o3spq9Z|_Yc&E`Q(<$ua2yH#=zpXbf#HPW>^YXpIDyYIxd(pOsqf( zxr9A;B1MI1pEx2Ps~VGWCkKng|gkZN|9r3dw8VSjW{d zWT7-XF^h&OAwV>U3V}N7eQ;cxUbNmeL1-JPRk=x33P;r;+&2g0smu~8^6C1{9^T?CE68%0aD z+FK@Z68N8P+TlGha^m4V1p^^CM{X2&ibC6Kl*y3znDE57T&@io0ynGT$?2*p7K!>) zERC*w+s)>b7gC_8peCc*tmq4l^`YkD)0WVbtvjLN{W&V%zRF3181+T?zqsL0f~VJw zfA#!Sq|IxKc3s|XFK_jkit7IxSN;cJaz}mUbL|sd@Rx;vEO=OsubXql4B$u}0ZoAh zL5K6cJ30T0b;zk4ehCmND~DjA47~zo-6}df?tJ0$6{RAW+2X_kPb1ynTOKXSuL6KT zoKaGXb^2a*c4WE$P#tIm!3hi8IY)f2?Ao;{pluT3e6Fk8yWcbm|aVlxkCm4(cP=IPN&Oh@<9?Jzhj3M*9Bm;%i zo_A6Lg8^2dgNV{5W)tj>`)V!c97}D2qtYZrb8ESUKP$o*^@Ju|KGrf8?A7MKI@FDZ z!Uto=X?yqc)Rs3eTX7Mnh+n5DUF6Hj{+@%*6xI7m03(KCgyVwaB7zJ>$snRY>iGbG zDS}1_FqGZ6QG!x>$q1$4ajG@kQH|f6XPj$j*K+qD@pwPe@N=mjJNe2F%>-nWO8KWo z)o&g9oy9azGjJP6~MOB zn29=`j=-1JM>1MB7dryz$SYshm^+m}!H)_r9HB{sahW4<1gW_S<>B(t^HXn-T@TH- z%o7w`s`4T@d`?S%^_m0UaEdO`4u%Y;d>qdL0?WQ%|eeq~q?~qRBZF`N!b`1X1 z?9g96yWF~V?S)4#(WysSWmYmzP)7#T6$LZby$y#?E6Q6X_zK3w$@~{5dc)?$#4_AW ztcRpj^1_jv!Z$(T^0b-+dcLuK53?(Uj71$9(wkTc09KnXyB1Z%vg5 z)*`SU7Mo%N>fAE7<-3wSd2yIw&CmQRGq^cH}#$Tc8QjybF>XHE~C= z9t=a6AM*G&Ni%O7S%H3l4$lqh^J~qeus`B4grsF;4^1JZ@^49UGW7_Cy2WklpZ5^D zE(uD3lSC#hA779*P%CrkPT$`$>|{B$sj3-g@v2xhKt;{J6+EBt>p(Ow&`=q5je#?# z4>5{ZPBqwt{|c0Sj%kK>C>X~l9B(PmF06K2>m3r0E|HGqKQIu!)?wBbT0|UqqzV>u z0q9gFtiFIp$!f(o)FimWaouYV;vFm<@#*4Ku;;#bbW8kL;%TxE6=R+u$tkGSI@-1r zNY@*}i1hfAjJQDs%Npbe@hc4;Y0La$2k#lleKz}jyc#k*Tw~%@Y+$P!+w<==w)KpW zWF7T(4oj7_1muru;34fOa}RWdsN~Z393%x-sJTwE4W~a>P;r~9rhFdcwW>X{A`#Bc zdMCQfS}+WCNSY3`O{fwm04Q>@HwsT}U_k5!<|c#~Wjy;o3l85NwJ_z*8({-1jI^E3pkH0q47N5sy$k(YEl zb#F!Wj9&fo-h$}$G`>q58i>!hk7o~ zf$<&!wfoOfdUq*!t2l0#;ONh1Q@WXnYs$x`3~8Kf8W$@&1EAd;HN>eD(KrhCTP*0lm zJCK^BkPO#pM%Y}Pa|zuN8#43DbX7ua2^nHme$1lrY+RKZ6-Px>;_A0}HL0&1zd=P+ zkzra!*MJz%8afPxQz0+@#KkN+1{*b=Q{X2UM@vnJldS)q6#0vTKP?+_9_(}@KfZz- z3l2Ou*Tv86`?Rk_U>31*+U^-=;o8Y$N64sNJaqn?Mv9MX!wz!-2vEC#&dN5HZ5Ohe-$Vn`d0C749o+m#pWE- zQ!q!KXpHlhQll2M}SBPKOhiO|> z`7Tdn6&4IJ5EESZ2eB+(0&#qvV<3>XfPVL}S6GzwnE-JTVz>-7qij36tyG zyexOUhEx}# z9nM~`_ko2UJgN#87PDm)<5SS{8`zPKU-0(-TC9?GD@ z)H8a)>Ht&z^GqSXrINryGLH)DC2gM_%e42Mn%YujU=<3j zxM&%LtA=qDF?O0pzD)^VXOg8cJcGR6Q>A?!Z5o`=N2orAGSl~0=OH)03ti7%?q#_F zme$w1(+D>!Etev)WnEKg8?M2Pecg5gYpcs|$+jMM0^{@2_+ATi417x2|8qMRM*c zi)}hdUGnz7T$@9)5QqP&ZY{3=k=^LNe2YVTR_yr35$c&tP$1}fJMw-gez6)!Avx&r zeGMCVNSNX;d0~>EbEM)FaZ#k{yQR#JNy|w~5xuN}$l`rU#a9iEukP8d#TfnY*1-nDP^doF zz~Z%mwEdD$?SS)JkWd1;!5^^XQfw7FfFxgPSUfI1zz**< zbQP5;i$+M&L=0g<=p3p5@fD`>N<`^`LuAYvmX>E|tu4O{sE4-`qNlfSjMWa$_1DQu zNU*CgDXTR*1Hr(kBTSHIjtjgiIeKjnQ<#fsWQ|~z&s#G4x}f^$%Dd_{ZLv_7|aN|5tm~c>gTlaMT^XT&5{k zYogp>XTrxD492C-T>G*njgS4fRJ>-_QpPr+IR z%q&vy&=MPMeqJM!ABqhU4MN4ONOGZfB_HIj*W6LzzhQf|-$+#tlf3EM2qqAD6z~ZU z$yAn6HRb0C)G6dfA=xV=6z}6So!%2*!3q=1qQO5h+LxC8)8z5S5PsyBl0P)706#~o!t=h z91wynWb2f;shu4eYZ9F@oS9QN?*PD0=1dxfK1wf$29W#;gg zVi=NPB?_U(p|41vS{=<>dUi$Bw6s$gHPW$PV;*1mi;wm~U^IkmG&X3N7sWMnI=w<4 zUmLJ#Ktt-{VO%SyFkr;Ml)b-IJ%)t?c;T91$CK@>TaWncphxn~wTlAp%k4X~MEGi2 z2gyJsWh`Rvl=GRs8jmFx)|YnYfY;#qP_%H9GKa} zd7n6xJI{_@_v~jATx(2$>|@6kFL#yPp~qL{!v&#_s2$r zV|V?cqg3_5C#WR?))LT)oi)=Cee3cHdQSa16AA-+hfacROf-oDXl#{VL|+U`p=AY} z2KN1<$nSma>F(cpKxcP$*2P3kW?7ug&64s~bMbpPVe~L6@xb)lF(pjam2I}I-2+OcK8Ygl|#luEph{xoy zxO7~OHMd@#BdYBaAwT0R@~DLGisDoCJThA-P%D%{;O|>74{=vO+VZ{5=oN59Rt@j< z)3%c(H4x4|OZMQ0Ysy9R6*~;GK@DJmB@2|E1fgqV2MUK|`oE7X2I?%(f9@EpZC%c* z!Ys-p6_=v0kBE>$OL!*;(Mw66aT=)}F`1>>5~ru*rro2WkwH^O5JDF#rdz0_XPtlY zwR1ral1RQNWelz3VN^my#sy1=|0$DoQ(i2Dbd(UHHPMKk85FYmO(P3TT!9A$C(fhK zufxRa|HLO3mzFN!&6YY0l|qI92CJ$p5gEJ;=n{S`pDAqUgOMc5r7KcLoUypHpiU1u zx(r%w?rP_j?9EJ@Hu&y$UrgF69x_IAKX3Cv`ZuAjYU&N8x(bKS=LSK&6kp zhs*w3oy$yZAI*a5M?O z4$n;0LJx#$TaSZHZXD5vAR~|ujBaipW-V?7Unqa+QYN+mb5xs2UD3u%qPTw$@#B&U zno#dND&j=;4{kyE72k~aUB=wqFMVns1d^_h7h0-;lL2wE3tKY7B z;U`?@j&*KFW8dWFACNw0$jCK-IJ$QK<~8|E*D4g-!BPzfN-pKDSRrRjrFNO>s)x3u zm&hjdn7WKzO2n#}#t{dmZDT3o1<^lJDnU%m+q(hg)cPd6Kw?o)IRoYt+?wC8?O zz?41-(+i#Vyj#rB4}J9S`u6^ARudePp2Ke9;7g5SUF>mCwdc;!oD8R8O2h!E)r{eF zy<(+|ngww)xGLBN-qx;3&)?!gWcqKSw zm(5d`ip`TjaRD;#PbZdCQ1Ch^hznQb31Bh0da3beDbPwAxWL&3QbV(Tw)S1r)WoYo z0aT;lG2Y`IrpkVm`1y@~>er_6^Wp_*7>*3QJ#)68;q%p8o$ zz>sB~8>x0Q;;ktBe6z;PyJodg$CH~}gfF@g)5xfA$niJMlegFV8@P5(*(ORJRtV$v z-nx2vaaNV!CeLPL?*2PjA(h}${uoeN&}bj={%<(VfEhmHsZZhS?5iW=n_U7X+TiRp zX&l2?Wyqy9DY2BVo}!Cg+K)q4Zoj5jTM@*(MT}5%)Af%VP@FFG?Car^K$ykm*OJVG`o7Rr{^^{mj$g(vi=; z{P?Ex6kJ}8wwA+oV7RtRoc7ZE)|32hBZEsW6@5Q?eJ)hF15p9`=P<|UuR4CuhseSo zo^5m4hotih3D3tj}3M#Dx?!5={_AIh16*Z<(Qq)#%0 zJihihAv#atzpsBFz9siB4?)Lc1Y3!~mj~>~h7v)Bc1LwT+J+r8^h1TSxH*Hj&)h#- zxN}jFG|(DxV<;>T;YeUiqo|<&0pxoUydQpDEx>(Ztw*5O#xB&Ec`0MdO_Q&JGD-$W zZ@YX>pSikNAqjgH;fRpvYfu*Am_bGIpkwyM&}it5%a-@oP6uL?C`VY^DiW}Sv8o~& zt}B(RdDJmh{-Dp%$++OXWfak}zHRtsmrg}690L1X|A0KQZLB+SxC+=oTY*EpaR}kx zF0ZZQpKfe) z6hLJ_X_R%@T^TA}NocS%QS+8_z+d-`$(gl79=A0N)bX<-el?X_Y`U94s(7U(*Or(; z?st8ukie{BR6wkJ61~yyQi^M%H$JJTIFu9nO{tBRw;3{v6ez4GGNlR0&C| z2w&3{Hj|@O`X%OX4p+JIdc5wLl^2r@9{2e24Dt%0mL+3&HoraJc#}mxJh;p|jyKZi!lH~)V6g#**jZE_j& zU3|X%UN|%=jS3&xw-0f#@Y@`A@40JoXOEc971;dF^#2$DQ2%29FdPNzkFsiin+^kJ zksyLEty)`(A0Lg}Pxj~vpkqD<9%42?8g)qI0q0D_0Cy4)IO){Zg*s{Zh*PiwW(aL^ z5W6aww0m->FcKo4i!NrUx4(tHL~>qOL)KK1UCqLvc6mqDcPpgKTT;(#f6+}rKB0GS zM-W9>%?+A*N6oGAOJ!pGA2<}e#e{L*K+$&YM?JxMWi&)X= zttxl7iFqC~g?;^I7{4XE;@d5mZ~-#>>{?^)PQ0I?!kkqDHTOOEI64ZvJ^;N0yJ zHo70Cd9Wjq{HH)dp5j0H{X-G)$+_4cF_M@DRu`TOdHPEsmnRE_MQ0eJz(kifb6t}g z9D4)YX7hQ)eU3nTZzPiG`z-^QX%mhTAJMoD4Aii?z_7B*u711~!~uEUvZ!^QG3B@+ z9x_TFlst7RBe!&{mx3O&36~XW_Wu^Ns=2H$wV-I!diN_1kHBgf_+@Uko3r$h`Ti;s z>xYi#gtbP3j((JhrEzORKJS2g+SSVNejSv7)1Z3HN@dV;tgOs%pV|ib1oG&GDHYIL z(Ob36sK^A$S!FWi^zkX~9r2fHdmh0YfAftkboNE6`g}=FtDOBSmuyU>>j_Q@CxR^O zZ@O*sH4I52v(n=;$SwWwM^VaakvL7q9whrTb{Ah3O4To5w$g6x zVaI5ocnW9T%_Yn#3I!Dy%(-dQap81}Vi{FoP_XK-WQt7VlW5`9|4B4v7r0)A^0`fZ za5_B?KILcYn^&hX1=4`1V^I1BQAZP^_m0l3cXv6-;foVlVp1v~L&B(aq!UC&Qveu< zdw^2~T*}=7xKc$5X3r zlI?XPLHf!$zX^?8@YyFJft0>EN^QkP>s`red*8Yh?Qry8>6WRf_tOqZn2CcZpM|W{ z6t>SyCxbQsPlm1csV=v(_m=@~j4;qKr1OIo1j^}w6P=(~ClP>a31<}iOaOiv9fAoz zK6VlD|3}p~MQ0W@+ja*X+ji1H$99KbY}>YNJL%ZAZQHiZFE(!eGw!+P+?Vyb$5^XI z?W$R`<`j%t42=p?ETIfr1Es9HK^7zbJO5%d6z@c&1`(#4>9%cXnWp1`GvH~f zR~e1=4Y-WlyHel_$t(5@J~0PUKFt?!!FSEO_g6`+{a%8zUm=t>Kmy!0>U4ntGxXPF zlV;P!?jhpUijR!l4pQW5=jDtuB9bM8u-$OCiPI@dn|)HWmSBKP5E^(bx&^EVINYiV zM+HO2D@@XxC>t4m6-`B5jzdNlE`1P$#;#xwXT%MN_D=~txWZc0huC3YH1{X}sNY9> zGkhC9e%mo(w&yvi%4_i9UxaFOwDbs6sU|_Hkqee4YSfAjXa7iv>M1xHS^ zJ}psFXk}P)-5uD$NF<$Q#SdO%qo7?n@2T4uAHY+l0&4kqpnJ~@ z?C&BNfy=n}w1RL)q`$K?eB^w9;TR#!P^wv|ffT@F^xz~aE;sXV@7r<9SH#oZ>euUh$J3r# z*4LCKzV7OmPE@#4bt);_`;>;!<{mYsxZq7w zy*b(gM@T4D+#ou&be+A4uzJt;-Zy&T*%AIGQXJM_(!hVR>(;B8p32SYN5(7Ymw}Qk zTaPZcA2aH-|LdpoU#zorUcE6Wanc;hcWJoLZ=3bB9`$5Y1QyKs!#;t@xK!RZTU1cEJ>bFFfj9Z|igE?SEd9V4%i$-YF)_SB$x92*^!5>3s^`+Q3{y-Kt-A&fAC&? z5ris8_ z_ObEMx}V{5%3Yl0ozCXt7|$3alq(d;A6K2}FBa(4MJqPNfulh;h?KezOGFZ{N8{52 zFpyjPO-d}zuXs{o#7^IFH`eZXfrCIiMLsODRSn{rO}2u5{PF+@#re|&TspF(IA0yz z=h8a19=c0^|M*{g`sJ8!(nHIBPVO;PhLUHXx0YVyAXabMg3~bzV!o~vM1ZX`ul@4H zS%?Vt``1OMT8)vYbJkO3V(YZ+Ft%)oK-3Rt1<;e9o2Jh7>4x7EnfN_YaRgK=;oq2y zqcwth)Rug!bnr}lM*)3$YDCl*H3UK&k_@EdN2r$Y$g7437e9Gwa7^>)F`)S;?>bFZ zxR{a&7@t~?x7wP|lyl1Bzx0ld+$)Tl8&UqYUvaY- zWOjL<-NAF<)L_nq&Z+KA4&7MlibTSaUTWeB!CO3siXTZiJk2^olrt1klTWZ-+m#xsgtwetVYl+o; zY_c-D?IK#=p_r%@!WWh0hL7VJ!>L)cT}~f~NGwfiVvqh7SY{uooOPJ>rUI4QYxDc) zV=P^T!_6(OAWK;)CrKsgHUx`^`JVJ<_bp1xL9nB}Rq`{v_f{aLbCa#r=LvT@J8q*~ zb>3+wBcBzLh_Gs$oMOOASUilnx;wf`^Opd;G7D74coSY3JD3=)>p!s8m4Vzsso|BSA_43`vl(!PWVdBfwrSF9Gk6KFXnz&Cc}+r-}TE5pN&Ygu}b%88{?0OwO6t{3usU#Kqa`m zgntF4io|g7q(7@1=g5MU)DjYnRk+W%Q-eE{8p@xF%i_A~hLRofkMxT^0TtD>3ppVN z+wO(G00qO2#>_wB(avvY;0d8>LYy<{=k|`vg1Fwd^rF#Q(<+}k<(l66$enEx!`uw< ze~6{TN=Im7%5L9oY1s52T4`- z-O{!#nVY5+xV8@#*np?UA+KMKUwY$jT6-`xe&5~%{r^2Qc6mg(@3}8x$0t;HXxY%z zo$PHpxEqR58Bmd8Li>?BJk5LZ+eL7BU0q&VERNxMKkpk^*_(070~n}>l~d096aC^$ z>FqZ|VzCDf`Q2;yBh{|D9J>16Sk`lP$DnsNIOc5mQ3a)n$fy2@lV?yi8DIo>L7W2D zeTJE;GM^^!xckc6pK2W3AYgWh^@)}nWr+s)E$j#}(~zA{8J(|~^S$0uHPnCQR9RH; zMlF|PBC!mTQU8t2@K|2M!xV9Zf#s5!OZ=dDe06b*{P9r;itBUp_fZijc)=BUMr5wm zND^NOO>YrLi*8mU-)!^0?)f?4y&HFQ}y zMK4-LD?*0DkII}6{0{^^DHFTXwsmZTUz!{*=onp3G1jygVxNAF^Du7F{W z0Vsy(-a0Q)fxn3b;lkIhrgi=j3bgcAMFT-bXI>tsywB{5v%{-SU;XujJ+MrcDC=;f z)=Ja6%1hl__RUNIZsijf=|qy&Lv-G+^`}62#tGd1#o%Ra4!9%RGeRojw96z@@?Jw0 z^2QX2@`Qgde#k4++h=RUh6?yW|LV#d6cglitG0~_c_mDaV+lTXDV{H;z4*AU?u1kn z>ch>0=HW-fH)SM-B$Poa?7$dVcx#XcFycyGU)H>YSv`$aq4uh=2rE61jNFr@W8Czy zNR>y@l7dI*SXX5VPwXY=QsUHmdxWH9U#aJx6jSAK8>*Mu*b)m+(qnPu&4bCrREvi$ zmGM7ILsq|TK0NQ9x<FP;l`sjpk_>IOPq%WF4dJ)o*F7sIRZW_ zD2z4tYeoe*41EvUJ!Fe(Sy=9upbD;MZ!x%0^6EiO0s@ z-kR<2#R);$Y0Z3WMk9hyOjRS>0>By6X-mq*B`GDg#t*c`93d~#fV-QhH?|K{52p&c zwP1o0wcE<*GF)GY6`m_jbXV7)>F73HC?`{sxuW=_8Lk1YZr~X5`1zQ|NQ=k?0Hk7p zZH}|~;!sg#@&Q4Gn6Lm8A)NSu>!X3#*ulg(FHiVAh1Af zRqK(fCrW^hkpz6kMA&Mc_`o2Rh9Je=>y1Y?pj|6%h}<1*p4p`*5F~3# zh~K^bHbg@lf;mHdIPrV)0GsoyqN#nDjm~IfV;6`s5qyuF??ukwvh!OJWGsbK_9^41ES1b2-4q3+P1NP6f zk@-(hZhtu9WSJ_$sES0Eb>F0ecBEWa3REc|ICZ{DfYiqS%HTlQj^j`WUx-Jimo4G_ zMrsks;P^j^8GxHm41G#%Q}PZcu~t;{7Eh;*(r!PZRnX4aPq6_qfIX$4qYK>UaEU4}uF=MO~G=6!r;Tn=}Y;nwq$ z{%?bi=rom+GJkMpVE|NhIHE%*Lgh&t?f%_ySU|7gM^PzM02{p;nONXIrZFP@wSN=i zOId`uiy)nuG5h7_5%`ley!V9d z*{_6eE|)_pnOyA-#woizfs>4P&3Bs*yxSLVm#7V!mYY#O$1f3oEU#ZhF=nx8bbX{> zo!`HmHr3@ZSH5u<3e|=Fc0IShZA9vqfXBcyXQ4upz&Ev*Geef9L8&EUtxn5ktJ~H4 zl!nI2HPV10QzIv*qA-Onfl>W7G;q6es~O*Zf#B;+kRkAu9y^oyx%duQQJE*8{!lbb zinzzVJVzBM;?dmj&r}JMFm%vThf-J~mS#IU;<>j#QNc7po4lZBjmHYlP4=-`y6^lv z>{e2_IXr}gb12s|t%%gV*VJq@a>-b13{bQHhD3r~cP4mSuP4g5DHx6js_x`lK^P5E zTOvWU9CH1ema;B~`@SBb{(1$u`_1PQUPZezvTA}PH)P&&v9!0uJ~@7LOC?xN20HW&y^*&b#ct)>zl@HglKRR0(kWoQ>>c49lrimJ?t%QO8*?@?RyH_;QUjvj| z?W01hLm5bPdCy}B`g!ot$1=a*X?5JD5fO}kWpZfa{IMld5?UrorRE(FM}YkPI(V7))+@_T*4W>sK{#sBeM=#e5{zVd>#iQ31RmXw3g|yi+8Dr8phYfz!%J zE>L&lPdUyAqh?uPY;fB4MI~|Ka|N^KPHI|sNWsTgA?4?5Ev`lb?3m=@bxo?Qq-kQk zLz1+@k~=-tMtYxDn#37Yk<;}yqq7xPyJzMCnSvPt_OZB>8$nBg8w2cFN+*k921yPrYIx8I3xCN%El z^{4oSX3GQ_F&D!Y792Z@%Wy^XGjyxg`vrutDe^G)@G{F}=I~V-B^>UN+P>?1t}O4g zLkRp7se6-@U8_C!ZC?o z+42@8hBu%Ix{g)5vDNFVAf*eQ^fQ#ynW!WpJ!K35j($7{kJ#vc8ULFL@bdO`D&z2c z!=qYF$t#VzK2q`QFep6&DEPN7qt7gFu7|~b6BsPO|6C`HpWeS)FuBy6F`8ZU6i{Jw z8?m$U&W1`ZIPsMZ0F5jyvM=GWS_9*(43wQ55Y+Gy=v9Bv)#FekMcIL(8I(L>ALJrp z@hfrjfEo-#79$-`QV!2MbRjfTc3|)23@(rCi~$}}_`DQ9zjwbsX}UdvjPnyf{1n1- zf>cbTONygJVOzrfyK0zCwq`#!cZK*nFg#^P5;FcWN1rj{cfSPap1F|AiI83$nw>4h z8SBOwM^oybd67P#^Zw-;_oO1i|Ef|n5rATQdS9Iq7h3AM+Yc2&GO$QtYV(z6wzL?t zFu8w)Pp2ktGTEwcPrR}a($SO#+Hv}4QssgeU z&^^LH+pDZ70)JVFu)_$?Y1bVJj|=sW2!{tDnxD8Ixm~psmHJ$L zfljAo!_0A{CQ}^8spV}MI%-6V4~8G8B+M%Im4QoV{I`*O%g(05l#uZA;GQ;gdsp~3 zGnpF1np%XBI1?b9(g+lY=iDz^OL)iECE7 zeBHIEuJsD~OXG2Ex*4~8Alv$p^Q$`#PHJ<+{7~qgqu71S!Pi z`f+5~v5$3bL-rz+B+x~H*#f^#Kni(uap4oh1ODN8(C3~B*VR#`N?5k;EKXB_pA-eb z7%D;0|E{&30oxpJfxC37F*`JlS-M~iGa6Y6CDf_sER-cql`W7QQDEeOOn*S0cu+`^ z!Q=vGCXgjw-=2W>7PKfP>fNr2Z1yVgoqK+bxRDA@7x`JV4lSo7hl>4JHkr?1OTp>3 zKF2lWiTBHiO0&A}0nI7LE5wo7R={552*$oH0MMt#KOx>pnZ4*1A8HOJ_55!J`pUzQ zfbT|vZxHxVzpa0L_5%wO#@f$LOJULpiwzEKl~cYrI;xR)y>ib$icH@)VLwnn*=*G- z9u}CmyND)$ax$_c-g60dXT!SFz3D&A*7mDmy)&zD_L9B0S;1$|cr8^g7|S^2{~l4U zGRO#1lw4eb@K-7Ot@R?qZZskpuNPz*AI35SmcZAB zstXMEussiko1=8UaY1bZ=T%9Aki2YC+_8yE8MyHkL9E^KiWGbgkn7X5{6>aL%=L195On}#|y0< zw}`x8_+%TafC2~;wiv0V`jxvV|B!RRlzA%W(+va3BzufYx`PH8a^C)}f*80UH( z!P08mc0x<#lIL;T`Mc2ELvHh9Mid@n%2F`Zm?yRUX@WRyzj zIg;0`q-(gG625SW-~gA5W7q0EV~|fa4b|66*Ou)wuS4gCTBY`7k8ZT0`VX|4-QH z{78R;GI;2mCmT5uz~Z4G)-0Nd_T=nVp#j82BDQb_&3#pG9`BuB2g4lC1J;t}8Ovgw zMapuzlB+I^sD+5$|JIvRqz}>x40%T*~fjOFq*FCAp40(0p)^ceGEjU=Y`^B{%aqhM5jZXM{&Uka~S&u1Zm@- z`Og?SXk16aLV>CXRIQTVzmIq{j9|c6fiiLj%FuTq9q_ASEgrL7;dp|%Y5mGMtnU&v zrAuy|t^OiX_P-eahAX&#U zi@0iG41H9%O0aEhhP@3Tb#-T1~JMV56+6(5+Y^&IWN>A#x%=zU5kYz223I0kJ|~M^x>%nn5TL# zDF;^8^i_EY3RLM2YGwwy)#LYk&8ZS@t@(v-(mdkjL98m12n%KXDXe}RU6NQCsm0eZ z2qO13sLBTVwq~T3m$mYHXw7H1!*IC##EUK+Vkt{QQz`=>hfA@H5~}r4wuqMuv-B#Z zxW|_>h84hno$Q~W$RIyn6*}zv=oAc5-Esg%T7KUsS7y}V@YxcRJ5R<=QZb3@iTqpx*ywKG6`2cN=te%$<@B@P5 zeLxQX{W_n9&kqaF}Bl`WlZ%1y9Mcr7C&w#(QpfY?fpZ$-~EVnGdz%^g~ z)L9*CGnBanO(XyE9_TJKR*4we9HE-|KY4oBhIOlcpuhLKI`KJVesX@zD5oyrjHK~T z2>Yioc$hb@PE?f9b>RBhaJ<+|dpDEYZh_&*Ra?iox54lFkntCl#;+~I?hhr=?^%JV z`{7-li&ZtdovlKPHO4sPd(;Koc&eN}O=7bjb;5o>jiXl#J+L2KfHQTN4#qi9^5(83 zbO{sxC`8rC540UKJ@G~oR7z*|&?=1j%2nn5qR#!b=LsBJF2Iy^!5B_K&I?<^o*=F| zOGx8H;Vt_%R-uxI-05LHRG*#yo!~A?s!CcW3hT+(fU1{_OjR1rBTI>I=Q0#Jn}Q@s zzf4v%M=(knu~n6Z9WNGCQn+*ci~WHV%^$iKmaY)j)I9c^^_^E>x}qBh2pEd3BqP<( z7XDWd{jVoX7oCAA15^x78`+65?S%CEE=ex7F z^{~w+u39cHtikh0I{$bxUN^PtbZXMzf4o#Lvs}DV9{H(f#2m@ok1d6d)}J^w1|L3_ z3;40NH#?`Dx&CGcH6fvKPDC^ynWfQZ8rmdqQE{7^{emg;aotb4ji@D{R4+ftNfw^@ z2aO~t8_7RJq=b&G&9ez=efSR#{P&3PlaJSwHSvJK;l@Dha9shL@$$r{NzKaN$&&{ekX&oxbEqTTx8N2FV@gp~sh=!W%_uFFR<4^vA z6VZwAMFoKYJ>muBXklbDJWX+3B$WJ8P*(ag)RyPlh>X{I1Md2B8TRMd5Vw0KncA=Z zCxy$5>2ARnsSIV(9ApSEDj)!v-XyYpte@|hH%)>?=Ofu?NdIKkhP#Y+`Rz9pV&S}z zF6*wMIFuRK)r0IfDOJeEcURi?~iBBSgS>!CwKmYi%YlWWeHG7R2~Hr-&oM zpnp1~2Fe;};#6k@P5V4Cdbm8MWS<{h9JydgXSI(tN3&_-a>jBKMUxj012L)rhxKWj zfrlI`^|7iKI_E*>R54DRzJijos(!AL*zrY;*wE~uXb(B$k-?7W=)g7C^j@k`>+85M z6?%q5;vg~)S^FP^%uw6e{ZdFIW&SU;`WzP352?x|#buz44q)K*3?9zQs+xn2r&Uq| z0!)1+40HvM3?N6cEJDPTET&_@sz=~=Xj5fQR*HBr34t;q`5fy=3}vV{RyD<7VI7e9 zc7l*MRgnleVR7YANSea-Jn|^Ed7&uCaiphBvxKRxw&~EE89;yK0s&U69o!a7dIn;V zI@}}M1cCFP6lzTI`lKSETXE#;H5ICMt;RuDYf+o^$!EPGW>-F;XSq+cPd5}dn_HIl zeEqO4Q1im^)y)%8OWE4i?WyzUNBLMhG?iLrmosdasR3$|ZS53BMp73Argm>_6#GZPXeD?G|4Uc6VR&%`@z2T$HH zg&Z1&I%9>yA+{PTB5R0p1Kh517HCrtkqdbjgGorc3n+v0V)3ERE#w=QBr4~Ss3QTN zTAZx8>~$i7M1>VpsH4!k;o;(M6NUUX*xUt} z{Qtyp{UKn26vxE7*&z^oUO#ZZ9_^-lKG!|rI_RSKp9I#n?9edMnfv2k1IocCqeGg7r9qUN>*siT&Y|1{GQG$T&SmU z{{LYQTfh^|&5!GukM>sael{8S*0}{oPM#qK#5}p(`aj`N1)-nlYtq5VWtJ*9clm@&g{yj4N&HI@kpc{vUe zE#`lISD(=@Pd{oHk=qohNy#Rqwn$5W;UET^+;DbB_p~Hn!d2x>qEQ_qq6nCtD553N zU<}hvjqN%76m#?|YyVuy%s$`M;*akSBNQ&3TT^WK3CTo8zI$sfwYgEHKgA}J8R$R) z;T`RcYwraP>>=L&2Z}v%S&tijFfd{k3aR9h6 zUCypV;f%cqDo$GIa6n2escOQ`=z^y+cf#Me}pQYQV4rg$7$1kLO&EWIxEJqX+o)dfak5 z20d;D!PQ?Cb{Ugk`9y(9m8O70z6Dq<8z~HIP9il+wsQNK&P|uNdtewv^Q=nU#6v`J z%js4je?{`6i^*fTp}^w;2+jRB!j z5KG@x97(3%{?`>whR-AW_7{Oq8VTS1@2WC-*eH2Ln$(@-1A8J4)gwGOOQgWUcgq z^EmYM%r^}*!|jXq$+h*V+va@j!AW)cNkHiTw1imJfltggwm=>U?kd0jZOrRiwGxFW z6{6iA{qi&kd%lYB+oh)F>~`e0)t?=^-Jhr7+H|{H!et>C(bLobW ziPKoX+Fp6WL4ZPx7^UqA!!!!Q)oMfM)CTVUAs*PDUX>_eF6*V5O zv_ip)5QqZbCKDvt^|WwdeY(qnEX-u%3O!rgs76Rv_aM}Lhl z<>@;kDii45hb*16^n61EJu}WTP4%ShyXt(itH$kLPM`*tBYedLkz>j@^xs$CGe2uv zprp4P3XO1Vi-r$&h(nBOYX*2;BXRQGlU%$X6x&9OKTA_kml+rQC$ya;-`Z@7I9s%B ztvDgwIuET;Q#NQ4UY*s6v=G?poL8sX=CKYm)Wj1FJ@ip(W|nwr^s@y=tLTWF#?OA3 zF8;XW9bLUp#r_3BI8!ib8=Q0SH%>(&G9cuzlYIKh;5k8EYw#KBiFOiF7>th}KthWrAA*m5KZ&_sp!*OU zu%Uw*OAGFPF8;zX>3)RGEE-CvLA~wDyl2yyCbyx$lQUKFGQvE6s%=cZ6!-_DKPF2Z z{C0H+@s<7+?ty?Wq*Tp8&WcH2V^`1^<_uFE}a-^fP6eOisBJHRn?XY9pByb7gSZ-Qlb8T z;H}-v$nPXZNIY>Gn(bOP-TmorZ-)s`6eLqeRpXz{p=eB@}|EBXe$#xX|A6 zcRiV&y;zeXflu%i3v|9!ArDf``ZlW5=IkH4QE36576aE>=sNF-J7JDXsFLfy;Z*?| zWj22)e0AJl;-?Kk7i;Uft|zn!JlzY<0@DIgwN5$4A9tEM6iX7dT>~e5^(S-lj%}Om zj6;bqHZ}jW`@@h#;HE6RPiUPGB( z{EUC);(Nt0@Ag(lD3n=M`ZlaJ)v#_}TL;Y5bLO8SMFo$@=Ilfo23n+d%GS~IF>=n| zgXv*k{OruWTYoy)pS||zrvLW3wLUWn_l7GMoccT=PF?+3nV}s69k>5-5c2EGnfHe>4n$lELsLA~qsS0PQ`WA@D<@{l*z#$P5|r zAoPVI*eM47_AN&rf4*~kUTk~fw}NoZA$Q5SYcrIWF4&T(A688E(m_L7fNq90j?EZ# zB=jIA1coHxn`*(`?k>M@kMRxOJsSZ*_}P1wMECtN4tJ@UUBewBe`S z2ownjDYaf&-KBiwT7?pJ>?pn%`?13gT@P(@&)6x_^!w3q97D&~u@7Qu$PCLskS0Vw zDpM~hF)}|&-M60dv}Y{vJ;X$ngRh8yi(LY8^>ty)E@uzAHXr4-ckFLsSk|a7MZ4$g zPAxOqzYW_AZQw_gZT_K+@=eX^6Ha%#TF^kHevzwc3_UKx^VL&|&6y;tB{xKz*bgqE zz$@DaD2yg5^r@~q!?n4`yfgbB&LO0Z8C#;y-z zELPj58vTiA!Nb~l4yVM}q83zDlofB84xpD97GGQXFV1-RdD8IpBO}!L`J&ZoK=;^x zE$^GaIW)jS%kOb}9$u7wM+1iTQakB5fq5Vs{_av;#w9C~roUk{X>^W=ZJ6G$0^bcU z>s9f2U*zsN1*XrNnj!Ome3RuLj7N({0}odzcd90lI%&?|mk4#Acci|IXZKj#j(iWU z9cK1D#>3*`VG?xR1AWb1!bW}~gy}H;suRPa@|uc5QA5=Iv%dfKlGax&HRiWBFHEfX z=(_J2BeuPpNU@7NeWc}&%-pt9NO94eu3`CZKP}d}@L#!{={a5eu3)P3%pwyxwB&^o z@{Rzb0MeOoSts_#_U`F7zPD71SFcXHR4h8L<95yWeLj=j3%5M*Hir0kU1&KJvAVwb z)SO&mr{mf;gdA_ykc@9IX3NV}1_dFuzJp+u>)=x>*-ZLDtrX7{T&iFOF=OV`k?@zi zWQ#Zw5KDMCS*JwpB8njZ;;FlSG^7pcebj~S`jU-PQ~RNWJyPf&;E-FDc+fbW2ouFM zq67GY+tbyrZ0%h(MMItVf~zM~la&6u^_oZbm|&F%*6SkDI9U!ekOAC6$PbmL`TViUlAhGnu09ha3-qpH)+*F4#PE7IO}bd%R^Q4H5E<2kOcC z-wFt%MYK@s2tU82|4Oia!!*#9))}he4N#8)2uDIups8CDl2h_S%Yn`U^{vgG+MJ#L zIIY%OgjRz%*(dD4q11zrh3Vo;1|?5d+m||5RKTmHbk`>t*nIC!SeUdeI0dPUrfS$} zf^gy6p%GC|Z3cU3=?fvNBU{1cjcx1-Mq*%u7S}~7X_U*auf#x#)p-RsCZTJ`RZ*}H zyAJcd@5k)pdl2N~wV&X!0~)`6diYPI1@PxmlXp29L3QtE~ zUsaH_VEU7t71p~(4@!sP<7kXD$Zi>S*Cb6C;)^Uh4JEbLK~|hF4b$b1Yvl6i;3LBL z-X8c0+Bc3*V!8-6_suGDK~QCAx)i!l!HOv+_&Nd*J`bMmlDl7wUP}ehz$A3%kfq$d z8bcNe*qhPX1ye6RACkhFjQj~lPKr`cs80zn#`?p1~V zp&?YC1G)sRoU zwOcA0Dxk5QOd-2cXL*g+qG=Lme8FywwsV(#0}|mLK**q@6IJ?#$G=5js(b*hX5mtA z;La6>tC0n<+$tTxNyWWsZ>J*0K3X7=f}6*oHd8LQSzso-cwT!wS`0O;W70Ks(A}H? zmVE33MIjQjYakWcmulmcoi$~ZWZ@#laU|5MGf#Jeo{n}FS&B> zq3J(9$Y2sd9YfrCo(=|ECjy*<^52qWy-c(&CL?=Gjx2_&ZU+sr*rrufOG9zUa(TyZ z{v2_lZ*fc@OU>I(cU&LD;UnN*S*?1zcxTnLMAFdcV}Q;E#TSawDq3;qS_#F2&Fyf1 zPMmAHeJCYGbGwR0I1D4Ix;-ep+H^vx(B>r(%&|a)j-#9-Gt&p|a6d#FofR6P7o#C4 zg~&M|1h{8KyVESX)zMz=XGuaU9b;oif}b7#Hy429XfL8*mOAo);nm^J#WM0ofW)xc ztxr&}3ZlTxp_b zMS*SDVh(OMNXV4-(JmK&I|YkkYN^AstdB%h(iKQ}BrtJQ_D(T&^b)}q!&*{b%OPiU z;|GCBORH-=xxTK3gFcp+hQ^acV!4+5d?Zg!Zxro(gjqUArx zVtW4rh^ftSdpZ>JmgM8ZCFJww@0u31VEy(jM_r3@Z=Ji=1CuyJWL~VPG)*ACx)Mmk zC@f$kw<@f1G)+KXKoQfBIyX?Q3EEMj?~U&TY))cEti-e_um_TpL12spd&2HEi0x(0 z#_K07w!?iXA~UNZ8DYVax?MI(;$NsyG=HJ>oGvveRVaa!--z~v;o3zb+EXcxk!7HL2I}K2S>5D50 zeg?1)P^B)!l?}`%&}!YOCX{6KXY6P}z`#YizaM_R2|MF$jm!Jc)t$tpSl9_=U(#{o z(Xd3~D)Q|6#qbW)eqF1fZVibc{rNOGrV);CEW@xRmb;$Y%{VOsBzd65+>sO1q8s(I&H=IX5>~ zdP*@2h)1#CTrN6S!rA9diVXI=cGi2pL;;W;x+QeH#M5bes{*cH{Trtyia%ZIdie$cRT1EwJ3BzUb2(?d>P&o~j-` z)@PoQ43!b3LP-~j6dl(|!I1^1#F1GBDS8VtOwkrYA$uuj$>J6AG`YIFXPcVZf;Rv+ zEKy*CTdf=CYw5X#P%l|ecBH0PUz7Y`@L$ixFH;1cKi$n)5`3Y`48elbQ5e#V4md2# zPjou_YU#Ygyt=Pp*0EK@!xXCVM7!VrxCba*ry9~Tm*e_MGh(xRD>*_n_U!~9*zJT3 zkJsK#g^N!9hgG!!=|e)S|Dhcc!`qf{ti#nWp2=QMm7?Sdgv=!x(Bxmn#YC!quc7D~ zPfrfw`@Hg9to^CNZV$xGoZ=GPLpRvlJ5-A9RJwcfq3m#M8?QlE$7)j>;7A6&g~}LD zb<;C+|52ey7GziZj8uozyI_#*X94ngprQGBi*X9v z?4FuCaqgSI{3cd^_8reARPP&PGBL#ev+TP-t6*OU{1@4FUK>K-_cG|D%vL7e+zrSN zL2Ul15_Ir5ik)R01?%onTPlePOtD_4>iqJi$=0)`sCCk{n`Bx^xv8O1>f z`(8Md&@Ji1z!gN89UoVj1yrHOtDl#|&}n%b&GsVY?e7G5VAtK1A7bzrzf{?|ll6PEfP8=T`J-sKK8`d_k#Mb1?h}WfhN89gU!uy3`;0uiMx-Z+oIT!N zfmMGMmT0LANq9jZK$?F}gPn&|=iBq`Z$&JK0@y?wD+NZo0Po2wgC*whedTD;rx!Jc zI?59TnAna1{FUgk`jZi35hYt$RNzRIZF|)pR-#QtqPh6{Rt>W`YLpT!!wc)^3&w!A zX#B&~e1y2EiWyb!Bikfk?n^4jfIyX(8I+dV?gmD8-%hblT5C`cj`q56;Qq8H=+l@5 zdl;+&oV1xgl9vV-iYFsIS?T`SJ8?Vjr(KfsRmOjaQS|-o$UY>Y5J-A@RpSV}tB3bz z&?Yf33{$&B=2eE^6?pYMfsF{I_W_FTj_K$=j1rqnA#3C;xYxXI zWJIcF4{E)_xqI;SyE-Cte)d@rYr>-IWhr$Qo zRo@w^;FDHs#nKV*)<&PEaJJo;{tYvwKhsk{rGPCU^Rf?c({(N$Mg^c1Pa&|^eLbQe zP-1PnopRG<1LXK z-uEs4NE-}!x}DAb@P_w#{_E&MM~y$Rg0UMG0V>UV2fh9B)itnsEM2qyFjG70kfrO7 z5<>2Ji8JhaT~2qsBjW>k%HE(6mvQ8+L6ieKL7Z7g8^ZfcZ}PbZmnPQ}mmstdzSOXJ z3TbttlpwF!R)Uri%f)DIf}PZ*$1k7E_Fef!I{h|(JX4oEgC{Qhr@+`5ksrIJ*94USMrE+3TXPtZ?I}S2l zHdnD=WLVN7LX|9a1#1h|`D$x2x;UGd`;G^@yq)0(;UTBo0MUC56`mV2+P@UDC?qbtQ=LItoc$>6kq7`L6^4 z>*E!7*Zqt8<+{`Jc_&sBK}a!bccx~?wkqHbZ7(ozHjp7;mnsjxf!Hi0VxL#Xvv$wt zx!J_+DvCj+3jR}l_;zV^{C%{({JpxHZ?6#srXm{mtj)3AOA(JjB!en#gz-5=3A!vR zGa?It0QYu;cX$eq4L)U>4yb#RPeAhMURaV`*&Qe0rpqGiFB?UCcT8)0+U%1YQ7p3^ zccbHa2Pgft)Lq2!LPZ%ZMoIiD@qEWKgj`5}0{`>u2in{Q2p8Wi!%-4?wV8~P+S-%Y z$;Gt7qmu!1Y^c~k#^1ve#hd^Y)2(;7E%%32F2jRR3T{SrZB;VWM7k6;Ofy2fRQ-zk2xY zZoc%g^7YpODc%0b=3o?f<~Tfm5)g-qFhAELrNGKy$e@YfJ)Lefd04-tRGtm6HXC${ z=eE5cc#7)a10<;^A+O1IZ4e1^;V%PLpZyN*K8~M+>vd~FvAQp+zd9GTu=W^J}b0sS&E6p($r@{LL~=QGTD#=3_0+YP|H5q z*}9@;erJ#SA2~b;vg8^IYek5`vT7~D%Bib-{oj9;DtLO@MFTjpGScBu93n2)Fwk5< z!eOO96o2s&)F_{;l%mY#{+Tl_vsyy|CsO{jyaF0AjX(ek2nkQbS-@MuS;SdHQ_R`6 zG35k`7?BhqMN$exi3AdjCK|PDwL!{#E`4;AeOK(FR1BvHFNv-hSb%w9{-JyL#4r9h z5B$jE$bCKPxk%HCSRZUrNc(#1|I}lA;#Yo}d!9PP6;{C|dkj9fI71Bk|M&k3F^0w{0*4aS6ANL-k$u>y7T!k0W|pV1 z>7E$O!(ww|(G(jl+jg%2vj&OM<@-wW_nKRDt%TcFB!)Jt{CPL=VXA-{#;VZO_t{22 zkpPHB%CqM<^^>1sj3I7UUPziubptS@e$U2`OxQIg;Z;1y)z1TsDlTZQnt;?-W_dPE z$B-a{n5v;=v-V&4lYmw*F;}2%J6EcCo}Jn(L`+QQK=PPy`PyLB{s&3^jjmsQFK?>> zOH4lRqBTg!l^b#0b;EkmR7jh+T+PLTlxN4{{{xW3|E(uj)?jM zdJ&1n<4vHp5hdU>CLhJ1v_izb2W!Hugp$r_LkLt70W1uzPa=j%gmw+BtH6tlwH*A! zeZ*bkM5{#OiKa%Vaq6+rlSipkffOR{37J3}YA{czmRQ2Y^TK-_PgojvSm`pe&Vst;A3O z*&vySxDqc8uLdkv*|qD+1pHdjYPTs59E2Nh zLE57@52fY>v;@Q`Dgh%Fp&-cJ_yOlnoVcdv;M|*Uu=xF_@IU%4h1Pg# zLgoobOeLbmS%Qhw##2+_$68be57ORq9Ywiz1x9E528!uVq2(crSey~m2Z%8n3`e2_ zFo9mLM`_xab5AhReS&x1dhwdJasJW_^T)qVd+tAD`fp&fTSZ+%U4u0ajRrzafY^XK zMJ8_J!s1RkqZ4ax*IC1!)*?p_rm|scOVY&F=Y>vUB-MDjMCKHZS1rEvR^(g1ymU?5 z=tJZ$o<7enP6-RXLrsT(Bi0p`fGY~bT8IJh6$LehZ7$$8h`51Y%kDexLJnMyFFGg% zLP^M4(at1^2aPdRJ%yT9-NS$T36`GzKE1iQYj%tPzT4&8pZzKF%=Z{Oe}>kvm+AdK zf5M6X?SJLcFJGqWcCYFFhB1s`3_1C&%BIO(D#W_?ZgmbqWu<{3_qh)CJ*gk*DVL=NuQ`b>$VFT$6mnSX?eU3pO#+ou+;{Hy}$3MX+F^QbxhyP$`IFQ<6l$ zsKsfKj1;!{Sg3(u67ZU&1Pj0=uf7b%nha2b!5W+PVI@iBHRm^6rkT;ni zr(s!gXoKhYG9Xrx-=EcjAeP!1q7_=f;1Ik?9tSFxU<_fk=k*o<+^dPpswT)4kr|w` zKp9hoeiTXb2~A^1w1�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 literal 0 HcmV?d00001 diff --git a/chapter_00_intro/static/growth_of_major_programming_languages.png b/chapter_00_intro/static/growth_of_major_programming_languages.png new file mode 100644 index 0000000000000000000000000000000000000000..acfdfbbf30734aedd1c5f7440025cd24b2ed9998 GIT binary patch literal 210392 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 ^4j`D+{B_bB)<`Rp9j=_hf zkbQ^CS(=2s*0xNezk`Gz0*U39`aD?!D-UY`m=z_($*H+(*hjzO%gJ3O$oZl@`Rc>M zXNw}W?ci~&;Tvv0LJVXq%`n`-*&#SlYY>bX`^YN~E#pDQS4Bv3rdGSmZ2<{A zdt1uAPD9qZ8}(A(mB@wN4B`}{t7LlMp*QnBDHaU0NISS(T&#ihsol+OT#`s;xfk@& zQs(u6sIHJZTPzP(u6I}X^rQKa;@U%xt%g6X#Gt4E$+nK6%;n%l5f*dmkhgG zkzD`Yve}^2ils-q4|+G&SK&f36MBa5!rRCBTfbK(Sd7em^L=yExNk(YZe$)odBT-S zhpE1)n5s1hT0)@c804+>_S|pgZj7V(XtMTKN~Uv(@cLk=d%*71D@xAi)4nAQY51RA zbT{!ES1$PN7_&TUvR++X)0BLJU5xfOF;GAw;10{zWGC?mD^mG!U@5UHy2vrkEm7QI zX1P!M6Fjpq0WD4zAlKp>t-u?JtP1pxo%KWpzrG_V00!K!uLsMTG8lOSn!klc{cJk$ zNL)JL#8*3Jl%AgXXH4#&mh~4mX8o?BO~E``!y`jZ{}BdXNZ)!fpkPM040n>~U3a-H3JbGX0lDEJGwYL#mL)*kn*hM4BtIVyLr4^%giFsW4X7_1D@` z-cm8HBNrdmRs_ej>>Fp1U+Dm&&tIOAheyXASTA^P-XqXfwb>MbnWy0&`4FL-urPVo zXW?&&0)ml)ct#C{(5BxTZd-|TC?ixnT;Q*s!?g9yV^nt~wRpwRB02oIRe9l-{A^aJ zuvl8K^nN6Am`l#0M#UIra`m8fbNY>=FMX&K!Y-9?!O*(K4pz4A@#LYJt%E8R@71&HLLjmf zQ{1C0=*O>x0mCcNE4Ys+t(JX%jAL$qAEvlreRFKtxA&TW*$Dnw4XJXmfCV+)*$=;S z2%Mwv{ZMhTnb9@l9P-|;MfgKM+J_c?T8CR5E`XtvJI(B2g;{JFXM8YXs-~phYS;Z$ zWVmNok_YF8GSwglF7U-OhKZ5?;LBnj<7(8C@EgDP+}iRDtLxviESOUW73+qN>cr;m zlVzR1{oAnhb2A#j1sC^9b={;8^4|U?B_A4~ApVL}F*o^5K+@;(Dw@Mvu-|d*A+Y3<$ded05pBd7=pI z-vnL8Vm6<#kG$Wc=J36;G8_oc`MP{0mBhFk5H$crhs(j4*<{%7!#1^x(tU-4-oU>7 zGXIOhKVyjtJ4s5&JVnGAR7?B7s{PVa{&Hr=tKYW>{`;Lh(w!(xK`deX@q}pvtUZ z9JimR50@>uRvUoSDJ%AKl+v+gCZF!^!2qiiTHKSWS%!`4IfH+|Y#y4wKS0LWiYv_N zs@E=HB6EZNuqRcE=`#A|#KI8V!0V04UZ;EKXKC$s2_)~UtxMA2wp2^2CL$o~C;ctk z-Wg6|5y+e8i1_)~6;YEDxCKcJe&>MttB}xi?J?XS*YTJv64Vwh?d0A0zdJDSZzK!y zgjGQGt!Y)lhaW$F{F#{0R#T(A729Pjp8q*cf0sT+K=A!pec+*~ONL~$W_kxU;r8YI zS!dKT>IB@L``eGOvaKB-8kc7sgFV6=RbI%JcaMrSGjp3hdK<6GwtiK({@c0(?e_Zi zq8_L0ZMyx!({HSbQ6Oiwr|MRXt9qrXSO#{J%(lEflBj#QZ?P^EDz1u%U0htm^$PQ} zJ`k+p6K{DPp?i2BvyDEcqaP$u4rVS#`APigi17k1{yHY}t?vO1#CEzC6q{9Q<($>r zeGgX0csP_(9-F8iof^1ru5)rtla*J-)`0kv{lckn4EN+MERgb=|$d-_p~g{CY$YQv?v_og zy|HyjhR~?0EHJybbIH~^+j*SThTyRZ1oQ%*)$w>^0DQuQ0T%$c{akXjz&5?B-Xaf( zLGA10o`bfwAbPqoBd#Z{#q1_}F>CdtJKkzWc1h3ZBKf;AIr=sKqRo$0xK9_Qb>Qi1 zhCSG2`4_>SlO$>q!#L_#hT$EV8%f@{Kb28;^VA@p)C|~p(&);zFF6#5PaiM|0551x zvL_Al)xEr&YyZi!3~H3*=HDgxYPIk?1TX(=O%vX;`G)l2_?eT!k0tWI{nNX^2k8Es zI@Vs99G(4us=FbuEZ6hmHphBCvM_wGRs}eJdP3`wBhx>R8%s~yo{qO1S$MD+aALDIggYd`Hu7Jp)86e^@BCe_u~T(~8qeFFG|T4`30*8Qi`sPQ`DCTK@# z78fnQi0|WvHBO)}gFiGu#c6_r*$qt9(xv3`@H^IUQNsHkR#K4+MeaTSYq{c*e;_Vj zQ6Xw)bGnG}i6?;^EBSzCv}u4~hYuYBtwtuFU39j@wJm^iiY|L~&?^*rT0A5q+ykR> zsV?8sxoOKD3paBRq-D6sc8{H6^svCybc6ZWn>+s-@@@gbJnEIg*t9l~kQ>LQ4(S)I zZf+hJ)hT|#e@WR|AvILv7e7BH9IQ*Gu(7*&Ve8er-j^NReOUZEdCBytS<}lngmETW z3rH`(>wC64#Y4k@tEer7BpQz2bCv&ob)fFK+wiGIA9<$wF}<++4LLCaglP0b z#A~DM0e0Pl_d3Z#D19Vc@c?k*K&*vggQ*$C9q(+w2R5krO-!y?-D^*il1l$y=8~$v zkc$2K+)fJT8dntkXw-grlup-4i4|f>*c!?-KS=&gpa>1no*ZjPeZlSjc3q`)twJ7nk5H=m)Dvw?+5R_a4d#?Ldh`AJ8{Y-iYwtZg8bw)52ak5H-0F_?0*ba zuJ8vrq^9g={QT6N8&(uS@EktrPCazJY4tPn4H259n%q|+lD3GQXT1nX2bGY_j7>0u z?bofH-{tnNNJn{)XO!ym7nKXv3}gq!!q6^FtL;vte0|VWjN)H;0-xM_l0-o(;E_0pEEZ9ib1W{f*3;3sR3(;E ztfn#Scd8f83>=_KnwLg~tFmHJc7PNq*MOos#_C~?s>blg<$YcyccX`aMmAD*gID#- zn{Q1n+zv?d&poR)MxtZ~U2DmoiQ@aaXw_h$hA%DqB~cko3Aq@}FzT}&bA`~L=2HI^ zQQ&AswwXNASs8hC&~>vdesN+=E;Dn$-Eif!kP~IZ^}qB6_x7!pqSs{_N|Psf22^1z zcX(K@w2Vk60_hE0>9DVoSt3YgbM#KWb9FAXfO9VpR|9(xwH+0}aaF%me&aWAc`sUB zrl`2Nurd|-zSpYkO?Elpln{%#+%UpjPIXBSnQi;n$2EdZI#U>J9QHhvyGTp4P{ ziT-_CvEJ{LorzX*;ouJj33Xm7E4WUGc-YX_^P<|}c4Vgq&pV&|!cgeDxz5c3dx|M4 zO2uHch)m)|4!iOMp~i*%zMx;*0ZYkkO9z*KWmVhnBS#CLes35u=@Y-i%$|bCGCf4= zlFgyyoDZ?PG3sFnksWJ9L4jmF{X?DP9d!3SF?@%V0yz)fHZk@=4*uGTG#YrhFsAua zF^w>?foG(=83g0U)$m3z^Js2G+{dP>=o%lgpt}dVLrt|O8>k=Nj$7$&h2wVl-ws;Q z81=saY$W&=yp-BU#SrLaFiXYsdF-JRgfm-ZG#r-Oj&YW*&^n05NK#Fl{oPEjv|}ch z4>kw*_c6*ZDs=*=6f5go)4boE_a%JZfZ=AO`PsDYP)|aGgi5;rw#d3Wv4g^O)A@0@`!MI zbBpcLpUK)(YY5*r6KwFJXpSl{&H0kjIPu~=A7OU*=3RGXHoienA2Ow9K?J}^G+=Jd z`Qq=l^Si?@I|mRs(pSD*o`#DAk{X(mGXp!Sx#qAm+Q+KuF@?;jjoY390`1v=R^_WL zgMdk2{Dtf9fR5hj8=%7P3(oHJ1x8ZDrO!5TLa0exEGueu3*kaI2yjpY{sP59OjsXI z*J)1Ee|SLNBum-bNyD4Cq|RgL0r!3yQjuL?&EV08(+~ESIyj}PL$H_9uU1_9VnjM^ zl}K;5S11rg0coZEX!;|_r;@0G7W?xhcWY7H@t9F0-l2F{93-Y2+-YA=(8{gx&{GI_ z|J&sJp|eEBBnkgGr6vGE$)*IZ2DBM?@Kxi_+=shmSshJ5gziL|C|s~^oKMDt(V|F! zEL=J&xRg3lToHc#ji^@2Aph z@00uY?%NDce1M@^$}a%_L@0Q@lcB(ORI8b2eFCT0o{(+&_Kr<~_YCx#dmeu6U=PgF z{)gFBp&@21`<9%iHr0dmSwNO5|KgKa^RnE7Bla&jn66$pKj5v&p38E*Z3gGQ;Rb5T z^-9h69tj_}P3isjfL9RG=DW~%$k{PO1G7MU7#DI>mvk-gdG{B}uO)dTYp=T4Fq>y#JjD!x5p;i!tc|JqR!F8ox z!}ZKYO1}NAq)k_3;l{_@hhn{lZ$8 zuvG^G&_C}T`6362mj*L=Z6_Zi_vEYiVy6VqwyTSlOd4H9`<9ghyX3BHFd-z=)$-B7 zGgXpudJIYrdg2CT;?$$p19aH^_SuVa_ANJt#?f;G(?C0s^KEx>2t7L{^^s+qsi^3P zspPK6j9o${rb3-)vvO@q<0HxIC#LUU(5hXLMUu6Wna^4ug*LRY;iIs#v#Y;g3T4!T zKtR#a(aWo=PmPUj(?nn3fc6IsI6~6U72#NK_(j88elc5uftOQzY#KlFK0mt-7q#vQ z)^ktWydqej$82uyd#bQpTiG8gGXM*3hOY0CV-` zd&B#4FBI};UUxhSK?~^qprP`lv3p|nG`{-NLkG1Psn0#{IAfxg2_OS~3X@A1k|?C0 z#*MnqRQVx*Vc!@+EZtiv?IU;B){qCw8DX8_>f*x_(@}jdqNEDL>_kcInwOqDH$fyd zm0~ypQS0bEy)7b z3{k=emN@5oND|(ej*+Onj5H+7ANdJMjy?GBM|MejF(ENnI`Dn(eyk_(!CH!`#!H}` zJzk*Iego|TlL2vX*qH2JRldr_l5gKW`R_J_4rIttOF-w8cbiYFb#+hKo#|<)X8hK& zwr3hxEK(YP;REJPzHUjew#&+<1ip9=vx*GPIaHcY`ou6d#;xxtBlnag+L=G1*md;h z^d%w6-K@i_*RKz7=O?}+4Ze(vZK_V8o>k?4U}VDjK>wFn;0ANSioq+hHS26e47*Dj z=~MpDHw~Pgl|hS2NqUCt_5-^+hx9|r&8`D=h~~N z?QT1g1v;{HHQP-7?ugmb23vi6=x-^mz_UT}GMJ|H1n3M})QP|2Uwd7s;3 z5aWQ?4ua3qdV1wZ%jC$)<;cq9NXzATd-K-MKC1CJg=d}kG7sH&60G)cx|TTrdezag zh@|M@ux}p+`3HGO^i|r#Py0`9qmyZRk;Q^wN9QretHuG6+KsP}P^L%N#X}XD_ z>(^bnS2u)VAG8^6tv;xWJy1}Xk#o2XDwc*c^_P-dghQR~1SDPbCdxJESQ{JDgh-ab zTV@`8@?e?my&)!48q<^b#@aiROrd>D2;bQwD)C4-OujHSZ0~EOhah#dl-x zk(52}UGp$`I}v%V0yo}~*~c%JE&v*u8M2j*VvY$V36?_EwnQ0~+yomWp@}FJ8^-04@ zDnH+p>O-u6gh}xUp66^|HDRLzd3?l#+oBHv`u3whthAnW!lCdtA4!m9TD7oD8j4>a zo_c5coSkW=%a`L<2i4P+7;~Dq)Dm(Riw7RlOd$Dwyyf%sIU)BV55Ph$Fv`Vtoyk)N z{NuCxpCGc0Q4i2A`Gv*7>{1n*e77=a zH;sMR$RwFO2WO%*e{R*zicD1wjk0yS(&b3n3N&9aJSK<>W%c1fmY=rwd3DN-mL_`5 zF@;m4A^q*&01|FF@Iusk`5evbkODZ2!skRI6DqiW{GZjw=|Le`ObXrX#Tko_l0v~>F9Z8D z$RCG>S7V2+z@&O;NNTZ^(t28CGsfJBVFPBqH+fVXSBu8shK7t33_1-q0%+`;+4|FKYabtA6>!keo?yHt=U+B9MwW@NyJMojM!;r* z=UKViRwrr=4-A$;YbqW71Ds!;Nr>9vM;l19!1CWx{oUdMq7Y!X2tg=h4H=}zB@nc1 zByVCbt~;W&0nEy?2J~{U3aUNjq0DBas+|B!0arRbdBj0NySl*yZ*Ww|FV zt__qa11EH`Bhtu3#(jS85D)Yb3}+E^2Pp0R6BId%mj3#Fk0C6<(xu_k780e3`9YDl z*trOGa^lPPGk#?HT2F#Q!^8v_@4_eOtXwaTeqpHR(Z}H+r>yYLKZGT)(A>Jh_pc3f zXDU3RcV@;S_#R~I@@IK*@zj8qa46{RM~dB+mhNN9istDqmcn#-68C~)e$S+@B9g%Xl z=Qztu_xZtLv#_Yer3=AyK@ZH#*i{kG(osn^ixgp@Fp_EsSat%Int+9r{4~k6cAs=99hk#y6g{=;GbF9R)?Dx_^ zMF8GiQBl!Vo~wS=g{F0C@j5U)<;n%%JA*x?Uc6UtZu&#GFB&n#2N~4sHf5rDnDay{ zOWv`-|^FSYE9oXK{SX*!$97_?ur1@gSiHk+bkwbzmkK~6N5$i!piQ_=}e8A zJP;PXEf1w^eLKpRN=#HSN@8QtNgcYGx8#*)^wm(uoP6p>Z+4=CaX^ zWIyNJMf0)#uoIMYTL4fnWp~X;q_`lm0`?V_9u)SltWBece9tR7m*K;%gx>4KM#Evo1_DA***ncBbi^r>FLu=mfoJbRKd* zqU$|x^ezi`=RWfBk@NHOyK$f*BifU^Kh~e~gsyHnYXl*)_^Z~=n{W#Tx<0v1qLEMy zez}4P46HCu9nYB6dYKYjT>N)z>|no*vD&WhdPGD7-G$&8w=V8@7D_;KP-1HI^yx!4 zH_0L8!z(9fI9SPhdp;-h)*|rW6%=$}3O0IIXRpR21(W(*aRo^GVtxHMCetNvP8JvU z_HIV{HoTQFe3>41pZqgd#ip3IiN1%P*l_x`7L>GZ>kNz5&*@XwR-`RknPfyRcu;Ekr%PX_3U~jb~zqSm5v~f0SzM$oqRb#XoNs(B#Pt2Gr4;h3F?PLntk0yYrh%Qdf4cVB|D0* zdt%XGjZ1FhV#{{eFcv}HD_mljJuVcq8FIF(r;<$;U#h5t)zl1!2w6 zwJ*kRrVDRO4|ujYpVHLqoT^ngmLzX(I(Ei_VvCEu%HFS|2g^GKV&W)=nC7q#AH@F!TlQA=OBj+ zGPl+4QveR-3Hz+t=B)l5E;g$_P3lTVm!j|e4{&ZSKfhl9D{wlq(}`fl?j2m4YDnzn zg)iiJQSSQ!0U@=~k3sRiFd)7j{1TqmnDM9fE{z-zg3Es{4#{1rpY8To z5>-(j!B_ebN57zp?molijZ;VjMa$4>g?Cgj3Npi>td@IS{e|c~2khop>GPTh!@$jF zzRhz&vnUc4ssBadBmQvhu#*$2vCDV$)5Cbm(K7=WnJwQ#DQs?#py~DPjv!2z);e!oktSFe=-!3~ex@7^tHkxL)nKU6^#kS9weMBz7)*<>+hs##q|+NZYw0Zxkq z_UDh)iAb?teXPO#nIi2f5-%?xMGGDz2t=Yf_O!3lJSR}@0$knNO@n<%?$kBVOL?)$&%59GU|>)Nk7ffC99 zaF~HmC95F1|Ng~}KOZD~Hy=xFzpS2OcU|1N|FPVxMqr>yLTjM<8UhYm_VV(QkwXVa z87d#p06r#wxCH_Ih*ST$Z<(ckN+>-YTcgn6CWLx99Ts?W;PezWoxbL*$;<4zg;A$^ zCCf+n`i{rG_5LB*!_q9PuB@X8A;>|?mXf&Bs33%B>)l4h09X zUc}_pJ4xyn^Tdf4jny>8#I-pfS)PC?Om+-p`{6>Y4_P%eW}CI_VfUla5wKi^3u8=_0v{qGv^ODKdB7Mx z0A+*0V7Aw4^;gKJ1^#~9`=fvByWF;-;0wJ6sqv!zcPAhwYyi<(4c|g4{Mfo=e~)D5 zm>NKBV)B;JbANl7-NM4_TC&2*XM7Y5ndAa0JUunHehLuX^unc1&UO}V*AjTzv&c${ ziVbeF7io+?tqnalurn|)?w^~p4p0RN2nZPML@YXtX82S%jH-7tFtP^_1R{F3o>q}6 zaXS@6&Xrfm!r4$wBYCe)R-hd4>C^k`AHoWEY=lj0O@tPD%_#x^47z9Iqeuz5VeWj_iCzC zd0a}e@w{AL&fk^g>1qd_OH^>>_fbCSmW!Qc$viECd=&j9sMH4R3D2AB&wt#U)(2Y$ z@Xh+F?x>Qr)4>*%qsc(Z8{2^hv*iUY5S;YSbkTtq7-0!faObFLxMZA5qA&g+?|1nj zU^opbrxFJM($7%bk}L*SJk(aX@MKz;u5zz(^mxlT+A}KQ&-AD1sKH-X*7WjJ&GG`O z2|a6ak=I;?SV_7*3O<7^$w-4UEKKm?^)FpKM|BDDgo2{ztn zTL19<<3KP!*b4kFPd`MGvl9jhRWNGl`w~>+)aww-N;%!9V7kf+{MPz;IIZB!6~lX| zcyXnDc18MJ&uMCIfImA?*jDPzR0T^-b2fZ^%V%a#&v7B!NL;BY$#jgdcSu+<%~B%r zcfl;{h4+9|@-84$;YGSS_F}d?;%PpD21_)*ky9v$3@i_fg6M)+E90~9fResf+7!45 z(|g`u$UUnQC)4M0-`FmwEmZLQIxeG9^pH*$NtLCjk-3;EC=^|%GWqOJAP?SI)f{Wx!OdAA^?_9+i@vV70 zxl}QHyQ;D@fnE^u`EnTb`;6(j>hI2+)tCaw9;J^~laboq9!4DsnHE1o?m_r6Nbk8# z5>4lOxDlp+u=;7S!v4fD^^qsH&6$#%q{9Yx@b%Nakke_eGs6pTsY7a7aOsKcyg1QL$xy$hlz)fF`j50Ia!{d;}Q}<L*b_*{}M8bdZm8f=Ro%3Bb1+R6CM&2Rq z;6w>)*^rNy?+vTHIG`eVjzf)1*y@skm7r8>@NFo-(W)^$w)w`38YVDa&HS{zquSXy zK?>hJ=Ju@JU91{<_aM#y=@EAcTX%L0=7QxlZMOr6ngbr#*^6?0ZT;uJBS8ePUn6Ig zxel5^oN`BJY7fQ;1aTgoyH-gD=k7cOjK$kYy$p2SL7TrWD1jp%kBhnSffkw;H#o1F zH>vev1Y^P3&UY{+#kxLSYEAe=-k)hmi{Eg?@c{^_mk-F8R zB9zVVKN18^DK9R5_r__h+*zq!8r=RDi^ywS;<;z2pm*;SL^FKNCqu^v7FepEL9X-+ z{S957{x7;n0rx?ZP!{(rNPid1V&uNw0;|rKlD~q*UVB}A=;zma;3NPXZ&X_Q%}~!J zxqvHQV&hwE-L)s`Zr-YwFV>Pl6p2n`g&a4IEj;Sfn1yr7u`-JL|7iiH zCMT!bFi!L>v}bCSi+zmH|N~3KN1|D8s(JJ5scUi#)2j zAle7b0w>$@(Z?vSden2Y=M2E z1se&63nl0f@~p`K!&F;FC-Sx71Y%;cUmtHjs4I_H2%BAlr@(y)_JWj0Eu-;}5L~M0 zmu52)FZ0^UsnUNp@f^KpVsH$8Nz(2LirMz$BDk>HOHLvCa!NVwYK`7rMgPlj`{Ne( zMS-LkaO6vfpcCY0e0eRg?KMYC*%%&8hUENOm{+Q)j-&lpW<0!*E#Hk8tM36`b@o03 zJXG`liV0pTS$$sk7b63>0R&yJ^>!y1&_*8bslRnnXZzdTOn#;EcFHq^KuB7g&^ss6 z_#`6`yMz&ylbORT*F4g@(BVl&Q3u)n?f!sRlNsM*U;$Q!M@Q9#SOHAoVrbj72_HpZ z^fjPpxIsfbo9PGK;Id+E)^FEJP&~cH2K8S)Q;RQd$%jQe9BbF>ib6*q0QtW|n^WBr z89#xoM1$>=wq7b|0jEm3n3GfX+TYU5x5C9byFCO^D@e6ObEltMCkRNqLJ0KX& z6L@(p23w0-0kZ32XY~*b>Hn`^Q6g0q!RJ>}pJW)^rlS_kEaBnd3FD1nOTEp?R>%sp z**ZEpip$7w>VZ@payXGRJa>Abp8AQU%AE;dM+-L-85H#POu3J}fG{8WIk@t|oopx{ zqLQOO0-BU}BednNb&OVvR2Fz9^(`TSIlu1A*Vxi}_}qz$x+^Cac=s&(_?MX1E?%=2 z`k(K%+vKjyIf z{l}T#Z_`PwHWCR~@$XhQ8{n#JTa2zRr}zF#HF~uD~|S2?RM&*I-&f$2kK47$rvT-nV@9h@JES6?|g-Gv4Nm zOcm4(2(ImqZ$J#cpVmp27Xq@5grr1ftj{tl71dIqbq_`l&WV+8tJvP#TwS2P*wE;G zT_N`i@SQH?ydkKq&HweQX@-qJ*A^h1Si*OEl8j1FxlB{_2cCp|77Ak~{!4FLbCZ_k zw))1weaKk5e8eE3z>rcR!IwIJEW9Q@P2g4^GL2Dc_%w`7^jZ(batRb6UY5^=Dy|^a zAl=@PA#wR!tJ!`asp~MAZ=3MVe;G~K--3Xh>#brV1L_|~VN>3ObpkT`J=<=q=~MGs z3LS685r9y}tqn|1PS0xQjP^`8=&MT{IHkm<+13GOI@ugZW56{yn{e)P;G=}lli;}m zDmxYJipkA(j9S;kusq0=zm!w|@2SB?FLJEXhFGJR)(sN{00;gR~ z%DJ0iq~024jhlm0yd_adywqVMnO6e%fE>zV?tE(}zZP)vbS>om(ft{k^O*5#bV=QC zi!8=D$*D;;?_&?(vml~rzai#|2)%MY_E0IbXQ5;Y79qd&JX1ll)BLB_1aNRimu(14AEme(RkPr7!T+z-!IBF>L`Ar?Q#5uTS5nci;WeA5;xzQcz=# zgfP*RXFO?x004ls6X(g^zkwyatvFAcjLZM+vOXxk_u~1<#c+ma{RdK&Y8|#45DqvJ zTag#2LTX($P$$5^dVZ}l_Z;#GmGbC1b;0sr8Z2yXv`@J)LR4UE)W zBXTe?FPbG$vg!rv07# ziFoZ2QcjlAueB}k{sfm(QBZ`HCB1@za33=HoV?cZd)uKKv+)~bLCy27)F58Q&V@XES|c_hXE(U^#p-N`n0g{gsnNs6Ho8;x znv`}jvgLFxT#mTJ{>(bi+6q?IKiIca`OT|4Na7t##lic`5JECn_MZPQjp(xJJr85S zw@^liES&Z?QnBuv(azC^w+ITJh)49-3zSh7@c%R>jL!X@YpFInTs8_5jHacwi}=5F z9b5EWBaC52{l3(!C3pM4(j<+*g3BmW9Z<7T9}mbkdFXikK8;?JK(Py%Y>Df}08nnq z3$8gLP4%XWwzqHYsR6ftLXwo3>SrF^^e^|r?&qdUz3EyVKs*38 zDswri1^p^FG3F3nx&<04G_p4J80&q6={ox};7KwMw16E3MzbNb{454cL~RDnIpPJz zwd;mmj^)pFE|P|eP$N@8?k~YaQQR?k6Lh^2a3}?LUUhmdPM~TniCCmmG+G}>Yd5b` z+>Np}nl1p-=Uja$EmWymfpw65jUSutV5z3+P|8SH0Rf6c<)>+=JUb;phVqB3-2F;h z{{2>-QWEko#W67J`syqSqCNfn@ef(pFThBLZudvF0}T`5@?J+*X?j}!Hpo2%G|@~N zqXAmM=OW*@p|0OSK|eCd-;K$hD{>>%|Je#41QZO0Y_*Bm-#LucBC2!BK9C6edbucQ z3QqgsG9AxA#>E7DTj$e{fWY$_EHK-<1YU5+i63AkshLr52$VoaPXu$J-B%`NCX8$VdpnzrS7++aiixvsPgpWh-s?n;Ha4_ zLNueGid2Ef!E7lUwM5TNe?*I=7+L3a0Itch2}|gr!zsO* zKakEU^Y0eVzEKIx5|aAtSLi`36vr`VH#v?s69nG>jJbCCl$YqIDP``x0i|$zQqHG9 z9{5*0>%tY(9iMFtYjO|=f3c*0D^Gs*owP9s?Q~@S?-Yevz|scKFE5M5dlj^C8tUz$ z?F`+6$1*h#AP*j7DqhVg5?a9zOabBo%+}%fNud&o%k&RJkSb-Ks)>o;@uUO3Q?8}8 zCGbo?k*<%;3~1{_$y6)%-Z?3F*oioAF#yhf%uCQxv?pN`dI*)~(NkJMC=`TtK!t!8 zs0n!p1K0$h%+}~>4HsZ7FJRekfLHi@%ZoeF6S^?BvOz5_FQB$c$U^}?UV0A|MeOeOnaGwa!j+ejYYPnET z#HHR-XX;2=`@gaCrMl1^Dc}IDa+Od68j^v5ff?7ftDat7Mi9tQu4d%^`{vK56g4$9 zMjxYJU-jZmcmAVAeIvF{1m(^AhL|q7iM{`SX!`1?rvLAMKok&_7Lo1}5JuOOkQR}a zk{0Q1W)RX+0wPT5MnGv8T_dDR7+q2$H(2~$yg%RHA3F!m*>=wBwfnmFaUYMzz0XX9 z_Ole&#`@LX!trD8f3T0d{_+Hs-)xM`Ya{=DDEX8O8dMWQ>sRA|V;{}+r1<-kU7)isV#`WG?*EEX7hh}+b>}@2{9xIhsf%-wl1OoqWi}Uk{7y6 zhZnIAO|e!lZiWxu251J74d@`oL3HRE;5~v|p1?1&^H~P@*xGyJ8F%*f)|>uSt|h6l zJpDof_S42&vMS>V-6q}F^{b%86xa62DC!x&QhTZv1Aef*@=>bo`3cgWjJQeKd&BnB zYE}5Nm3%NILj8K+7HPA6#u?W>dVi#gKW2T<>%VC!Bx?Bhp&Q_`0pjPn8}HhXigDz{ zC6xkAATh!!_TlZjT=)0QENkwpVlDxgrW_5F5#2COymGX2eoAqo-3k0v(KA&k@&mtS z!s4>Bt`!fJn^U|cV374>VPal<)mc$!m1`_r-=_|~bQM&*jRQJQ}kZmqP%1mwa6et6e}5kU#natF09ELZ&t0^YgEAAh^J`qX?1Z zvml{`htZi8Q`!FIer2Mz8%b}5?B)tVFhfZ7DfPz1#qZiREGv-!Fo==XIUe}YB{Ivs z4wpBIV7$#vPE^C!`wR3I=zvyGT54fsvj5G;0!f8hF6G*ko8$l|4%>G&G<>S}gqD(6 zw9w)azQgx0FY*IcFjwvl|nVKA`2X;ov8`{iiX!ANJChV6Cup}bXer>eS1(Qko!R`)- zSc4a*T?%e*7OL3W-CYfq^YgB$PrTO2{&IXS9{r(teS=v=pxRs4;9!rD`Zr@~J_kf> zm-VIL{0+jG?2(Kh;7%I?Lw);u`G@$h*zs}kKVD3L!1WEI3~ zngH-%BBru@VFE|a+(xYxA@>Swu{6Vd+Iu5a7XW>bBtc-4Ey~Bow~V_wl-g&F*@p-K)v&rx>S_-gH1R%n{ReqR_rVtI2zgp>q|GG4rs;|KhcuS+! zYCx#9B-?9uyjzAr%K0_()^c%4@wgeuyP&8CiJqsBT6%$6%cgzsT=Bnu-b`QH ztFP-5-<7VWaV1;^Wh6V}9&81mxk|zJF|U~ArmUP?xf`q%R&HK(ChqCqq?45;#q3u} zgV9dJ{5ZOlADmHoiBflnxoZf%1E-v%83LLBS?6n?5QkgX3&d7;G>zz)(htfkNmrZE90f9{JrL^U>cJ;QOJOT-Eib#o0Npj- zSJsq)5IHDz%{FCiL5HOmiIhi~TwfbIZhh)8GDthCaK#||^}<4zDVt^GYOiX_0XK>h zznq+$%%6SE{8CyuW}hgfkU!Ik(@ysG;asSZh3}1_QeJ-)kBsLUKQ0G5SD;@t-=e)~ zy$m&4=hmXUl|%X^4fv8X)oIIT1ik5W;a1KI4;dL{{*~9FRwy9-74=IGJ&R|~uCUid zD?pyb5`y)$GiC8PU*c=igX+P?Lb@~EIBh0ni{diC&Lg!Iw^9GPQz z?=r-PL>U85)kvz6iYfKFsADd7cP26LQi;BvBKF@JVG;YU2E&Z@`hk${`-}fKzuyZi zxrBMuM87Mo$uIuQZ%LucUdDAKW?K&PyBzMt6ba15A?EBf=Q#2ROk7hW3X`t$-;!1u&E5@@Wnc1Af(@3Xe*ZDh!fZ=6>`fpe;EAu_8+T@ol}0RE@tO>FtV6>Q>hev!2PtpWM$|0d{0 zZHqCj>=BuPjV?}y%PLgRE#IKo(`2Et0Pf-+1xl@NloDcFrlvc zvaPN~0Pm_7TG-L2B@i$dt}H1jY#)Qvyd-ki+!!z8<0~BmT$HuP-g#c?x!+H_ARjx}@RwD1&ETsYcu!8?Cd2hh+06Ajqgw-E(L}Ufi;1fd32;)Pp+!Y^ zEoh?pt8Q*%0XZaBJ9_&qZ*7<1JGuI=z<`DJPDAa>NW|^F7hYQFm%N~7k8q}ejhKxg zd->4v_pFCYaIPv|uh@5`rNFgA4(6gnXo(DOK3yIk^tyllz6!>u!Tgl}cF#SMlol4? z9_3PIPb6-QsuIw?Hd7;^`I}Sx4L9!b4fvkEC;>qf3MKINMhp#WaJ`_G`sFy18a zKjU|O6JB>xjR#5b8wY#U@)@{$2F>2Z9*xB!{0Uirn#P;_(G=%2fS6PPn#%qv?8?V5 zY!&`Sd~sg_wK?|Tv1#&snS=6ppT!{{edi7y?Qm+xRnF%j3B>jYeL0o zl2Bk- z3D%O*T+RniCQqBWqu~OBfSe^CrQP-j#3>~;*q6!8y=U&pj=rb19u@!es^|R-^O+uI9~0 z&Ul{WdM)bW33I}<0gOXarX|E6ZFzTwjWQ1Z57PY;(n{btZkO*gk+*uQ-X*e9SlP@I zpsL5kCPE?wIa~1)nz`O8&MBH_qX+opK2{M2K)S3X_#^o>v|ls1JKKMWtbHs`E%2y= zq;anX-mqV{a4Xb$`f{AN>dbZ8F||d}xK*UJg6d;Zf?*i2H%FJ#%5QzN&CwN1 z&C48D>5+;bZk?DB+_|D^g zO2Zf3KR+!CG*wz#_%3d2Roa+z#Sq>oE~~M5@y{N;XdZPhbUF!I6i)l#B?xqP`) z(;2iogMlEKE3e$dY2GN467O>f)AKj#gB@K#J;Fc>b0bw53?L)1X7)0Gum@TL>y8gM z$>2K^agU9@8(o$s4DV(!Gcued3&Tv0ZezA0t@M>ii6yfpYQlt17!zxg5Sqqfw`$k4 zm#h3_0}t07mKOhZ<2u*}na&%6ny*Mu2k({N+;MMxx#o2|t4cqA z=H)n!r`Z{2-4eLIE(U#Bd+#JL(AAkE(Vd9v*IFMXukM|N zILja1N3Y)*fLqS23Xw%uS5kt`1@IwAnb%w=)ENumKo4UE>B406ZVK`d)GrLjOD*-s zF@UIX8Rgxj*2!r)1z}B()=E+m@Q$xw?X38&IoKJA{K-+8Pxjw8ufXYO{SxS1Otec; zGPDE}^9WRwpCMZWstl>l^>7VcJQ^a<;K(J@+|xA2TCpgMq@VBQ2|m!?HsiQjvu4oE z_MXE}R6w0EXcoMITLSgQhAgBMiML(gpLPw14nAj9nOI!*1Hgh`pYQ=VB%D(bVxDcj ztlRTkxW`J)kJj~2@imHF;e6AQi1y}2vR3tQc9iR!*fUrlV}%H1@k0F%w}nXJE;Hg? zgkr*WG8dWs+_9%CHr;6pEQpWYR@7{4gXX4Qo7@x_hmhf0y z+|KlTTuWr58G${>%!t(}b&ngV<9gU5dMTBp=y(H?TjR*l_C9Yf1nt)OBTV+W%lA_2 zDTaScRkW*C%erY0PgDL25sPpEVnxG^aNx8Kv7k!@=G(|&EQt&Q=}~N6AG$sDzME^N zN}4rW$Ivu7SqVdqxry6brdK-J(%)x#@NnIZjZ$i&xMSbuZBi677TfY?YFOM|Z$8XU z43b+PNVB2Vdqn6#B#cj;oXjKNkR_c8$pamW@tlbL(rkXgy z+c&&^@J+9g)jEzoFQxxfYyIWEg5=3SOh>tSsc!`;Wo~>cQ*>PnCMsfx^Z1!K!OST* zeEU7uLUDOBLC?Wc()W~9b!|t7;Y!$4V$yVnt0LcYLIW;0mpz zk@fhAbnkvKK{$MF3EUpLsPK1QjK2sq90H$0l|@nHG0RGu8*@nhwjzDM?(y9`WZ(^R z5zW#Ds zhOg~`s@G&%i9Sr_I%y@1cM85Z*Z|zgxxfgHI{+XbnfxRu8dxYTrlq5ce?N||q7VIw zRC_z!9-R;$6pvopkeYD3FTI9Gx3gjw`cvXy_0DpmxG=1G=uf$pmYwXXWuYv|Z-ph{ zL<;@M@e%Y8vgSN-PV|WauH-1IdXokwI?ArmQq&mD_;5F1dzLK6<#4&Q>1%`Rm;{j* zK#%Bx2BuJq*k5mBi6E<4wIOS|61auS%N^2NCBGI-^(+;i;d>0}g%z}~AVT~%GN?42Xt3D-M5VtDUpuwM4Ir?hzc3xxGxDShv_YYe%rCtRI@ ztj97ue_?5~u%X!Lm9pxh*DjAmQuTjH0{HRe=+PCcR&VQ167_jcaQs4^mmFVvVf(W^ z<27PS)06K)*Ok(oF-A70?8a-*A-DICUtt_p`bbf|S7PAgosnNC5doo4 z0KHY5-}>YEm9FlkWAbPJo;gy*Z7aW%50>;&EhvCx(&Bw=TzP=o;}fbKAz~V)c*L9R zvSl2X@KB90E+*IMWOG4%*p%mcu;mbn zB#5GBN93>v?D0*@9RuRts3j~ewiuMCcbhO%sgqd^$!x{wt0%{C4KTo=he+Kty!PHY z5hNG&?@T?pn|x6Lb2&@KA|LeailY7)xiev6W&9ED00%_&bu-LKE52y3Dm`Q!h?b3e zBvO)-X7iE@3F7If6r^1+MpasSz0uK@0$*K!W`ly*qi^Srzw~a6VZQQp=Ru`hb!;(K z@=}#CUdin?y9WFNb2{_%f$8KN+KT=e*Bv_sqA-&)KI}2c5m0XwplvXJ}q&HqP5-e1Z2D*q~wNwP6&287c@`>GP)uVx~FX$Q>I4 z+hmkcB#GAkUqG|Le3Arg8j>!9v+jeZeaYfYBPxZ**EnHC_Y3qNW~UeO+*9-r`nR(x zfz@%eoq!e5lO)qh(b&NPQ|(oyqv^d{qftMWMG-5 zU(k8-Fh}Rzu;gY)b_r6~XjD@zw~2ucPwYo6n$L+Iakm#-K=(Z&Di(3^F|Yz0mPY`W zPed85GFxL0fABHL$;Cz7x4U>@CvcTeEdWe83vGvA@fheh5w;27(O-c&EYs;Au`6 zxe+M+Xc&QFt1It`W^ACAvcg8fnC@;I?xsOnY|h-1;K$v0^cL;M$2H}^9TDyY*VxMC z?i>8Qjcq@=xa#5Nqpyrf)9F2I*p&U?$~zVmWb=_sG?X+y`K62ZmOU*#GY3D!m9+}3 z+QNJHxbrl2LOrOS)ZU85A^!YnN3QkhF!pJi3#XUd6}&Q1%q%d)ejXkle&{TZamaP| z0AY>J07xhBmg}94XHSBb;FkA_A@~MAZeOVsc(XkRaDlLP$N}o+vaw;zorRwcAP5J4 zH>}-#18ORhmsm1C=z~*{&D#mSBtZ=$X=Zf?&CN|U=K38o9O}#^8GVK6d2c+mJUFm6 zHFeU&sc_SvheNcdj3m3QR5(;5<;g#d8DidVOI@8CmUT!3;nPxK4G*p)W~eTQt0cV)lDo<}Dc;uP556LaYH;7-ke4(YIV!RRk9pP|(_iAdPZP~IbT?3k z+1cR{ul@9>^jlRm;8m67%V$f9mU{;)T@!_N3a~kRBQ3=D-+dMTi=%2<1fpR4r3?H^ z(K-xglB0NIVUYZVRgh?*KHKNNEG}?k2U>_g-`Fn|^rhrrAxf7RB6)%L9)K1r)TCL7 z-jM1vV^)J7&Q^cq%?bE`y}AHNOMe=6$g8PI-hiijW6x&vU%iUaqn=ZrL?=%vq+9}( z96p1{=Q`odgp^!D3eR{GtTtwISUv2v6i6kyN77;a$8cDB&FI|dbvhn_Cj9ShwW7ER zMOLtrf|5GT-&#ByUNqRj5?SQ+?JbrF;&%*n3adLYV|H$z*w22qJUIV$oojNg1 z_JKk`hHEG`T;jb2OEBo05Jqq8D!Djhx(7_lb=-OKz!=NwcQ17+{oO>S0W66`qcaRG z`@(Q?xavbGhp~YT?BG`Z@$t}#E>fBfyR!|Kz?JXg`B{oyA!7P8jfIc;{0RT>KFm)1 z@K87Z#*G^xd$z1=?@3sm7*2^kBP=Z|YrI$rZbWZp^=*sE>G8gm&kF^q=iv-Yd%N5p zx%Z~28w(%bV46R+f8`&>`mS)t-FijUKkG6r_Wu&=={h-Hr3dn52EMg9wCXK)anz+; z@rYk=<7*POr8W~$p7DcP{(mmB#+iiNK#TX<9AB3aMZo*Ap8pT+A zp`IQHz$G;qVY}nNnWKOiv(vg$S3Y5ThXpqc-R_cOds121Q5P_Px`HhxV0WFXtAc5W zKfW*sx2x{=2MW`o3D1d)F}k(_57i12Tms5coM!p*(8IkHD259R44P+(J5ksF^&%&y z7tSv&y{$9x#@TtTAl-7+A9lrDzv#z!c6Mg&HNgEv_RJ}Wq+%~C+LRe&QL?Sx%j$ZA z^J7}8opxil)tnEV+J$0!95<^iZcsOIZeC>OKNxphwB{{*tgC_ne%4l@;TFH9ba378 zTC|Ud@yyh{Dnx1zW&oqP9<0~{PO7?z!)zMcWhUxE2f;d7A2>AlclfFo8hl9+c$r38IL{kz;7O z-2K2dcA5?%`sj&QH=c;_>gKE$fwBB2TccameJsG>K%jQ(f;%i5SZk13-*#3J0-%2f zV2;T4TP@MpmCdOR#HA3f>0c@fl%E>s0$xMb;a%=y3Fbw|k5=)-q^Nl$tyJG>s*#Z6 zKoj`o%&!M?n>9=f@SDKnj}Jh{E1K>M8{(qlu1}}xY>_FiD%+TEdbQ59s=FC1+fp|VcLC3FQrcIUU}h7hA? z!{D>s!2r!dLR)&0A-XV7sFCDtV28I++ZGVty|DS6_2Ii`GF?#h+enckdK5V(u#YvG z64!P3!rz6}S!*jYbof<%k~`iz2GI?0nGQJKRjHJa)zJ%7QKx%a`aY({GE=YG^p*Ic zVoi43=oUzV7Awbe4phrO zdUz8SngPR~do?|8d(uAh+<2i^l&NtL$UsoiFi!V2By=A|&c1#7R_zxAUj&LCHH@05 z&W^^}rxKeo7#nE;b>_JJ< z6|07lX{hM&w?>&hh~(Neb_A}0cWq{2P$F#Z%@dJit{$21BDF4aVt?JFz^D$2*Pg-CJ4Gh zZiOQHH(jw-0~fYkY6&y|*2&$Ak_kNKVw7}weRaNXU|?{xqYIgTnLfp*DG*k$Ry#+k zTk4mLwofanPuDgU@DqEFSb5}#vl~BXz(b)zEL>5O2#Lw1O@b|a!UaMSv zm@vgUUpWe^2BO4a$rTK92OHfzl9&}GP!h%nSCE`uh1AU#q(|$IIdAG#9xnn)C=hH}N-U%YWry!Tp?7t_=+;JSqJAqyUW)1Z#b)~+v0{deCGeF2$nbii6IM#p{ zqdvZqTx1=fNrVBGfF%zxST|Qqmzs$fFrgML=yKn16jPY%rufse^|W+X_T@tQdq;DVxtPus>8;3ZE85{G+%)2%#_R_2D$ zzZ_YEZ{6CteXGP%wl)&%xXOBmIta!73wyP?OGZphO-e~UJ7XE?wPVkw@ujXN8S#Ba z`$DA~h@~6XFw_ud=UbCxCF<}%73y}UZP;PW3b$d^fIVNBYw+Z}x;S+7@SrsPQc_xK zGgo6jlqE&6)&GHOfar7o+?%%QZ-!CYs_wbJ3{b-ujm=<5BsshE&;2Nuxg-9+z~=o1 zw9H>sUk#uI5m`XRkF#U6 znyXE;#SsuB{{HRl8pO(X!0UL)(r_^aQ3RdF}M#y-4dR3I7JCxD8@bw{I%^ZRB?uqw)Y}WRj zo7Gxk&SipVh_{32#6O)ZEz+lsj=WzB^-N6}%zPx7aXy0^Hkb+i^oncWc7S|N!(zk` zmod7c;dxXSv0=PyI>=fEq|n&Tnuf!LMMg&Q2?*ro=W}r@06W#2WXTjx8C8OVF&|sF z*k!WS-K1Wc%Di;l-)?{6Np@YZ{kFds+#PUGZUpX_MEZvTWp$1=J9Hy0-e6Fbg9@0v z288tAkFwyHUJMa_wg#972Ub=L4i4IkiZ`}bJy276y;mGXnI}m7m48ZH$cQKDMog~B zCZpQdWWo$|wsvwFBL!YXqYxSOvDF2#ga1)N_^_xoZNoT(B5PG#2*M+om+XXj){MRn z$y==LsjAOR#OTQ|Vk80N`L)4o|2+X~kS!xXss-x1N|0-5{nS9_!Gq*DMZVeKLSJi= zyP0lU4C_ojWNG@U;1=fH;nfCcy@|iGR~tv%6lE2XU&_&4Goa~+QiXD^EOc4qOK}|ireH!}iH6_&@j-cQl zXOENKSm`Fe5QY)ipK+_#eEZCA58?d^))@#tfu;fBeH~@1Qe{wb%op8t zqg$s5IkLVn?tr*0k3+5fs;gVQxDtxDf?&_&FSqkNoh~DFZmJ`m7{yyp>%}K)W*VSU zcC;LLQ_#$nUczk$*7sbR6S>pFXnHK|Or_{Tjlxv0qvF>{rg3>DeLxL&LWSRI4hkq~ zj!JI|LQUg7iU*owc5=x}{QJN|7ZIF){TtE%xl<&eoR#3kj>l1S=7f$6w}y?S6WN83 zMK@z>N+9rA`we?f`x~V(&UU>|IKs+emotxxUhx2=)!R&$Roy;^QhTvzmnTe%kWbnFw>LC&sk7OsFUr`Qcxs*U*yS8cpU0^IK0DB|!^l+PiP>DHo zHt~{@S^l$Ks#JxVsc%xj{>d=3G}}BLQtAiLy&X{PYerK`JD=?<{k^EbKDnii$h(Xl zve}c9lijW-j;Y(H4?z}dNp|tHX$Gv%CpK6UMRqn5iw;zdjVFa|d+5MVl3mI66Vf>+ zfOYCwWk}trg8;O?P|EXGmsKz;(#p_ugCm5tL(29E}{794K!(&%CO4BegMk#wo+)OH9R{~*;7_tWmb>ORJ%XW;c7 zVvHwvYG^yt&enWM(g@Ur#45|4)td1JuTn~;iz_Fe#uv|)?m+n=S>X1%HpQqbV|mKtt~*&^go@($j_E zX@;PsR{!CsYho=#Lkhy-!n2m>dgG0Jv?+h$=KfM^)?G%)$BrXRVYm}0$aP*Eeke{z zt6Dwj%sn7{tS7zp=2LY-(pb566Xw#k6xVkYZn;qiP zYfwlVNIkE4c7ZQSDViMvx1Zw(S={A%EW8~&Jq@`_v~469k;2_zQHVN?^m?`a1Mdhc z5u+JC+Z@A8?G^fVt9dySBco^W@My{pz*;^GdeyLHcCiZpgiIGC83#f!v3P7$qKAja zC-65HTfgdbsIV3F)tzN+6bvY;6j12_lEPm{5rN)f0=NH{2ZvIv zP{!>S=1J!MBUI;c;<4veecE^|;6iV1B8|^3xU#ZE+u_!!|3~zILIFAMf#8F*hwOP? zd$XbhZq56WUqr0j=HEF44f%Y<3WfhX?J-fKNLPoMDsN+Tq7Xx0^sFS2(1X97k!n>% zH3vk(ExV3}4RC=NhTbXMM(_>exUTVLhfzRDj+11SgauxXCX#wP%{vPA_JHWnSYpPS2Te*&}EtcL>8GS4}0HV z3n4LNJmEoThI4&ZNv^3055-E%8kIAxIU0QUdjIOz+uTbS!TKE>R(BE&Oj!Eh1kLdy z%&;^&Iasc7u{-e^qW9VLMzxwp6LWJ;U)>A(Zg=j5XAjYrc}4FQz68_Zkn^J|xTQ+h zmZQFU>IZKfi(zT%0gojKJa{k;_8>rytQpwl8WcIog1$IO0@JJ^_B>A}xyz`(=oA8z ztNjjZ&_bORp}S`xVHYfJE-vk5ag`*md)*7GDDh+X;4W*wGm4)rEq9{ao<4?_)>Kp_ zCc4dy02oBB=y^iL+GjzGQC)ABPaFDUX>a<)-UhBqjgn~BLf9qJ+y2x&T_-^<;mwDI zS6>60@=_FT+-RvUU?_T|ONt+i@nWhOT6+H!Ftxq?Hj)nwaZ!qUBaB(H)=a%-6@@@{xGIR>z|zosN6)p+-yQLldPrJ_8Xbpxq0DN>7adkHJxjeQb%3wAk?lIB5%ScjlFi z4}s#vvJpei03KzZ=(Uk4w94P>czxh{+ahLL4V+tDopik*YsE-jI6W4q-wooR1&2I7 zqvWAJgR^fsi_wF2L=ZlYukjEZ2}YThpC8)8Jp=yGT#SqJWtjs7ar7fC0r5-;JqMaZ7 zDk(~XMao*oW9Tq(0nMqu3gvrtiLqUVW8sJsC zdwh03kn3MO*?a-2`E4iJ4dFNhi=AQ8D`sFWD^k1C`mA}$WSHc~jI!!a!zr)r>A#(4 zuG0|E!@AWk)L<^p{$0B<`fN+t9QDHg003U`+#O3uZ*@V*O;^0(P zhkscA3aoYlq@9eX9Us<PRV)m3}PUEn*~W`&`4bd?*nRY#?e0dHvLW8V=8+)2l=>=*?y;Ko8b)9dnAn)E&1{ zXZJA|n8ucZYQ+(w7l!E7oX}tQ&?sE8D(ylaY}hVnyB4vRdCZf9_!OA-P|*BX#_ z6wkAnVtsk~lh|3)p7${b%?GySS5;9!N2B`Ie*F41`74f+$QRBSjz8s7iYMqUJksjn zR-c;cA07s7ebT?(jvbYnIm$5b4+Qu_oxkokVeB4+jI5-ZYD;b@!p<76&7JRr8Ev}} za=2{WsRRfV4#*WJ{oaCq6uk4ObXU2f5&`Ej0rbjv&#ZlPy<12`1(0+9&bBMWZaNy^ z<@L#;x_~uS`etTX=O1mJdRc{20{_z7i{cj&>RCf_Ri$$0wr>}|pL+A&Q(PsPizA6t zYAL|hMTnqiBJ+r&9QMBUVdJNm%+OjYlh7Q7s4+)S0`jk0oPGzO4Eh$z`|v{*hy&x{_R~`6Vq~&&9ac zL)tELbTnJ%#e~O#3qyzdq~kAZd+?lZZ(P^5ci{=F;D@3s&U*}>rI2^wn|U8^hv}^; z1e%U;Xb`o5L>&j@)V1xwwi@HMvYd~HNp#nHbF-+)$#ChgJ7=?FD;-1~M;~D-f2-wP zFOPmf>iZ@Web9t;r)Q#7`NVzwjx6^~H$k-yf42RQ+XMQ*BRh~Pk%p2}W7(@JE?I3W zh(#tmTEY%6Of_*fvsrwqEW!RlG_kQ*t8(2XVgmCqv*#4 zl+n??JMWY*wXu*~M*=%Wyo#3a5ny-Weg6BTK;h~^j0y@}z9TNp#6L{XFOK_MSX~Br z_g+0*tMu6w>WOq`Rt|&=$U8N=0@E{7HNVPfn-AjW+ulFDcjq3}qybUsv&^m$!`~A9 zvRPj#yG9u!V<7@zFo0uySoBeY$6c(Z1Wzt1djl{t*c)I{rVvYr(L7#V&f`pRj&U~^1*C=LXC&$`9HTO;}uyR zJk-8~q!L!+`YybOS;;7BY$q0({)Vx78-+wR z3MvKzD?xB}x3BS43h>u|4k|<(f=9#L%BpX7Q1krsVxqW&R7WcsSS96uPH_~K;tJ#; zjk3|cNe&8K3ookkBrnUJ-mWqu;9*$4-ZA7mbaY>|b~fuz;`Z*}XE}Cl?uY-jTC3Wg zH+GF+wdg@+Qe8G2DE%o>Dl9^K#2=$ShE(#W@Na8N%X#rhtT{VTJ1dzEhJRN`HAZaN z^Mwkj8v&+beAnbnGT#=O=e|#fT@4krQggV~jhhjT;}?Vjc&8gzc=j4~AJT&PNR>8v zxYSjZz@l~|0gVZlxNGWv4{*9AFV%!dSO>U zxh`u}f(owa6)&A!?{{c4ZgPw3|9b(dw!Xru9HpuhLwOfn8407`=Y*2};DFnzdGdZh zWQ4V$Q>HX}o=f(DO|uR5w7KvBA;a9)G8hL=v*;tRSjflEulHc!s?*vfnfAg+U;-6E zDetlVfP#q`_h^J&D|KNX;A0K`EfGn+3K8%ZUS%BTuQ=hcR~n~ha=3I<$Z5Mze*_7?L+C-sGx}yZEX=ak8qKZtS&hoiQZujYNeyRu8MC*Bn{3Z<+H*Z zMq#OQ`yr0V4{>O$<2)K7-**!?w57`pHbd)d9huyG1^EjIsg8;#3 z3|YM2L=^r_Ajo2F2;jW08Tbigm-bBK`iz!8UpLro@x8GgYHS>+i~9@%A{x|Cs2pR| zBTzwqSJV94uM-)<0m7@W{Fb;VVe=8@ta^{vZ$1H`23Mwkl$aHnLk7g@wT&3>ez0Tz zn0lo%#w+ZXi|60=7d&_zH`n={d*gJskpP(5%+f9b>&uv73eP0sk0x~4FRyyHsYtol zsVu?)is#Gyi*r_|0Jvf#{KxE1tKTg48xfBa>NL6`p`pxG*$4K1fNx)BoUaqflW5&l)1O9n}ptQ0M9)Xm;gqwfICt-C;%ovwc27u zj^y6)Su4q(cNdbnt0yN8W+Qw*j}IFVqaukcTbvU`4yS18>kI9dr&96|qilDr)dTAN zxHtgC3}n6qq=KwK+!j{3qvAkQ+*nWE9smFh0KW4H3Tl;v)YRi|W=c~j<-r^&dh9`; zUAv?hB(qW?%3uc49d!9@HwOe78%mP_yw+D#ceoC)AGpvyVVvU%8WE#O_W`dE67ICb zAzW3GP~g}lQ#ZE1*$4mfI%O?%II<(w<-Y2d#)8J3kmjViR5>R3pEmr5foBqfdwwys z+wVtbp{i_;Yj$Dx@yo!?Ld=F^kA2mj^D33Uf5+nllvnzpz_^F|C zGMAfsW;a0GX#QJaWh-|2+u{TQv};MzRRFxwk1*cMe7r-h7O+rg95|ZKgLAqxu#{Vb zUrE9`M2sHU2dbh2k^J;m{++cAbHh)>D=I4!05@g6{ZR3!ODZU7*ypTB-#_=P;N^wO z5jmChMVXc3=s$!VvIjpriT2M|P;ko)(YWp*F`H6aaG>h}>;o7ADamP?adE|ZXDQ?9 z=?s9~(F)Tc#W^Af0qT*hz)uch;!6mcn5}snbZ1Kh*Bgb`e@_5*RT%7Z&}OXdpP4(_ z2swf3gpPSxbg;=?k~H|(@qTjIgzgHh8q+`O6Iv+tLDttY@`HK)r&J;Y*&plZ3o;NF-q=4ko#! z7x7)PBXG<72&%ED9S8X?p(K|fd$-!XCvac7_REJN(XJZ{CPZ~;$vU2QD5KMA7xpi2eUmX>E%k( zX`_WF(CR&O8_s_B5K(;Mf~>y`DOP%Hzkc!yU>j2B>2`q)xs+Q#=69^?v#Jxwh*>Qb z(@1`%o6vN;rV*Q?JuH0W;2WR3=P*9|)5v$&$4(YsjZOR$w#Pu*oPLu&G|BKEel!{B za*~DQhKqEAA*f*&K%Knyd&4r-lWBudi7^7ir9C=ZbE_As29jsrdU>=$d@c=ul)5)- z_RA$65X%FD^*L1~839GlW1`S_R^suCZT#9g#oLFhh?;Er=3tLOF&(#bZe__6)(1@M z)5casQ(Id7_L-ZE9LIk_;bBOE|MZ6AaBkS0p+~}<+;H*c#aa8zTgI4qil>GfV84US z3*H53@paC8r@SWd_#>J`PNg4XQ`iDMVSqcinum|CLi=$doK%zI0PRA*IlKvW7>%nK zayOje+Y-Y84F>#V>t|uj2a#bY$RYHD%M~_DV+AASiBb6RIGBXQ=?o3*mvn06!?$%j z)!g^neYS);WMs0@)T4?SF{(orCHqybRc*5mJu^Cu7*5T4pRrN2@c8oCnN6sfPMf0~@sUMcraEi}$QQe}B>3g~YRi!kC%XpR zgnSc*pUW*^5qYzy>D-$wV+(zB5{3+{kas((s`3FH$D&(HF0yzAxWnL?2qVzJL7o_* zFWYX&dttaNeo)>sEu351tegWiwiE^hFg;I}-nlq|s72N{VayzP*>%>`q7vCSt81&E zJc(IV`wH!M@A?k$^D8=H22d{tMki4K$Mv0+`7y2nZ&lSl`=%EbZ&S!Q1&dew8a;KR zis7T_A`w3Ggm?ZX8|$@nTyn(dPuMSoLt?UZF-o6ig$dHq(yA>SPMh%4V*|mKfp^~} zK8Fq#IRbsteE~D{s3WlQ4E=`r*c08a+~2rKQ7UkPzx2Pw(d8a(UNdC8Il7TOtYf?P z0QlBa9D+6aXwan?=_-*r85o?*Ej zQ+SphTlVkmC(;h(K3$@tWrL&atK5{N z-<=-%H8VFW1lP$5^f#ucb189AcWgYo>MdZqNu{)WaYg9SXx`7`eqqSoaB)P{2Fbn4 zfI3=tP!ubnI?-A~ZTN6?)+exRDeK&wecXh8x({&0B^ga{nt*Zu)g4xmv>++^l2CRaf zpTq-r*m301rrnOww`VfU(MLl&if+ZFPX@jtY_37<6SmL#3(cykk3+I2`#Ou9d?Gu4 zahdjm=yu{&-cUqjZipfx&)%@LXIUX4&5}`z$r05Z9Yu1@=}SzL)dwaCS}XJ5ji{Lk0IT3TA;OSio_xGuGU&Fe430!V1HhM(!_=?#w7 z#ds2Txo~lQWihmoylrPRVxmTBElZvBhL(Hmje0KCgIcd31kD_%Fmo~FK38LZjrp7A z4;h-jtY|OQo(^>fWIz7LD|R&Ir2mQUHdrC(+ouPXD?zz#_W;`ay5FnP_5BIo+5#d1 zZ1t=CSpTDW8gRVeoWhk8MFgt%TjnqA<#;c5=^t$j|pStsD z>+-3Txub%S&i_^~i0}tLjEIs*Pqbn(H_l%E^Llj;12`dkm+oe@qg0(T z3DDESO1)&6o`wHVzOCnA%ojM((*PG7w^5`S3tui$OY|H~S|g5FhUk7~ zR)O9qbxmjOM;$PB?apk0`Q4l)^ijFNj#0P5{B2M|Xe)%kN@Zg>-gh$iZ?$4uVN>$F z5pPsz7}ui2!YwAoW2!&m4s^lQ3ocA##GVT-+Q$Jup%H}Jb~YjW*#p?_p?Z~3SPb&V z(r&)X7gobur-wgY@U576WerC<+uc+HDeUBD-hGyl#+c{^3R@HR18Io=wv*o&sY{5i z%M)R|9Rb)Mq_aoDp#>))`yrdmG3qy zR3arNIXPedidsXm7q4yw&boDL8!+40Zn3mA2e1nc!l_3twF+sLefYW^{q{G|ZDBDk zk_qG{*3zu0M7;w!N~CU?Vwsn#Hvf;ND*=c4``#0Zk`S^bLMf6^gcw95TXu<2L}e}6 znIT&el8|+ZvM*U8>mX~E3E7vi@7oy53}fbhKmES{=jqAwsF}I*x%Zy)p7*@x9IN$3 z+r9VE<`*s)#Yc6sK!hG~Ui=kZ(BH}wZNixs2eW6cat}u_Pl|v{N-AyG;;9e@hu1k) z%r0n2#ocmkh6W+aabSQy7w`e+nv>W!Q*LA!BJer4=I{6~PLGW_i-?^wyU%6@%*c85NjKq9WLJ9Qq^r)uYAfA?ek zsCgaDjGty~Q?-tDx_Ke|c~(%G{--xAHTnkKNzM1NB`Z&*77%cH7SUTj25cCt{hgQ) zmeyFc%f}7xp@$A zRhc1ha3*|3C@a*D;k=o?ame7x+*4uI$iBNa-rQE|atntD1{KgQ+SnL~)R(klv?EYP z9~4&}oI3c#Em;@2v9>l>cd$Mqfg&+FIXM+yPPQv?9+!uTGg_C5lbozm>a4HCE>1s> zciFwGT5?P~1VNp-M!^22K3u5Sx{UbG!@K{lGLwU1L;~nA`MCqj<5!{PAQ`c>?(ev= zVGoqQ`&B#>6MySp70E^cjOnQ4pOiJK0OFlcz=TO!Kq{QBV!^99xwEOUJm&fjJ|y7L zhzE@lrdJnei=4jnFD&>cBNlS*T}|&Km}a<^#B;fl=<$#3MrQGYhQE6fSi#3s^xWZxOY?& z?uxiMbuUj8Jy9OZlMs8unX`jJF`X4E2oOjddUCwTvVrq{<<-Sj!YXbuvqI?3F(!jK zPSwwjKH-`u{8;=Or~|GV`C*qb&JF0TPZ=+!fg0|Dq53Nh*%PG5e0}Hn$g-P`^F-Mt-q#luU?U%g@WnY`9TXFz!XocPd+!Cg(Z^K@!wn0|x^iXnw0CRQYfB9Jt`#J0aTQqUoB&Os(cwedq7` zy_|m)%tg8SLxi12+StPv0negQd2bDdt59pLomZVqt^y%A{7!8y3wfoS@7Bxo$oW8| zoeL_+@^%nF<#%Fi(-}9L#kX&KICEItzIKO=M*TyN$C~i!&SM0MsaG?_5O1#F!;T-Z z`^sATf$PUN&C@#xmJJ7{fYS6qiLy-cCCl{jo|-vpUO-M$XEe)C?w8~W|9nCvJpI#_ zgX(ur>-QAR;6T@4ismO!(WWiF1qgNVbIhpPSwYdWyOIiTK!q5`q??;P+nLupRbf;$ z3R;H}7KNTY2s_2h&+y5@K>Mj}p8DMnu5D7DW!Fn<^O7+s>vMn(TThWsyc6p(QaZPf zSpQyHUiD#J|INk~t9qc=yoq2pw=;esmnYg$>Ck=UVL363s-u`y=|osL87%blBzn;K zx#OGE!QX2h6hC}O@k%Gkx+VB=GNGDrElv-;LNMd8>yE01)#jB^zP}@1_2O!&3PyQA)T4=eby|*2Vuu|PE@~laOI$m<{+{7FKu{v zkbwCP6cP?AE0=n7dbC?m2OrN2^qWWzKh_BZ($U^@io9*&l9kI?K67oiR#=c$n9v2} z(QL`FUkvBHEDUL--`1Yx-g=?qFFq&aQuR`tvb2rjCN4ZPH2*u^oD%6WiD!CLG5r3* z@$*dZZAQ9QKcvy;jJhKmTO6JKQ*sQ)Vz`j5-y=W9zSl?iAI)itD493-^CLD975qB; zV3*Z<-i5*V!v0wFb%S%qa|+Kr=~uV;B#T~ezIyMcqttEIIG}6!>GU#%LJ#=WiJ&lm z8bP+R^Xl$aR|vs|3@SQ6mZ)12!|yb71(601%Te!=^zHBNvD5)8^|(6?7=(1RJ`&i0 zi*yS6`#Elv*)Jlyk0qHJbdoBV(1>b5(l4Q|lUgk75_ESUSjHc_+0_h&_#R%)a;*XE-wC3)$Z zjBfgVI9aP72dZ*sQA$t=A23oonU6&hASc>RWCJ73`wk=+Rqn-vp}^hP(|g99F$0Q3 zfYGs2_m*?NnzeLv%MWk=oII{YPWI4Y07XG>aOhjj70hR+Q)837Jl)v(br-l$K}n(h zvvztPLR|#2*g2kXV0wyPh6UZr6OGBu13Iy$4_)Mz{hpV7%zU5c<~UV~zr?e*o)buS zAs_uybjPczJn$f^>lotwL448dhXh$c!I($>J`c~oar8QqGZf*p^pbFA^Wc6(jjk=h zPR(;Ld!x$kz3_3sh9(aPMV?$-Jj9e2#{V!jeiix}0Kb%N`lqj|XNb&9Og(%jd$>=Y z*Csx>o8DA7$s`8$BP ziIh)ipmx)3rY-(OI5Jx5JBavi2;tf_R`!A$9T%6UtM=vDQ*nu}DE#l#hJbplDrwm) zsnzb++BpRwg*YPW=`wp^c8;C%UCG$7^&a;$-7@upEELS-r4wwynB3>L6godm?zq*cEd#K97U~)kc!xF$VFb<5*vl~ z{3wM6RfWtHWb4A@*SrHd&`kIISM1aLSbo;mIqK?0BWu7_`6*r1&Bk;btb>JDyWySs=zWyQ;Mx9eNfUZp{ z&eTf%r}qiM{{FDSwKhVe&O?uogiS*|!B7mVzbiFIVbOJgwJ{RYRtB;V>T-RmAr};m`SB47y+v8Je=4 z1=YTpmpgKU8JbRwYpm(jutoQk)Upz^cIUE)g0A|zNM}dQ(qnr4(SDN^Rd=Pu5+caq zQJ@gv5R~WoMPoQnxgYY#!>3VU!gvtScvC-~LHe)KMW@_s_e~OM?ssB7R=E>5I6Q#E z-p8OpW&bBzGuZFn6t90_9l!VP1}Nm@r21pJO<`wGogK8qemojMM124qB~)~NJt;lU zcqAxD&B`b6>CU1(Ei7B~3r_LfOuT2{m5jPAspYhBmhh9Ke~K=%NMCvEZD7v5+fk@F zay6Wom#Uk4_SDtv>}v(IlYa|@UA{?=Tm&_hr85(*pAxEkZ!Yh-9XPmbcEojL%jTbd z`~;q8BzJZ@OsI3qhcw&y?4hA)MAv6&KBQuiwbvdeD76SJbS^|b5<-9}s>vcZGMim8 z6+~wM>fHpq0{?JH9H>?J_V#sB%b~GUVm48>%F++0$ZOlwU^Nwqn4-{Cm>LIscC94W}dY^y%B`W@4!jVqoKFO%BtLZn^NaN|tIGi@( zKPO(Hg@yJCC5V(xB{iXS@Cm}#A97Y9A7LPN^A_b0v)kv>M0NtAL$Zu zxqR<^%l~Tu0x!PCwVA)6l4dwlQ`M{{uIFp)(VY_)304-p+<+fQ{;tDYi#N5EE9W}* zA^2spe3LR9)U&;)3T6=qWM^R6pOa8FW_@mZ6LIa9zQEU>hOkeJLZ~*W-^l|(t%h%? za1&P%58qf9Cefy zr|_prMhdD=Eoek-fodt4yN^Np5-9js)j=7J(5hs5jTJknA#Qel*fKbKMk1l~3)Ld7 zD;P%wO?K?=fN`1y1x#qH>_x0hN!x7;i_uTFYC+5TCT`gSi;XhYMl&U2zMK*5CYQf) z`87QlwOzLu_yY|zTJK+>D6znd@UP(N6lOwKY0KV_ypsnCWw_p`dHu*rkO~Xp7q@84 zeY^G3$G#EW7OhIgDxzlRBCnMQy)IS?cxV^|xLM`Gf_tmg>DCxZ7XyxEq#-C{$*1|t zpQnn?h=}VbZ5+(Cx)|}|LU|?O1Rl;zX4JYzYWSc|o^h}+Gd5?n(7eu#Qe)UXd9nlu zk2tHVhsQx-i+6L|K=|p+`OtkyiVi!5?xDVUgHWEy)72(N@zO0_bD70njT|DR$#h)Y-GAbh5$9;0Dq}@pVY-SzQ$piH?k^SDm z#D|cDS}HGL`$@^+o<#T~Znxg+G$Fqpb&!qJ;-7R_(roYU=w93cPiobxW+{Ao#b3K0 zDZ1|#h^ekl*1x27_wI3Zbv(}ZLLaDo0QnCiAU%YG3BVv*T{BzJiG0a{d0AqYlB&fq5pmb!&0M#7xa5=@}k<8kf?{=Pp__oG&cTr1C5?ejt2+zZDqB>F@zfy?dL|y!06kp2cnJl~SOQ6-XB(~R34@{}V zQ1<}09pvvty(12dbiqZnr>u2*Or|9+s}N%ch4wdHRFac)IXpbJV*F-{ZUh7zb(jSx zqf6ygn|@Qq{osfy1WGi4OfGQ-&2ObBF&CP1B(*LhNey9M>zazYDL2E}@HgX`_}|iB zM8|D$-#2*I^X!S_AbWr4DhKMbd?CP3ojzsX ztoOTvl`^|iA8_RJ=g+}{J38?9JQ<&rZ_=%FhBM$^@;1uiLhHYl2IyaM&#zxZqsT5` z^59OS&P|@kMnO6adHKFAwJVHy^Jx(a^XLew%f8XWmhmVFPO_g~F4+bYK)qZ)OSYLf zOCAiCPeB-mKjWBtCW#vJghj8y-;de)W>e97sNjgfLq-Q4o)AU*4wrJ2J93zyI>(#~ z_!L7edAkaHitMMnn#W$802VYIFiTz^U6&wdtN0Oqw}IT zk|?=YbsN)@d~d6I4N}ZySitwamLhkE^Ul9=f^o*JeWO_zHkg?ry@D&{SZZd zhT&^>8#5hC66&15aEz;TUE18k+n*f;s_HiAxo>ebsB;Q#eUlJ+)&AuBbB9RE(H0a^ zhhsgPH!S2vKbOUWq^c2L)MmB0%!}h%FFee-F9qn|rQnKL$_u=jP_R zf+5=0pdGOnX)b{^y7e0;jnOk1w!Wokfj+ZeE<`uQ!NJ;X4V?@Wm5;9NdKf>r4Di^W zj_}sm=iOc8eAHvY#m}{T&p4-6q=1ol|LE7t7MDnLKspw8{NRs@0N3jqwha14!rPqv zuUg(wLg9mMf{d)MH&Wa$ibgaN(Vn0MB{wxej-lNaM*LTbl8sFUJ4JV-#xjuQ{0 z2b`6nh%2MYDk@J0T`1M3XjmSfzv-!@IoPe$O}wYYlFeJxS#MF7U!6}PXreHig!qms z_25ak$lphr_gOQIKiX)1y<|g?yl}zGv0Luu&_rPIg$v%_zVL7Fz2&GKB!M)JeYfxCY`&41S%svQQwhWMT6bCE}Aj|CXKjgLR$h^ynnP!BhHMn>LZk-Mee zKN*@Dt$e?hE;p=Y-%9kpUzoQ0t=Hk|GCK`swB{7KY6Tq*KY0C>?SBRXt;08b5c#gh zshgR6n2E9wn@*O^YUy=u=KRi*f`PGY51`2nts%twNTRoPK|- zc1CB+T=tY<_4?WR7QXdGwEz|)PfJ+OA`V4Pob3+HfRijy?cx$-eC?osd8yJlriS(lfW z4Z7M*q8QZZzV#O6qTh(IAi znr_;nLfCUZF$s*0WznSUqQlo*kXAw&m?_Q|?LHe!6srB0nWKWo*=pUCrmTvZP_$0+ zyNKrw??1Ee)s^xlCq+F{z80{n*0_knlGD@3jcAW!0juhLO!BA0t*078%znB)l7%O> zC(SGvQ_Y?n%D+f_nX})%-`#{QB0HmQ%5UcFb0fjpsC+9b_dOUi=?Pk$T#s9m3Gh48@12Oaa^|+1oAiSR4_rr{ zWgYid6Z?ei*w8Q0kRS7TwbIogHHB>&%!9)>OL>b;ufn(NKXibjkXTem1{K7c;4p}S z!?2u^%t3=fub{T*rbo-aF6idk}DB(IB@udU$h?Jp{ig*M}d$ z+g-`drTfPI>#Y!%$=L={zcKM|8|xkFty#|gt40)ociQxuI?{<{2Xw@<4S%{hJP3M56IaB()Lj* zC_P&+HvQHzfAU-~1_+TkrdVFbKiS4gBP*P~vXDYJk9@rFD8fJsj7RsJWJVEbuXJd2 z4VObwh6lRBry1yIvU78n<42j&)0HddLxC3t6iXAkxi;M9k*(7L7;+?HGZ zt;)~oKS)*J!bd!KfGVpHs`ED7zoohS0M_752vim*B9=;m6S<*BVZG4YD=)uEsGViR zSRg`v^V{;;^4GTPu8&8+$y3aql>Ft9_y~mc>QFHebhmhmpll_WReOkoq24T!cV6s` zd)kbaJN;Zp^9}Yc$?5uqABY`G+!f$u>so0--#rK4ikN4@4C!fr74GHE#J5Vq3IF!m zqR#OpCl3m-q<2}}uOqL%oo(FjI75C+hIOjR!iXMl#ygL|c^q!}{aDR};olV#<&m&O z-rH$oKp8#STOWr7VExzKKHYFQ1G*MTKK&!mH1h)V4)_c@`P>dW)m*zb5wXATL*7GC zWgSP?<2wqDVLIWS6Q67=hcYgR-Wb;&u*g)b?%(FcSomGMVT&h0TN;Yn^i?!nQ~&ow zyXFRpaHInR)GH6cx+R3AiY7{!u7Sk()xSzHVduj=q^vYRXdKdHA42t9_gDLhq{9Xm zIStw|6S{BXdPk3ksKNru%gg@~KS|pB85?`&iSq}gq+q2+rlvNamB^br895KF#L`#< zcWit;I_5g%D$UN_FVrUb<}Ey&UzR`)r)XjQ3RP!;v58Bo?c9J&eHX7wX61=Q5MLO- z^iG792acb3eS0iep0DsPjrv?(bOIT;wX2EThlkh=JLqzuE}}KaO)lOl_@jKuk~YVV zojMB$3^cXjobO`GHAQ=C_tjZwcG!o1VC$oS-)(ppjavrApINRj9 z=|&F^uqM7iTUIKrnSU!%o%WRGgYZhqEp-W^w7qqIxeaWwsDE)Lw8p&6(o7+60MTAfyo>l5 zf#A=hDDe1_Tzw|CO6|beOGlc6S%{v8(&Kl?P3S5nfrGZ7(#F!djsyt}FuM9*x75?& z0W7bfexM9tzr1E+H2wgeT~#GN_Oe5q2w94;nHeax5W|sYs&e?r8T7O-D52+^3my%+}AB`&!U+y`5MS~ z_Seaw2v4Mk74!gQ$)T7mvgpoXCysMpWWtAS+l-Cj6LTtEy9+gpDd+}L)r#Odw+R6z zOX7bHUKEqeA&AnST^xAxo9jFsE&Zt`u%F+$Hgzib@b8;*J9B2Yc-WflMv^GQZu+{i`dZfB)AKIt%F=N0)qRjyhx<>|xP z44S`r!=cSr_}{&{^&h(Hd^H8(Bpimky?B4fcNfN4~AOQ@P(zU{Ns*MpU$J&2``jXzJPcCsRNiuoUkwQugc3HN(aEUHjVc1tCy*ZpIJrew)vf;O>s|0TgV+<524{(=N1mlTJQ-PX9r zc-m!MyXAre3AKFN{=OtlU*S7d zRu39^yw?=|8o4_1gR$Ckqh)jI7wdA>QAd1IBqTRHfIt0dC#D~qAXBD)`=o_RpG(_rmROL6+(YAr z$vt|oQqPqR@uE{?xk30D^~a+FuQaA-OIcEGjFyUc)J+mOmWdBgA@az%g@so$kkj1b zHh=hKE6bU-?OeDuW1GqAC+0+OnBFBjR~-lL?bYlO^LdQ_&U|&^DRSii zoU%LmIWiK~oP#?QYPXBzrj>g2smv4(9q&SN`aaoB7N-iu4wj^9OS(VicC915~DS;rInr1^2=^CfLFTCC4aceQZX5UOm z_fE7xBCT2^4z3BV{L0JBb*Upn_!&RX-A#ai0u;bz-?>P3_Ft(7HY1mu{vws=mIMFB z9sxjh+xk&g%F__hQgoI zCmwpw#XS>iD#>?_v~#%fe)Z|`>eFhxx$o|Oj3`=Zkw{|eUkm5p!Q^|Cn>>YZ%5luw zXf(-K+Y>qP3IZ^)$oo`z{DJ|>m#Q?x&!_26K<6Gm;oy29zYnXqlA%w0q8PEfn2%W} z3nD!JK^=)1&O`(;C_gs#4b$UR;%h%Q@~6=gwt+P!*Ed!M*f1qT$vV>kSx#~;FAFYX z^wx$21Gz-@ktNZ1NS&lks3}4dYsvo%rWCR#IZKzkdTYahSiRA<1UzVt2sykfSve>5 z;T?ID>-?4X*556#q;3ft835^Gz?{-*iZj_+ky9RX!OixFx{mD$p$9RJe2GP)Ev@@o z{=Z*F_%Wf@N$M!;5-T-tH9|Rwlo^g_(FveMxiu7rE>BaN zj^xYoyY#+_PWfi=Dx+Lc8Ef+6ogAt}d62tTje zR9%PHt2RaqqPjQs)7)^Ao+BdXZ`)0Y40c;}{l>&CnP z4qVL=6u~8KcD$@^-iLAs#&v-&K(!Npv{3Pic++4ljn8b`WyC_o{>Omv7oK&^S*=lD z2z!{@J??`+C;59);W5g7giPBS!{m!tVOYRqikr%nJSs`9!|uS-=d$LZ^lH23C@NI( zNe5a-+}!c}R>$L#r9KIA4SwTBi4tQV?j;Sw`5aXGMWS#p%TReFC&G`_bHZE1WA&!{ ziDBQ3+AuxTe&5Z76gMz;N!GeAQzwUGifohozU#hLbyv8L&4QZPo9LOu(m78b_O2YM z{TPJpKaY1p$l)I-SWd~#yb3|w-x3xgb(7I|VumVdQ!$hak5LC=t3qKa>QvTtsV|>J zl6puB@P>&46 zQQD3o0NzC$E}Q-YoeM#bS`2zqvZ%TeHy>TGs^QwGA)EpeuRz1lw?GMYEYGy4<>O_; z;L4H$EVfjsg!pjK-v1y2sYJ+|-x3377EtiG*nI%8p!3_$*-Q$@As3vQOfV+_{R(66 zKDw{`AtJ|xoajPLP4%2eNV^#brY{Njt+{`1Nu9qQPMp{ITpn3gN?&!wtNY4(WdWBR ztnC43aaF0j+6y3JC+f-!?I0!PerkUj0-?A$Wl9C6>sD47177Yt$xLD}qP z>V_Y^m`Q&9Tmm8rL!OiPbjw@HB;PD>rZw6IG$I`SC}mFU+>4U>0sIbfnA0HT12H|u=X*78mcdV&6>U~7G%<()@7-`*4F70q_v*v6GAcKD+qfI?|_zwXRA*gJDSMD0!$mRiOt2 z#z2zX0R?uyJ32u486C!tVp4yAQxMCC znlk9z*4!t5V>4Z(?fp5GeP;AQdM0*+)OL0Ea&!iR>&K$0PF*o`GchDq4qBnyp0 zlP3GPt5ABh;)#PH$rpat-wyrefU%PQhQhx7qzk-kc#wiCeh6&1e|3MCu$~fu^Y^aA zN=6Y;z0w^D+_>RXo+S~=XX$!MccA+$?O6=ax(S{=H3KGJG|DZc1eKS|kT=8NYEPbA zfEsq`Ydsp3vGSTa3Z{y&-HX4Xa`WslL^O8oN+}^-(uDoKQsPrlH&HU#ZNl`U#$ODA z3_DeuNUBtw$@}-dMQx8g@zS_S5xSbeY<; zf-#1qs^>9NLM)WOk#8s@)}dCO>SjZIff&swxkD*;nk@7Y1Ig^Hyx=_#=}Q|N#0=i+a}Y_f{^cS4HO`_!U>f@T!C6*W2GyU`4IKtm;<|B(P(Jd-K| z1X3ZA7=#^gDghosB3&dU2{QEjp0QEmEy_pAkaO{(9mtsl^a^CMMafc1{~^M%$i*fJ zsbA=BiMQg{kO6t|NvtF5Fac_L z$aUj6dz#nu@swNMr)9@nX_fXWvENRt<1qa0?(R@lEgB4b%fwXP^`Nnhd~0pwGn>e8 zUQ4W1OtwQ{RFfzNd?6F6D!k>@v6G7IJ=i3E9@ zYfi^h0UBkw?6DT%?`&)&;MXVqMrog6QG-8KKB&GuVjX1a^7WD|84AN4PIT6tHKOB``52@Q)2OguJdZobS@lr{AK~E?)D(q z1}1rb9tRS&a?$}tCJj9BFM}lGt?c=oY5JEH83~atD8jcj;A^9Zjm(cZF}()CaCh4DX!x;%)QVX5Dy4L} zK`w^wG+MJph1;BEiP&`Ltp>)g)E|}|MRs~$5fl5>C5wC4rw8YA!5qr0S8pgm?l|_M zP9tbpu{kG!;?{#7URNS--Ugh+U>|Qu4tD=%*OSvY&K>o6S%As^+vtvIbvtlg^_DyQ zq}Jf0`&PC0wI0b{7pR)PtVZ4E`ZIBA*ad%_frd?2qer}=AvZ_S-CBTt1s&YTfbbWm ziOmw05FOg#hrQ3uo&7o6`POd$@vW}2qG)+f7K5fze18}9=Aj{`KYpKkk>Lb4YP2micpz{;()$E*{du7cjyx-UX={C(fAXIFl@YJ4u;r-Z@XE&XI2`tdO zl(lM7^}YUv)5i5PxBU(u2$wq^iBx|Sc{b#a$kkBe*yK{C;2Q_e4qTj_?|`|GT3T9= zs3kUK_!H>>3r5Ue32kY)^LJoi_rbW|{05_#EmG?;(8&Bx`~oufAM#($3ZcG-9Z&%w z3|X8zkvt*l5of-Xma_56IyV6yeviCUgi;3)J_N2F{R)NJvFrc7tZ>4zE&e!=bQo$R zU7z#p+^j~Gex0^xv)kU_Mtvz_27Y>LVRS#etLsgX@6qtJBHEs(BM+Acklh{@$vBu{czv}Y09t#H0m5+-*4Z$$H;IVn!F6nsCno$6#|jT zUn|vtRLkVGR&-5{>vk|g{Zi2wxWlC#Rhu2pml}bsaV8JX#x#4q$ex$kU*N>N-MImG zdaml%hj~9#&he%>SWyl`(6bD7v6gEzMKI>Aq__KL!ixhw#~C8cnGb&)=51#rRkM%| z$$RK~6vt^YbgV@yOa0-$?I~r|uHr+45+Muz^*5g&~L;xpQfCS0VFm_}L@zeD@ zm~S1{+uQ3bcUIYBjN2_w95ATiSCYp>Ux@zjhNlu!y)8-V$WR1XFcZpL`Qjrc$Z9)4 zG%*t0O->(VjoTNkG@eOeHryskHpx0FKW_E-oT2FcsMe^++UMKXl`<5K1Cb#(!IH_q z{Mq)#1tcY?{JC?F+4-%!-2w&7TlBx%A=83EdeugG^@Bcz0@RDkJ1U*UAbCv&Ng|*Q zfWiLu_fHj8uQ&F7yLaExGE)E2M-R0i+487l{?YtPp&V_D4?S!{c8up19IAINNx=!F z7bEtd81Wx14}Jp5=S&6!y7Ojt-#(df`;FQXo?fw1A^#W**xfZ$*#GK~g<#P=u1uo> zz=8T=V?;pV3Mbd08&=}&AQU=DPc1ljNfIF%*_fpMgE0&vu9GXpcfFFhm4JC?kM=*)8qLosBjoV0p!q_OY|TDF=oKkWz>@Q$)c)Q%I1c2I{c5HXFoTN_lWnE2;r#GuhhFUb$XS;f z)U9(G;6w}*A)+DGki%eMGnm`}_3QZU2sWa!ojdoZa?tR2$?T}lr zxTDIl6zxOz*^R|TO`uSLJlWoZOd}*G>>sLpl4yXpg!!c6%4582=pT!j!g{hdgKVBx zu3UkgKG`ULcrtii9L6or-c;h_f4I$qN?R-{83TYFjnXkFT94L_`Ty(IAfpk;@CM9`9;1}Esuz9 zN`2A|2mSl^0|6)3{^M@>se?Pe_XPY)=cY+<({_qeoTTrHIol#ho?GmKm~7_M{}2%U zKa!9WV}bgQ)Zo2cga?rhri2H-v+}|8PnPB3pKUIE7@a@^OMc%n%LCVQZQrK?tZQvM z!1K)|i*CS?^PV$=q`?rxd=^CF0Z`&cuIGPs`VEbm#OGB46jpV_iw}XG?=sF~Z&DRJ zp=8u|CooeN_A8X>SB*E}yLAK2m4`p|KWUhtWn-8(Bsyk(NR>=2tnB@5)g${kkAv01 zlbzK5KfBX|CD(9MR{dW#=v_W4FVEt?e0QjNLTmr)+KvLVaOH*zs~K=xPbd7|$7Fo1 z^li+9$N<8QJ49y6B9-X*XFQ=ToaWs5a6I{~9txi|eoUY4GOxm`MHWay;&tm3?E4kQ zEHqNyxoYWRCmPQ0w$R-ie4Nc%GOH?Qem%T$&ch^F>yWZsEb9hBTZknDSi$N=DEfe< z-;2EmbL+$d6kW~=nU#j6o>a~cvo>e=tE0SFLr@Nl*0<~Oq}2WQQi&O?u%4bcKjbP+ zDSs_t46NvuNg0_J(`ub*ioA?xuGEiJx=Gr$v8V~gl)YZfcy5J!m&(Rh%LdfVx%*Fg zW7~=_&c^TRZcmVt@`{dTidC772PT;W!oN{@IYNQo=vB;d%Zg4FHwBS?<5=F>`Vf zmvNbRm!6((WMF<}^mCgTMtfeW>pX8qDBAxzIT2m=;n_wAv;<)A6`~WjA|~3#I$g-3 zZi(~nBRNFj)JNlD^go;gVqi>kRhGee?Dw2_0h!9`yL425$GyLvN8!xW6VsqqmQSNL z+9AD64KsBN;$n;BFY*iq0y9xd1c?$n{gYExXBg;M80cUy`cr4?K^HXF#Xsl4nB2+q zh^viX@5aJl1qB690SoZ`(JzDI^tgtoVvWm2Ew1#0 zQxK$V{!fb1C?RF;d=Rw9iol#Ht2}C350H+iS^o_I=ubaTOfD(5%)C)7JrORRY>3&> zqtu}@&!raEZ*wzPzT{>(arZ!Se~%yYoC@jMK8Y zr<5HK%Us}@Ss|t(j4VEO6hUwOTVmxN#Nl|({FhQrAn&FrzM;&qeqQlWFZ*(iIO#8J zmNh*+dn7Q4$1KiRv!<4DH_SKoP;!B+fIZH@a#HZslsZFa5)*^c^Y3&`}4GCk7zC_17CY|rA*$` z4yXXP2#5d4IBvx{L;KnyvCrE|TBuip80cY_i}Mo4hg4CWESsU&Q6c6Plq_ExM1 z3E~tvIORC7MSvN9(6-QHZVXZtO#|uauXPN=h__;x*(NDSBKqX zZ4QYpG(I2h#UU8XVd4Y#?ugZc(!neI;3f%HP|KtKdTitru2sT}6_)o#v-LbX*KcPu+%fjQ< zf3jOEa#XvO#)(Au;?3gkgq?|MnoKeLC6X}zJcV_h&l^R z+PJ8b6f6OhMB^_@ct1Ht9c6cVpZ;XG(t?S*=mf?T<;<^7?v}_ag8XpaR>u||iJSw* zKW>6JQsPrG76RpCM9Ga4j=Vj_Vk*fzu{cVWoG`yCr{(MVrtr@qy7getpi_3(_#fDa zKC{p2&G&C;%TrJ0KrZ6j_dAgvb{)2{2;^>M6u468F5h?1)dE(>*Z;CDMS#EU%#+yf zE>+!IfORc)8kHdfJk<SpCFL8cv%|mT8>W zG1*c@1%I{NjHY4GO?zG6=>}7^X5wOSSo~gg>pIU-r{^?^$de$(QJ{>4L=gVLSR!9v z9PlEbu;BlZ{Yw(aaB#w6f^53E;2M{kK?v_kJ=_yI6LJ1rv=C3^YlF9d+;zSAPXCf{ zS8@TK_iHjgBAG8Xk}%(bZK)#7_nrtul&3I?(Lr}@4r`}K3WxIo*~|8krVerwHfd_3 z8$gwD3_qTenz%hxG|&9c@Afte#9x@xxI|}QkxXtai1nLKAmM>%4wcC7uo~t8p)^DY zPSO&916(9pHm&$QNxaTmRkMOq*W(>({Z{C&M4>M#p4}ykmfABT4_6R2KsXPG>}TG@xEMyHl2zO{5wd9IaAZvpiR1+V zEnGwhwg{5L*q8WkcK(BfqNv{wELi>bH$E3XT{b~o_pYSQ+T3SVO^`S$$cp$^n*qmu z`S+T}BB^=Eh$r9)^CHM4Js6RLEuwi7(6v+BeR{1#{7etfY5)Pp&%#RsK-YcEdu#Tu z)??H1r|;GRamBY@&N3i3gZL+R1`+fy^8O&^j<>fQ>~me+IY{<3-<2E&T8x9~!RMff zbCuhG-E2fjnnhB zx9?WF$cu6J!AHM8UWT+LJUl$#pY{k2#x;$6xNz_Ld~8m^#bzg=6$2sX zCLrKBO6|H-&CSh4ok1uH1s&7FiH(?jm?95#_QO;q^~TErA@+fZ*QV^n%;JWLGX3FJ46BvZ_g~h5xeA zNeP}@mFsoN{{z9wINYMd-e4l)$pl226*Xd84?^H=7dbdyen@remNNsTn)6t6G&tcP z-ne2>u5G4lD{bHXaens!$*~iBU}C{S&Wg1n5+LY91r;A z@tnDovQoC~r?jilN;C|C?hp9#Rf3ecacP@|T95dgVCDoVYw49hsg2t~^g@2ar{638 zSu8q_NnU~y@<7C623-@NA^01MAXXpJ3GB4AU*MBm>Wtn%fBy8Zs56NFrl zMtFi%e^EKP_jYIb-i0jSe4O2Z55ivb%p^s3pm1HSxy%i9y>sE-3BHOs{k5!2k2wBv<^QO9 z4{)mA_YeHodn?M8tq>Vm2Pu0*Mwum)t+FD=Dl3%A=45AQgsfwiP4-^dWOEK@{Ga3V z{r&&fzw7Ess?#~o`+4r?zF*_MzalLDVV4G8&A8*&<_{QmAb4?ev!Kp{;e(|H7SntYAwG%USptqy9eA8PX@oL;?qirH)pTt)$n3E zg@3g~px&aPp+UB?GzEcyop$q0Z;+j`O*}g-B?xd$@-arf_a7gfnx6is$!GZB!RI=v zu$y+p9m}2oN5lhXKBAomgF?ArxFb<6Gw?5bzg~o}kQ9)3GX0&Fi*WLsV4aMsS@{L) zdfi^>pc;=ki%m#-d*y4>M57Nd<+%1SGt~%WtKLZiiu>i->9^SO)@o3AgVzQVn!v4a z?4tl)-enpOs*zH&4$hab_82=mJBPYXgNz4YWtiF65yD^yxD~&g{xBwkJnh_NRpZ$`?l!e#MfF{-W#ZWvRF_!aKfM9`~m8I<+ z|L(>#;O%7R1DF6&2DrUWyuF4Cv#!A^>t@+0Eu1R{TpLUKzWs6+fNoNGh?KT!EXSRN zY$Ec2T9)8ATF8(Pd9IunMrn=Wd?+L*L6|)2kcC^>Pvk>iFo!lnfE`(DSuts*jI0Jk zje$#wHa$^{G-=@4e*kfk%?)PC+`!e+irpPQp*!B)s`u|R+FK$dLIgdZ9Ps$}b1kc)f>FHjh!L+@Eild(fl29U9$XG5I8;@e%o-9B$^~oe(0GOE z5%8q&e$<@(0?_R-A-%aDB$`bH$x@JmX89>q>(>myO7*G}L`z|OI4`Nd_c>UuhX~qo zU-qQK@?P&h)6=SB#rJH-Q?0_8YllTBf(}%zodM5*qA60H5HFlY{FEAl ztu~A&NvXf@DmFZMo;EHwDn1t)bb6NvInQsXjR(7SrfZf#@(Vx$w(nr(b+HYC6N>6G z*jostd(s}kfFIH#)_7zDkOO)^#{zwKfu+kQH!pLTEj2==(hZoYvjZMAzwO&z=RjPUCwW*Y`)$qW$t=I#gLj zSOTEbF*M|6mGkPrRJoshy7vKnLs{rgEYWwf4LY~k5EY@YL7-hvSm}DaJ0A{+#xD33 zb7F1+K1K?2t8#8gIU%`tJ!SBa9k3;F0W%;tfOo()0Y=rT3vre_xM^ZyVpwlIGwj^z zp~JtM4m!Tm=TFL6wRLpr)1ScO&=pO{TJGIu{fZCQi>0hVzeHi&wA<=8lI1o|5 z36SzqSpZpCXLg)&6rjBaVAw-nkjvM*DvBJZmxI)&qCzrF&Xd=*w6>NN6nL|ytz0;y z2A&B42TbkF8zrCud)xr1Kjc_V(_0``lkviYxPU^PfsJdNDeykcTsVAr85xsRgPeqN za-i%z0Y1PM2vYxzR=C00bU59$vx)4lF&dw+pEuyIZZL7XI>PhDHT%4j7}X%!ACcpv zz`>Q-Q%1Z(OPI6d%C`TbjJxUICGM+CoW33bp@*w&@+D&V=;DoUFrGt&n*p5vD+msV{A}FR;AH^wyB2`>Ko#JG-1I8f zFSmgzO9GHNRhGL5oHAa_9nSfh+@jh=6%e<8od;lYC|EtbI9>Mq=aOThAJ>-|Yvru9 zLZxn*thpseS#=JS$>vtC_>(_)EVU6i>$KSn7e5mO8m1$6gP`j4uu4Sw!8OhKD~<^a z)Ikr%o3AQeJ^IbSchUq;yQaBQtI!lVZbP)Qt&Z%r|7w-LD1W?@O%qXqRM;++LK}HK zJEeOxHsML0ak`&|uc)|?_Lz&Br4a3Q^CEYPMXSn0y4e3_^zrt7g2Q3}lt5(tdV{~% zs{S$=9j7`dj`Z|s`k}Z0JdXrs__l+4Bv*kza`_Diw_-p4GAiyh6XAt*tG&sgmqblL zw+3!9`t#;0=a{Q8gI{`##+f9597U${27(rUSUSZe@d<>DW8k96{Rzm) z?{~)3kXTNoD8S0#4H7`%3`*E`AO!WCcCA?i-qVil$x0BPW^aVMBwtc7<${Pc?5W`s zZ7{YqFsQp*n_cNOGk`qXCLQ7FQ2^!XWOLWG8d>g@8o>L`K``dT|BaWcy`GJ4GAR+y zNDeWTaX**LCO@Dw9`?^Iu>jWx$ZZPI*83M0sY`)Zip0>EOxWhnWn1?EQ}S4?eKUTxS1qguIgF_?03|H4-Z&R>ReI2Zqo=UfkzJhATu{d1S;Vd z68H%106m)VBPAsTAIO10Y_Z|6|BeLfE2|-Ebillei~C{yagLFW{hcie7^v03g#_Z8 zA#W{XPtUd z^S#f6A+5)kX|KInkt7z-XABf#e#9F*@p6E>$#1ye6U79brWFefUjm8Y)DA>-Tx~Nb zNcx$?qcwPVeB>wkNc9(OVCj{YeZ&aOLBt19@4tu-<&4V)rc`j9%sQ<%@2)x`KgIRG z?GqIdF|@RtDRGyPNzL%J4*Xezq%isY`9xs$xB(m#kZ`d|J|$PnG_2`79IDv4-@bdI zXj4*#Oot|bmj>wn>bLWZ)FII1I-Lm8ucUL{dJu_#7JopZh#)^}t)BAipT6@u)-=da zKZ^Wi3+6ri8Aq+oq9p!q`vPv0zXO$-7?_ZEmtW17oe**%c??Z+9ZE&;UCnFdX)bZM?}73@EM&!z${ z84t?f3wCj-^Jjq4#*rNDE#|n7Z(#dre=<4KT#4E<8E-8t(26!&5e$ZetijmHNf=-# z{tv!OBkca^1dfw_Iaboz+VpknjKuvJ)aI85CzF{1$1|h{=ldv=;nE!cm1cpzUd(25 zAw#!?N&ByD^$r}`#g?sijO|oKH$|+Oy)$cA9N>Ujm|c`doA4bS{1?D(_B#g#-9OF6 zzQQR3f?Mv{p{D@BX~bWXlQuIOqR+JSs}FX#!nkj_6VQvPp?^9Csq>=h1GzD;iJnss zI3KZsb3j2S*8)rVI{u8h?Mp)O_so1WToypm92(dm&!u1XcV|{7V>BK_ zyZ4Gh*#@XO35(i$I;mj;sK(k0DXle{_cA`}0K@(cjULdXpnoor1%V`)!T;aG!Dr*L z8_XUjjmG)2Od-vmpOfGC$)2;d7Wi|4t}lhAD2I3`KZE$meLiu5!Q;iSMiX0!hy}m3 zYshjxZqbYma}$0cih75W>s-N;3~2#iSzja|(bG~hj%snl#J=h*&oGATYMETqXDu@FK+E+)s7vqgxw zy(~yP_R#T~C9~>LSzNGpb^azHgn*!+;4bn&ROR0L-fyKq1*#x$4uQLhzwLy_fV~0l zvot`{z~&FhP(ZjQ%(Wez|2s~)W>^qeZuzU*de86jNBH8v4FqK(9ACM<4F0;0l{j{-9hZFlmV7=x`XEB`aQeC-J~FVa~`D|j=lfsOM05OR~_0- zOM@EE)2lq!s3Bv;-A?%C7h@lI-i&fWfH#$%H*5JZZY|oo;{EyV50+8Z=JWw)<>3@i*V^!dr(mC_fUtARe}uY#Fp&?J z*Sr=C*xcPit!(I?JXE<$qPY?D*L9Eib@}{k=tOt^V=i}XTDjqV7QzDikY*$W`Hd#_ zoCZa~ElY==6WE8wlL?fMaO_7};8p7`%**hLUt!hLc#0_<68NX6r7fIp8KAhlz%!E| zW)%ghoUE#<2b7Ayb>*h_JELdQk?=;8&x?p8sg={syO+xVdGmk#y1BUzg*GJb3Jh9WI|Lk!1{|K>{k0EX@VQ`%B zn<=*=12Dq(0MrXKNUg&=-moo_3Z1ptv7JVFG7UP`c)8}HAHqZQ-u+Yhf86GPrz zezY&R6oEiXE^Unyeirb-3I|nt4_15g#j=xnyBNg24dD(IeLOl6M9oH9^g?2ZO|}&a zh#c{M2JvQqdD=rz)fTNTsFGU+@)wscA4CEDwos|y@u41U7KeS=C4TjS$*+8a`(Hy9 z6ep$?E)Qu|J7~_@2l)!5YyPB?#NJPRpwY=eVSuo1l#xQLXk|2X(O@l6>2*xOo?qV@ zSY-XS-6pYyCk?AqZ$S(M9*L1vIKU=2q3xa&KPWs;Am!jtZ`-nU%4Tl^^^#sYZhyW_C47}|GU7v(pM$E_QYcc;mB zw`2qP7$b1bL`y|BgfSfpXj7=GAgAWe@BG=$xYKJ^w<(NzRQ|w6#&}<$E|I@y|6+zt zcEcr`^Q^mLvHsx**)gWw)B$urza?@X|DUj7TbJo7C{Winv#xE&x`X5kuPK9cZ4m&k za@&t!ew~CIT1W)M#58aGqh-yP4rq741C$@rsO_6Z;u9u>nfw3e;ul{&6opM&)`o0^`U*88bD^LwDXLliK%;N!iG z=^gx22RuN9+KPb!kxDLwV=;iYq1gSnw=oeeGqs+ro)6oy{KP#NxS^}nJa~PJ_|~1L zfq%^wKgXQo*>c12%kLkYlBDcz|%o{lD>H$tx`BlJ`ztgiy6QN z?13;u{^5mr9e83K84!Cu^`t>&H-F*5mHK~mI`ZPvK@Gah%U^n&`Tb(-+u$^*QFtmN z6{JY=hmMTKn&?{}Ri12JZnyU3T;JBk3Bb8`QvI0|`Fs}gb@l!=Pskxu;~JYP_q z2&WC+Zt?>c}Au|Cl|0k$kKxBYt?2moO=j}o!0UQ^DWjn(X(ZI$_?#;Nnx0<3($arX^f_B}7e z!Uv0CB@F$yKQQ;so)q2-Vdv#R%A1pNIqyecBmCh!WttC&{3GyGMf z&-Slwb%kq}nU4lGJi&CrpS$6uJ+QoEJQa%zS%7*HmYbQj1>%&xej^X|^!mQpiNBtN z#Y}evFTcgi`A4`O?m_qKJN}2ZU$CR#{woxJvMv<} z0S{N53r1a)sr;o)*#q))R+QU*<@>$ZFzLlt#GUU+NU0&$Mbo(;uFiL#tm(JcxDk{< z2`8+~_VQ;9js?4P&IRFgobLy}n6+G$*0#V)wXbB0UEBb>)tnbXYH`rq;^I4Xt`VCo z^3Y7Noa^N7cED+ZAcu_lkw5zHt19@W4 z0X>wJk>R}>lT}UI^OBJG&TYi+vk++chE0SLj{Sq? zt7#OO{p)L8_Ud-uPj^Ba-MR30QB-trzkYLd{Y;(r5rzKB)+7CuuMi3_p>a2W7&y!+ zh$;19)%n;7tRHL^Y42Hgc!*j5PxH@9iZ6wr#=zS6w%a{2KH@#>rqc3zDHZaTML)i6D6cy7!gy$h}KyPdmZWfp0a`7UWJW|sx+2H(PWfz=70 zQor6Wo6z0+E!qrIrf&Go5z*=2FJ5WA7}r4$JGs)H@rLvkCs{5t^DWfGF*`))oNMDu zBe}d<#!%to<;3pLe|F@gkAXsTFQ$fD4%aZ!kC>M4(ZHPBou5ixXy$;9sl{@nY||H* zzcwR!0kl=)X@x=s1TNB55TU8~>AKFwfOc~!TD%kJN>c~O&B!zzEbC(AuCOns; z@nT8elS#+@EgRg`^z?Lmql0M3X&))wwbz;6QsOW{!JfA9?u2<%(64c`+23f8yQI>h zLEi=Cy8XieuVwL97+3B6ZvCj|%RvMk#COODLdghe`iP-6Bn{;(NT?2Zv0Hf3DDtlR z=f(~L3p{}lRRFBS1(355@cf2-v@X z6cK+2A2Z#(d-wl38Rbd$uf;1lK7MK^!d9A!oCK6@hy_PAmlg7E&xN&(4eDOE&r*l2 zKOw^HQie>aGDB-4pJ3H(yIB|9aaq3qq;0#+3DtzsjFeVb9*QBK1DFVJ(AjHoLk#=q z4;ieitkp^X<4*_a^q5ty|v6#)?XvXv)ymKN}9tKhF2e@8Sa)W7(tezI;Ozl?iN`6kARLA?O@qeDIkt z46>5jToN6m;{X|5+1S^x2>L^A$Zl%)-St$qCF1~4pMXpp`o&4&>Pm9%ig@`YP>ozD zllx9MPs6s;;Mk|{KD+mpLQesMJ?y?Veo4Fmj5xH7VU29QT}dzZl9!%R7$J6Y8PQAm z{G8=YGk~A@O??9IH{jE2{AKPGn9PN^lc86td-hKU%l-NpcvR+fuY^IpMLAt<%yLFW z8d@s)>!Q}Yl)<4)>*CMF4H3(r{VskO2Dw9;d;W{DIn;n(oY{9)@oOGC>q8piBsf_E zn)LgQ(t^6e;2+xe1*FQii+s-Vt7F=?%6Lw;MF#t0OFIqT*co&c$fBFCFfjmW(eEGv zh!Z`l0&R;gdlgC!pO!xv;G7_X5`wfWkgeHLG$<_Eb~HxAV~pMWnnnn*Kib>5H>sSu zZ->nWZ?rJJ32llco*X85nh*V*kbd(-h^T%U{=CU0!M^e6o>E!Ln-<_`E%t>J z12S+o^xHz*zG+Bzu!<+E+{^ZYvVQyh@Dy@o7s!;naNwT$3v7)Iq+3THh43Lauj%o_ zaFaiToWD?eKCbU&fZ8Dj;qG1lMxHxE?%3kZDmBCqa#_`O%q2{$ft-(7yNrQ41o%n{ zi`5p=CRHp%w?oN3PBgNeG&KgfUXuV9$FM`t0~3@!E?^S-UuF+(?Ew;~vzy|DT4!|U z&QX)`I^I0)!E^$8Y+M|n+b>ra5-i+QfiIxxFn#xx1&?GZpktH|lLvBEyq&E_m=M=f z><;%twX2I;2i7hpFE5YQi=it5z3|N=WCMqi^wDpNaqSADY;&Zos!`SYAyI#fkMm-)Bf==Ku#-6j{n#SyBi!7k#O=6S+S%;IX`wfICM9M1 zDcD?^llXTk(B{bU9t(5Mb8iYN#L6;POT9W`rJPk_qEl6Ff$#QL7tu92?d&Vh9d4D^ zZ#F^yJ@E0;Gx0forsiYDWdScJl|M~8Y+N(-8ZW6kO}M1$cXK3xf2_?v{PB)H^0&0M z;09{e?%gp-eKBY}Q?myID}qWb@g`wbq(%cpCBg6c6H%!tDWKVFWUNE4HWHwjspz6@%{Ur_p8NZ5(2PNpP-D9gCip# zQME#B@=T*J8Mp`XM%n;?2WOpkMEvHmw|Q2vg4vN;+^;`#Y;i+sezvk??cJ#WYHBR zGzD9Hijy@{34HI5$%4Oes3Pa+zQz>ak#GMz09)PnkAx>no$%cTds&iw5@M zs>yx3+(8O9$obi-7Xz}ofNeXj=#9%PEQf=4=#D97glnIA&mL`iJ{0XN+`5by z#y+K))=4u>&Et>_EIUpqLhG;0o~-fW_e2zQ7TI{9Ls6%?JG|1nGSG?Udd^%`rQ;CC zLR{6dVleWy$APkQv=xUd&Z)GJC>01)JX@8|Dnn2zDU~Y6qsRsY^aKLNXCo?JNPSa& z9XJ5X+gB(@MnaR}im-XEzQDiuEx1?Tv4Y>1P?NN-(rc6bT&W2Y8RkV3r&_`r8yk;- zndUtgG~<5uy<=hVu&Z{!b3NJg20zkSH&y0_aM-kCP1K919;dc~KSq-$-R|?dox8a$ zE4&B`U44MTgT={Ts_9nQV`HR+Cp1*hRCB!BVj*|4PWg$ROv24KQn>HDOTDb49Ee#s zP|$i)^4CV0?^zSXB$t~ebbU%TqUqp8z=jfq9E%Twsz;ZRn(RyjdH5k}fEWAJ^DKCN z7j@e0ehGSjf6&NT7lFf1I%x1gB?ASm>Jct0EREjOL0)FFPHh2 zHyB9+Y_Ch%To|FDX92TM#V_sTY?=1cCKp;tI&4+5A-oi>AZATPXl1J(tVl-AYyIDI z)-c-F@IrR(^1*O6A&#=mLzN+|h7O007Fae5lC9H%H#&I&o3t_r6H2xCo1@%b>FaD| zOmB_{UTJ*I)(K*+#gMf_lV2|qw#v?EuIHs@`sCpPu!rcAq(k)NfIABUzh%3SvnS_o z!Dln**cJHM%vE){nousXbW8DM!Va|I_V_n*oA)C*aEj!9{n{-3?alc~x%0RfkU>R0 zW-V=*Jgv8CFF8-IPJfl|qVUC_!~MtB)1DjqZu@JfLv-LujdmS+^AgTC*;d8K=$qT2 z!)V*VhQ|hfB-;P#R|VeWhjf|Edxti<*lhVKZ_Ox5;=7A21BlOie@r9PA9o8SnTQ9q ztoShTsPBHljYl?NufB8hIBVD>F1*h0fH^epc~egL(RQ zi#UQm$A1%($Z7w;oy*N0_SODv=r>x9*{>ARiNW;ot%g`Mmp=AsAi5cUpjK+m_OD$W zf_V|Q^Owp0J!o;z7CZJ6q>cpm?odlC2u(1sPxrJ-5dIhZ++LpH{0-!)>JukXxrwgz zzNp2dO^qBgoVAN!5p=8RLG_Es`HfmQ5}e#JbV0Y^W&ztEV*67=9gQ+Z`P+@S!})rn z7d=Kvij>5Xrlcc#K{k!F({auG%L}Wy-(9Yjdv3RNz}>L^S$nOQyA=FwaazaPXcjk? z_oY|SXexrYk88b~_}I6d?S!UbM=@Y&PDC`N6JbJMr#gR9c{Fhd#6A$E;Z`A5+x~Fu z5}~`OzOT-DGi4*2%GcIUv6ISbZ#JK=&vo-2)Jc=GiPp3?;#b465PJF(J*8#{0Xf-X zJ)P=(Ik&Z+G2fTJb@%LuF8v}+nXgOlTf=o6_&?SXm;`++z{(la^!>exFta#uh3+ft zoc`~6(fR4J5~oM_jPB2rzil8bX=uQPj!jlkQ8cjoaQgjaz<0<2{RS4-RfcalIXTrX zMTsUsVNG@mUEC%66PYuwO=r3lCbh@5mfClj5y!R0t;UlBCV|eD7g5BxW4UuN4~MY% z*ig;!XiAl^tU(ajTWeNp3WNdT@_{ygS^YboK6bz4~^SgJJ^KZ z{DpERyk}A%5A`9)A$C1t4H?eM(o1EQ$E@XCZkqxz|2c z`S2fjosi4dL3FTc!RlCypvhXVg8#zkPKZHqo=>eh*nrenEV73`ux7vy+kS9jj_Ld_ zeobFrCy)9+Exxt^D#Zbor(zE!BhMW-~ECRr0jEz|FlLNu7AfOe<8R? zrE=!kKdA7tji7EL^P5DbmP_e~D#Jb84B;>FP)AwHIVHY_x1~NJG9U5=Z5jco*TDQJ zk+|z67exq}zN&xXU)UQ>iF)PI(^WAqBB<*vT`sCmn_o+fX4_LvvgFeg1t)54^qi2g zyJj3LYUGq{9n6 zms}~_uqLK1$6k4k?I@JW6QoqS-x6`qo)py@Vle+r-n^BEoQ%Y-kP*eA8^RS}M00H; zlCmy4-j@60d4&$K^vYhR_cND|?27#6lF~3NJ|?ww{4elXBX^c$@psWBgw=~k=rfEh z&6HE~qX|W83yU6O10}r1f$6AtWe2w7p$~C8PkT~%=jUX3XMziuHfE|+~TloUHciTj@6BtWdSR zjZ;-;vd=6Q$HchvOTMQca`JQ~s_(r(>DE4}R)DFqgc1I!usPWJF*tc`Ff6A+-OI^* zdD4sBo1JM}k&?VCM;=)d7c}@8)8@#BD<`E*5B?YN+J9lyKE5D(>F3Wwqf@j_L4ryZ zt*b|OYL+c2S62mrOtuGxInhy2J-!i)Bv0Y1+>D*KpvXS66 zzjdWK9l`yy$v*zKxP6-6#w~LhE01N~u9QBHtqL>>%8Wj#2wc8MDC;LrsPMb06Mmf* zx(6 zgG^D9#DR!u_J=&{u_||OzUSgemNT0D{m9N-n(A{`^jeva*55dFeinHWrTcPz`Y*Ds zY&w(BJML@;ugR!A{P?%AZS(no*}s@+8nVa7gsLi`B%dCiu@0Tr1vK=RW{7^f@o8H@ zcml@QIbN9RB^4V_C^yEp5!sp}Z+_Ft&}vf&2eTBP@F&EsVhk-MBv3FrGF`UpcUO)C zar!ci)wns1%>taomP)s;c1}llSo8K|*uPrQ>d*5Y)wmQ%-@U>n-2oT+>q&f9psmm~ z#HYUC2JB?q#byF2#_@^r3MGcmQ?2e*GFiW%)TdPixz{ zdgwKMh`qVY?8?iMT<405x+bw$#KPcn3HZ!h1F^ENpCHJnff$T_bQHN$Xhi;Ozv2$R zQDF^Jzm?&qEe<)7Xo0rgv)caZYkLMb*UOO176aTpnk(O@f=BjKv9ChID2}Nqw!FcD zv09XD`<6s6JDMo@yee8Gfcdb!FE}wB)9X(QAHb@+;(Ed#XfzvwY~ogivfe^^jqp^3 z@N*^Nwccy5&N(6seb)(fjb%83+?6v;FW1`Lo~gFfD@;nnMt_b6kRA}j3X6zb{pWEI z9}KL_YJh11*&;~qseR=9x>SIn?3T1NFx9V32!CLKMStGp@!@04%*qN`T3TA9ecsIX z_=<{g+H2KqCBAo$_aY4fLZqF?B%c4tndmqMTEJC+Ovx{LB(b?Sd{pv3*_*J7D}UO> zsEpY#$loT>8Y5cH{ncqL_R!$_CveT{E8X<=*Y^bdM@2S9%u4qxPHViIGe{@AEF~(` zUhD)MZ~D$~s+=6aU6~phOe(zd7e@TN?le42$;mmTcqIXe&Tr0AL$jc|0B|p zKulU|D-gb<%W-3Jn%g%Z{pq1<%lN<0 zrI48>?Bp`*`WC_j{a@nk!}wnb^uMq=PlYhqW}ockdPOCYtQrJ5>6Sb6T?MTvL9_2e zHW5+LA+L0N&yn@Ieh`>pF8}~PJUG}4x-|NM#vRDNf>14Pdd){D{pqeJm7DycTM3$i z{?^D(Z@T0rUq(x@pwg2t?d}PFkE0iAwiYbwr=WR{JdQ2tRfkE@YgF+G-l|r)DfIv1 zJwvp-Cfi^OeCQKBuVNwUWepVrbwl>p>GQ!a<$uZ%>FN8d6+F1g%~6^kpZAl|vrpyv zVZo28ORL$N&Y8jI{wA^mg_)`Eb^8~FE}xO?=i=Tioe6?md#j9L=DT$|U(lmH@gXWL#SNz)i1}Y7ClI)B zQ5WSu`R#>2^BjR&<(3O{b(g$f*nR=IHUWOR;QfaWDVh~_8`|(c!LICob2=MN9|GiN zV5jl{D}pK?BM&_}-^BilOz%Hhms6Q^3M)1a^3Uy;1^lqvYh*1b@8h%A$t!vzQ!kFS z10FK;vXfItWz5ct)Jh=F#833O%J2U+04PCq8wF=|1n) zdG`-3s}JWrXuW!bId73*r~C#DzWtJ7)?34+8uzr^zWcaLg1V+GXhrI475%Lnv{mPF z4UHP<28r*Df2B`7l6?2{dO^3t^6~tRMv@J(v2)cC`}E}hGIp+0d}aiH4>i#Rd#vg- zBuC`gKQ^V;w;&4M+szWxIiK-Ou(+?r9CU~W-pupIqWyqXg%uj8*kb$P!ILuIngLAY zbcrqu2^h|+dW8c&`{7YHrcnkp-}qPNCbO;g5nD!GN}|i<{e_7EJAo}#Lz^3N6aEJa zM52r4Olit=j*o1O(kCx{eLCRTH6D25n^5rW<;Pg{uENPksX;mc*dd9pzbq(1W78OP zIr&kYMC}QAqGBHcHfRTLRR%O+=wx9gTD!(CdyH2&H26D6Kn3TN-1flb$4Ut7Ea+;h z(fuqJ8Ue)iGr>AHj-lgS6~3jf5T|OGs_j2TwVGLi#(BZX1lUWpyG0F+WEqo?0k;of zcS3TF7v0_8**5!Q>C)d3%03Fv;l?-6*kM2tUWHI{s++(k*Sz_dq5)W|)i3BX`hb6Ct1fO? zj(ELuk|GB|N$(SK$~&t}9lv_2jJQ8#S={rxw_BwEZRu zvHb0CDuU%meT4d0s#E)4y_!U!X|&Cz{8`4Vy3K2X$(fm7)w9;yT`-}M zwWR4am*2Seh+|AM=+HziV}W zVf{z@Y-A3|W-0k~ZmGzzujWHB!d&PDGJNp+U}R)tGXUBKVJ6-;;9Jd40#OZ6(=NY+ zpQqXFVUFRkN585;t@-4tF3y^70J?{L8$|MHVd7!S!N&JjDX%e)UM3aw7P%5QaH;C9 zy@|Hne=y-gIHyp2^3X9hk+JF6Ur0D?lxjq@{UrIQ@hs73Qc6KWMQ`EskEXf&B{4Yv zw=zAqX{{GGaU1QoX3#rUAt{p2o`o2%En4pOWt-^VqM~ezFZ7^GgN6%NrAKtDZP1IM=> z^-bgt@yvHaFG!MPVAvyq)-;2sSatGaS|Y8)YsOOuo>3=e3%Y_Wn>$hgr##^M;qvng z3)2SSLgK;Vvjo>fcM>YE;ZR%i3;X$J1hAXf$_tR@$#irXfF=F0=fn3JHti(=)cHhV zc;g0<91N=n>`x1=Z>jKZ;s+)j^X_QSp`&+wDBBOY7=GT!p60i&e>wDuU7`7`ocKG~ zcYZb$*^4X9x~p;P^z{9*7$W`xX4_FF$=@8t;J_ebzMnpDa0^Ky1LKk z;~3t~9JJuvd+M-P-kpgAx&p~*xXcg1ONopr;+o#6}JD(u9F8?hpAQU5@%=C=PfmkO1yq+#w}IO=4cB?-phsn z`GOkNWrn(AnO~_e=wH}8MT>zAp;cjx+@=Ts5jOA5J7eS0O~%RedN{FGzxZ*>i4ek< ztiKTfx^&T}8n(*g1Nw(E024K`F#7>i3QQ5e;bGit?LH@VQ;e zml4#AeCpZvpWbJ9J8r4ri=qq*zx}k~ApctO2N?bOyE-!3sky!VrRCW>PF@ivl?&P9 zDsnN2Lhqt*>XlbFB?8q0Y#e0+R-t&b;o-59d0Ru|{w3y=)YRv4Hod2~2kn!lN%B{l zj=7&ba$A7{(=`|32T`+Sp}bX>12*^rn1t>Pp=Q>R*|&Lr1F=`{1$nA~$hUdWMNPr8 zzPXu|oBI|x42DNX?*d!N=;$V`+PD{xgp-hvWNhbd!MJ;lekE88d`y%iTKw-fY%tgk zO;{kTr&dcRd|@8`&NY0L=5T(78U7Jgd&o67Lbr6AX+%B5pY3a@NO>>U_#OL5(6436 zk6>_y)Y+#nHmtyIp;Tvdlf)X_OgNQk&AC}HwBJ~vDVTu2jWBXu>aiu%4;xQut2JWrPV`u(T7Hm{VL48}7XIhK*- z%9ZC-J;PRqmDJDF^L29_QMt@|8EIpE2=LDNjSsk}a}0HtmYap88*KX=j!&6nRXl@r z7MZKlVy&oQ2F%toga!>HprDT-TPn(Bez&N=b?E_fYTK2w?y8%6ai`7M&fpmd&_2FO z-t`$x=ben5=ssLS@eIJ%2CM?b11rAFdtQQwPk+rA#7~aGqnq1vjj8=}9gST6+c%XD zKE@3W54V{i&P=>_raE>5PZdCyWV^SMHO44Ck_<5oSkEN1o}<$JJb1oNJQX!iL)C;K z>-x96S+E|p5g$Zqiy?dE5$s`m1dv-ahIHCz_b-9n6< zlrIi*#d@CSn@~c)(bVFa`?C-(CGkl=%N_9>@oDvt?7}5`r=I2UTe8U#Y6s$6A%+tB zXNdy{1gd1|qWZM*ces9WaT#|wUy@ar{LNKGm_k-eFe%#d2njAC_BiD;p-e-(la7&` zc%tczKQ$jKH6v}f-)gcWawsmaeGQ89(@^k+oL4Jx7EOrG_T7_A#~$QIiJGv3WUJSv zdT&=TFJVmNMi}mtHc9sWth`^OC=-YdAtse0qS%-ld-bv;X1yU%@wNRLrI8O=D^36! z_(@zmaQiIz4>nu@XQci9jelgr8Rdcc9ZT^Ovv%(Z_tuhgrs}&5#gja?|7zlvS=78P zo1ZF7$#_LYk(V~4E#!)ROd*YlDa`N7#5U|Hd^t46mP`{6)Legi@T4V0&tvPL>F_l* z8BCR2Kh3!82!z6a=9#8S`>pO}2t$dFh-@}*E|%+QY3t*{8H-CkQ7uiN>FhClz9?2Y z?}BF=UOZ*=7dr%Kh#OkJsiFM~4LXG>X|((kIS=s1E&PIFZvvL>FYsA9EO*8PY+1}} z4-Vgo!Q0Jr)8szlTkgmde7Ynu*)jEeTK9t&{1b;Dn>9M=B>u~fGo8z3g*%ojr`?}u z(23TB@CJgWSNT_ABI@2Nhzj+C$kDyuzZRK_Mz1ewTn$HHrr+fL8OS?oDnj#HG za`KQzuhE)aKMpIPxJw=JY%ElzHrx!n^H`}Eo2#Unk%Lvy?zoTSA6dq}x=(#`SsIku zK6=1OpKDL-U`eQE%YHjV5ym;BU<`914-)ySXS5zU^+;{JMPS>lBUip^rd$6d?KT7=ccd9#rJ-e|7(0#cR6F={gej=M5d&+lU` zGPsw3d&xrF$3pyRYiYa(FtjXK%jY=LH~%q7sPv`13*0k8GCu5zkIA70(md zbNx%UUw|LYW3~4RB_-wHt-kW567NsJ`GpY=;3em2X!(vW+Vf322@4OjYBs?IGHyPz zeG(2WY-_J0+LKOW7m)GwfWovv8G~hmdhIS>mQj;CDZU?P4Xs@{|$L>FC z!z|FRyK&O7`4&3qxYhK&3nx*7Ta`hMF@+@1BA0fY{FHb|6KrG`I&Osg8ZU76u%imt zX^LL+u_FSUM5QbhrP|)w=?HNsl|W0?O;1i?62X+k!7Hk+w|m)HYDWL?pu%yC#c9IG z<`s!L5!u7tytTxW-MqxLQ#x41^8d1zX;}1MMnIZ|v9JaKZ7*3}FUe6b~|YEtmWb&Hu@`I?*$0d2-RsB3VXB~49P#7lLWmL{e>cUVxhJ~;`o z?DgI?C`jJH`VwK91CLJUI^=vI*Y9L!{fc9lZHOEk9cMKEPCw+|chu^hc3W@2oh$A} z`M8G0RFjI627Qu3Tr(OK(Jk^zT&kpkQABh|ELqD?JL1Up)-C*g_BpC2$&IrWejYy9#GL=zUz<|bUZmGigo zHg6`;!h-M$E`}onu^l{0wRp#@(@an^9Qx^S*FoHD3c=E4kB!{7X!wp+Yh3g*5TnwWHen+25v z**Nq0V_(zrIojzZqRq!r2P(_>vqvqhT9G>UzA(4q z8f+@gYi-x=Pr$S#E0u?Et3>fI*rP$FxBEZhe=`r2~})O6PA{wS{Nxa7{=)gEy?$1Q?$ ztGfv3!_T?g&L)40KP@|cWk6}z15w^@X9`$Tn12igxB`Xk`#I^1xAegNdRff!w@=d{ zhiS>96{mUxmdxk?D?W$#JUB_j@+fsMTj_IRqOpnRaA->YtqsOfB2 z>IJhD2&ZG4!x)@s&L502M|N`QrX1LmX=l9FkSM3cV)efj9bKMR(GvR$<=EKX&dJUW z1-g#M`@0=Yx2XRX7^{yXLYtiPU;lBP6V!Y_By!{K=Y&iXGmt2$kBnuFj`4~V0e7>E zY__{U-J5aRzmt_(gpgnR2PyY}hACv+I$xfhy-DuqMrvfSALr}3Pg8#!1_aPer*|Gc z{3Fg?b}aSbNsT)z$Rc7`->CxcaHgS9{uC?^1sDg70m?^Hj~D+UrKzx^>!8@X}V(cb-?;vrxE>KrrR8Wz6}HpHQ%^KnVec#pHea zCUj%&PJU)C(LFO&uPemTj<{I{^BN|n9{xkYhp^AWrMBEZ#1BtXXB$56ziCUySsu^- z|M2ghu(J45XaE*W-2hEVKod>By5OtS`!6w$bueLm@GbDLKrcn1e;gUNA7C5Vcgxx{ zKIKWe?S7T^r9Cq{8Suj7c z|Jcr z8@98iMq=^C_aO1h{HoHCxutFc`vS0A{{A^}%_{L*A&F(YzQ;ev6P-N;R_6m1!8E8i zC^NI}l~Es(J3CV>|;A2hK=NWk?kDsc3-c4`8QP>O zHf65DAB+nU{(3>eQ|leLAf<4x;iKEHS=L(d3C<3-fQ3Cc;tSD z5PH+A)+sj1zV;BlwS$0SXJZ}lXdJBERp38`&-MxIw=Y_k6k0thhZp$W#M1|4U+{jN+h{6XLHgXHhsGf-o(;+k|F#NNPwzLfF z++TgXOhYC;X14a1!%)o}vE0m{;LOa{{b#qiPIqOVXGq`kUkh5fXZ^~D>CKMGQHceG zm5MfLQ8mFQygBR@hVHI&8VC

%YV8^4QxYAHl<#1p5EnG+NGVcC@sIF8Ssxy3CZ78>fU zeX!?O78Q2yr7GG_z;3Paw?#)g|DP5>;V!v!cz4C~O&c>}N*Bachgh97Rp>ZBYmh-B zXlzC5qWn!B@oT)07GmgSYE#t2R;2TIiZux;Jk&-Zo42^cIQH3Ff0e8tK^?$F(~NnqJSe;@fN=F7xGq#x3HWlU^l% zxZaeZH#+SxX))+kXRJ7as^)s|$1k23ASqaze%xt(9F|>B5ShU#O%`<9deyStwnw$A zF5RO|ttIM;=lOJ8VGVhI)wf!KaSH5Cw&V$)FcWfkbl?X%zX%>RuT0kb;ZP@1(J$n; zh5M83S)y5`_RbTF=!d`1RR0f6UmezT+x|^TDP2l;gCdPEN>Wk~kyKEmk(Am9DG`uv zkP-n&=^EW3(mA?eG#iZV{c=CQ_Yc5%U~p}_&iK@jyx`5Jzb6K-2vt1AgBs6>fMWXT z6fe%$GANn$N)rE|E)#|Qu3NqHYBA~+9i&1w;S|m}y>79E)xUKK=LE}OvI;<`Ek>-+ zxXPQ0kf%DeW1#6G>rc|2`#Fzp1 z;G8x>VCm`iEzcD`JlJ}jU&?7+GV(eLS*kTv8Z$GxU+R6F*n|`jq5PfBLT#X*4!t|?5~@XUp4w7m z%91@Pm#+&}aGm^k_t?BXc}LbonRCseo|FmiC&+2gu+w`vn%9$gBAy7Tdo?tyA(F8G zg^gi?R<0gdH$AhsxZ678pYdF5iio^&AK+Xk!=GAk9sZsbp)rsj?tojk-{K4mHVlj)P@dRx-9ue^7p<%^XSp`;=fx| zZe4?iSK%&555!@-2GGNxZ7v@3qMFK`Q8PdS&PHW=Aqge-74BNaPDXC}6{@GlUIJY> z#73=>Ef+OD8}V!*V%&pveje~yVUPRV7c?LcnD-7RfrlzplEvvCCn25GjG`i2heHL= zV;Af=V~gI|Kc+qN4o@XS6yOCxL|uep(d~IXUz}TrD2Q!6&@XEtk^@PX@T)TjD>9)c z@d4_RjmAI|d}7RMAX*~ddTPsDl@b~X;p8L}j_b;=&R^Flf`wy!eT86 zCi;kR-ngN+wzj^oVa<)^@W1LmL=}Cd`L_)R7L33Z2t*TiY|eNBJk&`lOum|eozD!yNC^Iw>SBPXclN@ z3}YzKdRz>4AzoY0`oX%)0HLMZu zgt~QF*H`|#$SpnhJ2~sw1hM3Z!}Hkga4f000h+s!T6qL6hS#nOG^_yaSw|V`g?YaP zdX^kZAmZYWQ9P4-HOCiLfE7Gd$Q?T2DR-$n`pj7bzSUI%-+=Ma79q0yeNS@sQW>Bb z77{08CE+ds^h(%~^I7RIp=8I6`ebj}8`&B6twGi`5$LFQ0VLJzz^4nQ^lHY7LB+!Id1exQ`0R^Lz zVHOLp^pm;K?F6AIZQ%H#KRV)x^sG(lWEYT2C|RWISI}6_waluQ;2qx>V}m|~Y5=^b z)R<46*srj?JUveUB~R0yYQ8>@TQLC0l16it!y5p%BR<{;=;huP4~-eW#;Z6B{$2rS zYY^wnfsN!#q+;d8kMzcdg1>@AAH?ptpa#}7X@*?ymSf62Is9n(y#4-Z)$o>6J2`gsfC=ex%}&7QL^AelGRmxH<`MdxYn7USP*m zLfKG_eY~WU2rdfEqQ@S7r9)yr!zXiW%^y$Dbf0q8y}!=bq?WlYBl=sQ00&-+Oa<6K&J zP4v(Gv9+eUDJ1>~C^b;~P;_Z2i-^SHqa3@5Y^<9T!+2rn%lxXn%kT;9u&+e5kH-L#*If5o#Z53C+#b?Yh^Wx}1ml zcDlsm7|&Dc8!A~HnFS9ebJ*K=6bgsSVG0YsIJYIORMYI0g>@42qvU&jP|a2Ni2Zo@UhTj3U;G0X zBrDg*6P zz}fu)>~a9M$V>32W^AsgE+NHfYv&g_UM;7xJ!!+b{1^*Qf+$dl78oOe^2%!ePN~fq z!~G&0c*nlDLjc30pk%Rc6-*h%o#1)UE9TQ-yYK%_B3lC%uIJ?4A&#cqvUt3LoDH9e zItqu#lZ?7L;he>%B71v(eftDH_@NVe);Jrd(M#`6hLCXDC}W^_HI7ygi3^HQtMB|# zAyl+uGm}%Rc20TIvn}bsMZ!a*8O8W59*I(FpByLui)TC*bW@?{BKLX$%Kf97dYJU> zS!$yFL+haS$17JZiU5R!ctnU_9Rd>~y?c}QxCrgvZQr3FCoVcfXwBSArQ48)W^5p-Ad{&HShO^ai*rr0{rKp~CSVi-7!Hq2 zS6;$jO8y%RTTyxXtvJS;*+vG+w%V>SFgd=w0QpazVNcm!T}_N0+drPPG_*a2!H9-_ z_IWDLD` z7C80$jTfq1Q5-ZirqZA@T_&N=J8u*ncoyB$IO!WcqixK9t;H3Ji~yQQnO{W_a3cZC z@!y4nS_vMrtepqA!I~|*X%gYfqd|B~6fa*v28aAyP$|m#5K^}{=f(eM zCab!=I|OJ$Bppu45CD#0Q>Km9YqgyUC_aC(*kb6mdtXuG?bfmPC85RKy_wMj>QJMH z+T_guhvYWo(G_Opj+wQy6DOFKynw7hy6ARd@)rMD+A z@#B2YNrrruhiDS4e7_z=26TV@<-)VAQ7d5=9+KILUfx%&f8R)IXC!ACloZ`;Enruz z1G_!)k4Z^*$$}gWt`vDSVisU2gcJB+K?m)|k6}hGovzb?_v={hSjW4vy}GE&b!XIc z2IM7a=9i<0bgSd=vC^3}sGG>QVrDQp>_f*@PG|tns$7@_y^!%jx^hB) z59+4%_KtE(*OjALod(q<2(;r&Q|FToxE2F!Dm6<|G?zcGKL8d)Qv{t+oDM%M_&Y{o zjlm8`8zRj@mslTO1c!{67|~@`;Qm&hrx-RO5N6TK5AKJyelxlk#ljb7%P?N=s0gdY z%`Ya>o?)YQ6HM5tl$6-FfxI$W?KIR^-1#>|IBbdaya$QeMpB(;a*pN50X66f7t^x= z8K>^>mM0c*44SDcojX-C6_iJ(!gHhO2jPZ zc&00Sqt{r8Px~wD<==qFa%^yxat!(I{YU*PMl8J>@dEc5VP)b}Gp86BSXk%)YHq+6cLOk-{h7xupO#!ZJYVb3 zcrk7216V8Geca&fUrHOjPtPekSvYJ&ewt4ahVW+?X!d=Psqz`$B?CwxF9X~mkFi9Y zj4Uhh*#Lgz<3`~Ro(jmU!N#h_C)~2hfK2LC%Ums6N^xUJXh*iL)H9W{>c-1G4Bd_O zf}@)UO>&RuTaJR&Dm%_o;UL%9DQG~B=@J<}v=}~v%D4u)h5xuo&ZvRZ_`@+8bWlI! z`IAICu2UDe0`8%kb>9XX#>EzuY8(D9!68GCO~&t($z$IWsqf#1COp{pdQU`6qfVL? zRskyKCSWvO&q0~qBCBrwBH8}8!#R4{GCNEQ28Qgtaw3Ac8apbWgWtTv0q+b^%r895 zw@-&ec*qHl=N$m$I6fFmS~`*2z}c(>FGD!w>eZN zgXzDhrl)+P<$K@BkCm(Uu|auI>)zK!VuiQ4LpWDHyD5y%@G-z90aUU~DG1=Qh#FrZ z$d7_nhUGTjg{xA|)AJB75SGAOE~nkOp<;&BK!A#Gck zD*5BH{8&B%Q3usXPVwByI>?b)pl3K~8Z1qVSc3@>UBk zT|LJV2hJ4SX8|?uSJVIB(Q9yYdb4g+2Z;-I*DArH{i~IS4$H=ZuGb` zTmEG4USRug9Bt0+6oUt)8Hu$Xg6c`F*2VFAsrxO1r%71Smy7{GaF>bL%kf2-tzvnj~iWc94EPA`WkBl%vv$EibRL3j8$gZp+{ZZbsaR9sr*D z(j^DRY8x_7UwfWyUQzY@cp_yIg+0;NEpkwdct8|;YjS*`>iPS(`U5paK`@W0Gm1)0 zv+ahQ`U|B~mSb29vy|_>7ztu_{eiNfH3?ZiJWE6)XP&YYFCQV1yuY|$CS!fTapFd( zkZUf<8yUP@EYz*J9{tz8tL}{Ws~ExyBYwQT3@fCExX07~ddj5~*qv5D1m%2xZaC*L z(U02-rr~REpq?%QjM--ZP+=BuF*Ten9tC(`zcew?5PMWuRTby7P&fYfuL2-Yumr53 z|4yEl1~~GZS*I9AYK3>8ar2<>6XslH<>^sV@8c`LSlls*iL~QI+n;|SS`q!*AQI#Q z+;tV)aiGKUK2q@V2rMSBcZ7bAv_?IDmkB7=1yyuXBjnv_@!hxBm2wYObSW%Hw;_bc zrS3aq1!{~oM7w7}ew~s<_Cc|hnv3g?_oSad2k#} zUhHkq+STbylR5R#5GE~SB|9meBieQ2+rd*%p!2U>+$D;+8>Nf_+4x@*fAK^dcSj`S z*OU~+_~SaHx;FIx(!?1iE4)>A8`JbWgFHbYFJX}XySpnO1MB^-&_C?DeOZmjB9TZQ z5RwRRdf5OdF*FPHF17#xk?1{OF7mqN^ApRP`I!dv^}e~lr1OkM-?CQxZ_3tQmnq#8 zKnYv_`rs28Ei}yL=*#MM{XC;4(C81E&r3;4`|sk@V-@ifpQC1b887V>z6{l77-jn7a)UF0vId_8z@DjM>9?{fY0z z7&=q0tQNa6$)!wO^~ZVg|gN1D*{5#)3g958oG86 zMDH(+z9f2y0^svT*6o*Y!Y$c(`4SCjm5unQ&xQ4@uYFhe-D~k}(Q*$Smlzo6i=vR; zUqwt8AfmMYDO@ZNFE#2;Nz#OiLuzVj01E;OKFrxU=Pc z7CoiJQ8*8Va-&1Q^TQMmI`JTe5Z&-(xcKaNj-ff6s~50sXlice1kV&)0T~%L_nwq(9HnB&cj>te7i@vdi`gub>B59 zi9hc?tlF0ixICR1DbHYOjxqgav8Hu7x3>*MH0?BHmk6Kz6Bz$U&BvAga}&~FTM0zh zezXgkrtNNY|G1q#KANSRDI31?F&usQ!2Z#@gV*P2e*L;qX1C4&)EY>Yursb4D%5(tf*MhSPNdr=YCO-pG@dBCMao%kv znqN~RR_eFpu36A6<%1J2#=J47dQnkJ!%kr%Hh=X2$^uGO@sSSF`at=*fxfIEqj0q`^cI|M8z(0wKrjD?MjX+WG*=9_fBg~C>n>_tLtIy|F7 zSGZ<_hmLga+XjrYogEN~l+fAPSulk4p~>tO?!?|I9fTJ3UWCz3IJczfOWtq*eVX_? zVcb-VP&^$^+@0!*+7Fj?q0J$F2_qv9X82YlWZ^_Qtrz6s=)22GFF3H3OZl^4@3AJJ z`-MCarALWUvR!Yz$#a-t&zBq3WH#r1aZErc6e!^<7W7<;&`gv{Q?msM=2T%>x|0#> z9CnHTs(h$7ct69p(ZIIxJA}=u=YGVF>x~-#(2hR+i=sO-wUOd{i0RivbRGiC4XZs~ z#ZRO$<%~y{phYK}24uLYXcFXCIfrrphT(;`DE2gooBo^3jT+W~1N*OZNpnUU!Hn=JIv??(D6p)71=cBL#pRq(P^)DroH6 zS$%fMXB?lS%v0;epp?3_u#p(-8?XZ;i2TtzKrc!{OPMN5283CUz-g}n?@F%;fNlL> z$I(1@{-2626sF-84K2iNyHe}eJ&W)BZq;EkFrtO`r*HcV-UCbEO{AOK&{NdAsiqH_&iV>v=u)2m22v7Vl4{bkAt zFi{U`6trYfB_fQ{S!AQ8qe>M`YL71j$j0oxeb$!uA)-I;p4w><8PKU;G(g$9lmJAM zrAyBs*C*S=5O0;o4vZ?EitY?8f7D!ezp32*Do-|>tXW)#&R)WMQx9GX-~DjA$EHIY zI1oqDK$})_1I?zUruZo}&2cf{!oulecwUrE(p+FVS{B~3TR{hwtdhiO=G01M0cI7T z_4;)t(%c*Y;O+tYv;}XimXA5q7bP&kzSk`G6<7m*{ZhC?@V^0{OAZJ5d4&$UG9&MP z(m~+)7=zQHZPp0x#%lp+2Dtn<_V;(xp-?bMR^=6*qde9bPcL+(FtMf1ywbVI26?~Z zUvYeiK@hiF2xzYA4%o}#q$mFp67Q<_*!p+DFIJ0kS^k}a@yDDWH)x>=7Ui{=jxBHN zQGE1kg21W3rP_Y`7&vzS3v`%0Ai4;=nC%k6N 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 literal 0 HcmV?d00001 diff --git a/chapter_00_intro/static/growth_of_smaller_programming_languages.png b/chapter_00_intro/static/growth_of_smaller_programming_languages.png new file mode 100644 index 0000000000000000000000000000000000000000..51123f2757b6353f410636398c0c93bea7b7b982 GIT binary patch literal 175532 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

pca%1vW3?7P@i_St)#6|M& z78 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

Gx zFB$qg4SH6elkqrZu0BR#+ihn|j7qDftlPVfelJN+Fo!9t@{M-xcKuB~`{w9#?|)P~ z3ZDFMm}{&GDZYQm@awg_(d<)W&-r%Ma=&#}x#1fateCQ}TUvW{fM%rlZ=SD8>_Mxv z6z)@eb>xBT_e6$kX?&aLx7g`fmm{nDONpsEtcQ^n)IPa#hGKSHvf4l(4tnF7V+Z%| zM`{q{XAJ--!NtKormCdmC0YcebEf+}D>wt&gl#OwlyG)2(60o{jC4U=gL zrZ&C_ElDjK!V)+;i{WDG?(XpVwL^Nyc)`vXV1v24oG(2HwEvrm)e+%IF3-a&tz6>-G7qPy6U zFS-eI=cAC20h|K`1&(-EN-3$S%_VdS?${B~$Q~dih1=#lY*(`S<{(cnyBQs#vy)R; zLjzlB{fASauI6A$P214*WdKD)+q=0ro+ev7k-)R>yf4&j zK%Hnk^2Pq8ckhW6e|w1QX6J5tNWnoz{4Svl1c3vB0U63@&ndS{J>uZZCLVl^FVElz zeA8--uOvGoV|4os4cn>DX=%KNwl)F0hX9;`7P`229jY{xb?1;sfdU#lH70ElorAEP z0sAk*#8%Xq*l!|C6MXpSm=_ioZ)3V01|zsft!65G%0YcXBw9ZhlKEF1gS-b-?lNFS z^k}H^%1(Vjpcm%4{5e*u4UVR{iUa-Mj&fCQtlv`ooqYx{UV11*fJ53?v@ry;C+HdjiX*L0&@qS(; z1l?AW%@Id!TRpqNS8|dvBRYS{*fdQdjT;?@x7&5#wMAk!#E4 z4b;?Dy5p3$J^!?y@9NsVjefo}o0=9CToYnY8o1a`>QSnPbH(H!X7(va=dE8Ef6d_uYbP{sop&?I3R?-hh?L&Mo zS~BdA&=CZZev92>RlXC-YhdHZ^TbM>z|Lds?n>*2I0$5@wNL&=o&+;=rBf;(D^a|M z91}#EIu2PUEbR@tiLCFtjq`H;Hq4&&m{TUsxj;<+uU~<4Twi!Jo!#D5_w70+(gdO9T<2{ab2S9I-ZDD?6DB~e@*@?KW(cy`1jRgJ$0nn ztxRMq{WEFjQGFK=6hfn*X)(Qjp9gI+EO~{2qU2(5+gk6A`*aiu&UbpClm}An_8oN! z&I3UD&I&V%%(%-?==kV3kyiox#R_sZ(7mGTRI_JfVXvQGURG6B25pWzfx1lV!i6vV(^%OM+!I&p z-Z&zJS#o4&|K1(_e7mWGJeIv0#)j8B>KYn~3kwktEUBoNB{~-Y^$KPZ_~8^rq7IY> zq!I}t*nI$t{K%75=QF=i`(ff;vZUkmqhHYq4T*x%5ij?l~s!Q)| z%krT!8Vpxg&U0Jp?s;|XMCrnpNA_p%NPjN4@G0`clH!$7_JM#N#;F6lSo&36-W>Ci z+haO1_T-o|6}OsfNWYW*?9kqVR}oAubj8mXE!JKYzerC^nJ%>bz0B1wTmI}siO%r( z4-`dymMtE7>c?q>G>?rHZ3?+PPI&oa?hNaCd=@tj^q$yJ^4F z+LxW-m8Es%p|^ASNtgS>qtwFOV#`*ai(cZ#b~TI7Hpj8gZholB*)S+ckF}ksn9u8C z->|mT>Q?^V$*#CE@>fxF*h==bDIY?HF8B_tClfNiym6oJj@CV5XFk5Q9=9bOL)$wDE4mk7 zQBcbSDF0@~Ado4|xKLz2_%n%#xj>MJZyS)xw!`f!W&egRbiYq5DvGjdzS=h61kZeF zsq5Xl34`(P+!T>vk7B-{Kv-OZhM;@*t^gwaw?29iJb<8oCwtE?08=R1!ho^Y*tsBU z$InY_Z!C3P+t_C2J}Qr(1-KPf#QiseWNH`|DSyv-Fm|(SrCaHRReNsBZ(S+dg&sUC zWH699K!2F}U_jjPcHLwI7?+bLA;mY!fx8e@;`rp`)#Zsb_B*@<$iV>|il>SmbIQwO zWB8;X5n*CtVrA6_l!XVba4x3#m)gF+;gy)B6MX$`?0H*EJ-GA!u5VG%g)$kDt8tPy z!YC-HrCkI_Fo2=#>)!}Uk|lOhJQvVXl`VbdO8j)CVuv>4V$6H?_|!MmSLCR z*n_!`SD0+|kmOC!cDK+mx!)`2BN;f0+&z_d<@AS{J@r=5$3rd#8R2?Cd)saenLqRl zcP~n7B(cvepFAnC-!2kBx&QfE+wJDDTqz=8BXUUdh_CtYHa+*|8CmpG%V+8&$GW_AYga625|zx0{8 zL;ce1y}g%|wa%TtAHeA9$aeTSUsCw4dl5pHji|f$Z>Q5m_jQ|5kGFEXj4zsQq3|2` z?npj0nH2fo6>e<*6WsCaH=h$}{ph?g%h!3&#SeCzjovp&^sEbakJ)f2PwyLisI+ee^&oAp8O&E(sn;Gv2#*?}gY`cmR$cCzFKq z^P|T-IMA6$0VcbJyoaG{=%$@+d#6H+#N40r3)`nGF7jcwV$61xcqN3ZSTkmOo0y=hjdit_U4@u`sYXMT1 z-Amd&19c5EDNeQZ0;=^bk`L-rWFKywc+NZ;Js`YtZa_x_VxTCgZOFd&;bYe&pDIMhs*9X&-_fj?D>qxyo91FJ-YYFPFBXUG!lZ31n=4X zPo!^<6L4}g_jl9iFwD6#*dzrf>3Hn;bm<0d#Y_6agKx=lk<+KhT-U$QF;XN6800zM8&ID!Qp}fS{YU#tp;`m26t|5Zu?hu`Az~9)$Q%= z5Nzq1nQefH#}s22R(FK7Puk&+Xzb=fd$)!95r{?m0|x%YBx_Pt%HU=p=I zx#@NJX!eJK(TpH5u9VG?A+9q#BdFMfdvA zj`=TvXqwc*I9}pTJyZMA zeR!d<<$DTYddLcwXrhUIfUVhIXJtz_v4$5lCKiw5A;|trR_~8oF^h%uQR+#omW+ zAo~y7@0M@zYQL9Hty*YoG28VeFZ*OO7T!U`(1g3upG9GlU%r-S%Wdxr2P zzQ{O&!R<4GX{bOFQ*h0O`^B(-3)i8%Ot%k zuk$`;c|Qe~L20`Hf)h5veB)LYc=i)p(HDU85v}<(IA{e1a(dcqt8R^-JR38@;3UOy z4`1X);P>H?5onBg;IPZgEH&xmWDD)l3MMuY`uYePHpd)l!-k`@eme+IjoCUl>>(JN zn7}>Q*VBW8bR5htCCNpX6@FgcVE1cUAZyU1fZai=E>g2VtrG~SkTF_VJG&;LUUz*xNcrv00y|FF%ax$%!Pk83Q0?l2yIC>%n%IS0oOFbD%frfIV>=UwYkIqJr15WF|N5+DkkcN5uJz#4+MmtL z1)1JiE$4qT&0IThsM}o4#n`1EJ?GK%geKL?&U3|aDz~nGzviwf`gh)Y$W;HkLp5Xh z65VFu^tD=Y?dH>pyiAQr6vClhW8~izm{NZzoQ&@(jARO`i*1s2f;M}%wT8xNM_i#z56y?YUj35ikVD8nd*5R=`~s*m>XZQGlDn8h^yNB43^3@jva=Is zrXy>FT4v$bPHH4bOpJ|T(gqt3&qbTjS~N4WzuRN2grbAWF+frP%@_dN&#aXZb7~K$ zfY40ZJ(QDs3jj_4ZEv{0Y}RJ8lI;2wQm$ zJ3ADacz8DProi0(p}d6{ypL{#ALUXAkp@iAe(fQisYM9xlEyYL4HF{w(CSJL{mC$%9=~1S=6<$5oyC1VW$v8UGQR z9fSj!G1t@t*v(4~%QznnztG%kIX-CG`OkH8SH-JU8;Mt|y1o?76*+UPp4e_U{g2Vl z%Q;!TrBjumt;e^m$%DxP9yWVAFS}BPZh5LJ``dD?#;#`-V~-eTyjIU~f7_{Wv!ug8 zOW~SwpJj^Do7o~V61Q%gXff<1 znzwHQvoHrN@*gjbRZUOfFQI@5i?Oj57z{H-KnRyXYxsX*p)LHiH_VSSCM~5{| z&3m_*P&NrPSzz^Gl6jLR8ao<9n5`c=MvD)uJ%Zfkn|x5%FTPArjYt0KdQ5 zVR2V$Y1oQHH4Hu7lQ|pOx~>Utch)^^t=Blb_Q?M=!GrB+m-E18uhDYU{c&E^UQO0l z5s{G3F){lhvqGNEv)fBZo2T& zNg`$VXX_=Q2e%&wD7jugnR4_Nl_wCxvbScvqK==V-Wu8SOb;j~4?a++OTX^hK?k-f&&_5uq4-BG67P|UqMTyhYMiPd6WyS)f>PYx%4&XTX&re2KCMRx9&*VL zIs8JnZ>*>=3fY=J{#xH;v`oxE*rI90A_2IFL6}5NaR&jtFexc%ArwRi^H^v;9$H6_ z98-8G5M_w{=jpR&?=f;!Plw+KZ_cdl9&YGWZkF zz=tBtFj4^9=^M35@aPD=nH?Fi#Y{bz6F{9PrzjpexL>2J(w};R4KXYLoMFnwd_RQ$ zAqiv!HTA}^XJKI|3owl9WR{PwFEF8Pto6#%$Jiw#FmlCLE_oK@2h`?xa}0T@w1oN; zobxy2FZbqgq5tRNGR1W|Wv}CK056F{0e~lj?^`$xRzoF;ox*T68Q~K^f5c9;>fbGD z5b)E(o`?*C+eLPh;BZizLcOP5?q&`a^sfR&xIM(POVLIWVdThTi8K{r_0ri13=uLf zKi~s2G!e}~0+JDXymnQ+L)(IUKmdWvp z&brl)Ou@_=+Vr@xh;7FQzZJO`MjWL|dZ5*5%M@ z-_pN7uA@Z}ygaAIUiJO%m)T#X%MWb?b4I1wj<-DX-g!sCB&s{-pm;5xU$A~9k(B-npvi%oxJqRd~uEJfGfsA5?`~d0D|zs zi4ULwEg<3!@Iupq!VqBsMAsZrO$CfvwESnRS-rDEx~b2Yw0G8NW;oeZ(Gr?lDHct9YKSJC=KzI=I_g9E0skpi%* z`v{l?JV*Zr-x$K97xXv$t-xFlXCXrwb%CFr7{hfxo5$t{A7_ z=OZPnjrH}F{L|=V2p&kS+V}=_w(JvHPevk7T$l%C*QrxT#Xq`nwBHOm#UY6c7=3lH z>fOo;1`w_zeprk=q9Ix7EjY(d*D(v>!^1yQ&8t70PF3k!?sO|zywe-^p6`(+4#pYb z&%-r<@PJf)*#B$3Jz>hI%HvTw$J`lmd7e}u@<+8!YTg5B<|fDUSxoAiMOWSefn*=n zp%MtS;UeF~f)plxa?#kQe@!P?d7lgxyefH%f8?ao`9!ZfKKeWk<$_%61qYxJj7FbofUtCrI9G{MF+8$~`~b4wvar zUX%ObfwV4uu5!v~QuJGA$OyZ7Z6Q{2VwdM#NAK-m=%iv9bM$*0dx>(!YIwE{R1 z7Ci(8%?WhFO~N((6V^%uHI} z*!bBJ^X0G=NN>|hLs z8ld^(tl|p7a0mnxZl5zzMdG@xMCcVJ7LSN04GQn*TE%uVugOmi=o1s*sjA>R5FmWQ z?h&BtbSD0E8mzR)cW>4ZhjI$?Rpwp5wchhYDvR1JJvbBflKJjy(Idh1BJcKUE zrGEjE`2ts>Lym^V9Rt*O_sg_mf9&h-CRKIbivfV|ofhtH6kgh7S~+q%-we;OI>Hs) z$mXWeBn+m2nZ!~*Ui;Xnqu-80r|dIVCSx<&2CCW? zmU+dCJNTIrBCD(UGs=7~{?Z-Jym#uew=Kn~EU_+adotr+s1ZcXN-8$GV@flP1@=FA znZIrkA|QG;>fY?w@jcR`FU}68nBQ@#WE2hdlQ)kT!Z5|H|n5v8ry~Jv>6U&rLm_tCl=Kl0&Gh(&de4uUaXDaQTcNL)?v4 zz2Iw(#QAh0pbD$wp!6no=%^6}+zo&MAki}Rp1sk%dx!$UnM$QLPaH%|N+qg@9BPc) z0gpf54%P^QWyn>6C7y(awxf4JoEUgy#51X(Lc=W0(`C;Y zlFQE=1Y3d8G8mCsu!a8FC-}CyiDc}L8Jo!Gl;fUA1PQtRHZ_&hq3!EE<*C8EYDj;i z?z=ORA2%dE?@HkfSmBpOVO1oSmOP zC)rwK9ymOQLpuGaz2FEQXG}e`$3=A%dOkEQU~wRM#WCpL(upYl7=Aq|BoSnpu!Y_? zF?n3Rjo$)WD6@$h5;Ax%F8uc7&4~#MJ8Mu&d3W#%GCmO~07hZw=x_C?AEh&p8zQES zEnE)nV+f}!Ws}$PcOzVN7!?;2Y2^_hLm~(-LrG;VLchag?oj30Ll<8Rzk(9qj}htp z;5*bcHK|f6f2KFkDXsKgy?*H^;oXqZmzj*VwWG}^ZJSQym|e&|srK~nK{9&b9;LuK zPcqIZ3j^wMPD9f6Z}*OL7WYkDaWQN5OKngmzjauN%{?(v<<`BlOx=vmS<07ea`ri` zhb~KOh|s3Jt2$OuR&!9h_W ztNQa+1wVKII6gS`c*m!wgU{~K6#o5SEC^+Uj+^n*H-S@}@>d_ZE*HiabTO=Q9^;mo zY+++R-SG91itSEa{_}k-ISiw!1VV0DQpeb-6+^oFt~NWadot~Q>mW-y_D6xF`n$sy z$EV*uPd)4pUaSeAGGkC0sQkDw@ncMOZedeHaqMFky?D}Dyg0}LsfV4zOEDV?C z&pN&>c)W*=@Xxi`(3ZnDIc2U!eM={QeD}0hWnrW`Ib~j}*WUDeWc(f(Av}@YEmlM- zi@PxxV&tXdd^VKNb0uRkSUg&3Z`}zoUb{&yCE8-v&uDTh_yIw1Gab-5H1vyw#TUz; z)NB6UUczgHUEfQ=-~?~?RZM$WK3!5+NJ}Gi=MWWDRl^kzDVQ~&Ot>T^^$pDxYBp$a zk?R8#Cf+;!@QzBP9w2vNW%KG_1)v@z5qnsT1uo_F`!LA+D5hMG6gY(u7+AjGF`>bl z$qc=U0ClA2dT{v+`#kJ zROMOn!JDDaBGww6ip_F*3$JiRr?9bCxZhO1xp++`lwV-=cvI9Hp{;QS0{=fZKiEF; zy7_FgaU`Os#K4uzUwtd#^WSSMD^+i*I^@zNQrFn4Dq z+pI5t2CXDCsxfEpAKvox<%^{`e3a&uJ(G1zz?c4}{9V*1cMwvj7F#Z!UZGAnzRA(> zm7&1xn{T@1klhJtfD;iS6k^fezJJ*i)s1=ff_}G`woB@DH?7s1Y^9#pN&R;0$=SRm z`Izkzjq3-~w5O{qk3-nx!YQftZPXbw(_X9y`AxgK{1X4}`_?sjyD0dkmVer&a#eFQ zwCx~lXbrp&(zf8Jx}~YTLe44o_?C4KrJwPYo0)lE9Rww#32l3HDLi@zE`PJ>9?52wu`XB^#a>EkR6C^D)xgT+jsDwEafQDmeF{EPX)+1q%*~)zT&ZC z*M)l|E?cb|M-Gi&qkJO`4I)yEmH{f&)?Q(b;Pz@UCkAgKyiV`EI`(v+U6Bw5Ljr>J zj8Y)qa)5!7^6RQKFwG%()mno=yGTO_dmQJ)*PC#CWL`{wux3yGB`)c2RaM>wH`7nr%`x4D=!iEjY>XMxub0qKO@c2O0b01dt zgxt&`oWtq{jMM6_-a6AO9#OTsH0jNx;J(fvZuIULb0$E)~ZLmf&) zykcNLfZqGwU+oD0L4v9{68Izm~6(OuzFnj${RH8|dKDz8(~y``RT?PO;>zEVgJe&yYC!UcB0YgFbBN>rQpEZ0uj zJ{=y};5?e*diYjb`u@8@Bm7#v<3jz3CD+|%6#JWO`;N4X{}C%Ue3>QX;&5!kdP+u% z%z5pVPvF4;2MRV9YNHE}ulILQ_P@VDP9_pf_uG-i;`_?Y7x9qdeZIJUR@G|UZ7V_l z;2~-%aR))32O4x^iIHauqW3jg2vgC{>axY~2~xQX{zWds3I}|XT8CViE2EXNC&GX9 z^!5tSlDl|vHKV72bO{k*7xr$cA?7D1Hy0ZL%b9O$P#*?v~0}GLlYB^MEO5o0!s`<_+eXdP+5>)!Iw==cvnzRZZ~;&h&(?xms-d98!H&* z*RMUCG<_Cc?Ia*exS^$mT{{ViDVz#R=~)f3Yd4}!KgL6gkx*5bh%_fU6s*1G2OSG! zdqdOIGOa-GTcXa9k=`nvcga*swItOMohe?h`O;59zRT`RT5Q=AqxK7e$8gj)ii%f9 zzCBjru&tq}cGjV1Sd3SG$@`u@>GyS`eSv*j2`GrKix)kd4OeK8lskXwfKTE%l4=X1 zqhz53H-<5lPB$}$=su@{9658p(~cD1{0~Og9lhOo&0I9w(3C@NYoaeU^xqytO)xY5 z@!d}Q19G`SQM9|PG_k4k`+TyYz3fXkNT4M!NJ~0VmL6ib;ZL4$b(vpY`U(Usux~oH=%2oJtd%L^sNZ%q91~?-ohFXclKqZXa)4TW% z!A+bT|4w$v%F4>l?l;;NBx%7q1o#|sTC~n9vCa5~W&oF?9!9MURRhp7#Kl7niN;k+ zacbfdlgj-f+CQHp&M$pAko?crpeMuVBNT+@&|YX>JhJ0puy7~snOmpk&8C#@vEnB| z{?!YdovP6(f08#kXB2TjDO{*kweatUjjaX;zGZ4sfv;hKS9orPC#To9DwCyIigN*x zIC{(Uv>bP=ft_HELF;>eiI8vlxi(ozC82Kul{hE~pG=;%IVlHu-(l z_x-z;(?*Ripa|r8G}PL~jL0#SaQu0!-4k0{S_-u_x<5b%i0TD|Y6pb?c#gJ7JAwzM zPN0CDrIaFo7zZd?NVQ$oL^jw(`(Ee?aYP81_x!&6e)+0w$(=-84;t7_>rdRZmUR1;bd*4ehh_q>8HnuIlHVz{tAvxJ?XU)x- zJN-xmggw($2$FgApTcP-0$GEull!MBmHm2xM(wNQ?-w6xZVW~``@ugqRc(IJsX2c#s1q}WQ4KzQtWRq@ zHe?)Ydaq1k$|yg@=JOve0BS31t#&S|7Y~~90^IkyPHo}*O0{q7i=GhUm2SDPFq2i~ zk7VevGt$xqk#Kp!gXR^utx%_fagB=78?HeZ;`PC4?Y)O3%z*&gxHg?_`*eY;?p?(1)2eAcyq%@8N+OdRZ!@_>+R-=*$Jxc zJiqJ0l?q?JEe{_*?Nn97lu27<{hd&J7dPs8eeCL7yq4A3Pj~t~=eySY6(dO-3}@dM zPMAASYLVvp_6aPB&b6<_k1(E;E;02Qjik?ItC;k>e))|sIYp?^FTn2CkMoy3%T?5q zpK5B_dCsLB90NDA*q>(Rhvm~Kk5L?bW+do@tz$g+J(&BKd{dnYzkb$9IGJ>b*VsTv zPA9NP{D-3Lsy*|?oba1Nl4quWInqXdXTK{KiD6x8on?dyVDK_ZsgWO!d%j}n{Kdwi^#)dqz>EYo(uMkY(xP8?T-8McvJU5!^yD7y&M#z9!4TkVQR&jLsLP>`iKL`(& zyYr`M;T-P=vjq&p@lw`c4G1bZD(peG8P?k~IDrsEgtTNI3=?p0?WUmEKtkHzY!Qp4 zSq6F=@Do7Yk@_@*2TTFhnx6i;x7TYO%70J^TwUv(VWfwROcMEOnByV~Aq1ESs)6au zPYn&|A7OV6e>Pi;9j2Pab@(vUp%}QxK+LC|5ialw3kax#(ZlN68X6C0MC(D9#H#tT>N_@-)g2p! zY!9!0xAD(u=uGSk5^Z7Rw_&Q9SB>Vn>eGFG@AjR9U!Uea3}1WGi=AWym>n~#ujd*x zRP^lg%`&;M>Qhhq(WE#;{Q2b>zNu~Ik9pkemfa;(e#3@d2EAAFqW(Q>w$3PHNo2agkui33s+vg@edHcM#`+hee0gOTN?yXsZw_%JB?d4CH>&|v;DUMGld2B!1i>OAt%LH=*LB( zc+2bUCs`ggZKb*x+dG%`F5$E=RXhu;dJH8^d~8(g)N0+ncWw`#KS)e6lj=UVJJoFq zak=+^v$W=w$>uhn`h4fXx2j>Gnt=?p?t16-^%`9cK2f? z9&-1Eztk0taI7&?wYg$W$IG{$tlw42$RPN~1+Gt(85_JkH?D=NTF6q_*R=$OCht6Q zq&DcyOIfAZ>>1aVK>4&)v-vR$YvN*krsDP;f1$w;E&bn2aGo<{D#pgEj4fP0`Tk+C zQ!6X`(0@5O>|tTjlbrNZ#-UD456)DN^%F)$% zwzhZo4B;}o_@;VCs3$og$izxl{zmRh;OxlGb%Eb2Q8s_6I3hzY9s0hoW0Z5NKASOD zm^^)Q82F=MdfOO4R6W zshtRXQZk&CF|a3*j`O+X=*#^Ur1-Vnd+Ic4txE+NCi55=Cli}Dd3vX)XdqwF(mkhl zHzsECu*|LX)Bwx#`!dd+3GA@wiWdM6G;Ytm^ykM5cT1?mo6`rg8td6>Tiw_fpw(zu z@>+0@oQ~EN6`ljS7x(F+y6yl`cBL8R4p?QPKCAAQ2OX)Yqc>Roha3Jji+v!X`fLpc z7_I+xTl24Wop}3B;=g{xAtQL<#{HLAR1t^AI+o4KD_Jl7KyD=X~gZqCpa zRB-K1W!}?5mZPTDvGHe~$O){L6Jy7V-AIkv?09yw_#cxvnY2U1`(fqL`@8;_)5@J^ za+32fv9Q~hk(1asMCE1YeF%{;;9W;VtYsJOsDtbpE=k7{&9t&ndj;{471P?BF{jsa#rI{1*WL)q1pROf3FI(w!t= zg;%oJI$90Q%WA7lx;-ZItiN~5=HnuVw@IQ*3iXcM|B$n%K5HjGwf0>1ppapy`kh*L zW_MRdjX#MgmCQK zncO1nF*}UjYvrgMsASlkCbJ&zuD;*DViD@CEl3={>?QSKwU9+N>driIXK_Q5V$O#1 zYwMAgBe@&oThF@E0)h7AoT%x#1)%$sq9Q2LIIWj0{sm6GeE1ttyDDrjn5DzNddF;+?(D^ZVjT zsmnm4tMK@ed?(3{Fs+Bx`C0Osd;EzJuP3ipI#pj0V+!rN?{$mU`A zGCp%=YZHx)lEPjKWd(0FZATjk%-}D(x*{fO(;;14oMQLxx`M8*E(J=LpFN^jApkN# z@<(L|>y9BekCXjSnLULB131F;v?=n{F0%htG=Pq*2!=5HTA! ztfr_~-`%|oBy?$hX67;+xuHZvwFP7yCU)SgZ?ke zpN@`(_r*Pa6G_l)@thFe4EX##>&~|`gPTqG>c;vF*y$6mFkppMcfuLilV3o9729C} z0Sfzm{zL_5ySeCFfO}(=PjOTM~Y5aV?-vw(Xw0^~%f2?1fWJz`=^2 zVKSb7bzgPs{LyVc)YbU3Cbm}LL-FEn) zPM`NRlk=W5;kyad)HY<$pPuK{Cph`JarfPcC?V8)@`p9kr4o4@Y1RJNDr8Xy#}eKCM^D-HH}tJKCPdy&k$cL{_*S4spwM0 zi_5{%<*Q@&f0omK+@}>&+8?IQ%g)lwW0&A7Vz<;ozi8qnz*#e7mj3xeQ@0D}wT@1s zA9?AAonE@mSd;sg4p?6qloAqPDtlv)U;P7`m(PvAP8?n`;@%>#5?gtsV!*0JU#aa& zOq8LfiD8?u_el>tCO$px>im&-aej$qTX5PzojKckV^K*7Obj`pj8|{*Y5K}|+tbpA zK1?vLpVj{SrF7Q2=S7S7=_fV6--iyb&NlFGpOyLkf|J}#yNe&J)ZQFgJlA38imHuN zoDA1<$_hGexjd|BIwC2Vr2Yu|%@{PTVPQCp(C<^zoB%orb4KvkaJ3^~slmFqA_o3) zz-us+2A=|g-c4iHNk~LM{ReUQN5K{_d4m zM#l-iJ9pr+;e{pyLf6xeJ{~@s#KD?_Xm2o-F(o!Bx}XA99)J}@;Sdk2tNS}TjzRo~ z#|9+`DQ?e7N?P8%t4+P+=;#XM=E@atVOHT~3mZ;^;`Z|HLt_ajR>8z%4bGvAVh+&l zU%PUpHiML4-SPeBPncmb!6tr#Hcqvrt*z_h$7H-%xL8Rm!-Na!37AUeV|ow55i-oE z*V@Qepyj@g)GJI@yz)4=gi%W$!t(-?B>Zp;nU=e#fL`(WvnWhZZm~m&WNNzpDAkAB zGy;7aW-FUfc;M>~eg6&x2LST&PlLcI(0~n(jRBCb!vPD1nU4%7bF2dKe;_3i1SrCQ zk93Yee7pvo;b!tNK(QH~!-(^QQ~&D$&Rs$5tPQQNc9ecE5Lp~7KfV7>(MSEAYCdk` zgGLtYQr84hRc}V}vy3=A-50ZNarN!b*ved8r#heO^t0Rr6EjaV1_tt4Wzxb!1DDRX zF^Mj%zn@K|;FzWSWPPhBT7*yf@5j>{GHqc$_v*%7kY+rbV=35s+_;iOR;H8WOQWYO z__YOOnWx;{rC!-jq_d1Q$B2uPIsG`wRIm|J7I!;vEccJbOyn-YysKoYuI%d1Io;CM z=L-xH+?+LF2Wp5mm#Qmxp77g6`hHZ_ z(oy&?-RXdVM@Em|tL5)jYvZ%f}YQubyN1XBgi@mKnyEx7lZi_fGje1_9{=XGzrJ&pT^}U(bZ@W=Jvn;A{zi&-pjYA%u zo~_B^oB8$N_=DSv@@5MmnAi*^)j*Y(IJ~zosYX;8z`iN|UGTtw2n2SAa14|Ou+rQ6 z7iSLP2Z7*bmffpYtRUVZwUz?YxeMTK0B#4zjTv-Zn5-<#gGxq6N5{;3AHIgT=3uW; zQC?2nYQDtFyG;(sBugO5;VNH+y^1lv8zzS!dHT=GLf)w`Lvn_mb=lXiZYWKV;-&|o zc#7w7W#;2;L0c)05VAUfxg3Y2<<-0sNEIUcm1}#5X8*x#C9aHssMS?(2$8UwHf8aI zpmYC82rCm4(ZNQLu{KO&PzIl@jc>Ii|M0Cb6aWM+dqG&^GM>(3u7v#qq#87bqx8`npAbei^0|+nJ%fXn=*g2zsV%k^7D8A^AlZP7NDz2LV-u6ECq`Y%a^Ul$q6a3# zz8QevyS_|jW+@JHAJial)*@H%Cj3DDew39JK`)d}VMzV(vFXKA8X6Rv^o5)!Pxca! z)-TM=aC38i=;~?|oWcfxuLEQ`x^p1}0U|$Q%PX&)0Ji|}*rsc(py0D>pXTT1F*StQ z;A;(`Jty!j#y9csug|gl?K1Nn865?ikNm)~ZG<4o61e4T#_j;A$Bl`66u~Eufo~=D zFo?kB?rGUaZMdvUASML?j|jJ?Y76qCKOqxo`DIGTvzlfvL0Cn6{y5NCebjD@DPNdG zH*>M&pxn)epk}=*;O#y+{Gve0D%frYak_D@z`fzm5SMNaK@dxpo)bugHXW^p-2RWhG~AjQ2#`< zKEzJ&!W(QEmDx&qjh+vM&yZL8R^KlU;v&p@F82jBvap1`Uwdbmyj2bh>-F!IysyE7 zY(jfZW%@8kSqVzO97g=$;~j)xXS7?l&*4Xb7MYyB-2D1_=cby%ep7#ECkQVR(Z#;s zFRUm?V-6i+D|Lx(zo5oP_$5cQCWkeAy`!q`qbvH(_~Sr5Jt6qc_*(KQJx01^X;p%7 zn0uCDD~33=Ezf>m4IUU~!*GX$$Q-v+QQH7-he!-pEu#r1+Q)}K_A#RR8Pw5&+65pWn9*ycf?KHw-a5A^} z)pWvzr!Reiw?AOvJg`#OTi}!U#rC-D(~SP=7ys-HyX2z${QL%dH<*bT1k$6vL_z}z zfg!7&p<&Y9yBITUClaK*O2Pfy(An<4S@UvnDV;tI5ehRQ2wP5cI*QA@(0VFWdYQJT{Q|5y4m*9=?02vdD97#nr-M5Sk_| zxg7*OU~#NX6b!KO+m`1a7?`=SDf(X7PQP`?S7cWrL!p`$Dh$rI zEQ`x^g!bn7yPxrUMSrKxqRf@@`|##%8vz9i2CkKGMDu5T6R1EC{g=PP^K~#|GnSEZ z+h)4v8$O6=w)Dk8ctG|3Fd$beb8~bTMTnV!AQ6}KcPU9whLzxH#`VK@E3u!?@D!5( z)TxM_0K~ea-UqlI#eaCZE#PAd3$}GeKHq7Kn1X1bdr&%lR!dWpl8S1)gEel3v+nzB~EwgB3KG4Av7c;ZHjAC#I2C2>+Lk0OC3^ssz-y|uW)Ln8rKl&If z;9Uf=(D5x{!+;Cfs0K&P)g_fPAVfRRZNDT2Wjx)Sn`fD?mA;Tv?WS=)3Y zK`do|xh%E`Ohl1441;s_t8AJ<0D^beAKp^6s+K3(9Zd>CGG z*!)>ib8nNz{qmeT*HBI)F@;#NTxA*F{P|~$V-9Jq%k35U>5`Zciwmn|+NKYk6_I@7 z&nCE1$f6maIl((LPf)xzr#f;}VRWy~tI3-TSfn0bTjynMIO-L-679b?Qf{kMA~$p$ zRN)K|m6fCZb^fZw^XS?mxpprXi&A`99!86_tY%P9nC!C?qT1!3PWa`gdtxj!!@^9M z-zvXdFUz@~jFjM34h`8JNkRxpTsDv@epvZim#41w*$vsNkAtVG;zvT9M9`Ey)9W()D{z`=_TC7WdT5g$G@@jS7~9@b5x4^B6! z;ppSh3v`yasHe52d_%YLG#Nojm!)3%X~oQIk%dmTWMPYU%bvQg2?zM=6#4yGFNEGS zK4*IFQ|z~S^F-V0v{G3HY1LeYD_X)gM2pMb##oz5GM}qFmCe&%ulY_{OXaK%t=wJ8 zsP~r{6S{@HYJ441f-UIDI%xi;3@i#*Teq2d2?X1;)b;!|_}iAOF=imn-!!oF=Y9M7 zBn6%EOKtmmpQNJY-ZU! cWXk%R99JlcF%lZCsmT*?Wn5cx3Q)LX7IMaDLOeZfuSZQrp>C+aH8ez(K9ujo|-xu z^X_&V=ERLnX$&95^e`6z(|IQw8d1EvYyl)j!>4+}r99Hv#m$W_^wboiBRX&?l+*B~ zp^?YL^-ChJHU{}((@lMSefL_ojX8Fd%<&BkGu%@0YpwynTa_#rr8vNFIZb+fMN~J z6|G`WU|=(!T~1Z@0nJUHM!6ai)RX1h{fUl?*BOA!@S-M#(kcl`#Y4R7!& zYTh;*FsMq&T$~UIjJoU_mnZK%WoeL5bL7wJ{UyPJcRy5GMn$FVef}VVgrq9)uJj5% zRjM2sLBrRYTjf=zp39d7-_^DQ2 zN-F>CWsa_|zSMpo$&iPl@%r|~Dw7N1ENAptnrbQ*zQuJ+aEWJS7KFThH@Nu8vn5Z9 z@s4Sq*TOw?FX#x{fz@?XwAo zGeW&Z#X?y?TwvDl{OxVn!mwpNfBwnxoQ;r6d~7VGyrfced3mXa@<;P5O>XoZV1N|b zwx{DG$Jbx8GLeUo>2$|DIq^Z5Ehm7bR?AZVtlfd0Z)`My9>Q!@_O#Kvr0rOBgoU0{ zSKZSzOz)y5QM3)qzDW9=`c~cCUG$ffvC>@F` zy`h$uQ&>tFQ&<`Ho2}wr8OB&uUO%1gymb5ZKsBofui43tDH=U%wF zz%{)Yf3<&q!{{FvZd#Q_ilsVFigPgT{+u}d!BgOX^D&{%2B+6*X%dkcjp7lGADaPh zBkFOj3CuvwyTPoG6oZE{V95JXQO|Dty?$LZ2oj1%+1b)_toTJkBy)vL8%uZr1fsav ziX@@F#k)D1c&xsw%WdpKni$I}`dTC_M5U%~Gz+)k!}G5a`Q#bS{M#XKu>WD<%!v}k zi#IB|@8quBJ6y%QlkFr+DC5|*Im_;`Tm5Bu4EoiU7ST=kP(Sy)&e+RxOzwpcx#0Y% z0=t7`E`gmNpWpsrbc=*p_j8%x9wDEJ?n5rxsv?qWnWEiw0a7xil5gJ|DD7B2HtE?D z`g&l%E+>y;VQbK-^J$$4(m76@MKZ&El`_8X6JrCn(4>>lQnRrQ&+nC}NqyMtGpLrf z+iVx#6DLW7%g%Lo^Y&e8=_(Fh+bvnEw0UJ-pZIZFCE&r0j({gF4AC8SZ0W6%oi2=S zS;IbmuO44yG-SHCU~(kV&|tvw%rpDs=dm{zT%r>l|0t7E2~+IVyC1=0?V2gcsC_h& zA+v7dLB4pSsf2Xy2~UC`|DtkAPR9c{0($D6?-kAdx}Xoi&$HGXmXIA_oapDDhX-mI z{2{}CZ70a((rCx1UVGm+&b-bpok2V9^^TEwUnEK$s0!?nt8IuB$AFh&8H;!*O-7@RaEC0LR z{HtD~W-TY7ZRmgir>G}Kz^xo7vOJsA8w)ry0H{nU1fUxEdp+C;*<7Eyx)71ww$uz> z8>EAh>XkM2>^Zo?asg4 zKZ1(|$tOMn9R^a_2YT@Lu9AI_Wyazc8>Nzp^C`9TjNPR*E1Yj`W ze4vPx;p)aj4_eumL)Kt|#k{)^zMn4%3}snXWep9au;~ESN12Z-ai@zL;Ny3L{Q!_2 zhGem-BW?pXHg{Ce@G0dX7LZ;U%h*Z71!o#WrK3pK=8wO_BvF+2NOs5aw*U&rv+t** zc;of*^7XWy=G0aWOpY|kIFNqsR$0o6D>c>ibL^76aeCra)oi2nHmkW4M%5>M`no<_ z80++w*s%HVqa>hHyJTIzShm#P&L+Jj^7?1jOPgQtzzQq*Oz(HXiiK=~UPg=@a=_~d z2ZKl_87|_D@ci|!?`_+sFXAbwMzL{KyzJT45wkk ziP5lW@v?I^&U$-yJ=YVrT<|5+Se*+|u`k+?%q)0dUA;0`{`*q%y0m)iP1Ac<*rb~u zJyrQTAIf<4iOv;Wg)0{`YAU{V%gz0+Rx3H%W2)Z4n^i*A8qcMaXqK>;fSr2nMoNb| zty62c^uw}`5wk0EDaq_<-z-(mIy^2@!NKC#CWccI( zj&1-ULGpuGv`+n%?^>6OQ&SOJH@RGa3==2;AklW`r0Q>oG5PoAdq7~=6t1Q{;4h#>QxdEC!Y&~N5CnQr`C-ic3 zumxeJ2$iaaipmuim*)5Hg{xS&PMr9QQw^*jpn(R6H`h12Q7>HsCw-8OG^Cd~q@{hZ zSRJ&cA~*G*pfSOLM|{A$I4f%epD<9XUsa3!=zLjvs<3Df2u_^em6t?|7JYM#gu|z~-gx=V>$RjiE=@ ze4;mEgN!^Key;~!7N9e@^kd}7Bd3OwpY0pJQZ>vsd7L~pYbPN8HbMEl&Cg$hb7mT) z^;@z6=H!?@Ji8+M5NFgyo#BAvACe118HTPGk}# zm$Kx10x50C+I#HUpGeldo~v{X+4Xu=w_<>KWQ1K!<%HE5Q|jFHuj>mtPA87VG`~M_ zH7q)qblp|C_fyk{N1lA{Lc_-GYcDremy=VSTF%lx^PCkfi7>qWxy1EjM(X(FLfgSz zbeXAZgI%)+G9Gx3D*nkWM)B?-B3$ij8N1OLBmTotB1Ye3D*#{xU; zZostv0X!psG)iI2i7gf3=~hy?Fd77t6~92Z{dv1c%2`n5oFUsm@bIQVGxBdm>^i_Q zK5EK7Ju>170^dtxY3Ke*pgcGnVT@2Tum7b@BYt|0)Q=^|rm%Thp>Q&lUSH0lhUeIx zI>9|>KQ@HSkvQyhCV1yOt7^?3@|4JW)mM9vwUXH3}Xr$t2_h!DeaDD6tTfJxY<4et{@-Z)j>T#bdj$pT?L-{cc$#FL*HYDO zPVj`K+a+axTJ8M9-tu7Kimc)!gHzXHmx}xwa+b|kGQ}FF+I<`bM>k=0Uzn$zV7UG> zNUh9%vC`h+W(dpVUANamA4pD=_6EE~U8{?9<3rr(Ibc`8%3w zwjg`TH{kYkAtqtq5g?0l4OPP3b}4dimIAK>eF`P#g1^3QN_EzWV7~CAJYSRh!jNCp zmk~LphhL7%{^|Mdr#ba?KN;;VG4jWsC$K|vNlLC_#P1d#ODq2I)JM$c>&C@69Z~6#p}F{5{r8!hKew@bZLXuC zN)u-!+q2@eC(FXcyQgzN-9zv-W={vRpz^ za@~M;0^yf5ht*+i6~)KqSb>2n1}RdDU$1Sj(Ol`9ejEbe1@tX?ABw)(EvF&Z{!N+$o{$W%0hYKpF70yJu1xq-V0&NA^GRtH$e{9e}BXjocZr# q_|--I@5jAN|NkHC|CP_|uWv+2_D${M!}#(ELPb&i)KhtL|NjM}$IQe4 literal 0 HcmV?d00001 From 694eea7786686603de66fbe58bdec1a2be60f0d9 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 12 Oct 2020 22:52:43 +0200 Subject: [PATCH 023/142] Streamline slides --- chapter_00_intro/00_content.ipynb | 43 ++++++------------------------- 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/chapter_00_intro/00_content.ipynb b/chapter_00_intro/00_content.ipynb index b07b0c7..ae68a81 100644 --- a/chapter_00_intro/00_content.ipynb +++ b/chapter_00_intro/00_content.ipynb @@ -94,7 +94,7 @@ "cell_type": "markdown", "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "fragment" } }, "source": [ @@ -168,7 +168,7 @@ "cell_type": "markdown", "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "fragment" } }, "source": [ @@ -220,45 +220,26 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [ - { - "data": { - "text/plain": [ - "3" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "1 + 2" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": { "slideshow": { "slide_type": "fragment" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello World\n" - ] - } - ], + "outputs": [], "source": [ "print(\"Hello World\")" ] @@ -286,15 +267,7 @@ "slide_type": "skip" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Python 3.8.6\n" - ] - } - ], + "outputs": [], "source": [ "!python --version" ] @@ -540,7 +513,7 @@ "cell_type": "markdown", "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "-" } }, "source": [ From 6c2cc48d3ee4680b94fe332bbaa9a43dcda38cee Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 13 Oct 2020 10:41:55 +0200 Subject: [PATCH 024/142] Re-order exercises and review --- README.md | 8 ++++---- .../{02_exercises.ipynb => 01_exercises.ipynb} | 0 chapter_00_intro/{01_review.ipynb => 02_review.ipynb} | 0 3 files changed, 4 insertions(+), 4 deletions(-) rename chapter_00_intro/{02_exercises.ipynb => 01_exercises.ipynb} (100%) rename chapter_00_intro/{01_review.ipynb => 02_review.ipynb} (100%) diff --git a/README.md b/README.md index 208a5bd..a9cb8c8 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,11 @@ They can be viewed in a web browser [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/00_content.ipynb) [](https://www.youtube.com/watch?v=YTU8jaG27Xk&list=PL-2JV1G3J10lQ2xokyQowcRJI5jjNfW7f) | - [review ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/01_review.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/01_review.ipynb) + [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/01_exercises.ipynb) | - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/02_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/02_exercises.ipynb) + [review ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/02_review.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/02_review.ipynb) ) diff --git a/chapter_00_intro/02_exercises.ipynb b/chapter_00_intro/01_exercises.ipynb similarity index 100% rename from chapter_00_intro/02_exercises.ipynb rename to chapter_00_intro/01_exercises.ipynb diff --git a/chapter_00_intro/01_review.ipynb b/chapter_00_intro/02_review.ipynb similarity index 100% rename from chapter_00_intro/01_review.ipynb rename to chapter_00_intro/02_review.ipynb From 92dde01e0c51bf98a891f6c1480b79d1b3bf91f7 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 13 Oct 2020 11:02:29 +0200 Subject: [PATCH 025/142] Streamline text --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a9cb8c8..f0e0087 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,11 @@ This project is a *thorough* introductory course ### Table of Contents -The materials are designed to resemble a book. -They can be viewed in a web browser +The materials are designed to resemble an *interactive* book. +It is recommended + to follow the [installation instructions](https://github.com/webartifex/intro-to-python#installation) below + and work through the content on one's own computer. +Alternatively, the content can be viewed in a web browser either statically (i.e., read-only) on [nbviewer ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/tree/develop/) or interactively (i.e., code can be executed) on [Binder ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab). **Video** presentations on the content are available From 2dfc9540b68072ee13ead399c60e648145299acb Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 13 Oct 2020 11:10:37 +0200 Subject: [PATCH 026/142] Make YouTube references less prominent - the current video lectures regard the old one-notebook-per-chapter structure of the project - as the new structure involves several smaller notebooks per chapter, there is no one-on-one correspondence to the YouTube lectures any more => only reference the YouTube playlist once in the README.md --- README.md | 12 ++-- chapter_00_intro/00_content.ipynb | 106 ------------------------------ 2 files changed, 8 insertions(+), 110 deletions(-) diff --git a/README.md b/README.md index f0e0087..18df65d 100644 --- a/README.md +++ b/README.md @@ -13,15 +13,11 @@ It is recommended Alternatively, the content can be viewed in a web browser either statically (i.e., read-only) on [nbviewer ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/tree/develop/) or interactively (i.e., code can be executed) on [Binder ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab). -**Video** presentations on the content are available - either via the individual links to [YouTube ](https://www.youtube.com) below - or this [playlist ](https://www.youtube.com/playlist?list=PL-2JV1G3J10lQ2xokyQowcRJI5jjNfW7f). - *Chapter 0*: Introduction ( [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/00_content.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/00_content.ipynb) - [](https://www.youtube.com/watch?v=YTU8jaG27Xk&list=PL-2JV1G3J10lQ2xokyQowcRJI5jjNfW7f) | [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/01_exercises.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/01_exercises.ipynb) @@ -31,6 +27,14 @@ Alternatively, the content can be viewed in a web browser ) +#### Videos + +Presentations of the chapters are available on this [YouTube playlist ](https://www.youtube.com/playlist?list=PL-2JV1G3J10lQ2xokyQowcRJI5jjNfW7f). +The recordings are about 25 hours long in total + and were made in spring 2020 + after a corresponding in-class Bachelor course was cancelled due to Corona. + + ### Objective The **main goal** is to **prepare** students diff --git a/chapter_00_intro/00_content.ipynb b/chapter_00_intro/00_content.ipynb index ae68a81..bbc1dce 100644 --- a/chapter_00_intro/00_content.ipynb +++ b/chapter_00_intro/00_content.ipynb @@ -796,112 +796,6 @@ "source": [ "" ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "## Further Resources" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "A lecture-style **video presentation** of this chapter is integrated below (cf., the [video ](https://www.youtube.com/watch?v=YTU8jaG27Xk&list=PL-2JV1G3J10lQ2xokyQowcRJI5jjNfW7f) or the entire [playlist ](https://www.youtube.com/playlist?list=PL-2JV1G3J10lQ2xokyQowcRJI5jjNfW7f))." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "data": { - "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAUDBAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChALCAgOCggIDRYNDhERExMTCAsWGBYSGBASExIBBQUFCAcIDwkJDxgVERUWFxcYExMYGBgVFRgWFRYWGBcVGxIaEhMXFRoYGBISFRcVFRUVFRUVFRUVGBUSFxIVFf/AABEIAWgB4AMBIgACEQEDEQH/xAAdAAEAAgIDAQEAAAAAAAAAAAAABgcFCAIDBAEJ/8QAURAAAQQBAgMBCQgRAgUEAgMAAQACAwQRBRIGEyExBxQYIjJBVZTVCBUXUVJhk9QjMzVCU1RxcnN0dYGSsbKz05G0FiQ2YrU0gqGiY3YlQ0X/xAAcAQEAAgMBAQEAAAAAAAAAAAAAAgQBAwUHBgj/xAA9EQABAwIDBQUGBAYBBQEAAAABAAIRAyEEEjEFQVFhcRMVU4GRBiIyocHwFjRysRQ1QlLR4fEjM0NigpL/2gAMAwEAAhEDEQA/ANMkREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREWzPgVcVfj/D/AK1qPs5PAq4q/H+H/WtR9nIi1mRbM+BVxV+P8P8ArWo+zk8Crir8f4f9a1H2ciLWZFsz4FXFX4/w/wCtaj7OTwKuKvx/h/1rUfZyItZkWzPgVcVfj/D/AK1qPs5PAq4q/H+H/WtR9nIi1mRbM+BVxV+P8P8ArWo+zk8Crir8f4f9a1H2ciLWZFsz4FXFX4/w/wCtaj7OTwKuKvx/h/1rUfZyItZkWzPgVcVfj/D/AK1qPs5PAq4q/H+H/WtR9nIi1mRbM+BVxV+P8P8ArWo+zk8Crir8f4f9a1H2ciLWZFsz4FXFX4/w/wCtaj7OTwKuKvx/h/1rUfZyItZkWzPgVcVfj/D/AK1qPs5PAq4q/H+H/WtR9nIi1mRbM+BVxV+P8P8ArWo+zk8Crir8f4f9a1H2ciLWZFsz4FXF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hodQ0EBkkco21dQ8qJ4e0HOodmWhZBgrB0X6DItAPDV4q9H8P+q6j7RTw1eKvR/D/quo+0VhZW/6LQDw1eKvR/D/AKrqPtFPDV4q9H8P+q6j7RRFv+i0A8NXir0fw/6rqPtFPDV4q9H8P+q6j7RRFv8AotAPDV4q9H8P+q6j7RTw1eKvR/D/AKrqPtFEW/6LQDw1eKvR/D/quo+0U8NXir0fw/6rqPtFEW/6LQDw1eKvR/D/AKrqPtFPDV4q9H8P+q6j7RRFv+i0A8NXir0fw/6rqPtFPDV4q9H8P+q6j7RRFv8AotAPDV4q9H8P+q6j7RTw1eKvR/D/AKrqPtFEW/6LQDw1eKvR/D/quo+0U8NXir0fw/6rqPtFEW/6LQDw1eKvR/D/AKrqPtFPDV4q9H8P+q6j7RRFv+i0A8NXir0fw/6rqPtFPDV4q9H8P+q6j7RRFv8AotAPDV4q9H8P+q6j7RTw1eKvR/D/AKrqPtFEW/6LQDw1eKvR/D/quo+0U8NXir0fw/6rqPtFEW/682q/aJ/0Mn9DloT4avFXo/h/1XUfaK4WPdo8UvY9hoaBh7XNOKuo5w4EHGdR7eqItaURERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERf/2Q==\n", - "text/html": [ - "\n", - " \n", - " " - ], - "text/plain": [ - "" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from IPython.display import YouTubeVideo\n", - "YouTubeVideo(\"YTU8jaG27Xk\", width=\"60%\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "Also, to see some common shortcuts in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/), find a **video tutorial** below." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "outputs": [ - { - "data": { - "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAUDBAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChALCAgOCggIDRYNDhERExMTCAsWGBYSGBASExIBBQUFCAcIDwkJDxgVERUWFxcYExMYGBgVFRgWFRYWGBcVGxIaEhMXFRoYGBISFRcVFRUVFRUVFRUVGBUSFxIVFf/AABEIAWgB4AMBIgACEQEDEQH/xAAdAAEAAgIDAQEAAAAAAAAAAAAABgcFCAIDBAEJ/8QAURAAAQQBAgMBCQgRAgUEAgMAAQACAwQRBRIGEyExBxQYIjJBVZTVCBUXUVJhk9QjMzVCU1RxcnN0dYGSsbKz05G0FiQ2YrU0gqGiY3YlQ0X/xAAcAQEAAgMBAQEAAAAAAAAAAAAAAgQBAwUHBgj/xAA9EQABAwIDBQUGBAYBBQEAAAABAAIRAyEEEjEFQVFhcRMVU4GRBiIyocHwFjRysRQ1QlLR4fEjM0NigpL/2gAMAwEAAhEDEQA/ANMkREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREREWzPgVcVfj/D/AK1qPs5PAq4q/H+H/WtR9nIi1mRbM+BVxV+P8P8ArWo+zk8Crir8f4f9a1H2ciLWZFsz4FXFX4/w/wCtaj7OTwKuKvx/h/1rUfZyItZkWzPgVcVfj/D/AK1qPs5PAq4q/H+H/WtR9nIi1mRbM+BVxV+P8P8ArWo+zk8Crir8f4f9a1H2ciLWZFsz4FXFX4/w/wCtaj7OTwKuKvx/h/1rUfZyItZkWzPgVcVfj/D/AK1qPs5PAq4q/H+H/WtR9nIi1mRbM+BVxV+P8P8ArWo+zk8Crir8f4f9a1H2ciLWZFsz4FXFX4/w/wCtaj7OTwKuKvx/h/1rUfZyItZkWzPgVcVfj/D/AK1qPs5PAq4q/H+H/WtR9nIi1mRbM+BVxV+P8P8ArWo+zk8Crir8f4f9a1H2ciLWZFsz4FXF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hodQ0EBkkco21dQ8qJ4e0HOodmWhZBgrB0X6DItAPDV4q9H8P+q6j7RTw1eKvR/D/quo+0VhZW/6LQDw1eKvR/D/AKrqPtFPDV4q9H8P+q6j7RRFv+i0A8NXir0fw/6rqPtFPDV4q9H8P+q6j7RRFv8AotAPDV4q9H8P+q6j7RTw1eKvR/D/AKrqPtFEW/6LQDw1eKvR/D/quo+0U8NXir0fw/6rqPtFEW/6LQDw1eKvR/D/AKrqPtFPDV4q9H8P+q6j7RRFv+i0A8NXir0fw/6rqPtFPDV4q9H8P+q6j7RRFv8AotAPDV4q9H8P+q6j7RTw1eKvR/D/AKrqPtFEW/6LQDw1eKvR/D/quo+0U8NXir0fw/6rqPtFEW/6LQDw1eKvR/D/AKrqPtFPDV4q9H8P+q6j7RRFv+i0A8NXir0fw/6rqPtFPDV4q9H8P+q6j7RRFv8AotAPDV4q9H8P+q6j7RTw1eKvR/D/AKrqPtFEW/6LQDw1eKvR/D/quo+0U8NXir0fw/6rqPtFEW/682q/aJ/0Mn9DloT4avFXo/h/1XUfaK4WPdo8UvY9hoaBh7XNOKuo5w4EHGdR7eqItaURERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERf/2Q==\n", - "text/html": [ - "\n", - " \n", - " " - ], - "text/plain": [ - "" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "YouTubeVideo(\"5GGyKq1F_5c\", width=\"60%\")" - ] } ], "metadata": { From 4c7f7f4387911ba83c2c0458749f3c28ba9febb1 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 13 Oct 2020 11:15:33 +0200 Subject: [PATCH 027/142] Store notebooks with execution output --- chapter_00_intro/00_content.ipynb | 37 ++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/chapter_00_intro/00_content.ipynb b/chapter_00_intro/00_content.ipynb index bbc1dce..0d52716 100644 --- a/chapter_00_intro/00_content.ipynb +++ b/chapter_00_intro/00_content.ipynb @@ -220,26 +220,45 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "1 + 2" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": { "slideshow": { "slide_type": "fragment" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello World\n" + ] + } + ], "source": [ "print(\"Hello World\")" ] @@ -267,7 +286,15 @@ "slide_type": "skip" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.8.6\n" + ] + } + ], "source": [ "!python --version" ] From 76fcfc54eace1db03134bb7c09fbc2606069f2f9 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 13 Oct 2020 12:05:50 +0200 Subject: [PATCH 028/142] Streamline text --- chapter_00_intro/01_exercises.ipynb | 2 +- chapter_00_intro/02_review.ipynb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chapter_00_intro/01_exercises.ipynb b/chapter_00_intro/01_exercises.ipynb index 28a2cbf..7f51a1d 100644 --- a/chapter_00_intro/01_exercises.ipynb +++ b/chapter_00_intro/01_exercises.ipynb @@ -25,7 +25,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The exercises below assume that you have read [Chapter 0 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/00_content.ipynb) in the book." + "The exercises below assume that you have read [Chapter 0 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/00_content.ipynb)." ] }, { diff --git a/chapter_00_intro/02_review.ipynb b/chapter_00_intro/02_review.ipynb index 2ea6b98..0f75bfe 100644 --- a/chapter_00_intro/02_review.ipynb +++ b/chapter_00_intro/02_review.ipynb @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The questions below assume that you have read [Chapter 0 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/00_content.ipynb) in the book.\n", + "The questions below assume that you have read [Chapter 0 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/00_content.ipynb).\n", "\n", "Be concise in your answers! Most questions can be answered in *one* sentence." ] From 0bb99a13b1f6a578d85323efc4df9908c84d0660 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 13 Oct 2020 12:08:58 +0200 Subject: [PATCH 029/142] Add initial version of chapter 01, part 1 --- README.md | 6 + chapter_00_intro/00_content.ipynb | 2 +- chapter_01_elements/00_content.ipynb | 2188 ++++++++++++++++++++++++++ 3 files changed, 2195 insertions(+), 1 deletion(-) create mode 100644 chapter_01_elements/00_content.ipynb diff --git a/README.md b/README.md index 18df65d..de59194 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,12 @@ Alternatively, the content can be viewed in a web browser [review ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/02_review.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/02_review.ipynb) ) +- **Part A: Expressing Logic** + - *Chapter 1*: Elements of a Program + ( + [content 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/00_content.ipynb) + ) #### Videos diff --git a/chapter_00_intro/00_content.ipynb b/chapter_00_intro/00_content.ipynb index 0d52716..fc6a255 100644 --- a/chapter_00_intro/00_content.ipynb +++ b/chapter_00_intro/00_content.ipynb @@ -750,7 +750,7 @@ "**Part A: Expressing Logic**\n", "\n", "- What is a programming language? What kind of words exist?\n", - " - *Chapter 1*: Elements of a Program\n", + " - *Chapter 1*: [Elements of a Program ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb)\n", " - *Chapter 2*: Functions & Modularization\n", "- What is the flow of execution? How can we form sentences from words?\n", " - *Chapter 3*: Conditionals & Exceptions\n", diff --git a/chapter_01_elements/00_content.ipynb b/chapter_01_elements/00_content.ipynb new file mode 100644 index 0000000..ad71aa9 --- /dev/null +++ b/chapter_01_elements/00_content.ipynb @@ -0,0 +1,2188 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/00_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# 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 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. At least most of the time.\n", + "\n", + "It is intuitively best to take the very mindset of a small child when learning a new language. And a programming language is no different from that. 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 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." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Example: Averaging all even Numbers in a List" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As our introductory example, we want to calculate the *average* of all *evens* in a **list** of whole numbers: `[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]`.\n", + "\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 `]`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "scrolled": true, + "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 verify that something happened in our computer's memory, we **reference** `numbers`." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + "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 **computation** could be expressed as a **sequence of instructions** in the next code cell.\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 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 **initialized** to `0` and the single `=` symbol reads as \"... is *set* equal to ...\". It cannot 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", + "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\n", + "total = 0 # running total and the count of even numbers\n", + "\n", + "for number in numbers:\n", + " if number % 2 == 0: # only work with even numbers\n", + " count = count + 1\n", + " total = total + number\n", + "\n", + "average = total / count" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We do not see any **output** yet but obtain the value of `average` by referencing it again." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7.0" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Output in a Jupyter Notebook" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Only two of the previous four code cells generate an **output** while two remained \"silent\" (i.e., nothing appears below the cell after running it).\n", + "\n", + "By default, Jupyter notebooks only show the value of the **expression** in the last line of a code cell. And, this output may also be suppressed by ending the last line with a semicolon `;`." + ] + }, + { + "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": "skip" + } + }, + "outputs": [], + "source": [ + "\"I am invisible!\";" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To see any output other than that, we use the built-in [print() ](https://docs.python.org/3/library/functions.html#print) **function**. Here, the parentheses `()` indicate that we **call** (i.e., \"execute\") code written somewhere else." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": "skip" + } + }, + "source": [ + "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." + ] + }, + { + "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": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## (Arithmetic) Operators" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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, so-called **unary** operators (e.g., negation), or \"process\" the two numbers \"around\" them, so-called **binary** operators (e.g., addition).\n", + "\n", + "By definition, operators on their own 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 in the second part 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 mimic what mathematicians call [infix notation ](https://en.wikipedia.org/wiki/Infix_notation) and have the expected meaning." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "90" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "77 + 13" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "8" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "101 - 93" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `-` operator may be used as a unary operator as well. Then, it unsurprisingly flips the sign of a number." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 11, + "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`: They are the *same* number represented as a *different* **data type**." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "2 * 21" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42.0" + ] + }, + "execution_count": 13, + "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\" 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." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "84 // 2" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "85 // 2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Even though it appears that the `//` operator **truncates** (i.e., \"cuts off\") the decimals so as to effectively round 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" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "85 % 2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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's being divisible by another implies a remainder of `0`. The implication goes in *both* directions!\n", + "\n", + "So, `49` is divisible by `7`." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "49 % 7" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Modulo division is also useful if we want to extract the last couple of digits in a large integer." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "9" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "789 % 10" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "89" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "789 % 100" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The built-in [divmod() ](https://docs.python.org/3/library/functions.html#divmod) function combines the integer and modulo divisions into one step. However, grammatically 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 a single one." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(4, 2)" + ] + }, + "execution_count": 21, + "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** `**`. It is different from the `^` operator other programming languages may use and that also exists in Python with a *different* meaning." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "8" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "2 ** 3" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "18" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "3 ** 2 * 2 " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Parentheses help avoid confusion and take the role of a **delimiter** here." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "18" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(3 ** 2) * 2" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "81" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "3 ** (2 * 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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, this is *not* a good practice and parentheses convey a much clearer picture." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "18" + ] + }, + "execution_count": 26, + "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 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 ones mentioned above." + ] + }, + { + "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 may not only \"add\" numbers but also text: This is called **concatenation**." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "greeting = \"Hi \"\n", + "audience = \"class\"" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Hi class'" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "greeting + audience" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Multiplying text with a whole number also works." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Hi Hi Hi Hi Hi Hi Hi Hi Hi Hi '" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "10 * greeting" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Objects vs. Types vs. Values" + ] + }, + { + "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** 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, and each type comes with its own 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." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "a = 42\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 built-in [id() ](https://docs.python.org/3/library/functions.html#id) function shows an object's \"address\" in memory." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "94371758832672" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(a)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140673826512720" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(b)" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140673817995952" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(c)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "These addresses are *not* meaningful for anything other than checking if two variables reference the *same* object.\n", + "\n", + "Obviously, `a` and `b` have the same *value* as revealed by the **equality operator** `==`: We say `a` and `b` \"evaluate equal.\" The resulting `True` - and the `False` further below - is yet another data type, a so-called **boolean**. We look into them in [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_03_conditionals/00_content.ipynb#Boolean-Expressions)." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a == b" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "On the contrary, `a` and `b` are *different* objects as the **identity operator** `is` shows: They are stored at *different* addresses in the memory." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a is b" + ] + }, + { + "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, namely `is not`." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a is not b" + ] + }, + { + "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 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": 37, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "int" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(a)" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "float" + ] + }, + "execution_count": 38, + "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, may be \"asked\" if it is a whole number 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** (i.e., as opposed to functions) and we look at them in detail in [Chapter 10 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_10_classes/00_content.ipynb). For now, it suffices to know that we access them with the **dot operator** `.` on the object. Of course, `b` is a whole number, which the boolean object `True` tells us." + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 39, + "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 already know it is an `int`: We see the `AttributeError` below as `a` does not even know what `is_integer()` means." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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 (i.e., `str`), which is Python's way of representing text. Strings also come with peculiar behaviors, for example, to make a text lower or upper case." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "str" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(c)" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'python rocks'" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c.lower()" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'PYTHON ROCKS'" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c.upper()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Value / (Semantic) \"Meaning\"" + ] + }, + { + "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 the bag mean to *humans*. In other words, an object's value regards its *semantic* meaning.\n", + "\n", + "For built-in data types, Python prints out an object's value as a so-called **[literal ](https://docs.python.org/3/reference/lexical_analysis.html#literals)**: This means that we may copy and paste the value back into a code cell and create a *new* object with the *same* value." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42.0" + ] + }, + "execution_count": 45, + "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* notation for `str` objects. Both types of quotes may be used interchangeably. So, the `\"Python rocks\"` from above and `'Python rocks'` below create two objects that evaluate equal (i.e., `\"Python rocks\" == 'Python rocks'`)." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Python rocks'" + ] + }, + "execution_count": 46, + "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` 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", + "If we were to write an accounting program that adds up currencies, we would, for example, have to model dollar prices as `float` objects as the dollar symbol cannot be understood by Python." + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "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": 48, + "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": [ + "Furthermore, it relies on whitespace (i.e., indentation), unlike many other programming languages. The `IndentationError` below is just a particular type of a `SyntaxError`." + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "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 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** that occur whenever otherwise (i.e., syntactically) correct code does not run because of invalid input. Runtime errors are also often referred to as **exceptions**.\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": 50, + "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, 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 first example from above with a \"tiny\" error. How fast could you have spotted it without the comment?" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "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": 52, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3.5" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Systematically finding errors is called **debugging**. For the history of the term, see this [article ](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": [ + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "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": 53, + "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 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 built-in [sum() ](https://docs.python.org/3/library/functions.html#sum) and [len() ](https://docs.python.org/3/library/functions.html#len) functions (cf., [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/00_content.ipynb#Built-in-Functions)) as well as a so-called **list comprehension** (cf., [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_08_mfr/00_content.ipynb#List-Comprehensions)). Pythonic code runs faster in many cases and is less error-prone." + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "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": 55, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "evens = [n for n in numbers if n % 2 == 0] # use of a list comprehension" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[8, 12, 2, 6, 10, 4]" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "evens" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "average = sum(evens) / len(evens) # use built-in functions" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7.0" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To get a rough overview of the mindset of a typical Python programmer, look at these rules, also known as the **Zen of Python**, that are deemed so important that they are included in every Python installation." + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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" + ] + }, + { + "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": [ + "We 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 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 Kernel and Run All Cells\" in the navigation bar once a notebook is finished. That restarts the Python process forgetting all **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 crucial to understand that \"real\" programs are almost never \"linear\" (i.e., 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 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/develop/chapter_02_functions/00_content.ipynb#Local-Modules-and-Packages)) and then use Jupyter notebooks to build up a linear report or storyline for an analysis." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "303.333px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 263565f8a129f3240ad146c4c1d4e13f1ccf5aa6 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 13 Oct 2020 12:26:31 +0200 Subject: [PATCH 030/142] Add initial version of chapter 01's exercises, part 1 --- README.md | 3 + chapter_01_elements/01_exercises.ipynb | 336 +++++++++++++++++++++++++ 2 files changed, 339 insertions(+) create mode 100644 chapter_01_elements/01_exercises.ipynb diff --git a/README.md b/README.md index de59194..51b053e 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,9 @@ Alternatively, the content can be viewed in a web browser ( [content 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/00_content.ipynb) + | + [exercises 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/01_exercises.ipynb) ) diff --git a/chapter_01_elements/01_exercises.ipynb b/chapter_01_elements/01_exercises.ipynb new file mode 100644 index 0000000..6e057b5 --- /dev/null +++ b/chapter_01_elements/01_exercises.ipynb @@ -0,0 +1,336 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/01_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 1: Elements of a Program" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Coding Exercises" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read the first part of [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb).\n", + "\n", + "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Q1**: Printing Output" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1.1**: *Concatenate* `greeting` and `audience` below with the `+` operator and print out the resulting message `\"Hello World\"` with only *one* call of the built-in [print() ](https://docs.python.org/3/library/functions.html#print) function!\n", + "\n", + "Hint: You may have to \"add\" a space character in between `greeting` and `audience`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "greeting = \"Hello\"\n", + "audience = \"World\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1.2**: How is your answer to **Q1.1** an example of the concept of **operator overloading**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1.3**: Read the documentation on the built-in [print() ](https://docs.python.org/3/library/functions.html#print) function! How can you print the above message *without* concatenating `greeting` and `audience` first in *one* call of [print() ](https://docs.python.org/3/library/functions.html#print)?\n", + "\n", + "Hint: The `*objects` in the documentation implies that we can put several *expressions* (i.e., variables) separated by commas within the same call of the [print() ](https://docs.python.org/3/library/functions.html#print) function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1.4**: What does the `sep=\" \"` mean in the documentation on the built-in [print() ](https://docs.python.org/3/library/functions.html#print) function? Adjust and use it to print out the three names referenced by `first`, `second`, and `third` on *one* line separated by *commas* with only *one* call of the [print() ](https://docs.python.org/3/library/functions.html#print) function!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "first = \"Anthony\"\n", + "second = \"Berta\"\n", + "third = \"Christian\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1.5**: Lastly, what does the `end=\"\\n\"` mean in the documentation? Adjust and use it within the `for`-loop to print the numbers `1` through `10` on *one* line with only *one* call of the [print() ](https://docs.python.org/3/library/functions.html#print) function!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:\n", + " print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### **Q2**: Simple `for`-loops" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`for`-loops are extremely versatile in Python. That is different from many other programming languages.\n", + "\n", + "As shown in the first example in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List), we can create a `list` like `numbers` and loop over the numbers in it on a one-by-one basis." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2.1**: Fill in the *condition* of the `if` statement such that only numbers divisible by `3` are printed! Adjust the call of the [print() ](https://docs.python.org/3/library/functions.html#print) function such that the `for`-loop prints out all the numbers on *one* line of output!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in numbers:\n", + " if ...:\n", + " print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Instead of looping over an *existing* object referenced by a variable like `numbers`, we may also create a *new* object within the `for` statement and loop over it directly. For example, below we write out the `list` object as a *literal*.\n", + "\n", + "Generically, the objects contained in a `list` objects are referred to as its **elements**. We reflect that in the name of the *target* variable `element` that is assigned a different number in every iteration of the `for`-loop. While we could use *any* syntactically valid name, it is best to choose one that makes sense in the context (e.g., `number` in `numbers`).\n", + "\n", + "**Q2.2**: Fill in the condition of the `if` statement such that only numbers consisting of *one* digit are printed out! As before, print out all the numbers on *one* line of output!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for element in [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]:\n", + " if ...:\n", + " print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "An easy way to loop over a `list` object in a sorted manner, is to wrap it with the built-in [sorted() ](https://docs.python.org/3/library/functions.html#sorted) function.\n", + "\n", + "**Q2.3**: Fill in the condition of the `if` statement such that only odd numbers are printed out! Put all the numbers on *one* line of output!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in sorted(numbers):\n", + " if ...:\n", + " print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Whenever we want to loop over numbers representing a [series ](https://en.wikipedia.org/wiki/Series_%28mathematics%29) in the mathematical sense (i.e., a rule to calculate the next number from its predecessor), we may be able to use the [range() ](https://docs.python.org/3/library/functions.html#func-range) built-in.\n", + "\n", + "For example, to loop over the whole numbers from `0` to `9` (both including) in order, we could write them out in a `list` like below.\n", + "\n", + "**Q2.4**: Fill in the call to the [print() ](https://docs.python.org/3/library/functions.html#print) function such that all the numbers are printed on *one* line ouf output!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]:\n", + " print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2.5**: Read the documentation on the [range() ](https://docs.python.org/3/library/functions.html#func-range) built-in! It may be used with either one, two, or three expressions \"passed\" in. What do `start`, `stop`, and `step` mean? Fill in the calls to [range() ](https://docs.python.org/3/library/functions.html#func-range) and [print() ](https://docs.python.org/3/library/functions.html#print) to mimic the output of **Q2.4**!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in range(...):\n", + " print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2.6**: Fill in the calls to [range() ](https://docs.python.org/3/library/functions.html#func-range) and [print() ](https://docs.python.org/3/library/functions.html#print) to print out *all* numbers from `1` to `10` (both including)!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in range(...):\n", + " print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2.7**: Fill in the calls to [range() ](https://docs.python.org/3/library/functions.html#func-range) and [print() ](https://docs.python.org/3/library/functions.html#print) to print out the *even* numbers from `1` to `10` (both including)! Do *not* use an `if` statement to accomplish this!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in range(...):\n", + " print(...)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From abeedd1265a5f88a4f5d2b9c78e370ca296fdcde Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 13 Oct 2020 15:10:15 +0200 Subject: [PATCH 031/142] Add initial version of chapter 01, part 2 --- README.md | 3 + chapter_00_intro/00_content.ipynb | 2 +- chapter_01_elements/02_content.ipynb | 1665 ++++++++++++++++++++++++++ 3 files changed, 1669 insertions(+), 1 deletion(-) create mode 100644 chapter_01_elements/02_content.ipynb diff --git a/README.md b/README.md index 51b053e..ef3422e 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,9 @@ Alternatively, the content can be viewed in a web browser | [exercises 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/01_exercises.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/01_exercises.ipynb) + | + [content 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/02_content.ipynb) ) diff --git a/chapter_00_intro/00_content.ipynb b/chapter_00_intro/00_content.ipynb index fc6a255..219fbf3 100644 --- a/chapter_00_intro/00_content.ipynb +++ b/chapter_00_intro/00_content.ipynb @@ -496,7 +496,7 @@ } }, "source": [ - "The default Python implementation is written in the C language.\n", + "The default Python implementation is written in the C language and called CPython. This is also what the Anaconda Distribution uses.\n", "\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.\n", "\n", diff --git a/chapter_01_elements/02_content.ipynb b/chapter_01_elements/02_content.ipynb new file mode 100644 index 0000000..ae9459c --- /dev/null +++ b/chapter_01_elements/02_content.ipynb @@ -0,0 +1,1665 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/02_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 1: Elements of a Program (Part 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this second part of Chapter 1, we look a bit closer into how the memory works and introduce a couple of \"theoretical\" terms." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Variables vs. Names vs. Identifiers vs. References" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Variables** are created with the **[assignment statement ](https://docs.python.org/3/reference/simple_stmts.html#assignment-statements)** `=`, which is *not* an operator 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* means the combination of a *name* and a *reference* to an object in memory." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "variable = 20.0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When used as a *literal*, a variable evaluates to the value of the object it references. Colloquially, we could say that `variable` 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." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "20.0" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "variable" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "variable = 20" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "20" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "variable" + ] + }, + { + "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 may also **update** it using a so-called **[augmented assignment statement ](https://docs.python.org/3/reference/simple_stmts.html#augmented-assignment-statements)** (i.e., *not* operator), as 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." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "variable *= 4 # same as variable = variable * 4" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "80" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "variable" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "variable //= 2 # same as variable = variable // 2; \"//\" to retain the integer type" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "40" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "variable" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "variable += 2 # same as variable = variable + 2" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "variable" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Variables are **[dereferenced ](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 a variable references but merely removes the variable's name from the \"global list of all names.\"" + ] + }, + { + "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": [ + "variable" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "del variable" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we refer to an unknown name, a *runtime* error occurs, namely a `NameError`. The `Name` in `NameError` gives a hint why we choose the term *name* over *identifier* above: Python uses it more often in its error messages." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'variable' 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[0mvariable\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mNameError\u001b[0m: name 'variable' is not defined" + ] + } + ], + "source": [ + "variable" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Some variables magically exist when a Python process is started or are added by Jupyter. We may safely ignore the former until [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_11_classes/00_content.ipynb) and the latter for good." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'__main__'" + ] + }, + "execution_count": 14, + "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": 15, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['In',\n", + " 'Out',\n", + " '_',\n", + " '_10',\n", + " '_11',\n", + " '_14',\n", + " '_2',\n", + " '_4',\n", + " '_6',\n", + " '_8',\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", + " '_i2',\n", + " '_i3',\n", + " '_i4',\n", + " '_i5',\n", + " '_i6',\n", + " '_i7',\n", + " '_i8',\n", + " '_i9',\n", + " '_ih',\n", + " '_ii',\n", + " '_iii',\n", + " '_oh',\n", + " 'exit',\n", + " 'get_ipython',\n", + " 'quit',\n", + " 'register_readline_completion',\n", + " 'sys']" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dir()" + ] + }, + { + "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 (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)** like `for` or `if`.\n", + "\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." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### Good examples" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "pi = 3.14" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "answer_to_everything = 42" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "my_name = \"Alexander\"" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "work_address = \"WHU, Burgplatz 2, Vallendar\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "#### Bad examples" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "PI = 3.14 # unless used as a \"global\" constant" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "answerToEverything = 42 # this is a style used in languages like Java" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "name = \"Alexander\" # name of what?" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "cannot 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 = \"WHU, Burgplatz 2, Vallendar\"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m cannot assign to operator\n" + ] + } + ], + "source": [ + "address@work = \"WHU, Burgplatz 2, Vallendar\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If a variable name collides with a built-in name, we add a trailing underscore." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "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 jargon, are used for built-in functionalities and to implement object-oriented features as we see in [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_11_classes/00_content.ipynb). We must *not* use this style for variables!" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'__main__'" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "__name__" + ] + }, + { + "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 *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", + "Let's make `b` reference whatever object `a` is referencing." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "a = 42" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "b = a" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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* `87` object and make `a` reference it." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "a = 87" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "87" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`b` \"is still the same\" as before. To be precise, `b` still references the *same object* as before." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, if a variable references an object of a more \"complex\" type (e.g., `list`), predicting the outcome of a code snippet may be unintuitive for a beginner." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "x = [1, 2, 3]" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "list" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(x)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "y = x" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3]" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "y" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's change the first element of `x`.\n", + "\n", + "[Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_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 yet another operator, namely the **indexing operator**. So, `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 we put in the list. The indexing operator must be an operator as we merely *read* the first element and do not change anything in memory permanently.\n", + "\n", + "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": 36, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 36, + "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 *not* create a *new* variable, nor overwrite an existing one, but only changes the object referenced as the first element in `x`. As we only change parts of the `x` object, we say that we **mutate** 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." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "x[0] = 99" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[99, 2, 3]" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The changes made to the object `x` is referencing can also be seen through the `y` variable!" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[99, 2, 3]" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "y" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The difference in behavior illustrated in this sub-section has to do with the fact that `int` and `float` objects are **immutable** types while `list` objects are **mutable**.\n", + "\n", + "In the first case, an object cannot be changed \"in place\" once it is created in memory. When we assigned `87` 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 `int` 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* `int` 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 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 must always expect that the latter may have more than one variable referencing it.\n", + "\n", + "Visualizing what is going on in 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) may be helpful for a beginner." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## How Python reads \"Commands\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As we saw in the previous `list` example, it is important to understand in what order Python executes the \"commands\" (= not an officially used term) we give it. In this last section of the chapter, we introduce a classification scheme and look at its implications." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Expressions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "An **[expression ](https://docs.python.org/3/reference/expressions.html)** is any syntactically correct *combination* of *variables* and *literals* with *operators* that **evaluates** (i.e., \"becomes\") to an object. That object may already exist *before* the expression is parsed or created as a result thereof. The point is that *after* the expression is parsed, Python returns a reference to this object that may be used for further processing.\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", + "What we have said about *individual* operators before, namely that they have *no* permanent side effects in memory, actually belongs here, to begin with: The absence of any *permanent* side effects is the characteristic property of expressions, and all the code cells in the \"*(Arithmetic) Operators*\" section in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb#%28Arithmetic%29-Operators) of this chapter are examples of expressions.\n", + "\n", + "The simplest possible expressions contain only one variable or literal. The output below a code cell is Jupyter's way of returning the reference to the object to us!\n", + "\n", + "Whereas `a` evaluates to the *existing* `87` object, ..." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "87" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "... parsing the literal `42` creates a *new* `int` object and returns a reference to it (Note: for optimization reasons, the CPython implementation may already have a `42` object in memory)." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For sure, we need to include operators to achieve something useful. Here, Python takes the existing `a` object, creates a new `42` object, creates the resulting `45` object, and returns a reference to that." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "45" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a - 42" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The definition of an expression is **recursive**. So, the sub-expression `a - 42` is combined with the literal `9` by the operator `//` to form the full expression `(a - 42) // 9`." + ] + }, + { + "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": [ + "(a - 42) // 9" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Below, the variable `x` is combined with the literal `2` by the indexing operator `[]`. The resulting expression evaluates to the third element in the `x` list." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x[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 saw this syntax before when we called built-in functions and methods." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "104" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(x)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Statements" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A **[statement ](https://docs.python.org/3/reference/simple_stmts.html)** is any *single* command Python understands.\n", + "\n", + "So, what we mean with statement is a [strict superset ](https://en.wikipedia.org/wiki/Subset) of what we mean with expression: All expressions are statements, but some statements are *not* expressions. That implies that all code cells in the previous \"*Expressions*\" section are also examples of statements.\n", + "\n", + "Unfortunately, many texts on Python use the two terms as mutually exclusive concepts by saying that a statement (always) *changes* the *state of a program* with a permanent *side effect* in memory (e.g., by creating a variable) whereas an expression does not. That is not an accurate way of distinguishing the two in all cases!\n", + "\n", + "So, often, the term statement is used in the meaning of \"a statement that is *not* an expression.\" We can identify such statements easily in Jupyter as the correspoding code cells have *no* output below them.\n", + "\n", + "While many statements, for example `=` and `del`, indeed have *permanent* side effects, ..." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "a = 21 * 2" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "del a" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... calling the built-in [print() ](https://docs.python.org/3/library/functions.html#print) function does *neither* change the memory *nor* evaluate to an object (disregarding the `None` object explained in [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/00_content.ipynb#Function-Definitions)). We could view changing the computer's screen as a side effect but this is outside of Python's memory!\n", + "\n", + "Also, the cell below has *no* output! It only looks like it does as Jupyter redirects whatever [print() ](https://docs.python.org/3/library/functions.html#print) writes to the \"screen\" to below a cell. We see a difference to the expressions above in that there are no brackets `[...]` next to the output showing the execution count number." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Don't I change the state of the computer's display?\n" + ] + } + ], + "source": [ + "print(\"Don't I change the state of the computer's display?\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Logical vs. Physical Lines" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "How many lines of code does the next cell constitute?" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The answer is: 42\n" + ] + } + ], + "source": [ + "result = 21 * 2; print(\"The answer is:\", result)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The answer depends on how we are counting. If we count the number of lines of written source code, the answer is *one*. This gives us the number of **physical lines**. On the contrary, if we count the number of statements separated by the `;`, the answer is *two*. This gives us the number of **logical lines**.\n", + "\n", + "While physical lines are what we, the humans, see, logical lines are how computers read. To align the two ways, it is a best practice to not write more than one logical lines on a single physical one. So, from Python's point of view, the cells above and below are the same. More importantly, the one above is *not* somehow \"magically\" faster." + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The answer is: 42\n" + ] + } + ], + "source": [ + "result = 21 * 2\n", + "print(\"The answer is:\", result)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It is also possible to write a single logical line over several physical ones. The cell below is yet another physical representation of the same *two* logical lines. Any pair of delimiters, like `(` and `)` below, can be used to \"format\" the code in between with whitespace. The style guides mentioned before should still be taken into account (i.e., indent with 4 spaces)." + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The answer is: 42\n" + ] + } + ], + "source": [ + "result = 21 * 2\n", + "print(\n", + " \"The answer is:\",\n", + " result\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Another situation, in which several physical lines are treated as a logical one, is with so-called **[compound statements ](https://docs.python.org/3/reference/compound_stmts.html)**. In contrast to simple statements like `=` and `del` above, the purpose of compound statements is to group other statements.\n", + "\n", + "We have seen two examples of compound statements already: `for` and `if`. They both consist of a **header** line ending with a `:` and an indented **(code) block** spanning an arbitrary number of logical lines.\n", + "\n", + "In the example, the first logical line spans the entire cell, the second logical line contains all physical lines except the first, and the third and fourth logical lines are just the third and fourth physical lines, respectively." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2 -> 4\n" + ] + } + ], + "source": [ + "for number in [1, 2, 3]:\n", + " if number % 2 == 0:\n", + " double = 2 * number \n", + " print(number, \"->\", double)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Comments" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A comment is a physical line that is *not* a logical line.\n", + "\n", + "We use the `#` symbol to write comments in plain text right into the code. Anything after the `#` until the end of the physical line is ignored by Python.\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. Rather, comments should describe *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." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "distance = 891 # in meters\n", + "elapsed_time = 93 # in seconds\n", + "\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 need to use a comment.\n", + "The second cell is a lot more Pythonic." + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "seconds = 365 * 24 * 60 * 60 # = seconds in the year" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "seconds_per_year = 365 * 24 * 60 * 60" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "303.333px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 5163c34d534e33d46e2bddcabe13a220f0118a5f Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 13 Oct 2020 15:19:55 +0200 Subject: [PATCH 032/142] Add initial version of chapter 01's exercises, part 2 --- README.md | 3 + chapter_01_elements/03_exercises.ipynb | 219 +++++++++++++++++++++++++ 2 files changed, 222 insertions(+) create mode 100644 chapter_01_elements/03_exercises.ipynb diff --git a/README.md b/README.md index ef3422e..07873b2 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,9 @@ Alternatively, the content can be viewed in a web browser | [content 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/02_content.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/02_content.ipynb) + | + [exercises 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/03_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/03_exercises.ipynb) ) diff --git a/chapter_01_elements/03_exercises.ipynb b/chapter_01_elements/03_exercises.ipynb new file mode 100644 index 0000000..d9d0bbf --- /dev/null +++ b/chapter_01_elements/03_exercises.ipynb @@ -0,0 +1,219 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/03_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 1: Elements of a Program (Part 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Coding Exercises" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read the second part of [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/02_content.ipynb).\n", + "\n", + "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Python as a Calculator" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The [volume of a sphere ](https://en.wikipedia.org/wiki/Sphere) is defined as $\\frac{4}{3} * \\pi * r^3$.\n", + "\n", + "**Q1**: Calculate it for `r = 2.88` and approximate $\\pi$ with `pi = 3.14`!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pi = 3.14\n", + "r = 2.88" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "While Python may be used as a calculator, it behaves a bit differently compared to calculator apps that phones or computers come with and that we are accustomed to.\n", + "\n", + "A major difference is that Python \"forgets\" intermediate results that are not assigned to variables. On the contrary, the calculators we work with outside of programming always keep the last result and allow us to use it as the first input for the next calculation.\n", + "\n", + "One way to keep on working with intermediate results in Python is to write the entire calculation as just *one* big expression that is composed of many sub-expressions representing the individual steps in our overall calculation.\n", + "\n", + "**Q2.1**: Given `a` and `b` like below, subtract the smaller `a` from the larger `b`, divide the difference by `9`, and raise the result to the power of `2`! Use operators that preserve the `int` type of the final result! The entire calculations *must* be placed within *one* code cell.\n", + "\n", + "Hint: You may need to group sub-expressions with parentheses `(` and `)`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "a = 42\n", + "b = 87" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The code cell below contains nothing but a single underscore `_`. In both, a Python command-line prompt and Jupyter notebooks, the variable `_` is automatically updated and always references the object to which the *last* expression executed evaluated to.\n", + "\n", + "**Q2.2**: Execute the code cell below! It should evaluate to the *same* result as the previous code cell (i.e., your answer to **Q2.1** assuming you go through this notebook in order)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "_" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2.3**: Implement the same overall calculation as in your answer to **Q2.1** in several independent steps (i.e., code cells)! Use only *one* operator per code cell!\n", + "\n", + "Hint: You should need *two* more code cells after the `b - a` one immediately below. If you *need* to use parentheses, you must be doing something wrong." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "b - a" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "_ ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "_ ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3.1**: When answering the questions above, you should have used only **expressions** in the code cells. What are expressions syntactically?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3.2**: The code cells that provide the numbers to work with contain **statements** that are *not* expressions. What are statements? How are they different from expressions?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 777da3568d7a539023cf909e1113f0c7558e2dd3 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 13 Oct 2020 15:24:21 +0200 Subject: [PATCH 033/142] Add initial version of chapter 01's summary --- README.md | 2 + chapter_01_elements/04_summary.ipynb | 154 +++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 chapter_01_elements/04_summary.ipynb diff --git a/README.md b/README.md index 07873b2..425e21f 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,8 @@ Alternatively, the content can be viewed in a web browser | [exercises 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/03_exercises.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/03_exercises.ipynb) + | + [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/04_summary.ipynb) ) diff --git a/chapter_01_elements/04_summary.ipynb b/chapter_01_elements/04_summary.ipynb new file mode 100644 index 0000000..9f52d19 --- /dev/null +++ b/chapter_01_elements/04_summary.ipynb @@ -0,0 +1,154 @@ +{ + "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": [ + "## TL;DR" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We end each chapter with a summary of the main points (i.e., **TL;DR** = \"too long; didn't read\"). The essence in this first chapter is that just as a sentence in a real language like English may be decomposed into its parts (e.g., subject, predicate, and objects), the same may be done with programming languages.\n", + "\n", + "- program\n", + " - **sequence** of **instructions** that specify how to perform a computation (= a \"recipe\")\n", + " - a \"black box\" that processes **inputs** and transforms them into meaningful **outputs** in a *deterministic* way\n", + " - conceptually similar to a mathematical function $f$ that maps some input $x$ to an output $y = f(x)$\n", + "\n", + "\n", + "- input (examples)\n", + " - data from a CSV file\n", + " - text entered on a command line\n", + " - data obtained from a database\n", + " - etc.\n", + "\n", + "\n", + "- output (examples)\n", + " - result of a computation (e.g., statistical summary of a sample dataset)\n", + " - a \"side effect\" (e.g., a transformation of raw input data into cleaned data)\n", + " - a physical \"behavior\" (e.g., a robot moving or a document printed)\n", + " - etc.\n", + "\n", + "\n", + "- objects\n", + " - distinct and well-contained areas/parts of the memory that hold the actual data\n", + " - the concept by which Python manages the memory for us\n", + " - can be classified into objects of the same **type** (i.e., same abstract \"structure\" but different concrete data)\n", + " - built-in objects (incl. **literals**) vs. user-defined objects (cf., [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_11_classes/00_content.ipynb))\n", + " - e.g., `1`, `1.0`, and `\"one\"` are three different objects of distinct types that are also literals (i.e., by the way we type them into the command line Python knows what the value and type are)\n", + "\n", + "\n", + "- variables\n", + " - storage of intermediate **state**\n", + " - are **names** referencing **objects** in **memory**\n", + " - e.g., `x = 1` creates the variable `x` that references the object `1`\n", + "\n", + "\n", + "- operators\n", + " - special built-in symbols that perform operations with objects in memory\n", + " - usually, operate with one or two objects\n", + " - e.g., addition `+`, subtraction `-`, multiplication `*`, and division `/` all take two objects, whereas the negation `-` only takes one\n", + "\n", + "\n", + "- expressions\n", + " - **combinations** of **variables** (incl. **literals**) and **operators**\n", + " - do *not* change the involved objects/state of the program\n", + " - evaluate to a **value** (i.e., the \"result\" of the expression, usually a new object)\n", + " - e.g., `x + 2` evaluates to the (new) object `3` and `1 - 1.0` to `0.0`\n", + "\n", + "\n", + "- statements\n", + " - instructions that **\"do\" something** and **have side effects** in memory\n", + " - (re-)assign names to (different) objects, *change* an existing object *in place*, or, more conceptually, *change* the state of the program\n", + " - usually, work with expressions\n", + " - e.g., the assignment statement `=` makes a name reference an object\n", + "\n", + "\n", + "- comments\n", + " - **prose** supporting a **human's understanding** of the program\n", + " - ignored by Python\n", + "\n", + "\n", + "- functions (cf., [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_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/develop/chapter_03_conditionals/00_content.ipynb) and [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_04_iteration/00_content.ipynb))\n", + " - expression of **business logic** or an **algorithm**\n", + " - conditional execution of parts of a program (e.g., `if` statements)\n", + " - repetitive execution of parts of a program (e.g., `for`-loops)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "303.333px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 6fb318d13aaae4c0c454bffa0d2297a9bc803f62 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 13 Oct 2020 15:25:22 +0200 Subject: [PATCH 034/142] Add initial version of chapter 01's review --- README.md | 2 + chapter_01_elements/05_review.ipynb | 222 ++++++++++++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100644 chapter_01_elements/05_review.ipynb diff --git a/README.md b/README.md index 425e21f..d090fdb 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ Alternatively, the content can be viewed in a web browser [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/03_exercises.ipynb) | [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/04_summary.ipynb) + | + [review ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/05_review.ipynb) ) diff --git a/chapter_01_elements/05_review.ipynb b/chapter_01_elements/05_review.ipynb new file mode 100644 index 0000000..d8ec024 --- /dev/null +++ b/chapter_01_elements/05_review.ipynb @@ -0,0 +1,222 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 1: Elements of a Program" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Content Review" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb) and the [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/02_content.ipynb) part of Chapter 1.\n", + "\n", + "Be concise in your answers! Most questions can be answered in *one* sentence." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Essay Questions " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: Elaborate on how **modulo division** might be a handy operation to know!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: What is a **dynamically typed** language? How does it differ from a **statically typed** language? What does that mean for Python?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: Why is it useful to start counting at $0$?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: What is **operator overloading**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: What are the basic **naming conventions** for variables? What happens if a name collides with one of Python's [keywords ](https://docs.python.org/3/reference/lexical_analysis.html#keywords)?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: Advocates of the [functional programming ](https://en.wikipedia.org/wiki/Functional_programming) paradigm suggest not to use **mutable** data types in a program. What are the advantages of that approach? What might be a downside?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### True / False Questions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Motivate your answer with *one short* sentence!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: \"**dunder**\" refers to a group of Australian (i.e., \"down under\") geeks that work on core Python." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: The **Zen of Python** talks about Indian genius programmers." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: When NASA famously converted some measurements to the wrong unit and lost a Mars satellite in 1999 (cf., [source](https://www.wired.com/2010/11/1110mars-climate-observer-report/)), this is an example of a so-called **runtime error**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: [PEP 8 ](https://www.python.org/dev/peps/pep-0008/) suggests that developers use **8 spaces** per level of indentation." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From ba9addd56272790b2599dbb59bbe0837d1fd3917 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 13 Oct 2020 15:26:18 +0200 Subject: [PATCH 035/142] Chapter reviews are not interactive --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index d090fdb..ff4e0b7 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,6 @@ Alternatively, the content can be viewed in a web browser [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/01_exercises.ipynb) | [review ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/02_review.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/02_review.ipynb) ) - **Part A: Expressing Logic** - *Chapter 1*: Elements of a Program From 5b6fd7122fc8625c725ca71f17957d0caf3a9416 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 13 Oct 2020 15:28:02 +0200 Subject: [PATCH 036/142] Add initial version of chapter 01's further resources --- README.md | 3 + chapter_01_elements/06_resources.ipynb | 120 +++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 chapter_01_elements/06_resources.ipynb diff --git a/README.md b/README.md index ff4e0b7..0c4e26d 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,9 @@ Alternatively, the content can be viewed in a web browser [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/04_summary.ipynb) | [review ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/05_review.ipynb) + | + [resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/06_resources.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/06_resources.ipynb) ) diff --git a/chapter_01_elements/06_resources.ipynb b/chapter_01_elements/06_resources.ipynb new file mode 100644 index 0000000..bdc2e29 --- /dev/null +++ b/chapter_01_elements/06_resources.ipynb @@ -0,0 +1,120 @@ +{ + "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": [ + "## 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 and, thus, reviews how *variables are just names referencing objects*. 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": 1, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from IPython.display import YouTubeVideo\n", + "YouTubeVideo(\"_AEJHKGk9ns\", width=\"60%\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "303.333px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 138bc12945589801cbfe22ca1f28b48b659f4814 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 13 Oct 2020 15:36:35 +0200 Subject: [PATCH 037/142] Streamline slides --- chapter_01_elements/02_content.ipynb | 80 +++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/chapter_01_elements/02_content.ipynb b/chapter_01_elements/02_content.ipynb index ae9459c..ea72a59 100644 --- a/chapter_01_elements/02_content.ipynb +++ b/chapter_01_elements/02_content.ipynb @@ -24,7 +24,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "In this second part of Chapter 1, we look a bit closer into how the memory works and introduce a couple of \"theoretical\" terms." ] @@ -1089,14 +1093,22 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "source": [ "## How Python reads \"Commands\"" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "As we saw in the previous `list` example, it is important to understand in what order Python executes the \"commands\" (= not an officially used term) we give it. In this last section of the chapter, we introduce a classification scheme and look at its implications." ] @@ -1157,7 +1169,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "... parsing the literal `42` creates a *new* `int` object and returns a reference to it (Note: for optimization reasons, the CPython implementation may already have a `42` object in memory)." ] @@ -1400,7 +1416,7 @@ "execution_count": 48, "metadata": { "slideshow": { - "slide_type": "skip" + "slide_type": "fragment" } }, "outputs": [ @@ -1418,14 +1434,22 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "source": [ "### Logical vs. Physical Lines" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "How many lines of code does the next cell constitute?" ] @@ -1433,7 +1457,11 @@ { "cell_type": "code", "execution_count": 49, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [ { "name": "stdout", @@ -1449,7 +1477,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "The answer depends on how we are counting. If we count the number of lines of written source code, the answer is *one*. This gives us the number of **physical lines**. On the contrary, if we count the number of statements separated by the `;`, the answer is *two*. This gives us the number of **logical lines**.\n", "\n", @@ -1459,7 +1491,11 @@ { "cell_type": "code", "execution_count": 50, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "name": "stdout", @@ -1476,7 +1512,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "It is also possible to write a single logical line over several physical ones. The cell below is yet another physical representation of the same *two* logical lines. Any pair of delimiters, like `(` and `)` below, can be used to \"format\" the code in between with whitespace. The style guides mentioned before should still be taken into account (i.e., indent with 4 spaces)." ] @@ -1484,7 +1524,11 @@ { "cell_type": "code", "execution_count": 51, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "name": "stdout", @@ -1504,7 +1548,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "Another situation, in which several physical lines are treated as a logical one, is with so-called **[compound statements ](https://docs.python.org/3/reference/compound_stmts.html)**. In contrast to simple statements like `=` and `del` above, the purpose of compound statements is to group other statements.\n", "\n", @@ -1516,7 +1564,11 @@ { "cell_type": "code", "execution_count": 52, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [ { "name": "stdout", From 9b5e395a0e6668a6990c7dd7f7e6b656ca504dce Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 13 Oct 2020 20:10:46 +0200 Subject: [PATCH 038/142] Make notebooks bullet points in the ToC --- README.md | 63 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 0c4e26d..4bcb4c6 100644 --- a/README.md +++ b/README.md @@ -15,37 +15,42 @@ Alternatively, the content can be viewed in a web browser or interactively (i.e., code can be executed) on [Binder ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab). - *Chapter 0*: Introduction - ( - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/00_content.ipynb) - | - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/01_exercises.ipynb) - | - [review ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/02_review.ipynb) - ) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/00_content.ipynb) + (Python's History & Background; + Open-source & Communities; + JupyterLab; + Programming vs. Computer Science; + Learning Tips) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/01_exercises.ipynb) + (Mastering Markdown) + - [review ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/02_review.ipynb) - **Part A: Expressing Logic** - *Chapter 1*: Elements of a Program - ( - [content 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/00_content.ipynb) - | - [exercises 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/01_exercises.ipynb) - | - [content 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/02_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/02_content.ipynb) - | - [exercises 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/03_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/03_exercises.ipynb) - | - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/04_summary.ipynb) - | - [review ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/05_review.ipynb) - | - [resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/06_resources.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/06_resources.ipynb) - ) + - [content 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/00_content.ipynb) + (A first Example: Averaging Even Numbers; + Operators; + Objects & Data Types; + Errors) + - [exercises 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/01_exercises.ipynb) + (Printing Output; + Simple `for`-loops) + - [content 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/02_content.ipynb) + (Memory in Detail; + Variables & References; + Mutability; + Expressions & Statements) + - [exercises 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/03_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/03_exercises.ipynb) + (Python as a Calculator) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/04_summary.ipynb) + - [review ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/05_review.ipynb) + - [resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/06_resources.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/06_resources.ipynb) #### Videos From 1cf468610e86115bbb3078953a8d9ceeca137fea Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 13 Oct 2020 23:19:17 +0200 Subject: [PATCH 039/142] Add initial version of chapter 02, part 1 --- README.md | 8 + chapter_00_intro/00_content.ipynb | 2 +- chapter_02_functions/00_content.ipynb | 3060 +++++++++++++++++++++++++ 3 files changed, 3069 insertions(+), 1 deletion(-) create mode 100644 chapter_02_functions/00_content.ipynb diff --git a/README.md b/README.md index 4bcb4c6..2065664 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,14 @@ Alternatively, the content can be viewed in a web browser - [review ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/05_review.ipynb) - [resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/06_resources.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/06_resources.ipynb) + - *Chapter 2*: Functions & Modularization + - [content 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_02_functions/00_content.ipynb) + (Built-in Functions & Constructors; + Function Definitions; + Function Calls & Scoping Rules; + Positional vs. Keyword Arguments; + Modularization) #### Videos diff --git a/chapter_00_intro/00_content.ipynb b/chapter_00_intro/00_content.ipynb index 219fbf3..b3f20a3 100644 --- a/chapter_00_intro/00_content.ipynb +++ b/chapter_00_intro/00_content.ipynb @@ -751,7 +751,7 @@ "\n", "- What is a programming language? What kind of words exist?\n", " - *Chapter 1*: [Elements of a Program ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb)\n", - " - *Chapter 2*: Functions & Modularization\n", + " - *Chapter 2*: [Functions & Modularization ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/00_content.ipynb)\n", "- What is the flow of execution? How can we form sentences from words?\n", " - *Chapter 3*: Conditionals & Exceptions\n", " - *Chapter 4*: Recursion & Looping" diff --git a/chapter_02_functions/00_content.ipynb b/chapter_02_functions/00_content.ipynb new file mode 100644 index 0000000..d7e21b5 --- /dev/null +++ b/chapter_02_functions/00_content.ipynb @@ -0,0 +1,3060 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_02_functions/00_content.ipynb)." + ] + }, + { + "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 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List), we simply typed the code to calculate the average of the even numbers in a list of whole numbers 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. 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", + "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. Also, we look at how we can extend our Python installation with functionalities written by other people." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## [Built-in Functions ](https://docs.python.org/3/library/functions.html)" + ] + }, + { + "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 (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), or [id() ](https://docs.python.org/3/library/functions.html#id)). The [documentation ](https://docs.python.org/3/library/functions.html) has the full list. Just as core Python itself, they are mostly implemented in C and thus very fast.\n", + "\n", + "Below, [sum() ](https://docs.python.org/3/library/functions.html#sum) adds up all the elements in the `numbers` list while [len() ](https://docs.python.org/3/library/functions.html#len) counts the number of elements in it." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "78" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`sum` and `len` are *no* [keywords ](https://docs.python.org/3/reference/lexical_analysis.html#keywords) like `for` or `if` but variables that reference *objects* in memory. Often, we hear people say that \"everything is an object in Python\" (e.g., this [question ](https://stackoverflow.com/questions/40478536/in-python-what-does-it-mean-by-everything-is-an-object)). While this phrase may sound abstract in the beginning, it simply means that the entire memory is organized with \"bags\" of $0$s and $1$s, and there are even bags for the built-in functions. That is *not* true for many other languages (e.g., C or Java) and often a source of confusion for people coming to Python from another language.\n", + "\n", + "The built-in [id() ](https://docs.python.org/3/library/functions.html#id) function tells us where in memory a particular built-in function is stored." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139940703477088" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(sum)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139940703476048" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(len)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[type() ](https://docs.python.org/3/library/functions.html#type) reveals that built-in functions like [sum() ](https://docs.python.org/3/library/functions.html#sum) or [len() ](https://docs.python.org/3/library/functions.html#len) are objects of type `builtin_function_or_method`." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "builtin_function_or_method" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(sum)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "builtin_function_or_method" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(len)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Python's object-oriented nature allows us to have functions work with themselves. While seemingly not useful from a beginner's point of view, that enables a lot of powerful programming styles later on." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139940703475648" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(id)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "type" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(type)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To execute a function, we **call** it with the **call operator** `()` as shown many times in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb) and above.\n", + "\n", + "If we are unsure whether a variable references a function or not, we can verify that with the built-in [callable() ](https://docs.python.org/3/library/functions.html#callable) function.\n", + "\n", + "Abstractly speaking, *any* object that can be called with the call operator `()` is a so-called **callable**. And, objects of type `builtin_function_or_method` are just one kind of examples thereof. We will see another one already in the next sub-section." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "callable(sum)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "callable(len)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`list` objects, for example, are *not* callable." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "callable(numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Constructors" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The list of [built-in functions ](https://docs.python.org/3/library/functions.html) in the documentation should really be named a list of built-in *callables*.\n", + "\n", + "Besides the built-in functions, the list also features **constructors** for the built-in types. They may be used to **[cast ](https://en.wikipedia.org/wiki/Type_conversion)** (i.e., \"convert\") any object as an object of a given type.\n", + "\n", + "For example, to \"convert\" a `float` or a `str` into an `int` object, we use the [int() ](https://docs.python.org/3/library/functions.html#int) built-in. Below, *new* `int` objects are created from the `7.0` and `\"7\"` objects that are *newly* created themselves before being processed by [int() ](https://docs.python.org/3/library/functions.html#int) right away *without* ever being referenced by a variable." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(7.0)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(\"7\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Casting an object as an `int` is different from rounding with the built-in [round() ](https://docs.python.org/3/library/functions.html#round) function!" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(7.99)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "8" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "round(7.99)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Notice the subtle difference compared to the behavior of the `//` operator in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb##%28Arithmetic#%29-Operators) that \"rounds\" towards minus infinity: [int() ](https://docs.python.org/3/library/functions.html#int) always \"rounds\" towards `0`." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-7" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(-7.99)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Not all conversions are valid and *runtime* errors may occur as the `ValueError` shows." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "ValueError", + "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\"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(\"seven\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We may also cast in the other direction with the [float() ](https://docs.python.org/3/library/functions.html#float) or [str() ](https://docs.python.org/3/library/functions.html#func-str) built-ins." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7.0" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(7)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'7'" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "str(7)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Constructors are full-fledged objects as well." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "94623097764960" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(int)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "94623097758720" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(float)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "They are of type `type`, which is different from `builtin_function_or_method` above." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "type" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(int)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "type" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(float)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As already noted, constructors are *callables*. In that regard, they behave the same as built-in functions. We may call them with the call operator `()`." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "callable(int)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "callable(float)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The attentive student may already have discovered that we refer to `builtin_function_or_method` objects as \"built-in functions\" and `type` objects as just \"built-ins.\" For a beginner, that difference is not so important. But, the ambitious student should already be aware that such subtleties exist.\n", + "\n", + "Next, let's look at a third kind of callables." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Function Definitions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We may create so-called *user-defined* **functions** with the `def` statement (cf., [reference ](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)). To extend an already familiar example, we reuse the introductory example from [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb#Best-Practices) in its final Pythonic version and transform it into the function `average_evens()` below. We replace the variable name `numbers` with `integers` for didactical purposes in the first couple of examples.\n", + "\n", + "A function's **name** must be chosen according to the same naming rules as ordinary variables since 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 can then be referenced within the indented **code block**: They are listed within the parentheses in the `def` statement (i.e., `integers` below). \n", + "\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(integers)` below).\n", + "\n", + "A function may specify an *explicit* **return value** (i.e., \"result\" or \"output\") 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**. For example, the built-in [print() ](https://docs.python.org/3/library/functions.html#print) function changes what we see on the screen. Strictly speaking, [print() ](https://docs.python.org/3/library/functions.html#print) and other void functions also have an *implicit* return value, namely the `None` object.\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/develop/chapter_06_text/00_content.ipynb#The-str-Type). Widely adopted standards for docstrings are [PEP 257 ](https://www.python.org/dev/peps/pep-0257/) and section 3.8 of [Google's Python Style Guide ](https://github.com/google/styleguide/blob/gh-pages/pyguide.md)." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def average_evens(integers):\n", + " \"\"\"Calculate the average of all even numbers in a list.\n", + "\n", + " Args:\n", + " integers (list of int's): whole numbers to be averaged\n", + "\n", + " Returns:\n", + " average (float)\n", + " \"\"\"\n", + " evens = [n for n in integers 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 may be referenced just like any other variable by its name (i.e., *without* the parentheses)." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This works as functions are full-fledged *objects*. So, `average_evens` is just a name referencing an object in memory with an **identity**, a **type**, namely `function`, and a **value**. In that regard, `average_evens` is *no* different from the variable `numbers` or the built-ins' names." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139940571731424" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(average_evens)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "function" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(average_evens)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Its value may seem awkward at first: It consists of a location showing where the function is defined (i.e., `__main__` here, which is Python's way of saying \"in this notebook\") and the signature wrapped inside angle brackets `<` and `>`.\n", + " \n", + "The angle brackets are a convention to indicate that the value may *not* be used as a *literal* (i.e., typed back into another code cell). [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_11_classes/00_content.ipynb) introduces the concept of a **text representation** of an object, which is related to the *semantic* meaning of an object's value as discussed in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb#Value-/-\"Meaning\"), and the angle brackets convention is one such way to represent an object as text. When executed, the angle brackets cause a `SyntaxError` because Python expects the `<` operator to come with an operand on both sides (cf., [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_03_conditionals/00_content.ipynb#Relational-Operators))." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "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 \u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + ] + } + ], + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`average_evens` is, of course, callable. So, the `function` type is the third kind of callable in this chapter." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "callable(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": 33, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on function average_evens in module __main__:\n", + "\n", + "average_evens(integers)\n", + " Calculate the average of all even numbers in a list.\n", + " \n", + " Args:\n", + " integers (list of int's): whole numbers to be averaged\n", + " \n", + " Returns:\n", + " average (float)\n", + "\n" + ] + } + ], + "source": [ + "help(average_evens)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In JupyterLab, we can just as well add a question mark `?` to a function's name to achieve the same." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\u001b[0;31mSignature:\u001b[0m \u001b[0maverage_evens\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mintegers\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", + " integers (list of int's): whole numbers to be averaged\n", + "\n", + "Returns:\n", + " average (float)\n", + "\u001b[0;31mFile:\u001b[0m ~/repos/intro-to-python/chapter_02_functions/\n", + "\u001b[0;31mType:\u001b[0m function\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "average_evens?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Two question marks `??` show a function's source code." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\u001b[0;31mSignature:\u001b[0m \u001b[0maverage_evens\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mintegers\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[0mintegers\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 integers (list of int's): whole numbers to be averaged\u001b[0m\n", + "\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m Returns:\u001b[0m\n", + "\u001b[0;34m average (float)\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[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\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/chapter_02_functions/\n", + "\u001b[0;31mType:\u001b[0m function\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "average_evens??" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[help() ](https://docs.python.org/3/library/functions.html#help) and the `?`s also work for built-ins." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on built-in function sum in module builtins:\n", + "\n", + "sum(iterable, /, start=0)\n", + " Return the sum of a 'start' value (default: 0) plus an iterable of numbers\n", + " \n", + " When the iterable is empty, return the start value.\n", + " This function is intended specifically for use with numeric values and may\n", + " reject non-numeric types.\n", + "\n" + ] + } + ], + "source": [ + "help(sum)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Function Calls" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Once defined, we may call a function with the call operator `()` as often as we wish. The formal parameters are then filled in by **passing** *expressions* (e.g., literals or variables) as **arguments** to the function within the parentheses." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7.0" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens([7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4])" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7.0" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens(numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A function call's return value is commonly assigned to a variable for subsequent use. Otherwise, we lose access to the returned object right away." + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "result = average_evens(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7.0" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "result" + ] + }, + { + "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": [ + "The parameters listed in a function's definition (i.e., `integers` in the example) 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 are dereferenced immediately when the function call returns. We say they go out of **scope**. That is why we see the `NameError`s below." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'integers' 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[0mintegers\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mNameError\u001b[0m: name 'integers' is not defined" + ] + } + ], + "source": [ + "integers" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": 43, + "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": "skip" + } + }, + "source": [ + "[PythonTutor ](http://pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%5D%0A%0Adef%20average_evens%28integers%29%3A%0A%20%20%20%20evens%20%3D%20%5Bn%20for%20n%20in%20integers%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%0Aresult%20%3D%20average_evens%28numbers%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 `numbers` passed in as the `integers` 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 integers 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)." + ] + }, + { + "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 may 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 `integers` parameter but the `numbers` variable in the **global scope** instead." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def average_wrong(integers):\n", + " \"\"\"Calculate the average of all even numbers in a list.\n", + "\n", + " Args:\n", + " integers (list of int's): whole numbers to be averaged\n", + "\n", + " Returns:\n", + " average (float)\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": [ + "`numbers` in the global scope is, of course, *not* changed by merely defining `average_wrong()`." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Sometimes a function may return a correct solution for *some* inputs ..." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7.0" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_wrong(numbers) # correct by accident" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... but still be wrong *in general*." + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7.0" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_wrong([123, 456, 789])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[PythonTutor ](http://pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%5D%0A%0Adef%20average_wrong%28integers%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%0Aresult%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 `numbers` 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 with 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/develop/chapter_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." + ] + }, + { + "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 in its body?\n", + "\n", + "`average_evens()` below works like `average_evens()` above except that it rounds the numbers in `integers` with the built-in [round() ](https://docs.python.org/3/library/functions.html#round) function before filtering and averaging them. [round() ](https://docs.python.org/3/library/functions.html#round) returns `int` objects independent of its argument being an `int` or a `float` object. On the first line in its body, `average_evens()` introduces a *local* variable `numbers` whose name collides with the one defined in the global scope." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def average_evens(integers):\n", + " \"\"\"Calculate the average of all even numbers in a list.\n", + "\n", + " Args:\n", + " integers (list of int's/float's): numbers to be averaged;\n", + " if non-whole numbers are provided, they are rounded\n", + "\n", + " Returns:\n", + " average (float)\n", + " \"\"\"\n", + " numbers = [round(n) for n in integers]\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": [ + "As a good practice, let's first \"verify\" that `average_evens()` is \"correct\" by calling it with inputs for which we can calculate the answer in our heads. Treating a function as a \"black box\" (i.e., input-output specification) when testing is also called [unit testing ](https://en.wikipedia.org/wiki/Unit_testing) and plays an important role in modern software engineering." + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42.0" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens([40.0, 41.1, 42.2, 43.3, 44.4])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Such tests are often and conveniently expressed with the `assert` statement (cf., [reference ](https://docs.python.org/3/reference/simple_stmts.html#the-assert-statement)): If the expression following `assert` evaluates to `True`, nothing happens." + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "assert average_evens([40.0, 41.1, 42.2, 43.3, 44.4]) == 42.0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, if the expression evaluates to `False`, an `AssertionError` is raised." + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "AssertionError", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAssertionError\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;32massert\u001b[0m \u001b[0maverage_evens\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m40.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m41.1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m42.2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m43.3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m44.4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m87.0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mAssertionError\u001b[0m: " + ] + } + ], + "source": [ + "assert average_evens([40.0, 41.1, 42.2, 43.3, 44.4]) == 87.0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Calling `average_evens()` leaves `numbers` in the global scope unchanged." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To add to the confusion, let's also pass the global `numbers` list as an argument to `average_evens()`. The return value is the same as before." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7.0" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens(numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In summary, Python is smart enough to keep all the involved `numbers` variables apart. So, the global `numbers` variable is still referencing the *same* `list` object as before." + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The reason why everything works is that *every* time we (re-)assign an object to a variable *inside* a function's body 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.\n", + "\n", + "[PythonTutor ](http://pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%5D%0A%0Adef%20average_evens%28integers%29%3A%0A%20%20%20%20numbers%20%3D%20%5Bround%28n%29%20for%20n%20in%20integers%5D%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%0Aresult%20%3D%20average_evens%28%5B40.0,%2041.1,%2042.2,%2043.3,%2044.4%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how *two* `numbers` variables exist in *different* scopes referencing *different* objects (cf., steps 14-25) when we execute `average_evens([40.0, 41.1, 42.2, 43.3, 44.4])`.\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 humans and should be avoided if possible. But, as the software engineering wisdom goes, \"[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 a hard problem as well, and we have to be prepared to encounter shadowing variables.\n", + "\n", + "Shadowing also occurs if a parameter in the function definition goes by the same name as a variable in an outer scope. Below, `average_evens()` is identical to the first version in this chapter except that the parameter `integers` is now called `numbers` as well." + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "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 of int's): whole numbers to be averaged\n", + "\n", + " Returns:\n", + " average (float)\n", + " \"\"\"\n", + " evens = [n for n in numbers if n % 2 == 0]\n", + " average = sum(evens) / len(evens)\n", + " return average" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7.0" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[PythonTutor ](http://pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%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%0Aresult%20%3D%20average_evens%28numbers%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) reveals that in this example there are *two* `numbers` variables in *different* scope referencing the *same* `list` object in memory (cf., steps 4-23)." + ] + }, + { + "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 specified only one parameter in each of our user-defined functions. In [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb#%28Arithmetic%29-Operators), however, we saw the built-in [divmod() ](https://docs.python.org/3/library/functions.html#divmod) function take two arguments. And, the order in which they are passed in matters! 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": 58, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(4, 2)" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "divmod(42, 10)" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(0, 10)" + ] + }, + "execution_count": 59, + "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: For example, for any kind of division passing the dividend first and the divisor second seems intuitive. But what if that is not the case in another setting? 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." + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def scaled_average_evens(numbers, scalar):\n", + " \"\"\"Calculate the scaled average of all even numbers in a list.\n", + "\n", + " Args:\n", + " numbers (list of int's/float's): numbers to be averaged;\n", + " if non-whole numbers are provided, they are rounded\n", + " scalar (float): multiplies the average\n", + "\n", + " Returns:\n", + " scaled_average (float)\n", + " \"\"\"\n", + " numbers = [round(n) for n in numbers]\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 may pass in the arguments by position." + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "14.0" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scaled_average_evens(numbers, 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Now, this function call is a bit harder to understand as we always need to remember what the `2` means. This becomes even harder with more parameters.\n", + "\n", + "Luckily, we may also pass in arguments *by name*. Then, we refer to them as **keyword arguments**." + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "14.0" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scaled_average_evens(numbers=numbers, scalar=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When passing all arguments by name, we may do so in any order." + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "14.0" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scaled_average_evens(scalar=2, numbers=numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We may even combine positional and keyword arguments in the same function call." + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "14.0" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scaled_average_evens(numbers, scalar=2)" + ] + }, + { + "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": 65, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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=numbers, 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=numbers, 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Similarly, we must always pass in the right number of arguments. Otherwise, a `TypeError` is raised." + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "scaled_average_evens() missing 1 required positional argument: 'scalar'", + "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[0mscaled_average_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[0m", + "\u001b[0;31mTypeError\u001b[0m: scaled_average_evens() missing 1 required positional argument: 'scalar'" + ] + } + ], + "source": [ + "scaled_average_evens(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "scaled_average_evens() takes 2 positional arguments but 3 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[0mscaled_average_evens\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnumbers\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\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: scaled_average_evens() takes 2 positional arguments but 3 were given" + ] + } + ], + "source": [ + "scaled_average_evens(numbers, 2, 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Modularization" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Defining `average_evens()` and `scaled_average_evens()` as above leads to a repetition of most of their code. That is *not* good as such a redundancy makes a code base hard to maintain in the long run: Whenever we change the logic in one function, we must *not* forget to do so for the other function as well. And, most likely, we forget about such issues in larger projects.\n", + "\n", + "Below, three of four lines in the functions' bodies are identical!" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def average_evens(numbers):\n", + " \"\"\" ... ... ... \"\"\"\n", + " numbers = [round(n) for n in numbers]\n", + " evens = [n for n in numbers if n % 2 == 0]\n", + " average = sum(evens) / len(evens)\n", + " return average" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "def scaled_average_evens(numbers, scalar):\n", + " \"\"\" ... ... ... \"\"\"\n", + " numbers = [round(n) for n in numbers]\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": [ + "A better way is to design related functions in a **modular** fashion such that they reuse each other's code.\n", + "\n", + "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 version, the function resembling the *special* case, `average_evens()`, **forwards** the call to the more *general* function, `scaled_average_evens()`, passing a `scalar` argument of `1`. As the name `scaled_average_evens` within the body of `average_evens()` is looked up each time the function is *being* executed, we may define `average_evens()` before `scaled_average_evens()`." + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def average_evens(numbers):\n", + " \"\"\" ... ... ... \"\"\"\n", + " return scaled_average_evens(numbers, scalar=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "def scaled_average_evens(numbers, scalar):\n", + " \"\"\" ... ... ... \"\"\"\n", + " numbers = [round(n) for n in numbers]\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": [ + "After **refactoring** the functions, it is a good idea to test them again." + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "assert average_evens(numbers) == 7.0" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "assert scaled_average_evens(numbers, 2) == 14.0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Default Arguments" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "*Assuming* that scaling the average occurs rarely, it may be a good idea to handle both cases in *one* function definition by providing a **default argument** of `1` for the `scalar` parameter." + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "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 of int's/float's): numbers to be averaged;\n", + " if non-whole numbers are provided, they are rounded\n", + " scalar (float, optional): multiplies the average; defaults to 1\n", + "\n", + " Returns:\n", + " scaled_average (float)\n", + " \"\"\"\n", + " numbers = [round(n) for n in numbers]\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 call the function with or without passing a `scalar` argument.\n", + "\n", + "If `scalar` is *not* passed in, it automatically takes the value `1`." + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7.0" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens(numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If `scalar` is passed in, this may be done as either a positional or a keyword argument. Which of the two calls where `scalar` is `2` is faster to understand in a larger program?" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "14.0" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens(numbers, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "14.0" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens(numbers, scalar=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Keyword-only Arguments" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Because we *assumed* that scaling occurs rarely, we would prefer that our new version of `average_evens()` be called with a *keyword argument* whenever `scalar` is passed in. Then, a function call is never ambiguous when reading the source code.\n", + "\n", + "Python offers a **keyword-only** syntax when defining a function that *forces* a caller to pass the `scalar` argument *by name* if it is passed in at all: To do so, we place an asterisk `*` before the arguments that may only be passed in by name. Note that the keyword-only syntax also works *without* a default argument." + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "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 of int's/float's): numbers to be averaged;\n", + " if non-whole numbers are provided, they are rounded\n", + " scalar (float, optional): multiplies the average; defaults to 1\n", + "\n", + " Returns:\n", + " scaled_average (float)\n", + " \"\"\"\n", + " numbers = [round(n) for n in numbers]\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 before, we may call `average_evens()` without passing in an argument for the `scalar` parameter." + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7.0" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens(numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we call `average_evens()` with a `scalar` argument, we *must* use keyword notation." + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "14.0" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens(numbers, scalar=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If instead we pass in `scalar` as a positional argument, we get a `TypeError`." + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "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[0mnumbers\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(numbers, 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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 references a *new* `function` object in memory.\n", + "\n", + "We can thus think of it as doing *two* things **atomically** (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 `lambda` expression syntax that allows us to *only* create a `function` object in memory *without* making a name reference it (cf., [reference ](https://docs.python.org/3/reference/expressions.html#lambda)). It starts with the keyword `lambda` followed by an optional listing of comma separated parameters, a mandatory colon, and *one* expression that serves as the return value of the resulting `function` object. Because it does *not* create a name referencing the object, we effectively create \"anonymous\" functions with it.\n", + "\n", + "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": 82, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(x)>" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "lambda x: x + 3" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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 inspect the object created by a `lambda` expression, we use the simple `=` statement and assign it to the variable `add_three`, which is really `add_three()` as per our convention from above." + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "add_three = lambda x: x + 3 # we could and should use def instead" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[type() ](https://docs.python.org/3/library/functions.html#type) and [callable() ](https://docs.python.org/3/library/functions.html#callable) confirm that `add_three` is indeed a callable `function` object." + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "function" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(add_three)" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 85, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "callable(add_three)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Now we may call `add_three()` as if we defined it with the `def` statement." + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "add_three(39)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Alternatively, we could call an `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 one serves as a delimiter whereas the second represents the call operator." + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 87, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(lambda x: x + 3)(39)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The main point of having functions without a reference to them 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 (cf., [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_08_mfr/00_content.ipynb#Lambda-Expressions))." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 7119fd4c459623f25964e15ab1aaba61142fc537 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 13 Oct 2020 23:29:08 +0200 Subject: [PATCH 040/142] Add initial version of chapter 02's exercises --- README.md | 3 + chapter_02_functions/01_exercises.ipynb | 257 ++++++++++++++++++++++++ 2 files changed, 260 insertions(+) create mode 100644 chapter_02_functions/01_exercises.ipynb diff --git a/README.md b/README.md index 2065664..23289c5 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,9 @@ Alternatively, the content can be viewed in a web browser Function Calls & Scoping Rules; Positional vs. Keyword Arguments; Modularization) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_02_functions/01_exercises.ipynb) + (Volume of a Sphere) #### Videos diff --git a/chapter_02_functions/01_exercises.ipynb b/chapter_02_functions/01_exercises.ipynb new file mode 100644 index 0000000..664e8f0 --- /dev/null +++ b/chapter_02_functions/01_exercises.ipynb @@ -0,0 +1,257 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_02_functions/01_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 2: Functions & Modularization" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Coding Exercises" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/00_content.ipynb) of Chapter 2.\n", + "\n", + "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Volume of a Sphere" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: The [volume of a sphere ](https://en.wikipedia.org/wiki/Sphere) is defined as $\\frac{4}{3} * \\pi * r^3$. Calculate this value for $r=10.0$ and round it to 10 digits after the comma.\n", + "\n", + "Hints:\n", + "- use an appropriate approximation for $\\pi$\n", + "- you may use the [standard library ](https://docs.python.org/3/library/index.html) to do so if you have already looked at the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/02_content.ipynb) of Chapter 2." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import ... # you may drop this cell and use your own approximation for Pi" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "r = ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: Encapsulate the logic into a function `sphere_volume()` that takes one *positional* argument `radius` and one *keyword-only* argument `digits` defaulting to `5`. The volume should be returned as a `float` object under *all* circumstances." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def sphere_volume(...):\n", + " \"\"\"Calculate the volume of a sphere.\n", + "\n", + " Args:\n", + " radius (float): radius of the sphere\n", + " digits (optional, int): number of digits\n", + " for rounding the resulting volume\n", + "\n", + " Returns:\n", + " volume (float)\n", + " \"\"\"\n", + " return ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: Evaluate the function with `radius = 100.0` and 1, 5, 10, 15, and 20 digits respectively." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "radius = ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sphere_volume(...)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sphere_volume(...)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sphere_volume(...)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sphere_volume(...)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sphere_volume(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: What observation do you make?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: Using the [range() ](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": [ + "**Q6**: What lesson do you learn about the `float` type?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From ca1ccf21eb9b012f764bb898cbafb889d76205b0 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 13 Oct 2020 23:39:22 +0200 Subject: [PATCH 041/142] Add numpy to the dependencies --- poetry.lock | 38 +++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index bc75853..dc2cc29 100644 --- a/poetry.lock +++ b/poetry.lock @@ -631,6 +631,14 @@ virtualenv = ">=14.0.0" [package.extras] tox_to_nox = ["jinja2", "tox"] +[[package]] +name = "numpy" +version = "1.19.2" +description = "NumPy is the fundamental package for array computing with Python." +category = "main" +optional = false +python-versions = ">=3.6" + [[package]] name = "packaging" version = "20.4" @@ -959,7 +967,7 @@ python-versions = "*" [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "1fd17e55acd76edd6126d5ee9223c0dd291bc2acacaaa242e80a3a2aaa5decce" +content-hash = "abc30be36880f8b203c72895f70f906ef451fca6b4e5113891c9487bc95e02e5" [metadata.files] appdirs = [ @@ -1260,6 +1268,34 @@ nox = [ {file = "nox-2020.8.22-py3-none-any.whl", hash = "sha256:55f8cab16bcfaaea08b141c83bf2b7c779e943518d0de6cd9c38cd8da95d11ea"}, {file = "nox-2020.8.22.tar.gz", hash = "sha256:efa5adcf1134012f96bcd0a496ccebd4c9e9da53a831888a2a779462440eebcf"}, ] +numpy = [ + {file = "numpy-1.19.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b594f76771bc7fc8a044c5ba303427ee67c17a09b36e1fa32bde82f5c419d17a"}, + {file = "numpy-1.19.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:e6ddbdc5113628f15de7e4911c02aed74a4ccff531842c583e5032f6e5a179bd"}, + {file = "numpy-1.19.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3733640466733441295b0d6d3dcbf8e1ffa7e897d4d82903169529fd3386919a"}, + {file = "numpy-1.19.2-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:4339741994c775396e1a274dba3609c69ab0f16056c1077f18979bec2a2c2e6e"}, + {file = "numpy-1.19.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7c6646314291d8f5ea900a7ea9c4261f834b5b62159ba2abe3836f4fa6705526"}, + {file = "numpy-1.19.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:7118f0a9f2f617f921ec7d278d981244ba83c85eea197be7c5a4f84af80a9c3c"}, + {file = "numpy-1.19.2-cp36-cp36m-win32.whl", hash = "sha256:9a3001248b9231ed73894c773142658bab914645261275f675d86c290c37f66d"}, + {file = "numpy-1.19.2-cp36-cp36m-win_amd64.whl", hash = "sha256:967c92435f0b3ba37a4257c48b8715b76741410467e2bdb1097e8391fccfae15"}, + {file = "numpy-1.19.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d526fa58ae4aead839161535d59ea9565863bb0b0bdb3cc63214613fb16aced4"}, + {file = "numpy-1.19.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:eb25c381d168daf351147713f49c626030dcff7a393d5caa62515d415a6071d8"}, + {file = "numpy-1.19.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:62139af94728d22350a571b7c82795b9d59be77fc162414ada6c8b6a10ef5d02"}, + {file = "numpy-1.19.2-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:0c66da1d202c52051625e55a249da35b31f65a81cb56e4c69af0dfb8fb0125bf"}, + {file = "numpy-1.19.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:2117536e968abb7357d34d754e3733b0d7113d4c9f1d921f21a3d96dec5ff716"}, + {file = "numpy-1.19.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:54045b198aebf41bf6bf4088012777c1d11703bf74461d70cd350c0af2182e45"}, + {file = "numpy-1.19.2-cp37-cp37m-win32.whl", hash = "sha256:aba1d5daf1144b956bc87ffb87966791f5e9f3e1f6fab3d7f581db1f5b598f7a"}, + {file = "numpy-1.19.2-cp37-cp37m-win_amd64.whl", hash = "sha256:addaa551b298052c16885fc70408d3848d4e2e7352de4e7a1e13e691abc734c1"}, + {file = "numpy-1.19.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:58d66a6b3b55178a1f8a5fe98df26ace76260a70de694d99577ddeab7eaa9a9d"}, + {file = "numpy-1.19.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:59f3d687faea7a4f7f93bd9665e5b102f32f3fa28514f15b126f099b7997203d"}, + {file = "numpy-1.19.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:cebd4f4e64cfe87f2039e4725781f6326a61f095bc77b3716502bed812b385a9"}, + {file = "numpy-1.19.2-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:c35a01777f81e7333bcf276b605f39c872e28295441c265cd0c860f4b40148c1"}, + {file = "numpy-1.19.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d7ac33585e1f09e7345aa902c281bd777fdb792432d27fca857f39b70e5dd31c"}, + {file = "numpy-1.19.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:04c7d4ebc5ff93d9822075ddb1751ff392a4375e5885299445fcebf877f179d5"}, + {file = "numpy-1.19.2-cp38-cp38-win32.whl", hash = "sha256:51ee93e1fac3fe08ef54ff1c7f329db64d8a9c5557e6c8e908be9497ac76374b"}, + {file = "numpy-1.19.2-cp38-cp38-win_amd64.whl", hash = "sha256:1669ec8e42f169ff715a904c9b2105b6640f3f2a4c4c2cb4920ae8b2785dac65"}, + {file = "numpy-1.19.2-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:0bfd85053d1e9f60234f28f63d4a5147ada7f432943c113a11afcf3e65d9d4c8"}, + {file = "numpy-1.19.2.zip", hash = "sha256:0d310730e1e793527065ad7dde736197b705d0e4c9999775f212b03c44a8484c"}, +] packaging = [ {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, diff --git a/pyproject.toml b/pyproject.toml index e3b89bd..588c1b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ license = "MIT" python = "^3.8" jupyterlab = "^2.2.8" +numpy = "^1.19.2" [tool.poetry.dev-dependencies] # Task runners From 559c8e7b71ca13045285d962c89ad18e0aedd8d2 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 13 Oct 2020 23:40:11 +0200 Subject: [PATCH 042/142] Pin the dependencies - upgrade colorama (transient dependency) --- poetry.lock | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index dc2cc29..9da7a06 100644 --- a/poetry.lock +++ b/poetry.lock @@ -122,7 +122,7 @@ python-versions = "*" [[package]] name = "colorama" -version = "0.4.3" +version = "0.4.4" description = "Cross-platform colored terminal text." category = "main" optional = false @@ -1067,8 +1067,7 @@ chardet = [ {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, ] colorama = [ - {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, - {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, ] colorlog = [ {file = "colorlog-4.4.0-py2.py3-none-any.whl", hash = "sha256:f14f30f58e2ce6ef40b0088307cac7efb9ecff5605fa2267a2d29955f26aff23"}, From 917952176324d3877656a5b7c44d9b1dc8fa5742 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 13 Oct 2020 23:58:55 +0200 Subject: [PATCH 043/142] Add initial version of chapter 02, part 2 --- README.md | 5 + chapter_02_functions/02_content.ipynb | 1637 +++++++++++++++++++++++++ chapter_02_functions/sample_module.py | 76 ++ 3 files changed, 1718 insertions(+) create mode 100644 chapter_02_functions/02_content.ipynb create mode 100644 chapter_02_functions/sample_module.py diff --git a/README.md b/README.md index 23289c5..05c8bce 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,11 @@ Alternatively, the content can be viewed in a web browser - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/01_exercises.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_02_functions/01_exercises.ipynb) (Volume of a Sphere) + - [content 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_02_functions/02_content.ipynb) + (Standard Library: `math` & `random` Modules; + Third-party Packages: `numpy` Library; + Writing one's own Modules) #### Videos diff --git a/chapter_02_functions/02_content.ipynb b/chapter_02_functions/02_content.ipynb new file mode 100644 index 0000000..87fd9f5 --- /dev/null +++ b/chapter_02_functions/02_content.ipynb @@ -0,0 +1,1637 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_02_functions/02_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 2: Functions & Modularization (Part 2)" + ] + }, + { + "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 \"reinventing the wheel\" in each new project." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Extending Core Python" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### The Standard Library" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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 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", + "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." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### [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** them with the `import` statement (cf., [reference ](https://docs.python.org/3/reference/simple_stmts.html#import)). " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import math" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140177537558144" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(math)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "module" + ] + }, + "execution_count": 4, + "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 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." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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", + " 'comb',\n", + " 'copysign',\n", + " 'cos',\n", + " 'cosh',\n", + " 'degrees',\n", + " 'dist',\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", + " 'isqrt',\n", + " 'ldexp',\n", + " 'lgamma',\n", + " 'log',\n", + " 'log10',\n", + " 'log1p',\n", + " 'log2',\n", + " 'modf',\n", + " 'nan',\n", + " 'perm',\n", + " 'pi',\n", + " 'pow',\n", + " 'prod',\n", + " 'radians',\n", + " 'remainder',\n", + " 'sin',\n", + " 'sinh',\n", + " 'sqrt',\n", + " 'tan',\n", + " 'tanh',\n", + " 'tau',\n", + " 'trunc']" + ] + }, + "execution_count": 5, + "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": 6, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3.141592653589793" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.pi" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2.718281828459045" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.e" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.sqrt" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": 10, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1.4142135623730951" + ] + }, + "execution_count": 10, + "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 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/develop/chapter_01_elements/02_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": 11, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2.0" + ] + }, + "execution_count": 11, + "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": 12, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10.0" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "math.sqrt(sum([99, 100, 101]) / 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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 reference an object defined inside a module directly." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "from math import sqrt" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4.0" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sqrt(16)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### [random ](https://docs.python.org/3/library/random.html) Module" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import random" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "random" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_11_classes/00_content.ipynb), while the latter is explained further below." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['BPF',\n", + " 'LOG4',\n", + " 'NV_MAGICCONST',\n", + " 'RECIP_BPF',\n", + " 'Random',\n", + " 'SG_MAGICCONST',\n", + " 'SystemRandom',\n", + " 'TWOPI',\n", + " '_Sequence',\n", + " '_Set',\n", + " '__all__',\n", + " '__builtins__',\n", + " '__cached__',\n", + " '__doc__',\n", + " '__file__',\n", + " '__loader__',\n", + " '__name__',\n", + " '__package__',\n", + " '__spec__',\n", + " '_accumulate',\n", + " '_acos',\n", + " '_bisect',\n", + " '_ceil',\n", + " '_cos',\n", + " '_e',\n", + " '_exp',\n", + " '_inst',\n", + " '_log',\n", + " '_os',\n", + " '_pi',\n", + " '_random',\n", + " '_repeat',\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": 17, + "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": 18, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "random.random" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": 20, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.782609162553633" + ] + }, + "execution_count": 20, + "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 call it with, for example, the `numbers` list and it draws one element out of it with equal chance." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + ">" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "random.choice" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": 23, + "metadata": {}, + "outputs": [], + "source": [ + "numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "random.choice(numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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 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." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "random.seed(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.6394267984578837" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "random.random()" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "random.seed(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.6394267984578837" + ] + }, + "execution_count": 28, + "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 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 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 (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." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### [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.\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/develop/chapter_00_intro/00_content.ipynb#Markdown-Cells-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": 29, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: numpy in /home/webartifex/repos/intro-to-python/.venv/lib/python3.8/site-packages (1.19.2)\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 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": 30, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`np` is used in the same way as `math` or `random` above." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's convert the above `numbers` list into a vector-like object of type `numpy.ndarray`." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "vec = np.array(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4])" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "vec" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "numpy.ndarray" + ] + }, + "execution_count": 34, + "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 may now [scalar-multiply ](https://en.wikipedia.org/wiki/Scalar_multiplication) `vec`. Also, [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." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([14, 22, 16, 10, 6, 24, 4, 12, 18, 20, 2, 8])" + ] + }, + "execution_count": 35, + "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 `numbers` 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": 36, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "2 * numbers # 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": 37, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "78" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(vec)" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7" + ] + }, + "execution_count": 38, + "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 local modules and packages. In the Chapter 2 directory, there is a [*sample_module.py* ](https://github.com/webartifex/intro-to-python/blob/develop/chapter_02_functions/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 `pwd` utility tells us in which directory Python is currently in. We refer to that as the **working directory**, and `pwd` reads \"print working directory.\" JupyterLab automatically sets this to the directory in which the notebook is in." + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/home/webartifex/repos/intro-to-python/chapter_02_functions\n" + ] + } + ], + "source": [ + "!pwd" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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) 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." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import sample_module as mod" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mod" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Disregarding the dunder-style attributes, `mod` defines the attributes `_round_all`, `_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/develop/chapter_02_functions/sample_module.py) file.\n", + "\n", + "A convention when working with imported code is to *disregard* any attributes starting with a single 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": 42, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['__builtins__',\n", + " '__cached__',\n", + " '__doc__',\n", + " '__file__',\n", + " '__loader__',\n", + " '__name__',\n", + " '__package__',\n", + " '__spec__',\n", + " '_round_all',\n", + " '_scaled_average',\n", + " 'average',\n", + " 'average_evens',\n", + " 'average_odds']" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dir(mod)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We use the imported `mod.average_evens()` just like `average_evens()` defined in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/00_content.ipynb) of this chapter. 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." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mod.average_evens" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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 of int's/float's): numbers to be averaged;\n", + " if non-whole numbers are provided, they are rounded\n", + " scalar (float, optional): multiplies the average; defaults to 1\n", + " \n", + " Returns:\n", + " scaled_average (float)\n", + "\n" + ] + } + ], + "source": [ + "help(mod.average_evens)" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7.0" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mod.average_evens(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "14.0" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mod.average_evens(numbers, scalar=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Packages are a generalization of modules, and we look at one in detail in [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_11_classes/00_content.ipynb). You may, however, already look at a [sample package ](https://github.com/webartifex/intro-to-python/tree/develop/chapter_11_classes/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)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/chapter_02_functions/sample_module.py b/chapter_02_functions/sample_module.py new file mode 100644 index 0000000..3a097e1 --- /dev/null +++ b/chapter_02_functions/sample_module.py @@ -0,0 +1,76 @@ +"""This is a sample module. + +It defines three functions average(), average_evens(), and average_odds(). +The point is to show how we can put Python code in a .py file to be re-used +in some other place. + +We should never forget to document the code as well, both on the module +level (i.e., this docstring) but also in every function it defines. + +When imported, Python modules are executed top to bottom before the flow of +execution returns to wherever they were imported into. + +An important convention is to prefix variables and functions that are not to +be used outside the module with a single underscore "_". This way, we can +design the code within a module in a modular fashion and only "export" what we +want. + +Here, all three functions internally forward parts of their computations +to the utility functions _round_all() and _scaled_average() that contain all +the logic common to the three functions. + +While this example is stylized, it shows how Python modules are often +designed. +""" + +def _round_all(numbers): + """Internal utility function to round all numbers in a list.""" + return [round(n) for n in numbers] + + +def _scaled_average(numbers, scalar): + """Internal utility function to calculate scaled averages.""" + average = sum(numbers) / len(numbers) + return scalar * average + + +def average(numbers, *, scalar=1): + """Calculate the average of all numbers in a list. + + Args: + numbers (list of int's/float's): numbers to be averaged; + if non-whole numbers are provided, they are rounded + scalar (float, optional): multiplies the average; defaults to 1 + + Returns: + scaled_average (float) + """ + return _scaled_average(_round_all(numbers), scalar) + + +def average_evens(numbers, *, scalar=1): + """Calculate the average of all even numbers in a list. + + Args: + numbers (list of int's/float's): numbers to be averaged; + if non-whole numbers are provided, they are rounded + scalar (float, optional): multiplies the average; defaults to 1 + + Returns: + scaled_average (float) + """ + return _scaled_average([n for n in _round_all(numbers) if n % 2 == 0], scalar) + + +def average_odds(numbers, *, scalar=1): + """Calculate the average of all odd numbers in a list. + + Args: + numbers (list of int's/float's): numbers to be averaged; + if non-whole numbers are provided, they are rounded + scalar (float, optional): multiplies the average; defaults to 1 + + Returns: + scaled_average (float) + """ + return _scaled_average([n for n in _round_all(numbers) if n % 2 != 0], scalar) From 54e63991a11a8a2ab0f2ae3e2f2ff2cdc04ee1b0 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 14 Oct 2020 00:00:52 +0200 Subject: [PATCH 044/142] Add initial version of chapter 02's summary --- README.md | 1 + chapter_02_functions/03_summary.ipynb | 101 ++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 chapter_02_functions/03_summary.ipynb diff --git a/README.md b/README.md index 05c8bce..8278bff 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ Alternatively, the content can be viewed in a web browser (Standard Library: `math` & `random` Modules; Third-party Packages: `numpy` Library; Writing one's own Modules) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/03_summary.ipynb) #### Videos diff --git a/chapter_02_functions/03_summary.ipynb b/chapter_02_functions/03_summary.ipynb new file mode 100644 index 0000000..c5d712b --- /dev/null +++ b/chapter_02_functions/03_summary.ipynb @@ -0,0 +1,101 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 2: Functions & Modularization" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## TL;DR" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A user-defined **function** is a **named sequence** of statements that perform a computation.\n", + "\n", + "Functions provide benefits as they\n", + "\n", + "- make programs easier to comprehend and debug for humans as they give names to the smaller parts of a larger program (i.e., they **modularize** a code base), and\n", + "- eliminate redundancies by allowing **reuse of code**.\n", + "\n", + "Functions are **defined** once with the `def` statement. Then, they may be **called** many times with the call operator `()`.\n", + "\n", + "They may process **parameterized** inputs, **passed** in as **arguments**, and output a **return value**.\n", + "\n", + "Arguments may be passed in by **position** or **keyword**. Some functions may even require **keyword-only** arguments.\n", + "\n", + "**Lambda expressions** create anonymous functions.\n", + "\n", + "Functions are a special kind of **callables**. Any object that may be **called** with the call operator `()` is a callable. Built-in functions and **constructors** are other kinds of callables.\n", + "\n", + "Core Python can be extended with code from either the **standard library** or **third-party** libraries.\n", + "\n", + "Outside Jupyter notebooks, Python code is put into **modules** that are grouped in **packages**." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 8a55f014a9a316cc27297ab38d5ec17144a6b4c1 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 14 Oct 2020 00:02:49 +0200 Subject: [PATCH 045/142] Add initial version of chapter 02's review --- README.md | 1 + chapter_02_functions/04_review.ipynb | 236 +++++++++++++++++++++++++++ 2 files changed, 237 insertions(+) create mode 100644 chapter_02_functions/04_review.ipynb diff --git a/README.md b/README.md index 8278bff..35f4dbc 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ Alternatively, the content can be viewed in a web browser Third-party Packages: `numpy` Library; Writing one's own Modules) - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/03_summary.ipynb) + - [review ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/04_review.ipynb) #### Videos diff --git a/chapter_02_functions/04_review.ipynb b/chapter_02_functions/04_review.ipynb new file mode 100644 index 0000000..daa9d88 --- /dev/null +++ b/chapter_02_functions/04_review.ipynb @@ -0,0 +1,236 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 2: Functions & Modularization" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Content Review" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/00_content.ipynb) and the [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/02_content.ipynb) part of Chapter 2.\n", + "\n", + "Be concise in your answers! Most questions can be answered in *one* sentence." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Essay Questions " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: What behavior of the `def` statement makes it a **statement**? Is there a way to use an **expression** to create a function?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: One of the first confusions of experienced programmers coming from other languages to Python regards the observation that **\"everything in Python is an object\"** (cf., this [discussion](https://www.reddit.com/r/learnpython/comments/8rypx9/everything_in_python_is_an_object/)). How does this relate to **functions**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: What does it mean for a variable to go out of **scope**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: How can a **global** variable be **shadowed**? Is this good or bad?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: Explain the concept of **forwarding** a **function call**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: What are **keyword-only arguments** and when is it appropriate to use them?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: What are **callables**? How do they relate to `function` objects?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### True / False Questions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Motivate your answer with *one short* sentence!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: A mere function **call** is just an **expression**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: When using the `import` statement, we need to ensure that the imported attributes do *not* overwrite any already defined variables and functions." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10:** Functions always have a name by which we can call them." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: The [standard library ](https://docs.python.org/3/library/index.html) is a collection of numerical tools often used in scientific computing, for example, advanced mathematical functions or utilities for simulation." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From d9011445690852d2dc3018ae471510456c24e4ab Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 14 Oct 2020 00:07:02 +0200 Subject: [PATCH 046/142] Streamline slides --- chapter_02_functions/00_content.ipynb | 6 +++++- chapter_02_functions/02_content.ipynb | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/chapter_02_functions/00_content.ipynb b/chapter_02_functions/00_content.ipynb index d7e21b5..4d18040 100644 --- a/chapter_02_functions/00_content.ipynb +++ b/chapter_02_functions/00_content.ipynb @@ -2577,7 +2577,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "If `scalar` is passed in, this may be done as either a positional or a keyword argument. Which of the two calls where `scalar` is `2` is faster to understand in a larger program?" ] diff --git a/chapter_02_functions/02_content.ipynb b/chapter_02_functions/02_content.ipynb index 87fd9f5..4c18bb4 100644 --- a/chapter_02_functions/02_content.ipynb +++ b/chapter_02_functions/02_content.ipynb @@ -863,7 +863,11 @@ { "cell_type": "code", "execution_count": 23, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [], "source": [ "numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" @@ -874,7 +878,7 @@ "execution_count": 24, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "-" } }, "outputs": [ @@ -1344,7 +1348,11 @@ { "cell_type": "code", "execution_count": 39, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [ { "name": "stdout", @@ -1376,7 +1384,7 @@ "execution_count": 40, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "fragment" } }, "outputs": [], From 9ec2f5758017ff4701c8f74f782ad4658aeed23a Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 15 Oct 2020 15:10:22 +0200 Subject: [PATCH 047/142] Pin the dependencies - upgrade transient dependencies: + nbformat + traitlets + virtualenv --- poetry.lock | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/poetry.lock b/poetry.lock index 9da7a06..9f17fc7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -555,7 +555,7 @@ webpdf = ["pyppeteer (0.2.2)"] [[package]] name = "nbformat" -version = "5.0.7" +version = "5.0.8" description = "The Jupyter Notebook format" category = "main" optional = false @@ -568,7 +568,8 @@ jupyter-core = "*" traitlets = ">=4.1" [package.extras] -test = ["pytest", "pytest-cov", "testpath"] +fast = ["fastjsonschema"] +test = ["fastjsonschema", "testpath", "pytest", "pytest-cov"] [[package]] name = "nest-asyncio" @@ -905,7 +906,7 @@ python-versions = ">= 3.5" [[package]] name = "traitlets" -version = "5.0.4" +version = "5.0.5" description = "Traitlets Python configuration system" category = "main" optional = false @@ -932,7 +933,7 @@ socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] [[package]] name = "virtualenv" -version = "20.0.34" +version = "20.0.35" description = "Virtual Python Environment builder" category = "dev" optional = false @@ -1248,8 +1249,8 @@ nbconvert = [ {file = "nbconvert-6.0.7.tar.gz", hash = "sha256:cbbc13a86dfbd4d1b5dee106539de0795b4db156c894c2c5dc382062bbc29002"}, ] nbformat = [ - {file = "nbformat-5.0.7-py3-none-any.whl", hash = "sha256:ea55c9b817855e2dfcd3f66d74857342612a60b1f09653440f4a5845e6e3523f"}, - {file = "nbformat-5.0.7.tar.gz", hash = "sha256:54d4d6354835a936bad7e8182dcd003ca3dc0cedfee5a306090e04854343b340"}, + {file = "nbformat-5.0.8-py3-none-any.whl", hash = "sha256:aa9450c16d29286dc69b92ea4913c1bffe86488f90184445996ccc03a2f60382"}, + {file = "nbformat-5.0.8.tar.gz", hash = "sha256:f545b22138865bfbcc6b1ffe89ed5a2b8e2dc5d4fe876f2ca60d8e6f702a30f8"}, ] nest-asyncio = [ {file = "nest_asyncio-1.4.1-py3-none-any.whl", hash = "sha256:a4487c4f49f2d11a7bb89a512a6886b6a5045f47097f49815b2851aaa8599cf0"}, @@ -1462,16 +1463,16 @@ tornado = [ {file = "tornado-6.0.4.tar.gz", hash = "sha256:0fe2d45ba43b00a41cd73f8be321a44936dc1aba233dee979f17a042b83eb6dc"}, ] traitlets = [ - {file = "traitlets-5.0.4-py3-none-any.whl", hash = "sha256:9664ec0c526e48e7b47b7d14cd6b252efa03e0129011de0a9c1d70315d4309c3"}, - {file = "traitlets-5.0.4.tar.gz", hash = "sha256:86c9351f94f95de9db8a04ad8e892da299a088a64fd283f9f6f18770ae5eae1b"}, + {file = "traitlets-5.0.5-py3-none-any.whl", hash = "sha256:69ff3f9d5351f31a7ad80443c2674b7099df13cc41fc5fa6e2f6d3b0330b0426"}, + {file = "traitlets-5.0.5.tar.gz", hash = "sha256:178f4ce988f69189f7e523337a3e11d91c786ded9360174a3d9ca83e79bc5396"}, ] urllib3 = [ {file = "urllib3-1.25.10-py2.py3-none-any.whl", hash = "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"}, {file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"}, ] virtualenv = [ - {file = "virtualenv-20.0.34-py2.py3-none-any.whl", hash = "sha256:4ecd607e7809bd7384039065639a8babc9fa562fe1b73b24095ce85b83d55fcf"}, - {file = "virtualenv-20.0.34.tar.gz", hash = "sha256:4bf0e2bf99d33b123a895a5a244f0d7adb2a92171c6bbb31c3e2db235624abf1"}, + {file = "virtualenv-20.0.35-py2.py3-none-any.whl", hash = "sha256:0ebc633426d7468664067309842c81edab11ae97fcaf27e8ad7f5748c89b431b"}, + {file = "virtualenv-20.0.35.tar.gz", hash = "sha256:2a72c80fa2ad8f4e2985c06e6fc12c3d60d060e410572f553c90619b0f6efaf3"}, ] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, From 9df4d89e4a773e9780133c79d2cb5563afb32634 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 15 Oct 2020 15:16:11 +0200 Subject: [PATCH 048/142] Streamline text --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 35f4dbc..3193767 100644 --- a/README.md +++ b/README.md @@ -204,10 +204,10 @@ The following instructions assume that -The screeshot above shows how this project can be set up in an alternative way +The screenshot above shows how this project can be set up in an alternative way with the [zsh](https://en.wikipedia.org/wiki/Z_shell) CLI. -First, the [git](https://git-scm.com/) tool is used +First, [git](https://git-scm.com/) is used to **clone** the course materials as a **repository** into a new folder called "*intro-to-python*" that lives under a "*repos*" folder. @@ -216,7 +216,7 @@ First, the [git](https://git-scm.com/) tool is used The `cd` command is used to "change directories". -In the screenshot, the [pyenv](https://github.com/pyenv/pyenv) tool is used +In the screenshot, [pyenv](https://github.com/pyenv/pyenv) is used to set the project's Python version. [pyenv](https://github.com/pyenv/pyenv)'s purpose is to manage *many* parallel Python installations on the same computer. @@ -225,7 +225,7 @@ It is highly recommended for professional users; - `pyenv local ...` -On the contrary, the [poetry](https://python-poetry.org/docs/) tool is used +On the contrary, [poetry](https://python-poetry.org/docs/)'s purpose is to manage third-party packages within the *same* Python installation and, more importantly, on a per-project basis. So, for example, @@ -255,7 +255,7 @@ The following *one* command not only [poetry](https://python-poetry.org/docs/) is also used to execute commands in the project's (virtual) environment. -The command is then prefixed with `poetry run ...`. +To do that, the command is prefixed with `poetry run ...`. The project uses [nox](https://nox.thea.codes/en/stable/) to manage various maintenance tasks. @@ -286,7 +286,8 @@ With that, the instructor can execute code in *presentation* mode during a class However, the RISE extension does *not* work in the more recent [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) app but only in the older [Jupyter Notebook](https://jupyter-notebook.readthedocs.io/en/stable/) app, - which comes with less features and a simpler [GUI](https://en.wikipedia.org/wiki/Graphical_user_interface). + which comes with less features + and a simpler [GUI ](https://en.wikipedia.org/wiki/Graphical_user_interface). The instructor can start the latter with: - `poetry run jupyter notebook` @@ -322,4 +323,5 @@ Alexander Hess is a PhD student at the Chair of Logistics Management at [WHU - Otto Beisheim School of Management](https://www.whu.edu) where he conducts research on urban delivery platforms and teaches coding courses based on Python in the BSc and MBA programs. -Connect him on [LinkedIn](https://www.linkedin.com/in/webartifex). \ No newline at end of file + +Connect him on [LinkedIn](https://www.linkedin.com/in/webartifex). From 086d0bafec616cc3aac9fdeaa5a5f6bb04ba2045 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 15 Oct 2020 15:25:16 +0200 Subject: [PATCH 049/142] Restructure folders & notebooks - drop 'chapter_' prefix for chapter folders - drop numbers for ToC sub-chapters - split exercises into one problem set per notebook - adjust titles in notebooks => show sub-chapter titles on the same line as the actual chapter titles --- .../00_content.ipynb | 6 +- .../01_exercises.ipynb | 15 +- .../02_review.ipynb | 15 +- .../static/cli_example.png | Bin .../static/example_python_users.png | Bin .../growth_of_major_programming_languages.png | Bin ...rowth_of_smaller_programming_languages.png | Bin .../static/logo.png | Bin .../static/xkcd.png | Bin .../00_content.ipynb | 10 +- 01_elements/01_exercises.ipynb | 172 +++++++++ 01_elements/02_exercises.ipynb | 218 ++++++++++++ .../03_content.ipynb | 14 +- .../04_exercises.ipynb | 15 +- .../05_summary.ipynb | 20 +- .../06_review.ipynb | 15 +- .../07_resources.ipynb | 16 +- .../00_content.ipynb | 24 +- .../01_exercises.ipynb | 26 +- .../02_content.ipynb | 58 +-- .../03_summary.ipynb | 13 +- .../04_review.ipynb | 15 +- .../sample_module.py | 0 README.md | 56 +-- chapter_01_elements/01_exercises.ipynb | 336 ------------------ 25 files changed, 497 insertions(+), 547 deletions(-) rename {chapter_00_intro => 00_intro}/00_content.ipynb (99%) rename {chapter_00_intro => 00_intro}/01_exercises.ipynb (91%) rename {chapter_00_intro => 00_intro}/02_review.ipynb (94%) rename {chapter_00_intro => 00_intro}/static/cli_example.png (100%) rename {chapter_00_intro => 00_intro}/static/example_python_users.png (100%) rename {chapter_00_intro => 00_intro}/static/growth_of_major_programming_languages.png (100%) rename {chapter_00_intro => 00_intro}/static/growth_of_smaller_programming_languages.png (100%) rename {chapter_00_intro => 00_intro}/static/logo.png (100%) rename {chapter_00_intro => 00_intro}/static/xkcd.png (100%) rename {chapter_01_elements => 01_elements}/00_content.ipynb (98%) create mode 100644 01_elements/01_exercises.ipynb create mode 100644 01_elements/02_exercises.ipynb rename chapter_01_elements/02_content.ipynb => 01_elements/03_content.ipynb (96%) rename chapter_01_elements/03_exercises.ipynb => 01_elements/04_exercises.ipynb (91%) rename chapter_01_elements/04_summary.ipynb => 01_elements/05_summary.ipynb (90%) rename chapter_01_elements/05_review.ipynb => 01_elements/06_review.ipynb (90%) rename chapter_01_elements/06_resources.ipynb => 01_elements/07_resources.ipynb (98%) rename {chapter_02_functions => 02_functions}/00_content.ipynb (95%) rename {chapter_02_functions => 02_functions}/01_exercises.ipynb (92%) rename {chapter_02_functions => 02_functions}/02_content.ipynb (93%) rename {chapter_02_functions => 02_functions}/03_summary.ipynb (93%) rename {chapter_02_functions => 02_functions}/04_review.ipynb (90%) rename {chapter_02_functions => 02_functions}/sample_module.py (100%) delete mode 100644 chapter_01_elements/01_exercises.ipynb diff --git a/chapter_00_intro/00_content.ipynb b/00_intro/00_content.ipynb similarity index 99% rename from chapter_00_intro/00_content.ipynb rename to 00_intro/00_content.ipynb index b3f20a3..75b5fb4 100644 --- a/chapter_00_intro/00_content.ipynb +++ b/00_intro/00_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/00_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/00_intro/00_content.ipynb)." ] }, { @@ -750,8 +750,8 @@ "**Part A: Expressing Logic**\n", "\n", "- What is a programming language? What kind of words exist?\n", - " - *Chapter 1*: [Elements of a Program ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb)\n", - " - *Chapter 2*: [Functions & Modularization ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/00_content.ipynb)\n", + " - *Chapter 1*: [Elements of a Program ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb)\n", + " - *Chapter 2*: [Functions & Modularization ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb)\n", "- What is the flow of execution? How can we form sentences from words?\n", " - *Chapter 3*: Conditionals & Exceptions\n", " - *Chapter 4*: Recursion & Looping" diff --git a/chapter_00_intro/01_exercises.ipynb b/00_intro/01_exercises.ipynb similarity index 91% rename from chapter_00_intro/01_exercises.ipynb rename to 00_intro/01_exercises.ipynb index 7f51a1d..c5082eb 100644 --- a/chapter_00_intro/01_exercises.ipynb +++ b/00_intro/01_exercises.ipynb @@ -4,35 +4,28 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/02_exercises.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/00_intro/02_exercises.ipynb)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Chapter 0: Introduction" + "# Chapter 0: Introduction (Coding Exercises)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## \"Coding\" Exercises" + "The exercises below assume that you have read [Chapter 0 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/00_content.ipynb)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "The exercises below assume that you have read [Chapter 0 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/00_content.ipynb)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Mastering Markdown" + "## Mastering Markdown" ] }, { diff --git a/chapter_00_intro/02_review.ipynb b/00_intro/02_review.ipynb similarity index 94% rename from chapter_00_intro/02_review.ipynb rename to 00_intro/02_review.ipynb index 0f75bfe..9a8d7c2 100644 --- a/chapter_00_intro/02_review.ipynb +++ b/00_intro/02_review.ipynb @@ -4,21 +4,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Chapter 0: Introduction" + "# Chapter 0: Introduction (Review Questions)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Review" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The questions below assume that you have read [Chapter 0 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/00_content.ipynb).\n", + "The questions below assume that you have read [Chapter 0 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/00_content.ipynb).\n", "\n", "Be concise in your answers! Most questions can be answered in *one* sentence." ] @@ -27,7 +20,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Essay Questions " + "## Essay Questions " ] }, { @@ -90,7 +83,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### True / False Questions" + "## True / False Questions" ] }, { diff --git a/chapter_00_intro/static/cli_example.png b/00_intro/static/cli_example.png similarity index 100% rename from chapter_00_intro/static/cli_example.png rename to 00_intro/static/cli_example.png diff --git a/chapter_00_intro/static/example_python_users.png b/00_intro/static/example_python_users.png similarity index 100% rename from chapter_00_intro/static/example_python_users.png rename to 00_intro/static/example_python_users.png diff --git a/chapter_00_intro/static/growth_of_major_programming_languages.png b/00_intro/static/growth_of_major_programming_languages.png similarity index 100% rename from chapter_00_intro/static/growth_of_major_programming_languages.png rename to 00_intro/static/growth_of_major_programming_languages.png diff --git a/chapter_00_intro/static/growth_of_smaller_programming_languages.png b/00_intro/static/growth_of_smaller_programming_languages.png similarity index 100% rename from chapter_00_intro/static/growth_of_smaller_programming_languages.png rename to 00_intro/static/growth_of_smaller_programming_languages.png diff --git a/chapter_00_intro/static/logo.png b/00_intro/static/logo.png similarity index 100% rename from chapter_00_intro/static/logo.png rename to 00_intro/static/logo.png diff --git a/chapter_00_intro/static/xkcd.png b/00_intro/static/xkcd.png similarity index 100% rename from chapter_00_intro/static/xkcd.png rename to 00_intro/static/xkcd.png diff --git a/chapter_01_elements/00_content.ipynb b/01_elements/00_content.ipynb similarity index 98% rename from chapter_01_elements/00_content.ipynb rename to 01_elements/00_content.ipynb index ad71aa9..6003bd9 100644 --- a/chapter_01_elements/00_content.ipynb +++ b/01_elements/00_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/00_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/00_content.ipynb)." ] }, { @@ -1165,7 +1165,7 @@ "source": [ "These addresses are *not* meaningful for anything other than checking if two variables reference the *same* object.\n", "\n", - "Obviously, `a` and `b` have the same *value* as revealed by the **equality operator** `==`: We say `a` and `b` \"evaluate equal.\" The resulting `True` - and the `False` further below - is yet another data type, a so-called **boolean**. We look into them in [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_03_conditionals/00_content.ipynb#Boolean-Expressions)." + "Obviously, `a` and `b` have the same *value* as revealed by the **equality operator** `==`: We say `a` and `b` \"evaluate equal.\" The resulting `True` - and the `False` further below - is yet another data type, a so-called **boolean**. We look into them in [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb#Boolean-Expressions)." ] }, { @@ -1342,7 +1342,7 @@ "source": [ "Different types imply different behaviors for the objects. The `b` object, for example, may be \"asked\" if it is a whole number 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** (i.e., as opposed to functions) and we look at them in detail in [Chapter 10 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_10_classes/00_content.ipynb). For now, it suffices to know that we access them with the **dot operator** `.` on the object. Of course, `b` is a whole number, which the boolean object `True` tells us." + "Formally, we call such type-specific functionalities **methods** (i.e., as opposed to functions) and we look at them in detail in [Chapter 10 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/10_classes/00_content.ipynb). For now, it suffices to know that we access them with the **dot operator** `.` on the object. Of course, `b` is a whole number, which the boolean object `True` tells us." ] }, { @@ -1934,7 +1934,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 built-in [sum() ](https://docs.python.org/3/library/functions.html#sum) and [len() ](https://docs.python.org/3/library/functions.html#len) functions (cf., [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/00_content.ipynb#Built-in-Functions)) as well as a so-called **list comprehension** (cf., [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_08_mfr/00_content.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 built-in [sum() ](https://docs.python.org/3/library/functions.html#sum) and [len() ](https://docs.python.org/3/library/functions.html#len) functions (cf., [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb#Built-in-Functions)) as well as a so-called **list comprehension** (cf., [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb#List-Comprehensions)). Pythonic code runs faster in many cases and is less error-prone." ] }, { @@ -2136,7 +2136,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/develop/chapter_02_functions/00_content.ipynb#Local-Modules-and-Packages)) and then use Jupyter notebooks to build up a linear report or storyline for an analysis." + "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/develop/02_functions/00_content.ipynb#Local-Modules-and-Packages)) and then use Jupyter notebooks to build up a linear report or storyline for an analysis." ] } ], diff --git a/01_elements/01_exercises.ipynb b/01_elements/01_exercises.ipynb new file mode 100644 index 0000000..d073a26 --- /dev/null +++ b/01_elements/01_exercises.ipynb @@ -0,0 +1,172 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/01_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 1: Elements of a Program (Coding Exercises)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb) of Chapter 1.\n", + "\n", + "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Printing Output" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: *Concatenate* `greeting` and `audience` below with the `+` operator and print out the resulting message `\"Hello World\"` with only *one* call of the built-in [print() ](https://docs.python.org/3/library/functions.html#print) function!\n", + "\n", + "Hint: You may have to \"add\" a space character in between `greeting` and `audience`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "greeting = \"Hello\"\n", + "audience = \"World\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: How is your answer to **Q1** an example of the concept of **operator overloading**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: Read the documentation on the built-in [print() ](https://docs.python.org/3/library/functions.html#print) function! How can you print the above message *without* concatenating `greeting` and `audience` first in *one* call of [print() ](https://docs.python.org/3/library/functions.html#print)?\n", + "\n", + "Hint: The `*objects` in the documentation implies that we can put several *expressions* (i.e., variables) separated by commas within the same call of the [print() ](https://docs.python.org/3/library/functions.html#print) function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: What does the `sep=\" \"` mean in the documentation on the built-in [print() ](https://docs.python.org/3/library/functions.html#print) function? Adjust and use it to print out the three names referenced by `first`, `second`, and `third` on *one* line separated by *commas* with only *one* call of the [print() ](https://docs.python.org/3/library/functions.html#print) function!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "first = \"Anthony\"\n", + "second = \"Berta\"\n", + "third = \"Christian\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: Lastly, what does the `end=\"\\n\"` mean in the documentation? Adjust and use it within the `for`-loop to print the numbers `1` through `10` on *one* line with only *one* call of the [print() ](https://docs.python.org/3/library/functions.html#print) function!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:\n", + " print(...)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/01_elements/02_exercises.ipynb b/01_elements/02_exercises.ipynb new file mode 100644 index 0000000..5ae269c --- /dev/null +++ b/01_elements/02_exercises.ipynb @@ -0,0 +1,218 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/02_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 1: Elements of a Program (Coding Exercises)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb) of Chapter 1.\n", + "\n", + "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Simple `for`-loops" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`for`-loops are extremely versatile in Python. That is different from many other programming languages.\n", + "\n", + "As shown in the first example in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List), we can create a `list` like `numbers` and loop over the numbers in it on a one-by-one basis." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: Fill in the *condition* of the `if` statement such that only numbers divisible by `3` are printed! Adjust the call of the [print() ](https://docs.python.org/3/library/functions.html#print) function such that the `for`-loop prints out all the numbers on *one* line of output!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in numbers:\n", + " if ...:\n", + " print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Instead of looping over an *existing* object referenced by a variable like `numbers`, we may also create a *new* object within the `for` statement and loop over it directly. For example, below we write out the `list` object as a *literal*.\n", + "\n", + "Generically, the objects contained in a `list` objects are referred to as its **elements**. We reflect that in the name of the *target* variable `element` that is assigned a different number in every iteration of the `for`-loop. While we could use *any* syntactically valid name, it is best to choose one that makes sense in the context (e.g., `number` in `numbers`).\n", + "\n", + "**Q2**: Fill in the condition of the `if` statement such that only numbers consisting of *one* digit are printed out! As before, print out all the numbers on *one* line of output!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for element in [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]:\n", + " if ...:\n", + " print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "An easy way to loop over a `list` object in a sorted manner, is to wrap it with the built-in [sorted() ](https://docs.python.org/3/library/functions.html#sorted) function.\n", + "\n", + "**Q3**: Fill in the condition of the `if` statement such that only odd numbers are printed out! Put all the numbers on *one* line of output!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in sorted(numbers):\n", + " if ...:\n", + " print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Whenever we want to loop over numbers representing a [series ](https://en.wikipedia.org/wiki/Series_%28mathematics%29) in the mathematical sense (i.e., a rule to calculate the next number from its predecessor), we may be able to use the [range() ](https://docs.python.org/3/library/functions.html#func-range) built-in.\n", + "\n", + "For example, to loop over the whole numbers from `0` to `9` (both including) in order, we could write them out in a `list` like below.\n", + "\n", + "**Q4**: Fill in the call to the [print() ](https://docs.python.org/3/library/functions.html#print) function such that all the numbers are printed on *one* line ouf output!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]:\n", + " print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: Read the documentation on the [range() ](https://docs.python.org/3/library/functions.html#func-range) built-in! It may be used with either one, two, or three expressions \"passed\" in. What do `start`, `stop`, and `step` mean? Fill in the calls to [range() ](https://docs.python.org/3/library/functions.html#func-range) and [print() ](https://docs.python.org/3/library/functions.html#print) to mimic the output of **Q4**!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in range(...):\n", + " print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: Fill in the calls to [range() ](https://docs.python.org/3/library/functions.html#func-range) and [print() ](https://docs.python.org/3/library/functions.html#print) to print out *all* numbers from `1` to `10` (both including)!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in range(...):\n", + " print(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: Fill in the calls to [range() ](https://docs.python.org/3/library/functions.html#func-range) and [print() ](https://docs.python.org/3/library/functions.html#print) to print out the *even* numbers from `1` to `10` (both including)! Do *not* use an `if` statement to accomplish this!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in range(...):\n", + " print(...)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/chapter_01_elements/02_content.ipynb b/01_elements/03_content.ipynb similarity index 96% rename from chapter_01_elements/02_content.ipynb rename to 01_elements/03_content.ipynb index ea72a59..74c3477 100644 --- a/chapter_01_elements/02_content.ipynb +++ b/01_elements/03_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/02_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/03_content.ipynb)." ] }, { @@ -19,7 +19,7 @@ } }, "source": [ - "# Chapter 1: Elements of a Program (Part 2)" + "# Chapter 1: Elements of a Program (continued)" ] }, { @@ -367,7 +367,7 @@ } }, "source": [ - "Some variables magically exist when a Python process is started or are added by Jupyter. We may safely ignore the former until [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_11_classes/00_content.ipynb) and the latter for good." + "Some variables magically exist when a Python process is started or are added by Jupyter. We may safely ignore the former until [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb) and the latter for good." ] }, { @@ -679,7 +679,7 @@ } }, "source": [ - "Variables with leading and trailing double underscores, referred to as **dunder** in Python jargon, are used for built-in functionalities and to implement object-oriented features as we see in [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_11_classes/00_content.ipynb). We must *not* use this style for variables!" + "Variables with leading and trailing double underscores, referred to as **dunder** in Python jargon, are used for built-in functionalities and to implement object-oriented features as we see in [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb). We must *not* use this style for variables!" ] }, { @@ -960,7 +960,7 @@ "source": [ "Let's change the first element of `x`.\n", "\n", - "[Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_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 yet another operator, namely the **indexing operator**. So, `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 we put in the list. The indexing operator must be an operator as we merely *read* the first element and do not change anything in memory permanently.\n", + "[Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/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 yet another operator, namely the **indexing operator**. So, `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 we put in the list. The indexing operator must be an operator as we merely *read* the first element and do not change anything in memory permanently.\n", "\n", "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)." ] @@ -1136,7 +1136,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 have said about *individual* operators before, namely that they have *no* permanent side effects in memory, actually belongs here, to begin with: The absence of any *permanent* side effects is the characteristic property of expressions, and all the code cells in the \"*(Arithmetic) Operators*\" section in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb#%28Arithmetic%29-Operators) of this chapter are examples of expressions.\n", + "What we have said about *individual* operators before, namely that they have *no* permanent side effects in memory, actually belongs here, to begin with: The absence of any *permanent* side effects is the characteristic property of expressions, and all the code cells in the \"*(Arithmetic) Operators*\" section in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#%28Arithmetic%29-Operators) of this chapter are examples of expressions.\n", "\n", "The simplest possible expressions contain only one variable or literal. The output below a code cell is Jupyter's way of returning the reference to the object to us!\n", "\n", @@ -1406,7 +1406,7 @@ } }, "source": [ - "... calling the built-in [print() ](https://docs.python.org/3/library/functions.html#print) function does *neither* change the memory *nor* evaluate to an object (disregarding the `None` object explained in [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/00_content.ipynb#Function-Definitions)). We could view changing the computer's screen as a side effect but this is outside of Python's memory!\n", + "... calling the built-in [print() ](https://docs.python.org/3/library/functions.html#print) function does *neither* change the memory *nor* evaluate to an object (disregarding the `None` object explained in [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb#Function-Definitions)). We could view changing the computer's screen as a side effect but this is outside of Python's memory!\n", "\n", "Also, the cell below has *no* output! It only looks like it does as Jupyter redirects whatever [print() ](https://docs.python.org/3/library/functions.html#print) writes to the \"screen\" to below a cell. We see a difference to the expressions above in that there are no brackets `[...]` next to the output showing the execution count number." ] diff --git a/chapter_01_elements/03_exercises.ipynb b/01_elements/04_exercises.ipynb similarity index 91% rename from chapter_01_elements/03_exercises.ipynb rename to 01_elements/04_exercises.ipynb index d9d0bbf..f1cca6d 100644 --- a/chapter_01_elements/03_exercises.ipynb +++ b/01_elements/04_exercises.ipynb @@ -4,28 +4,21 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/03_exercises.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/04_exercises.ipynb)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Chapter 1: Elements of a Program (Part 2)" + "# Chapter 1: Elements of a Program (Coding Exercises)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Coding Exercises" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The exercises below assume that you have read the second part of [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/02_content.ipynb).\n", + "The exercises below assume that you have read the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_content.ipynb) Chapter 1.\n", "\n", "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." ] @@ -34,7 +27,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Python as a Calculator" + "## Python as a Calculator" ] }, { diff --git a/chapter_01_elements/04_summary.ipynb b/01_elements/05_summary.ipynb similarity index 90% rename from chapter_01_elements/04_summary.ipynb rename to 01_elements/05_summary.ipynb index 9f52d19..c3b6339 100644 --- a/chapter_01_elements/04_summary.ipynb +++ b/01_elements/05_summary.ipynb @@ -8,19 +8,7 @@ } }, "source": [ - "\n", - "# Chapter 1: Elements of a Program" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "## TL;DR" + "# Chapter 1: Elements of a Program (TL;DR)" ] }, { @@ -57,7 +45,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 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_11_classes/00_content.ipynb))\n", + " - built-in objects (incl. **literals**) vs. user-defined objects (cf., [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb))\n", " - e.g., `1`, `1.0`, and `\"one\"` are three different objects of distinct types that are also literals (i.e., by the way we type them into the command line Python knows what the value and type are)\n", "\n", "\n", @@ -92,14 +80,14 @@ " - ignored by Python\n", "\n", "\n", - "- functions (cf., [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/00_content.ipynb))\n", + "- functions (cf., [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/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/develop/chapter_03_conditionals/00_content.ipynb) and [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_04_iteration/00_content.ipynb))\n", + "- flow control (cf., [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb) and [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb))\n", " - expression of **business logic** or an **algorithm**\n", " - conditional execution of parts of a program (e.g., `if` statements)\n", " - repetitive execution of parts of a program (e.g., `for`-loops)" diff --git a/chapter_01_elements/05_review.ipynb b/01_elements/06_review.ipynb similarity index 90% rename from chapter_01_elements/05_review.ipynb rename to 01_elements/06_review.ipynb index d8ec024..d975603 100644 --- a/chapter_01_elements/05_review.ipynb +++ b/01_elements/06_review.ipynb @@ -4,21 +4,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Chapter 1: Elements of a Program" + "# Chapter 1: Elements of a Program (Review Questions)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Content Review" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb) and the [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/02_content.ipynb) part of Chapter 1.\n", + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb) and the [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_content.ipynb) part of Chapter 1.\n", "\n", "Be concise in your answers! Most questions can be answered in *one* sentence." ] @@ -27,7 +20,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Essay Questions " + "## Essay Questions " ] }, { @@ -118,7 +111,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### True / False Questions" + "## True / False Questions" ] }, { diff --git a/chapter_01_elements/06_resources.ipynb b/01_elements/07_resources.ipynb similarity index 98% rename from chapter_01_elements/06_resources.ipynb rename to 01_elements/07_resources.ipynb index bdc2e29..c73cc49 100644 --- a/chapter_01_elements/06_resources.ipynb +++ b/01_elements/07_resources.ipynb @@ -8,19 +8,7 @@ } }, "source": [ - "\n", - "# Chapter 1: Elements of a Program" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "## Further Resources" + "# Chapter 1: Elements of a Program (Further Resources)" ] }, { @@ -58,7 +46,7 @@ " " ], "text/plain": [ - "" + "" ] }, "execution_count": 1, diff --git a/chapter_02_functions/00_content.ipynb b/02_functions/00_content.ipynb similarity index 95% rename from chapter_02_functions/00_content.ipynb rename to 02_functions/00_content.ipynb index 4d18040..964ecaa 100644 --- a/chapter_02_functions/00_content.ipynb +++ b/02_functions/00_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_02_functions/00_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/00_content.ipynb)." ] }, { @@ -30,7 +30,7 @@ } }, "source": [ - "In [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List), we simply typed the code to calculate the average of the even numbers in a list of whole numbers 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. 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/develop/01_elements/00_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List), we simply typed the code to calculate the average of the even numbers in a list of whole numbers 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. 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", "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. Also, we look at how we can extend our Python installation with functionalities written by other people." ] @@ -307,7 +307,7 @@ } }, "source": [ - "To execute a function, we **call** it with the **call operator** `()` as shown many times in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb) and above.\n", + "To execute a function, we **call** it with the **call operator** `()` as shown many times in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb) and above.\n", "\n", "If we are unsure whether a variable references a function or not, we can verify that with the built-in [callable() ](https://docs.python.org/3/library/functions.html#callable) function.\n", "\n", @@ -538,7 +538,7 @@ } }, "source": [ - "Notice the subtle difference compared to the behavior of the `//` operator in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb##%28Arithmetic#%29-Operators) that \"rounds\" towards minus infinity: [int() ](https://docs.python.org/3/library/functions.html#int) always \"rounds\" towards `0`." + "Notice the subtle difference compared to the behavior of the `//` operator in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb##%28Arithmetic#%29-Operators) that \"rounds\" towards minus infinity: [int() ](https://docs.python.org/3/library/functions.html#int) always \"rounds\" towards `0`." ] }, { @@ -869,7 +869,7 @@ } }, "source": [ - "We may create so-called *user-defined* **functions** with the `def` statement (cf., [reference ](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)). To extend an already familiar example, we reuse the introductory example from [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb#Best-Practices) in its final Pythonic version and transform it into the function `average_evens()` below. We replace the variable name `numbers` with `integers` for didactical purposes in the first couple of examples.\n", + "We may create so-called *user-defined* **functions** with the `def` statement (cf., [reference ](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)). To extend an already familiar example, we reuse the introductory example from [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Best-Practices) in its final Pythonic version and transform it into the function `average_evens()` below. We replace the variable name `numbers` with `integers` for didactical purposes in the first couple of examples.\n", "\n", "A function's **name** must be chosen according to the same naming rules as ordinary variables since 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", @@ -881,7 +881,7 @@ "\n", "A function may specify an *explicit* **return value** (i.e., \"result\" or \"output\") 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**. For example, the built-in [print() ](https://docs.python.org/3/library/functions.html#print) function changes what we see on the screen. Strictly speaking, [print() ](https://docs.python.org/3/library/functions.html#print) and other void functions also have an *implicit* return value, namely the `None` object.\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/develop/chapter_06_text/00_content.ipynb#The-str-Type). Widely adopted standards for docstrings are [PEP 257 ](https://www.python.org/dev/peps/pep-0257/) and section 3.8 of [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/develop/06_text/00_content.ipynb#The-str-Type). Widely adopted standards for docstrings are [PEP 257 ](https://www.python.org/dev/peps/pep-0257/) and section 3.8 of [Google's Python Style Guide ](https://github.com/google/styleguide/blob/gh-pages/pyguide.md)." ] }, { @@ -1012,7 +1012,7 @@ "source": [ "Its value may seem awkward at first: It consists of a location showing where the function is defined (i.e., `__main__` here, which is Python's way of saying \"in this notebook\") and the signature wrapped inside angle brackets `<` and `>`.\n", " \n", - "The angle brackets are a convention to indicate that the value may *not* be used as a *literal* (i.e., typed back into another code cell). [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_11_classes/00_content.ipynb) introduces the concept of a **text representation** of an object, which is related to the *semantic* meaning of an object's value as discussed in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb#Value-/-\"Meaning\"), and the angle brackets convention is one such way to represent an object as text. When executed, the angle brackets cause a `SyntaxError` because Python expects the `<` operator to come with an operand on both sides (cf., [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_03_conditionals/00_content.ipynb#Relational-Operators))." + "The angle brackets are a convention to indicate that the value may *not* be used as a *literal* (i.e., typed back into another code cell). [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb) introduces the concept of a **text representation** of an object, which is related to the *semantic* meaning of an object's value as discussed in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Value-/-\"Meaning\"), and the angle brackets convention is one such way to represent an object as text. When executed, the angle brackets cause a `SyntaxError` because Python expects the `<` operator to come with an operand on both sides (cf., [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb#Relational-Operators))." ] }, { @@ -1148,7 +1148,7 @@ "\n", "Returns:\n", " average (float)\n", - "\u001b[0;31mFile:\u001b[0m ~/repos/intro-to-python/chapter_02_functions/\n", + "\u001b[0;31mFile:\u001b[0m ~/repos/intro-to-python/02_functions/\n", "\u001b[0;31mType:\u001b[0m function\n" ] }, @@ -1197,7 +1197,7 @@ "\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[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\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/chapter_02_functions/\n", + "\u001b[0;31mFile:\u001b[0m ~/repos/intro-to-python/02_functions/\n", "\u001b[0;31mType:\u001b[0m function\n" ] }, @@ -1647,7 +1647,7 @@ "source": [ "[PythonTutor ](http://pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%5D%0A%0Adef%20average_wrong%28integers%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%0Aresult%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 `numbers` 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 with 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/develop/chapter_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." + "The frames logic shown by PythonTutor is the mechanism with 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/develop/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." ] }, { @@ -2021,7 +2021,7 @@ } }, "source": [ - "So far, we have specified only one parameter in each of our user-defined functions. In [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb#%28Arithmetic%29-Operators), however, we saw the built-in [divmod() ](https://docs.python.org/3/library/functions.html#divmod) function take two arguments. And, the order in which they are passed in matters! 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 specified only one parameter in each of our user-defined functions. In [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#%28Arithmetic%29-Operators), however, we saw the built-in [divmod() ](https://docs.python.org/3/library/functions.html#divmod) function take two arguments. And, the order in which they are passed in matters! 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**." ] }, { @@ -3012,7 +3012,7 @@ "source": [ "The main point of having functions without a reference to them 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 (cf., [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_08_mfr/00_content.ipynb#Lambda-Expressions))." + "Popular applications of lambda expressions occur in combination with the **map-filter-reduce** paradigm (cf., [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb#Lambda-Expressions))." ] } ], diff --git a/chapter_02_functions/01_exercises.ipynb b/02_functions/01_exercises.ipynb similarity index 92% rename from chapter_02_functions/01_exercises.ipynb rename to 02_functions/01_exercises.ipynb index 664e8f0..795be04 100644 --- a/chapter_02_functions/01_exercises.ipynb +++ b/02_functions/01_exercises.ipynb @@ -4,28 +4,21 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_02_functions/01_exercises.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/01_exercises.ipynb)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Chapter 2: Functions & Modularization" + "# Chapter 2: Functions & Modularization (Coding Exercises)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Coding Exercises" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/00_content.ipynb) of Chapter 2.\n", + "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb) of Chapter 2.\n", "\n", "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." ] @@ -34,7 +27,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Volume of a Sphere" + "## Volume of a Sphere" ] }, { @@ -45,7 +38,7 @@ "\n", "Hints:\n", "- use an appropriate approximation for $\\pi$\n", - "- you may use the [standard library ](https://docs.python.org/3/library/index.html) to do so if you have already looked at the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/02_content.ipynb) of Chapter 2." + "- you may use the [standard library ](https://docs.python.org/3/library/index.html) to do so if you have already looked at the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/02_content.ipynb) of Chapter 2." ] }, { @@ -57,15 +50,6 @@ "import ... # you may drop this cell and use your own approximation for Pi" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "r = ..." - ] - }, { "cell_type": "code", "execution_count": null, diff --git a/chapter_02_functions/02_content.ipynb b/02_functions/02_content.ipynb similarity index 93% rename from chapter_02_functions/02_content.ipynb rename to 02_functions/02_content.ipynb index 4c18bb4..06f1765 100644 --- a/chapter_02_functions/02_content.ipynb +++ b/02_functions/02_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_02_functions/02_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/02_content.ipynb)." ] }, { @@ -19,7 +19,7 @@ } }, "source": [ - "# Chapter 2: Functions & Modularization (Part 2)" + "# Chapter 2: Functions & Modularization (continued)" ] }, { @@ -32,28 +32,8 @@ "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 \"reinventing the wheel\" in each new project." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "## Extending Core Python" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ + "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 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." @@ -67,7 +47,7 @@ } }, "source": [ - "### The Standard Library" + "## The Standard Library" ] }, { @@ -95,7 +75,7 @@ } }, "source": [ - "#### [math ](https://docs.python.org/3/library/math.html) Module" + "### [math ](https://docs.python.org/3/library/math.html) Module" ] }, { @@ -448,7 +428,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/develop/chapter_01_elements/02_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", + "So just as a reminder from the expression vs. statement discussion in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_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)." ] @@ -570,7 +550,7 @@ } }, "source": [ - "#### [random ](https://docs.python.org/3/library/random.html) Module" + "### [random ](https://docs.python.org/3/library/random.html) Module" ] }, { @@ -629,7 +609,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 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_11_classes/00_content.ipynb), 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 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb), while the latter is explained further below." ] }, { @@ -992,7 +972,7 @@ } }, "source": [ - "### Third-party Packages" + "## Third-party Packages" ] }, { @@ -1020,7 +1000,7 @@ } }, "source": [ - "#### [numpy](http://www.numpy.org/) Library" + "### [numpy](http://www.numpy.org/) Library" ] }, { @@ -1033,7 +1013,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.\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/develop/chapter_00_intro/00_content.ipynb#Markdown-Cells-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/develop/00_intro/00_content.ipynb#Markdown-Cells-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." ] @@ -1327,7 +1307,7 @@ } }, "source": [ - "### Local Modules and Packages" + "## Local Modules and Packages" ] }, { @@ -1338,7 +1318,7 @@ } }, "source": [ - "For sure, we can create local modules and packages. In the Chapter 2 directory, there is a [*sample_module.py* ](https://github.com/webartifex/intro-to-python/blob/develop/chapter_02_functions/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 Chapter 2 directory, there is a [*sample_module.py* ](https://github.com/webartifex/intro-to-python/blob/develop/02_functions/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", @@ -1358,7 +1338,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "/home/webartifex/repos/intro-to-python/chapter_02_functions\n" + "/home/webartifex/repos/intro-to-python/02_functions\n" ] } ], @@ -1404,7 +1384,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 41, @@ -1424,7 +1404,7 @@ } }, "source": [ - "Disregarding the dunder-style attributes, `mod` defines the attributes `_round_all`, `_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/develop/chapter_02_functions/sample_module.py) file.\n", + "Disregarding the dunder-style attributes, `mod` defines the attributes `_round_all`, `_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/develop/02_functions/sample_module.py) file.\n", "\n", "A convention when working with imported code is to *disregard* any attributes starting with a single 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", @@ -1475,7 +1455,7 @@ } }, "source": [ - "We use the imported `mod.average_evens()` just like `average_evens()` defined in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/00_content.ipynb) of this chapter. 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." + "We use the imported `mod.average_evens()` just like `average_evens()` defined in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb) of this chapter. 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." ] }, { @@ -1591,7 +1571,7 @@ } }, "source": [ - "Packages are a generalization of modules, and we look at one in detail in [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_11_classes/00_content.ipynb). You may, however, already look at a [sample package ](https://github.com/webartifex/intro-to-python/tree/develop/chapter_11_classes/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 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb). You may, however, already look at a [sample package ](https://github.com/webartifex/intro-to-python/tree/develop/11_classes/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/chapter_02_functions/03_summary.ipynb b/02_functions/03_summary.ipynb similarity index 93% rename from chapter_02_functions/03_summary.ipynb rename to 02_functions/03_summary.ipynb index c5d712b..2161f17 100644 --- a/chapter_02_functions/03_summary.ipynb +++ b/02_functions/03_summary.ipynb @@ -8,18 +8,7 @@ } }, "source": [ - "# Chapter 2: Functions & Modularization" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "## TL;DR" + "# Chapter 2: Functions & Modularization (TL;DR)" ] }, { diff --git a/chapter_02_functions/04_review.ipynb b/02_functions/04_review.ipynb similarity index 90% rename from chapter_02_functions/04_review.ipynb rename to 02_functions/04_review.ipynb index daa9d88..a85dade 100644 --- a/chapter_02_functions/04_review.ipynb +++ b/02_functions/04_review.ipynb @@ -4,21 +4,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Chapter 2: Functions & Modularization" + "# Chapter 2: Functions & Modularization (Review Questions)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Content Review" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/00_content.ipynb) and the [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/02_content.ipynb) part of Chapter 2.\n", + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb) and the [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/02_content.ipynb) part of Chapter 2.\n", "\n", "Be concise in your answers! Most questions can be answered in *one* sentence." ] @@ -27,7 +20,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Essay Questions " + "## Essay Questions " ] }, { @@ -132,7 +125,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### True / False Questions" + "## True / False Questions" ] }, { diff --git a/chapter_02_functions/sample_module.py b/02_functions/sample_module.py similarity index 100% rename from chapter_02_functions/sample_module.py rename to 02_functions/sample_module.py diff --git a/README.md b/README.md index 3193767..658d95c 100644 --- a/README.md +++ b/README.md @@ -15,60 +15,62 @@ Alternatively, the content can be viewed in a web browser or interactively (i.e., code can be executed) on [Binder ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab). - *Chapter 0*: Introduction - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/00_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/00_intro/00_content.ipynb) (Python's History & Background; Open-source & Communities; JupyterLab; Programming vs. Computer Science; Learning Tips) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/01_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/00_intro/01_exercises.ipynb) (Mastering Markdown) - - [review ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/02_review.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/02_review.ipynb) - **Part A: Expressing Logic** - *Chapter 1*: Elements of a Program - - [content 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/00_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/00_content.ipynb) (A first Example: Averaging Even Numbers; Operators; Objects & Data Types; Errors) - - [exercises 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/01_exercises.ipynb) - (Printing Output; - Simple `for`-loops) - - [content 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/02_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/02_content.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/01_exercises.ipynb) + (Printing Output) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/02_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/02_exercises.ipynb) + (Simple `for`-loops) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/03_content.ipynb) (Memory in Detail; Variables & References; Mutability; Expressions & Statements) - - [exercises 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/03_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/03_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/03_exercises.ipynb) (Python as a Calculator) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/04_summary.ipynb) - - [review ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/05_review.ipynb) - - [resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/06_resources.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/06_resources.ipynb) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/04_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/05_review.ipynb) + - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/06_resources.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/06_resources.ipynb) - *Chapter 2*: Functions & Modularization - - [content 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_02_functions/00_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/00_content.ipynb) (Built-in Functions & Constructors; Function Definitions; Function Calls & Scoping Rules; Positional vs. Keyword Arguments; Modularization) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_02_functions/01_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/01_exercises.ipynb) (Volume of a Sphere) - - [content 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/02_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_02_functions/02_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/02_content.ipynb) (Standard Library: `math` & `random` Modules; Third-party Packages: `numpy` Library; Writing one's own Modules) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/03_summary.ipynb) - - [review ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_02_functions/04_review.ipynb) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/03_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/04_review.ipynb) #### Videos diff --git a/chapter_01_elements/01_exercises.ipynb b/chapter_01_elements/01_exercises.ipynb deleted file mode 100644 index 6e057b5..0000000 --- a/chapter_01_elements/01_exercises.ipynb +++ /dev/null @@ -1,336 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_01_elements/01_exercises.ipynb)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Chapter 1: Elements of a Program" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Coding Exercises" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The exercises below assume that you have read the first part of [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb).\n", - "\n", - "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Q1**: Printing Output" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q1.1**: *Concatenate* `greeting` and `audience` below with the `+` operator and print out the resulting message `\"Hello World\"` with only *one* call of the built-in [print() ](https://docs.python.org/3/library/functions.html#print) function!\n", - "\n", - "Hint: You may have to \"add\" a space character in between `greeting` and `audience`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "greeting = \"Hello\"\n", - "audience = \"World\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(...)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q1.2**: How is your answer to **Q1.1** an example of the concept of **operator overloading**?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " < your answer >" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q1.3**: Read the documentation on the built-in [print() ](https://docs.python.org/3/library/functions.html#print) function! How can you print the above message *without* concatenating `greeting` and `audience` first in *one* call of [print() ](https://docs.python.org/3/library/functions.html#print)?\n", - "\n", - "Hint: The `*objects` in the documentation implies that we can put several *expressions* (i.e., variables) separated by commas within the same call of the [print() ](https://docs.python.org/3/library/functions.html#print) function." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(...)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q1.4**: What does the `sep=\" \"` mean in the documentation on the built-in [print() ](https://docs.python.org/3/library/functions.html#print) function? Adjust and use it to print out the three names referenced by `first`, `second`, and `third` on *one* line separated by *commas* with only *one* call of the [print() ](https://docs.python.org/3/library/functions.html#print) function!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "first = \"Anthony\"\n", - "second = \"Berta\"\n", - "third = \"Christian\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(...)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q1.5**: Lastly, what does the `end=\"\\n\"` mean in the documentation? Adjust and use it within the `for`-loop to print the numbers `1` through `10` on *one* line with only *one* call of the [print() ](https://docs.python.org/3/library/functions.html#print) function!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for number in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:\n", - " print(...)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### **Q2**: Simple `for`-loops" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`for`-loops are extremely versatile in Python. That is different from many other programming languages.\n", - "\n", - "As shown in the first example in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_01_elements/00_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List), we can create a `list` like `numbers` and loop over the numbers in it on a one-by-one basis." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q2.1**: Fill in the *condition* of the `if` statement such that only numbers divisible by `3` are printed! Adjust the call of the [print() ](https://docs.python.org/3/library/functions.html#print) function such that the `for`-loop prints out all the numbers on *one* line of output!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for number in numbers:\n", - " if ...:\n", - " print(...)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Instead of looping over an *existing* object referenced by a variable like `numbers`, we may also create a *new* object within the `for` statement and loop over it directly. For example, below we write out the `list` object as a *literal*.\n", - "\n", - "Generically, the objects contained in a `list` objects are referred to as its **elements**. We reflect that in the name of the *target* variable `element` that is assigned a different number in every iteration of the `for`-loop. While we could use *any* syntactically valid name, it is best to choose one that makes sense in the context (e.g., `number` in `numbers`).\n", - "\n", - "**Q2.2**: Fill in the condition of the `if` statement such that only numbers consisting of *one* digit are printed out! As before, print out all the numbers on *one* line of output!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for element in [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]:\n", - " if ...:\n", - " print(...)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "An easy way to loop over a `list` object in a sorted manner, is to wrap it with the built-in [sorted() ](https://docs.python.org/3/library/functions.html#sorted) function.\n", - "\n", - "**Q2.3**: Fill in the condition of the `if` statement such that only odd numbers are printed out! Put all the numbers on *one* line of output!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for number in sorted(numbers):\n", - " if ...:\n", - " print(...)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Whenever we want to loop over numbers representing a [series ](https://en.wikipedia.org/wiki/Series_%28mathematics%29) in the mathematical sense (i.e., a rule to calculate the next number from its predecessor), we may be able to use the [range() ](https://docs.python.org/3/library/functions.html#func-range) built-in.\n", - "\n", - "For example, to loop over the whole numbers from `0` to `9` (both including) in order, we could write them out in a `list` like below.\n", - "\n", - "**Q2.4**: Fill in the call to the [print() ](https://docs.python.org/3/library/functions.html#print) function such that all the numbers are printed on *one* line ouf output!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for number in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]:\n", - " print(...)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q2.5**: Read the documentation on the [range() ](https://docs.python.org/3/library/functions.html#func-range) built-in! It may be used with either one, two, or three expressions \"passed\" in. What do `start`, `stop`, and `step` mean? Fill in the calls to [range() ](https://docs.python.org/3/library/functions.html#func-range) and [print() ](https://docs.python.org/3/library/functions.html#print) to mimic the output of **Q2.4**!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for number in range(...):\n", - " print(...)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q2.6**: Fill in the calls to [range() ](https://docs.python.org/3/library/functions.html#func-range) and [print() ](https://docs.python.org/3/library/functions.html#print) to print out *all* numbers from `1` to `10` (both including)!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for number in range(...):\n", - " print(...)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Q2.7**: Fill in the calls to [range() ](https://docs.python.org/3/library/functions.html#func-range) and [print() ](https://docs.python.org/3/library/functions.html#print) to print out the *even* numbers from `1` to `10` (both including)! Do *not* use an `if` statement to accomplish this!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for number in range(...):\n", - " print(...)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.6" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": false, - "sideBar": true, - "skip_h1_title": true, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": false, - "toc_window_display": false - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} From c94574b86051ee758e0173ccb4a52cac828b290b Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 15 Oct 2020 15:29:58 +0200 Subject: [PATCH 050/142] Add initial version of chapter 03 --- 00_intro/00_content.ipynb | 2 +- 03_conditionals/00_content.ipynb | 2831 ++++++++++++++++++++++++++++++ README.md | 8 + 3 files changed, 2840 insertions(+), 1 deletion(-) create mode 100644 03_conditionals/00_content.ipynb diff --git a/00_intro/00_content.ipynb b/00_intro/00_content.ipynb index 75b5fb4..a2e8778 100644 --- a/00_intro/00_content.ipynb +++ b/00_intro/00_content.ipynb @@ -753,7 +753,7 @@ " - *Chapter 1*: [Elements of a Program ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb)\n", " - *Chapter 2*: [Functions & Modularization ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb)\n", "- What is the flow of execution? How can we form sentences from words?\n", - " - *Chapter 3*: Conditionals & Exceptions\n", + " - *Chapter 3*: [Conditionals & Exceptions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb)\n", " - *Chapter 4*: Recursion & Looping" ] }, diff --git a/03_conditionals/00_content.ipynb b/03_conditionals/00_content.ipynb new file mode 100644 index 0000000..7a54f13 --- /dev/null +++ b/03_conditionals/00_content.ipynb @@ -0,0 +1,2831 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/00_content.ipynb)." + ] + }, + { + "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 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb) except for the `if`-related parts. While it does what we expect it to, there is a whole lot more to learn by taking it apart. In particular, the `if` may occur within both a **statement** or an **expression**, analogous as to how a noun in a natural language can be the subject of *or* an object in a sentence. What is common to both usages is that it leads to code being executed for *parts* of the input only. It is a way of controlling the **flow of execution** in a program.\n", + "\n", + "After deconstructing `if` in the first part of this chapter, we take a close look at a similar concept, namely handling **exceptions**." + ] + }, + { + "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**. It is such simple true-or-false observations about the world on which mathematicians, and originally philosophers, base their rules of reasoning: They are studied formally in the field of [propositional logic ](https://en.wikipedia.org/wiki/Propositional_calculus).\n", + "\n", + "A trivial example involves the equality operator `==` that evaluates to either `True` or `False` depending on its operands \"comparing equal\" or not." + ] + }, + { + "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": "fragment" + } + }, + "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": [ + "The `==` operator handles objects of *different* types: Because of that, it implements a notion of equality in line with how humans think of things being equal or not. After all, `42` and `42.0` are different $0$s and $1$s for a computer and other programming languages may say `False` here! Technically, this is yet another example of operator overloading." + ] + }, + { + "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 the `==` operator seems to not work intuitively. [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb#Imprecision) provides more insights into this \"bug.\"" + ] + }, + { + "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": "slide" + } + }, + "source": [ + "## The `bool` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`True` and `False` are built-in *objects* of type `bool`." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "True" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "94478067031520" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(True)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "False" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "94478067031552" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(False)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "bool" + ] + }, + "execution_count": 10, + "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 built-in object! We saw the latter before in [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb#Function-Definitions) as the *implicit* return value of a function without a `return` statement.\n", + "\n", + "We might think of `None` indicating a \"maybe\" or even an \"unknown\" answer; however, for Python, there are no \"maybe\" or \"unknown\" objects, as we see further below!\n", + "\n", + "Whereas `False` is of type `bool`, `None` is of type `NoneType`. So, they are unrelated! On the contrary, as both `True` and `False` are of the same type, we could call them \"siblings.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "None" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "94478066920032" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(None)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "NoneType" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(None)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Singletons" + ] + }, + { + "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 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/develop/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." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = True\n", + "b = True\n", + "\n", + "a is b" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To contrast this, we create *two* `789` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = 789\n", + "b = 789\n", + "\n", + "a is b" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "So the following expression regards *four* objects in memory: *One* `list` object holding six references to *three* other objects." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[True, False, None, True, False, None]" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[True, False, None, True, False, 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 `bool` object." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 == 123" + ] + }, + { + "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": [ + "42 != 123 # \"not equal to\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The \"less than\" `<` or \"greater than\" `>` operators mean \"*strictly* less than\" or \"*strictly* greater than\" but may be combined with the equality operator into just `<=` and `>=`. This is a shortcut for using the logical `or` operator as described in the next section." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 < 123" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 <= 123" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 > 123" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 >= 123" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Logical Operators" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Boolean expressions may be combined or negated with the **logical operators** `and`, `or`, and `not` to form new boolean expressions. This may be done repeatedly to obtain boolean expressions of arbitrary complexity.\n", + "\n", + "Their usage is similar to how the equivalent words are used in everyday English:\n", + "\n", + "- `and` evaluates to `True` if *both* operands evaluate to `True` and `False` otherwise,\n", + "- `or` evaluates to `True` if either one *or* both operands evaluate to `True` and `False` otherwise, and\n", + "- `not` evaluates to `True` if its *only* operand evaluates to `False` and vice versa." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "a = 42\n", + "b = 87" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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." + ] + }, + { + "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": [ + "a > 5 and b <= 100" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, sometimes, it is good to use *parentheses* around each operand for clarity." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(a > 5) and (b <= 100)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This is especially so when several logical operators are combined." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a <= 5 or not b > 100" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(a <= 5) or not (b > 100)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(a <= 5) or (not (b > 100)) # no need to \"over do\" it" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For even better readability, some practitioners suggest to *never* use the `>` and `>=` operators (cf., [source](https://llewellynfalco.blogspot.com/2016/02/dont-use-greater-than-sign-in.html); note that the included example is written in [Java ](https://en.wikipedia.org/wiki/Java_%28programming_language%29) where `&&` means `and` and `||` means `or`).\n", + "\n", + "We may **chain** operators if the expressions that contain them are combined with the `and` operator. For example, the following two cells implement the same logic, where the second is a lot easier to read." + ] + }, + { + "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": [ + "5 < a and a < 87" + ] + }, + { + "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": [ + "5 < a < 87" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Truthy vs. Falsy" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The operands of a logical operator do not need to be *boolean* expressions but may be *any* expression. If an operand does *not* evaluate to an object of type `bool`, Python automatically casts it as such. Then, Pythonistas say that the expression is evaluated in a boolean context.\n", + "\n", + "For example, any non-zero numeric object is cast as `True`. While this behavior allows writing more concise and thus more \"beautiful\" code, it may also be a source of confusion.\n", + "\n", + "So, `(a - 40)` is cast as `True` and then the overall expression evaluates to `True` as well." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(a - 40) and (b < 100)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Whenever we are unsure how Python evaluates a non-boolean expression in a boolean context, the [bool() ](https://docs.python.org/3/library/functions.html#bool) built-in allows us to do it ourselves. [bool() ](https://docs.python.org/3/library/functions.html#bool), like [int() ](https://docs.python.org/3/library/functions.html#int), is yet another *constructor*." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bool(a - 40)" + ] + }, + { + "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(a - 42)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's keep in mind that negative numbers also evaluate to `True`!" + ] + }, + { + "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": [ + "bool(a - 44)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In a boolean context, `None` is cast as `False`! So, `None` is *not* a \"maybe\" answer but a \"no.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 35, + "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 `False` whenever they are empty and `True` if they hold at least one element." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "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": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "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": 38, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bool(\"\")" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bool(\"Lorem ipsum dolor sit amet.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Pythonistas use the terms **truthy** or **falsy** to describe a non-boolean expression's behavior when evaluated in a boolean context." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Short-Circuiting" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When evaluating expressions involving the `and` and `or` operators, Python follows the **[short-circuiting ](https://en.wikipedia.org/wiki/Short-circuit_evaluation)** strategy: Once it is clear what the overall truth value is, no more operands are evaluated, and the result is *immediately* returned.\n", + "\n", + "Also, if such expressions are evaluated in a non-boolean context, the result is returned as is and *not* cast as a `bool` type.\n", + "\n", + "The two rules can be summarized as:\n", + "\n", + "- `a or b`: If `a` is truthy, it is returned *without* evaluating `b`. Otherwise, `b` is evaluated *and* returned.\n", + "- `a and b`: If `a` is falsy, it is returned *without* evaluating `b`. Otherwise, `b` is evaluated *and* returned.\n", + "\n", + "The rules may also be chained or combined.\n", + "\n", + "Let's look at a couple of examples below. To visualize which operands are evaluated, we define a helper function `expr()` that prints out the only argument it is passed before returning it." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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 truthy operand is returned." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0 or 1" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Arg: 0\n", + "Arg: 1\n" + ] + }, + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr(0) or expr(1) # both operands are evaluated" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 or 2" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Arg: 1\n" + ] + }, + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr(1) or expr(2) # 2 is not evaluated" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0 or 1 or 2" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Arg: 0\n", + "Arg: 1\n" + ] + }, + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr(0) or expr(1) or expr(2) # 2 is not evaluated" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If all operands are falsy, the last one is returned." + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "False or [] or 0" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Arg: False\n", + "Arg: []\n", + "Arg: 0\n" + ] + }, + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr(False) or expr([]) or expr(0) # all operands are evaluated" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With the `and` operator, the first falsy operand is returned." + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0 and 1" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Arg: 0\n" + ] + }, + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr(0) and expr(1) # 1 is not evaluated" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 and 0" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Arg: 1\n", + "Arg: 0\n" + ] + }, + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr(1) and expr(0) # both operands are evaluated" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 and 0 and 2" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Arg: 1\n", + "Arg: 0\n" + ] + }, + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr(1) and expr(0) and expr(2) # 2 is not evaluated" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If all operands are truthy, the last one is returned." + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 and 2 and 3" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Arg: 1\n", + "Arg: 2\n", + "Arg: 3\n" + ] + }, + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "expr(1) and expr(2) and expr(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The crucial takeaway is that Python does *not* necessarily evaluate *all* operands and, therefore, our code should never rely on that assumption." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The `if` Statement" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To write useful programs, we need to control the flow of execution, for example, to react to user input. The logic by which a program follows the rules from the \"real world\" is referred to as **[business logic ](https://en.wikipedia.org/wiki/Business_logic)**.\n", + "\n", + "One language feature to do so is the `if` statement (cf., [reference ](https://docs.python.org/3/reference/compound_stmts.html#the-if-statement)). It consists of:\n", + "\n", + "- *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** here, while the `else`-clause serves as the \"catch everything else\" case.\n", + "\n", + "In contrast to our intuitive interpretation in natural languages, only the code in *one* of the alternatives, also called **branches**, is executed. To be precise, it is always the code in the first clause whose condition evaluates to `True`.\n", + "\n", + "In terms of syntax, the header lines end with a colon, and the code blocks are indented. Formally, any statement that is written across several lines is called a **[compound statement ](https://docs.python.org/3/reference/compound_stmts.html#compound-statements)**, the code blocks are called **suites** and belong to one header line, and the term **clause** refers to a header line and its suite as a whole. So far, we have seen three compound statements: `for`, `if`, and `def`. On the contrary, **[simple statements ](https://docs.python.org/3/reference/simple_stmts.html#simple-statements)**, for example, `=`, `del`, or `return`, are written on *one* line.\n", + "\n", + "As an example, let's write code that checks if a randomly drawn number is divisible by `2`, `3`, both, or none. The code should print out a customized message for each of the *four* cases." + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "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": 58, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "import random" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(789)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### \"Wrong Logic\" Example: Is the number divisible by `2`, `3`, both, or none?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "It turns out that translating this task into code is not so trivial. Whereas the code below looks right, it is *incorrect*. The reason is that the order of the `if`-, `elif`-, and `else`-clauses matters." + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6 is divisible by 2 only\n" + ] + } + ], + "source": [ + "number = random.choice(numbers)\n", + "\n", + "if number % 2 == 0:\n", + " print(number, \"is divisible by 2 only\")\n", + "elif number % 3 == 0:\n", + " print(number, \"is divisible by 3 only\")\n", + "elif number % 2 == 0 and number % 3 == 0:\n", + " print(number, \"is divisible by 2 and 3\")\n", + "else:\n", + " print(number, \"is divisible by neither 2 nor 3\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### \"Correct Logic\" Example: Is the number divisible by `2`, `3`, both, or none?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As a number divisible by both `2` and `3` is always a special (i.e., narrower) case of a number being divisible by either `2` or `3` on their own, we must check for that condition first. The order of the two latter cases is not important as they are mutually exclusive. Below is a correct implementation of the program." + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(789)" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6 is divisible by 2 and 3\n" + ] + } + ], + "source": [ + "number = random.choice(numbers)\n", + "\n", + "if number % 3 == 0 and number % 2 == 0:\n", + " print(number, \"is divisible by 2 and 3\")\n", + "elif number % 3 == 0:\n", + " print(number, \"is divisible by 3 only\")\n", + "elif number % 2 == 0:\n", + " print(number, \"is divisible by 2 only\")\n", + "else:\n", + " print(number, \"is divisible by neither 2 nor 3\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### \"Concise Logic\" Example: Is the number divisible by `2`, `3`, both, or none?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A minor improvement could be to replace `number % 3 == 0 and number % 2 == 0` with the conciser `number % 6 == 0`. However, this has no effect on the order that is still essential for the code's correctness." + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(789)" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6 is divisible by 2 and 3\n" + ] + } + ], + "source": [ + "number = random.choice(numbers)\n", + "\n", + "if number % 6 == 0:\n", + " print(number, \"is divisible by 2 and 3\")\n", + "elif number % 3 == 0:\n", + " print(number, \"is divisible by 3 only\")\n", + "elif number % 2 == 0:\n", + " print(number, \"is divisible by 2 only\")\n", + "else:\n", + " print(number, \"is divisible by neither 2 nor 3\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Only the `if`-clause is mandatory" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Often, we only need a reduced form of the `if` statement.\n", + "\n", + "For example, below we **inject** code to print a message at random." + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "You read this as often as you see heads when tossing a coin\n" + ] + } + ], + "source": [ + "if random.random() > 0.5:\n", + " print(\"You read this as often as you see heads when tossing a coin\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Common Use Case: A binary Choice" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "More often than not, we model a **binary choice**. Then, we only need to write an `if`- and an `else`-clause." + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(789)" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6 is even\n" + ] + } + ], + "source": [ + "number = random.choice(numbers)\n", + "\n", + "if number % 2 == 0:\n", + " print(number, \"is even\")\n", + "else:\n", + " print(number, \"is odd\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To write the condition even conciser, we may take advantage of Python's implicit casting and leave out the `== 0`. However, then we *must* exchange the two suits! The `if`-clause below means \"If the `number` is odd\" in plain English. That is the opposite of the `if`-clause above." + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(789)" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6 is even\n" + ] + } + ], + "source": [ + "number = random.choice(numbers)\n", + "\n", + "if number % 2: # Note the opposite meaning!\n", + " print(number, \"is odd\")\n", + "else:\n", + " print(number, \"is even\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### \"Hard to read\" Example: Nesting `if` Statements" + ] + }, + { + "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.\n", + "\n", + "For example, the code cell below implements an [A/B Testing ](https://en.wikipedia.org/wiki/A/B_testing) strategy where half the time a \"complex\" message is shown to a \"user\" while in the remaining times an \"easy\" message is shown. To do so, the code first \"tosses a coin\" and then checks a randomly drawn `number`." + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(789)" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6 can be divided by 2 without a rest\n" + ] + } + ], + "source": [ + "number = random.choice(numbers)\n", + "\n", + "# Coin is heads.\n", + "if random.random() > 0.5:\n", + " if number % 2 == 0:\n", + " print(number, \"can be divided by 2 without a rest\")\n", + " else:\n", + " print(number, \"divided by 2 results in a non-zero rest\")\n", + "# Coin is tails.\n", + "else:\n", + " if number % 2 == 0:\n", + " print(number, \"is even\")\n", + " else:\n", + " print(number, \"is odd\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### \"Easy to read\" Example: Flattening nested `if` Statements" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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 (i.e., only a reference is created) and also helps to document the code *inline* with meaningful variable names.\n", + "\n", + "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": 72, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(789)" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6 can be divided by 2 without a rest\n" + ] + } + ], + "source": [ + "number = random.choice(numbers)\n", + "\n", + "coin_is_heads = random.random() > 0.5\n", + "number_is_even = number % 2 == 0\n", + "\n", + "if coin_is_heads and number_is_even:\n", + " print(number, \"can be divided by 2 without a rest\")\n", + "elif coin_is_heads and not number_is_even:\n", + " print(number, \"divided by 2 results in a non-zero rest\")\n", + "elif not coin_is_heads and number_is_even:\n", + " print(number, \"is even\")\n", + "else:\n", + " print(number, \"is odd\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The `if` Expression" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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:" + ] + }, + { + "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, \\text{ otherwise}\n", + "\\end{cases}\n", + "$" + ] + }, + { + "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": 74, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x = 3\n", + "\n", + "if x <= 0:\n", + " y = 0\n", + "else:\n", + " y = x\n", + "\n", + "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": 75, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x = 3\n", + "\n", + "y = 0 if x <= 0 else x\n", + "\n", + "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": 76, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x = 3\n", + "\n", + "y = max(0, x)\n", + "\n", + "y" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The `try` Statement" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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.\n", + "\n", + "Consider a situation where we are given some user input that may contain values that cause problems. To illustrate this, we draw a random integer between `0` and `5`, and then divide by this number. Naturally, we see a `ZeroDivisionError` in 16.6% of the cases." + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(123)" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "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[1;32m 1\u001b[0m \u001b[0muser_input\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrandom\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mchoice\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;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m4\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[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0;36m1\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0muser_input\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": [ + "user_input = random.choice([0, 1, 2, 3, 4, 5])\n", + "\n", + "1 / user_input" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With the compound `try` statement (cf., [reference ](https://docs.python.org/3/reference/compound_stmts.html#the-try-statement)), we can **handle** any *runtime* error.\n", + "\n", + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(123)" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Something went wrong\n" + ] + } + ], + "source": [ + "user_input = random.choice([0, 1, 2, 3, 4, 5])\n", + "\n", + "try:\n", + " print(\"The result is\", 1 / user_input)\n", + "except:\n", + " print(\"Something went wrong\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, it is good practice *not* to handle *any* possible exception but only the ones we may *expect* from the code in the `try`-clause. The reason for that is that we do not want to risk *suppressing* an exception that we do *not* expect. Also, the code base becomes easier to understand as we communicate what could go wrong during execution in an *explicit* way to the (human) reader. Python comes with a lot of [built-in exceptions ](https://docs.python.org/3/library/exceptions.html#concrete-exceptions) that we should familiarize ourselves with.\n", + "\n", + "Another good practice is to always keep the code in the `try`-clause short to not *accidentally* handle an exception we do *not* want to handle.\n", + "\n", + "In the example, we are dividing numbers and may expect a `ZeroDivisionError`." + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(123)" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Something went wrong\n" + ] + } + ], + "source": [ + "user_input = random.choice([0, 1, 2, 3, 4, 5])\n", + "\n", + "try:\n", + " print(\"The result is\", 1 / user_input)\n", + "except ZeroDivisionError:\n", + " print(\"Something went wrong\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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`-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`-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. It is randomized: So, run the cell several times and see for yourself." + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(123)" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Oops. Division by 0. How does that work?\n", + "I am always printed\n" + ] + } + ], + "source": [ + "user_input = random.choice([0, 1, 2, 3, 4, 5])\n", + "\n", + "try:\n", + " result = 1 / user_input\n", + "except ZeroDivisionError:\n", + " print(\"Oops. Division by 0. How does that work?\")\n", + "else:\n", + " print(\"The result is\", result)\n", + "finally:\n", + " print(\"I am always printed\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index 658d95c..be6a7e9 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,14 @@ Alternatively, the content can be viewed in a web browser Writing one's own Modules) - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/03_summary.ipynb) - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/04_review.ipynb) + - *Chapter 3*: Conditionals & Exceptions + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/00_content.ipynb) + (Boolean Expressions; + Relational Operators; + Logical Operators; + `if` statement; + Exception Handling) #### Videos From ef438bd2bf2714e5ab2e6a9a4a2110b4bb6ccd44 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 15 Oct 2020 15:31:07 +0200 Subject: [PATCH 051/142] Add initial version of chapter 03's exercises --- 03_conditionals/01_exercises.ipynb | 205 +++++++++++++++++++++++++++++ 03_conditionals/02_exercises.ipynb | 140 ++++++++++++++++++++ README.md | 6 + 3 files changed, 351 insertions(+) create mode 100644 03_conditionals/01_exercises.ipynb create mode 100644 03_conditionals/02_exercises.ipynb diff --git a/03_conditionals/01_exercises.ipynb b/03_conditionals/01_exercises.ipynb new file mode 100644 index 0000000..513c67e --- /dev/null +++ b/03_conditionals/01_exercises.ipynb @@ -0,0 +1,205 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/01_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 3: Conditionals & Exceptions (Coding Exercises)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb).\n", + "\n", + "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Discounting Customer Orders" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: Write a function `discounted_price()` that takes the positional arguments `unit_price` (of type `float`) and `quantity` (of type `int`) and implements a discount scheme for a line item in a customer order as follows:\n", + "\n", + "- if the unit price is over 100 dollars, grant 10% relative discount\n", + "- if a customer orders more than 10 items, one in every five items is for free\n", + "\n", + "Only one of the two discounts is granted, whichever is better for the customer.\n", + "\n", + "The function should then return the overall price for the line item. Do not forget to round appropriately." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def discounted_price(unit_price, quantity):\n", + " \"\"\"Calculate the price of a line item in an order.\n", + "\n", + " Args:\n", + " unit_price (float): price of one ordered item\n", + " quantity (int): number of items ordered\n", + "\n", + " Returns:\n", + " line_item_price (float)\n", + " \"\"\"\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + "\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + "\n", + " return ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: Calculate the final price for the following line items of an order:\n", + "- $7$ smartphones @ $99.00$ USD\n", + "- $3$ workstations @ $999.00$ USD\n", + "- $19$ GPUs @ $879.95$ USD\n", + "- $14$ Raspberry Pis @ $35.00$ USD" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(...)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(...)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(...)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: Calculate the last two line items with order quantities of $20$ and $15$. What do you observe?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(...)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discounted_price(...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: Looking at the `if`-`else`-logic in the function, why do you think the four example line items in **Q2** were chosen as they were?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/03_conditionals/02_exercises.ipynb b/03_conditionals/02_exercises.ipynb new file mode 100644 index 0000000..432ca73 --- /dev/null +++ b/03_conditionals/02_exercises.ipynb @@ -0,0 +1,140 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/02_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 3: Conditionals & Exceptions (Coding Exercises)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb).\n", + "\n", + "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Fizz Buzz" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The kids game [Fizz Buzz ](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 starts counting upwards in an alternating fashion. Whenever a number is divisible by $3$, the person must say \"Fizz\" instead of the number. The same holds for numbers divisible by $5$ when the person must say \"Buzz.\" If a number is divisible by both numbers, one must say \"FizzBuzz.\" Probably, this game would also make a good drinking game with the \"right\" beverages." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: First, create a list `numbers` with the numbers from 1 through 100. You could type all numbers manually, but there is, of course, a smarter way. The built-in [range() ](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": [ + "**Q2**: Loop over the `numbers` list and *replace* numbers for which one of the two (or both) conditions apply with text strings `\"Fizz\"`, `\"Buzz\"`, or `\"FizzBuzz\"` using the indexing operator `[]` and the assignment statement `=`.\n", + "\n", + "In [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_content.ipynb#Who-am-I?-And-how-many?), we saw that Python starts indexing with `0` as the first element. Keep that in mind.\n", + "\n", + "So in each iteration of the `for`-loop, you have to determine an `index` variable as well as check the actual `number` for its divisors.\n", + "\n", + "Hint: the order of the conditions is important!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in numbers:\n", + " ...\n", + "\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: Create a loop that prints out either the number or any of the Fizz Buzz substitutes in `numbers`! Do it in such a way that the output is concise!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for number in numbers:\n", + " print(...)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index be6a7e9..b9acb26 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,12 @@ Alternatively, the content can be viewed in a web browser Logical Operators; `if` statement; Exception Handling) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/01_exercises.ipynb) + (Discounting Customer Orders) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/02_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/02_exercises.ipynb) + (Fizz Buzz) #### Videos From fcde5176865a65f9a2db72789155630477deffad Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 15 Oct 2020 15:31:37 +0200 Subject: [PATCH 052/142] Add initial version of chapter 03's summary --- 03_conditionals/03_summary.ipynb | 76 ++++++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 77 insertions(+) create mode 100644 03_conditionals/03_summary.ipynb diff --git a/03_conditionals/03_summary.ipynb b/03_conditionals/03_summary.ipynb new file mode 100644 index 0000000..86c8468 --- /dev/null +++ b/03_conditionals/03_summary.ipynb @@ -0,0 +1,76 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 3: Conditionals & Exceptions (TL;DR)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "- **boolean expressions** evaluate to either `True` or `False`\n", + "- **relational operators** (e.g., `==` or `!=`) compare operands according to \"human\" interpretations\n", + "- **logical operators** (e.g., `and` )combine boolean sub-expressions to more \"complex\" expressions\n", + "- the **conditional statement** (i.e., the `if` statement) allows **controlling** the **flow of execution** depending on some **conditions**\n", + "- a **conditional expression** is a short form of a conditional statement\n", + "- **exception handling** is also a common way of **controlling** the **flow of execution**, in particular, if we have to be prepared for bad input data" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index b9acb26..3390a5e 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ Alternatively, the content can be viewed in a web browser - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/02_exercises.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/02_exercises.ipynb) (Fizz Buzz) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/03_summary.ipynb) #### Videos From 2ea34bf960bac328a149298e68b77314d5517896 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 15 Oct 2020 15:32:08 +0200 Subject: [PATCH 053/142] Add initial version of chapter 03's review --- 03_conditionals/04_review.ipynb | 201 ++++++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 202 insertions(+) create mode 100644 03_conditionals/04_review.ipynb diff --git a/03_conditionals/04_review.ipynb b/03_conditionals/04_review.ipynb new file mode 100644 index 0000000..78d9061 --- /dev/null +++ b/03_conditionals/04_review.ipynb @@ -0,0 +1,201 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 3: Conditionals & Exceptions (Review Questions)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The questions below assume that you have read [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb).\n", + "\n", + "Be concise in your answers! Most questions can be answered in *one* sentence." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Essay Questions " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: What is the **singleton** design pattern? How many objects does the expression `[True, False, True, False]` generate in memory?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: What do we mean when we talk about **truthy** and **falsy** expressions?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: Describe in your own words the concept of **short-circuiting**! What does it imply for an individual sub-expression in a boolean expression?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: Explain how the conceptual difference between a **statement** and an **expression** relates to the difference between a **conditional statement** and a **conditional expression**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: Why is the use of **temporary variables** encouraged in Python?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: What does the `finally`-clause enforce in this code snippet? How can a `try` statement be useful *without* an `except`-clause?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```python\n", + "try:\n", + " print(\"Make a request to a service on the internet\")\n", + "finally:\n", + " print(\"This could be clean-up code\")\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## True / False Questions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Motivate your answer with *one short* sentence!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: The objects `True`, `False`, and `None` represent the idea of *yes*, *no*, and *maybe* answers in a natural language.\n", + "\n", + "Hint: you also respond with a code cell." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: The `try` statement is useful for handling **syntax** errors." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index 3390a5e..7328211 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,7 @@ Alternatively, the content can be viewed in a web browser [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/02_exercises.ipynb) (Fizz Buzz) - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/03_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/04_review.ipynb) #### Videos From 105cd574451acc656b5b1fad62efc71ee3443f52 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 15 Oct 2020 15:41:11 +0200 Subject: [PATCH 054/142] Streamline slides --- 03_conditionals/00_content.ipynb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/03_conditionals/00_content.ipynb b/03_conditionals/00_content.ipynb index 7a54f13..5a6bc8e 100644 --- a/03_conditionals/00_content.ipynb +++ b/03_conditionals/00_content.ipynb @@ -228,7 +228,7 @@ "execution_count": 6, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "skip" } }, "outputs": [ @@ -276,7 +276,7 @@ "execution_count": 8, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "fragment" } }, "outputs": [ @@ -300,7 +300,7 @@ "execution_count": 9, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "skip" } }, "outputs": [ @@ -376,7 +376,7 @@ "execution_count": 12, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "skip" } }, "outputs": [ @@ -610,7 +610,7 @@ } ], "source": [ - "42 != 123 # \"not equal to\"" + "42 != 123" ] }, { From 93008de309abdad0ce449b4657d974dd4d629c11 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 15 Oct 2020 18:12:43 +0200 Subject: [PATCH 055/142] Add initial version of chapter 04, part 1 --- 00_intro/00_content.ipynb | 2 +- 04_iteration/00_content.ipynb | 4919 +++++++++++++++++++++++++++++++++ README.md | 8 + 3 files changed, 4928 insertions(+), 1 deletion(-) create mode 100644 04_iteration/00_content.ipynb diff --git a/00_intro/00_content.ipynb b/00_intro/00_content.ipynb index a2e8778..dbe3f49 100644 --- a/00_intro/00_content.ipynb +++ b/00_intro/00_content.ipynb @@ -754,7 +754,7 @@ " - *Chapter 2*: [Functions & Modularization ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb)\n", "- What is the flow of execution? How can we form sentences from words?\n", " - *Chapter 3*: [Conditionals & Exceptions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb)\n", - " - *Chapter 4*: Recursion & Looping" + " - *Chapter 4*: [Recursion & Looping ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb)" ] }, { diff --git a/04_iteration/00_content.ipynb b/04_iteration/00_content.ipynb new file mode 100644 index 0000000..580d340 --- /dev/null +++ b/04_iteration/00_content.ipynb @@ -0,0 +1,4919 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/00_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 4: Recursion & Looping" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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", + "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 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." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Recursion" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A popular joke among programmers by an unknown author goes like this (cf., [discussion](https://www.quora.com/What-does-the-phrase-in-order-to-understand-recursion-you-must-first-understand-recursion-mean-to-you)):" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "> \"In order to understand **recursion**, you must first understand **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 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:\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, 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." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Recursion in Mathematics" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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)." + ] + }, + { + "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 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 indeed 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 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 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`." + ] + }, + { + "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 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! " + ] + }, + { + "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 ](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." + ] + }, + { + "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 operator `%`. However, this is *not* true for recursion in general, which may result in slow programs if not applied correctly." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gcd(7, 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 ](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." + ] + }, + { + "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(7, 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$ 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." + ] + }, + { + "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 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 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 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_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." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "38.9 µs ± 1.48 µ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": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "12.2 ms ± 77.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + ] + } + ], + "source": [ + "%%timeit -n 100\n", + "fibonacci(24)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.89 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n 1 -r 1\n", + "fibonacci(36)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6.28 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 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." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "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": 28, + "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 infinite recursion. Let's call it with `3.1` instead of `3`. What goes wrong here?" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "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", + "-2950.9\n", + "-2951.9\n", + "-2952.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": 30, + "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. However, even then, calling either `countdown()` or `factorial()` with a non-integer number is still *semantically* wrong.\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 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." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Duck Typing" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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 use the \"technical\" term **[duck typing ](https://en.wikipedia.org/wiki/Duck_typing)** to express the idea of two objects of *different* types behaving in the *same* way in a given context. The colloquial saying goes, \"If it walks like a duck and it quacks like a duck, it must be a duck.\"\n", + "\n", + "For example, we could call `factorial()` with the `float` object `3.0`, and the recursion works out fine. So, because the `3.0` \"walks\" and \"quacks\" like a `3`, it \"must be\" a `3`." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6.0" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(3.0)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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`." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3.0" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 + 2.0" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3.0" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1.0 + 2.0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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 defining 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", + "\n", + "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." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Type Checking & Input Validation" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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 (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", + "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`." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "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": 36, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Instead of running into a situation of infinite recursion, we now receive specific error messages." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "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": 39, + "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": [ + "Forcing `n` to be an `int` is a very puritan way of handling the issues discussed above. So, we can *not* call `factorial()` with, for example, `3.0`." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "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.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;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.0)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Type Casting" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A similar way to prevent an infinite recursion is to **cast** the **type** of the `n` argument with the built-in [int() ](https://docs.python.org/3/library/functions.html#int) constructor." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "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 cannot be cast as an integer\n", + " ValueError: if n is negative\n", + " \"\"\"\n", + " n = int(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": [ + "The not so strict type casting implements duck typing for `factorial()` as, for example, `3.0` \"walks\" and \"quacks\" like a `3`." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(3)" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(3.0)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, if we now call `factorial()` with a non-integer `float` object like `3.1`, *no* error is raised. This is a potential source for *semantic* errors as the function runs for invalid input." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(3.1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We could adjust the type casting logic such that a `TypeError` is raised for `n` arguments with non-zero decimals. " + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "code_folding": [], + "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 cannot be cast as an integer\n", + " ValueError: if n is negative\n", + " \"\"\"\n", + " if n != int(n):\n", + " raise TypeError(\"n is not integer-like; it has non-zero decimals\")\n", + " n = int(n)\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": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(3.0)" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "n is not integer-like; it has non-zero decimals", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 13\u001b[0m \"\"\"\n\u001b[1;32m 14\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;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\"n is not integer-like; it has non-zero 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 16\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: n is not integer-like; it has non-zero decimals" + ] + } + ], + "source": [ + "factorial(3.1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, using built-in constructors for type casting leads to another subtle inconsistency. As constructors are designed to take *any* object as their argument, they do not raise a `TypeError` when called with invalid input but a `ValueError` instead. So, if we, for example, called `factorial()` with `\"text\"` as the `n` argument, we see the `ValueError` raised by [int() ](https://docs.python.org/3/library/functions.html#int) in a situation where a `TypeError` would be more appropriate." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "invalid literal for int() with base 10: 'text'", + "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\"text\"\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 12\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0mnegative\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 13\u001b[0m \"\"\"\n\u001b[0;32m---> 14\u001b[0;31m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 15\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"n is not integer-like; it has non-zero decimals\"\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[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: invalid literal for int() with base 10: 'text'" + ] + } + ], + "source": [ + "factorial(\"text\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We could, of course, use a `try` statement to suppress the exceptions raised by [int() ](https://docs.python.org/3/library/functions.html#int) and replace them with a custom `TypeError`. However, now the implementation as a whole is more about type checking than about the actual logic solving the problem. We took this example to the extreme on purpose. In practice, we rarely see such code!" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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 cannot be cast as an integer\n", + " ValueError: if n is negative\n", + " \"\"\"\n", + " try:\n", + " casted_n = int(n)\n", + " except ValueError:\n", + " raise TypeError(\"n cannot be casted as an integer\") from None\n", + " else:\n", + " if n != casted_n:\n", + " raise TypeError(\"n is not integer-like; it has non-zero decimals\")\n", + " n = casted_n\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": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "n cannot be casted as an integer", + "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;34m\"text\"\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[0mcasted_n\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mValueError\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\"n cannot be casted as an integer\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0;32mNone\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;32melse\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;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mcasted_n\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 cannot be casted as an integer" + ] + } + ], + "source": [ + "factorial(\"text\")" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "n is not integer-like; it has non-zero decimals", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 18\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 19\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mcasted_n\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 20\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"n is not integer-like; it has non-zero 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 21\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcasted_n\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 22\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: n is not integer-like; it has non-zero decimals" + ] + } + ], + "source": [ + "factorial(3.1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Which way we choose for **hardening** the `factorial()` function depends on the concrete circumstances. If we are the main caller of the function ourselves, we may choose to *not* do any of the approaches at all. After all, we should be able to call our own function in the correct way. The lesson is that just because Python has no static typing, this does *not* mean that we cannot do this \"manually.\" Yet, the idea behind a dynamically typed language is to *not* deal with the types too much at all." + ] + }, + { + "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, 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!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index 7328211..39fafd5 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,14 @@ Alternatively, the content can be viewed in a web browser (Fizz Buzz) - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/03_summary.ipynb) - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/04_review.ipynb) + - *Chapter 4*: Recursion & Looping + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/00_content.ipynb) + (Recursion; + Examples: Factorial, Euclid's Algorithm, & Fibonacci; + Duck Typing; + Type Casting & Checking; + Input Validation) #### Videos From d770080fd37e5c6532e6bd09081cd9e89fd5114a Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 15 Oct 2020 18:17:02 +0200 Subject: [PATCH 056/142] Add initial version of chapter 04's exercises, part 1 --- 04_iteration/00_content.ipynb | 2 +- 04_iteration/01_exercises.ipynb | 610 ++++++++++++++++++++++++ 04_iteration/static/towers_of_hanoi.gif | Bin 0 -> 197609 bytes README.md | 3 + 4 files changed, 614 insertions(+), 1 deletion(-) create mode 100644 04_iteration/01_exercises.ipynb create mode 100644 04_iteration/static/towers_of_hanoi.gif diff --git a/04_iteration/00_content.ipynb b/04_iteration/00_content.ipynb index 580d340..f7d1e41 100644 --- a/04_iteration/00_content.ipynb +++ b/04_iteration/00_content.ipynb @@ -859,7 +859,7 @@ } }, "source": [ - "##### Efficiency of Algorithms" + "#### Efficiency of Algorithms" ] }, { diff --git a/04_iteration/01_exercises.ipynb b/04_iteration/01_exercises.ipynb new file mode 100644 index 0000000..cf4b0ce --- /dev/null +++ b/04_iteration/01_exercises.ipynb @@ -0,0 +1,610 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/01_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 4: Recursion & Looping (Coding Exercises)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb) of Chapter 4.\n", + "\n", + "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Towers of Hanoi" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A popular example of a problem that is solved by recursion art the **[Towers of Hanoi ](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 Initiative](https://blossoms.mit.edu/) is primarily aimed at high school students and does not have any prerequisites.\n", + "\n", + "The video consists of three segments, the last of which is *not* necessary to have watched to solve the tasks below. So, watch the video until 37:55." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import YouTubeVideo\n", + "YouTubeVideo(\"UuIneNBbscc\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Video Review Questions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: Explain for the $n = 3$ case why it can be solved as a **recursion**!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: How does the number of minimal moves needed to solve a problem with three spots and $n$ disks grow as a function of $n$? How does this relate to the answer to **Q1**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: The **[Towers of Hanoi ](https://en.wikipedia.org/wiki/Tower_of_Hanoi)** problem is of **exponential growth**. What does that mean? What does that imply for large $n$?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: The video introduces the recursive relationship $Sol(4, 1, 3) = Sol(3, 1, 2) ~ \\bigoplus ~ Sol(1, 1, 3) ~ \\bigoplus ~ Sol(3, 2, 3)$. The $\\bigoplus$ is to be interpreted as some sort of \"plus\" operation. How does this \"plus\" operation work? How does this way of expressing the problem relate to the answer to **Q1**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Naive Translation to Python" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As most likely the first couple of tries will result in *semantic* errors, it is advisable to have some sort of **visualization tool** for the program's output: For example, an online version of the game can be found [here](https://www.mathsisfun.com/games/towerofhanoi.html)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's first **generalize** the mathematical relationship from above and then introduce the variable names used in our `sol()` implementation below.\n", + "\n", + "Unsurprisingly, the recursive relationship in the video may be generalized into:\n", + "\n", + "$Sol(n, o, d) = Sol(n-1, o, i) ~ \\bigoplus ~ Sol(1, o, d) ~ \\bigoplus ~ Sol(n-1, i, d)$\n", + "\n", + "$Sol(\\cdot)$ takes three \"arguments\" $n$, $o$, and $d$ and is defined with *three* references to itself that take modified versions of $n$, $o$, and $d$ in different orders. The middle reference, Sol(1, o, d), constitutes the \"end\" of the recursive definition: It is the problem of solving Towers of Hanoi for a \"tower\" of only one disk.\n", + "\n", + "While the first \"argument\" of $Sol(\\cdot)$ is a number that we refer to as `n_disks` below, the second and third \"arguments\" are merely **labels** for the spots, and we refer to the **roles** they take in a given problem as `origin` and `destination` below. Instead of labeling individual spots with the numbers `1`, `2`, and `3` as in the video, we may also call them `\"left\"`, `\"center\"`, and `\"right\"`. Both ways are equally correct! So, only the first \"argument\" of $Sol(\\cdot)$ is really a number!\n", + "\n", + "As an example, the notation $Sol(4, 1, 3)$ from above can then be \"translated\" into Python as either the function call `sol(4, 1, 3)` or `sol(4, \"left\", \"right\")`. This describes the problem of moving a tower consisting of `n_disks=4` disks from either the `origin=1` spot to the `destination=3` spot or from the `origin=\"left\"` spot to the `destination=\"right\"` spot.\n", + "\n", + "To adhere to the rules, an `intermediate` spot $i$ is needed. In `sol()` below, this is a temporary variable within a function call and *not* a parameter of the function itself." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In summary, to move a tower consisting of `n_disks` (= $n$) disks from an `origin` (= $o$) to a `destination` (= $d$), three steps must be executed:\n", + "\n", + "1. Move the tower's topmost `n_disks - 1` (= $n - 1$) disks from the `origin` (= $o$) to an `intermediate` (= $i$) spot (= **Sub-Problem 1**),\n", + "2. move the remaining and largest disk from the `origin` (= $o$) to the `destination` (= $d$), and\n", + "3. move the `n_disks - 1` (= $n - 1$) disks from the `intermediate` (= $i$) spot to the `destination` (= $d$) spot (= **Sub-Problem 2**).\n", + "\n", + "The two sub-problems themselves are solved via the same *recursive* logic." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Write your answers to **Q5** to **Q7** into the skeleton of `sol()` below.\n", + "\n", + "`sol()` takes three arguments `n_disks`, `origin`, and `destination` that mirror $n$, $o$, and $d$ above.\n", + "\n", + "For now, assume that all arguments to `sol()` are `int` objects! We generalize this into real labels further below in the `hanoi()` function.\n", + "\n", + "Once completed, `sol()` should *print* out all the moves in the correct order. For example, *print* `\"1 -> 3\"` to mean \"Move the top-most `n_disks - 1` disks from spot `1` to spot `3`.\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def sol(n_disks, origin, destination):\n", + " \"\"\"A naive implementation of Towers of Hanoi.\n", + "\n", + " This function prints out the moves to solve a Towers of Hanoi problem.\n", + "\n", + " Args:\n", + " n_disks (int): number of disks in the tower\n", + " origin (int): spot of the tower at the start; 1, 2, or 3\n", + " destination (int): spot of the tower at the end; 1, 2, or 3\n", + " \"\"\"\n", + " # answer to Q5\n", + " ...\n", + " ...\n", + "\n", + " # answer to Q6\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + "\n", + " # answer to Q7\n", + " ...\n", + " ...\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: What is the `n_disks` argument when the function reaches its **base case**? Check for the base case with a simple `if` statement and return from the function using the **early exit** pattern!\n", + "\n", + "Hint: The base case in the Python implementation may be slightly different than the one shown in the generalized mathematical relationship above!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: If not in the base case, `sol()` determines the `intermediate` spot given concrete `origin` and `destination` arguments. For example, if called with `origin=1` and `destination=2`, `intermediate` must be `3`.\n", + "\n", + "Add *one* compound `if` statement to `sol()` that has a branch for *every* possible `origin`-`destination`-pair that assigns the correct temporary spot to a variable `intermediate`.\n", + "\n", + "Hint: How many 2-tuples of 3 elements can there be if the order matters?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: `sol()` calls itself *two* more times with the correct 2-tuples chosen from the three available spots `origin`, `intermediate`, and `destination`.\n", + "\n", + "*In between* the two recursive function calls, use [print() ](https://docs.python.org/3/library/functions.html#print) to print out from where to where the \"remaining and largest\" disk has to be moved!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: Execute the code cells below and confirm that the moves are correct!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sol(1, 1, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sol(2, 1, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sol(3, 1, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sol(4, 1, 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Pythonic Refactoring" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The previous `sol()` implementation does the job, but the conditional statement is unnecessarily tedious. \n", + "\n", + "Let's create a concise `hanoi()` function that, in addition to a positional `n_disks` argument, takes three keyword-only arguments `origin`, `intermediate`, and `destination` with default values `\"left\"`, `\"center\"`, and `\"right\"`.\n", + "\n", + "Write your answers to **Q9** and **Q10** into the subsequent code cell and finalize `hanoi()`!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def hanoi(n_disks, *, origin=\"left\", intermediate=\"center\", destination=\"right\"):\n", + " \"\"\"A Pythonic implementation of Towers of Hanoi.\n", + "\n", + " This function prints out the moves to solve a Towers of Hanoi problem.\n", + "\n", + " Args:\n", + " n_disks (int): number of disks in the tower\n", + " origin (str, optional): label for the spot of the tower at the start\n", + " intermediate (str, optional): label for the intermediate spot\n", + " destination (str, optional): label for the spot of the tower at the end\n", + " \"\"\"\n", + " # answer to Q9\n", + " ...\n", + " ...\n", + "\n", + " # answer to Q10\n", + " ...\n", + " ...\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: Copy the base case from `sol()`!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: Instead of conditional logic, `hanoi()` calls itself *two* times with the *three* arguments `origin`, `intermediate`, and `destination` passed on in a *different* order.\n", + "\n", + "Figure out how the arguments are passed on in the two recursive `hanoi()` calls and finish `hanoi()`.\n", + "\n", + "Hint: Do not forget to use [print() ](https://docs.python.org/3/library/functions.html#print) to print out the moves!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: Execute the code cells below and confirm that the moves are correct!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hanoi(1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hanoi(2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hanoi(3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hanoi(4)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We could, of course, also use *numeric* labels for the three steps like so." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hanoi(3, origin=1, intermediate=2, destination=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Passing a Value \"down\" the Recursion Tree" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The above `hanoi()` prints the optimal solution's moves in the correct order but fails to label each move with an order number. This is built in the `hanoi_ordered()` function below by passing on a \"private\" `_offset` argument \"down\" the recursion tree. The leading underscore `_` in the parameter name indicates that it is *not* to be used by the caller of the function. That is also why the parameter is *not* mentioned in the docstring.\n", + "\n", + "Write your answers to **Q12** and **Q13** into the subsequent code cell and finalize `hanoi_ordered()`! As the logic gets a bit \"involved,\" `hanoi_ordered()` below is almost finished." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def hanoi_ordered(n_disks, *, origin=\"left\", intermediate=\"center\", destination=\"right\", _offset=None):\n", + " \"\"\"A Pythonic implementation of Towers of Hanoi.\n", + "\n", + " This function prints out the moves to solve a Towers of Hanoi problem.\n", + " Each move is labeled with an order number.\n", + "\n", + " Args:\n", + " n_disks (int): number of disks in the tower\n", + " origin (str, optional): label for the spot of the tower at the start\n", + " intermediate (str, optional): label for the intermediate spot\n", + " destination (str, optional): label for the spot of the tower at the end\n", + " \"\"\"\n", + " # answer to Q12\n", + " ...\n", + " ...\n", + "\n", + " total = (2 ** n_disks - 1)\n", + " half = (2 ** (n_disks - 1) - 1)\n", + " count = total - half\n", + "\n", + " if _offset is not None:\n", + " count += _offset\n", + "\n", + " # answer to Q18\n", + " hanoi_ordered(..., _offset=_offset)\n", + " ...\n", + " hanoi_ordered(..., _offset=count)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q12**: Copy the base case from the original `hanoi()`!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q13**: Complete the two recursive function calls with the same arguments as in `hanoi()`! Do *not* change the already filled in `offset` arguments!\n", + "\n", + "Then, adjust the use of [print() ](https://docs.python.org/3/library/functions.html#print) from above to print out the moves with their order number!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q14**: Execute the code cells below and confirm that the order numbers are correct!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hanoi_ordered(1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hanoi_ordered(2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hanoi_ordered(3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "hanoi_ordered(4)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lastly, it is to be mentioned that for problem instances with a small `n_disks` argument, it is easier to collect all the moves first in a `list` object and then add the order number with the [enumerate() ](https://docs.python.org/3/library/functions.html#enumerate) built-in." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Open Question" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15**: Conducting your own research on the internet, what can you say about generalizing the **[Towers of Hanoi ](https://en.wikipedia.org/wiki/Tower_of_Hanoi)** problem to a setting with *more than three* landing spots?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/04_iteration/static/towers_of_hanoi.gif b/04_iteration/static/towers_of_hanoi.gif new file mode 100644 index 0000000000000000000000000000000000000000..7ab13d43bcb28bf830b0d864cb72062ed393ec2d GIT binary patch literal 197609 zcmZsCWmFVS)cz(cy>ug;5(?6dvPbCEQ^?g75 zKmEVWoS8G{o;x%5&OFa^l~t8Q#mr=Z{(x%?0P`?~i3Fslr$5f9z!Wj(l_r=r2vp?p z5eVeBeZ-8wAk4WKPrxHD?}RlzMnmIuEauVC5p!QRmjnfqTVPgTby{E-CnwA!5_1fD zOy-;efgIGKKmhQ5b~>&4zlVf`<={OC$9#!|X{T|K@v+=z}mLq77iM z-80bLq#ZvG&wo>2?9=amE!ARHMKPGe1PdspjSTqg7<2OOv4e$N`SG1a$=&I}BN-Vh z0pP3{xMu~x?5JUeNih{9lZ!W)b_q#Qg`X(HK)pxYjZg}*&)8nH@rYo^^7@Gg8IICjUS=|F^)IA06lsSh%pT zu(7eR+JnEpzkhyyo}C`BBW73i3x zuSo#qMl6_Q045uNDFzG<4#vjDc6D`CS63Go7dJIEVX6U`Y8a*%cKxrB4Z~orG08aB z#Q@Cp?`sU^cQp=xYxnBs#=p+_`QGnjz>oR)iHU*T&FRq4xPKP)byZy_<0l0AT^GX| zn)RZYx}=Py&+MqBk<)*Z#BsA4OL)#Pa$52YJL z)H9U^rOr9*Qt6*K``SE7_x!0^&NY$^&0N>Tg-_?d>$nBbXM3Np_r_@n6pTDW+e$(r3`8=0Q#CV4l=Uu=7p7$r-U8?-p?~LfiO$ zB3;|igWyH&L7p00LBKbC>z_w9P};$E!=IN!VO^gwPtH4h&(D!9*MtliQVy3#ZY&2BQWRwTrRfXS52Rh1p9Iu!4H|yz$+!OE$sR?S;k_&6#TbH|R@!zURAb-t%CJ4i z4aGld&N94xVP#}4`Rv+A>*2K2$c=5VmBD}|0J<5D6Yj;}P35@sA>8Ej1`#e&k6jS@ z#C3^23ML#?sF!1FRp7?ctCA8F@hl=g@uTgML8O8KY!fLGth$^2G6{z@Q}GIBo^hI? zY?ah7P_kvNRxiAnO#W8iGAQeXZtlmy>Lz34w?iA2OsD31glr zG#BCalHj`_Hk*LY2Kz>FYvrZ%#W{}D)(LjK7z5j?Uapaf;^vuXo|5=8-2JML1}Yr6 zY9Z!HT#w(rO5L1>h5UHhLZ61%%HRt}QCyJzV#d!3b=V?}~{l#lm%KHUjuoqQPjbp!9)5QCdT`Jk|;k=2CIdF65T z6Yzssd`6Rzp~ulThgDQ<*zHkK%b!l3N%1~w!Aaeih`IqWx9Z>H+JDvDm)>4iR2Rwq zx5^P_O~1AkR@qB;HDswfa=Fg!9e29m;fay_6KC?Dzssa($j9?dA?%alZ1HYmp=8f9 zo4sY~#h9B-zkQO{O>ehuv3+uCUAYI1u% zq$_jxtBjxj;V_KniRj<_A%5}2-i8Fp`=h>^?_0N;lQKuW>R+2{t@*EG4^=wARNv`z zgm)j#+$X=79{g!(zbV%Y&YU&9yu0qkwbBasbBwr>wvqWkUK4ulwzV#8-CA?Wr)(9I zKkVh%y5K+IKM}%!CAicBKbndl^?UMwQR3{GPptKk%ibc?)N7~xGa0P#v;eQ#T4Bli z#1Jnu^A9_P6anG4P|70436(NQ){6K5$^I>pm`E9+lFK;8U)ykRQDwS`$v6wvoZo&! zy#x!BVM0P#ZJ^r$4qSMY!Hy|)kmrzifODvnBs1=zsH&O)NdnUtD}BnrpnxN9bk5us zt$r)|>J1Tc8INWi6;V?~o|Guz@PR#!Q<>fTZBkZ|1&O=&sQTSin#DaEM+oc{*>mD( zH%JaEuZ^^2?iKQRq1lrlT{IbAP1JQE+cWRO(T@f*sRQ_>qHbanu0Et`nJ+(F{Wy>% zUARoK^2zgbxJr{vEs+_y|>(N8FJ@}SwC{iC92JR3Woc9y)P%sQk^|e z{p9sndGH*Q@g{HNQw2}l(c8tN_sws~E8cePzT3XjDg2gF{Hlw`aA!pa8U7_bP@|MZ zB14n#%*8JG?t@?)$9tx`x(a{yBb8I!MfBU>)o<6KR;HQyNeu4wnIDggj=2rGf271` zS67L8Mh(X`3#U;+w={_V4pd>Rey1mNu}Uz-EH}T_`R2p>(dLcXTC2%IOJ zpOkGIS7le|W4fcZH~?vOib|v1A+6NyQ_*?$`S7nfC(-o?6V`=qspR@CciZ>bb^D-7 z?rjzE*_1d-YXlSC=6`q1m~Ivi+i2r4bGYGc-|0u!+?>vRvWV7U9g}AfX>>m&X?>=Y znjM{?_%Iv$K5d-=9f8Pi@1uA$Dq?^doF4#k{iNn5CX8E^%{tr}0yK zhkD@8fLr>1H0yx@pUR|k1hOuV4}dM+ZI8T;sWsVl?_#LtEBTu1(#B7$cvhD^a~sMO zs(%t-`~His|5dX@O?*7h>~oAe^TGajqSHdXGO5(kzBuk^&GL7>@=UNm`RS4Ahl~+B zHan8VSY;>dxibCOsZD5hr>i0*e0B9LTEOI*>955930t9 zsrv5r_7Xsg8?p6EFnX z-~G?EKi~9t`v0!q_s5*~oL{ef`Fg$ar2A+{^Y#qeWN)!Wa`bEWkCyMZEkcAXx##|_ z=BBxKT8loQvVa|3uf0Y8Z4obAm`7DwVEukz-C`gSr#DGtz}b+0_>d1oCD2sYMF!i_ zUfB`2Z%M2BN|D`KQ8*Yq5w-zN zeDPI+3`K%qdcN4L{%xK?L`$Kt^iY-bz#5J)HL4&Khu`U+&(&7e)1JCe4+CK7XZn`l z{&2^(T4*36tQroJV#JdQgh~lSxQa%2EMXVIVZyCYsYqY!n^0xFfR|KZp*Nx7UXhV{ zk&#r9Q7VzK7_T6ugMc4H{_VP9!7ZL1B{p1M!C!TilLssY_8t1Su&V{IKiNc-rAJqY zM$k@y{o$|xEka#%XrNwz)>33^dQ9g9UOSaX=Tc0%A#{$L{wgx3!|`4 zLZirDpG~Q?`z<4^5E0?t8eP!}tVYL4O~q{5<>PA`e?*_Lt&`TI0{k zVt!JEe%1}z^z^V%`OKnXx9`qLHqB<*fG20+jvB(dn!+tDi(6TWuA++HfW=o+!FXh2 z&Te8lVF@Sc@!y>jL#-1}4r0!!k{HF3F7%REqTMY673O zv^i9oZ(EvIRGMehm*?AJZzJ%YBjfPOlQ+=O%7;k!a9q+dICu)CH=J^`l&C|UQjeX= zXq!lTn30B)s-v3p^)^NCFypItl97I@&2r>_=)msMAQMmjn;TynO8@Dpv`$rm?%S-H zi>x_#cJEd-&jX4R zU~%Vx?DD|Tc@XaG&(!H2iL`A1g77@#w0;gUBYBZ4W4SzM=`cTdE9HDSLtCuC1n27! zb>?n)!Txf=v3KqXP9c|1rddWx-eG~hZE7WT!n>u=6OKqly(k0G&zUKCAOQkpT$plu zQQ%Y&D32(K5$6t(r(BVTrji6G?nG-8nDlehSEO`xC>oIO3(F6{Z=ZwS1JSUDtl?lT31qpM{d4D>18=1Rr&t%^LTYD9-dW2gNPspHtIqd%^rtE@9V zu45c6*Ho(q(hyKi0X4UZ6RB(X>}&q`)CkKqxW&{6A47!mYB1uB%Ib~z)Qz7j3snC$ zsCG1jNHj*$HilF-s*Se1lBhb&DAndFVDl;bkdaD^7wH(~KiXVpcHETrw~e~8dIOhW z5w~1REzh>H+2Jqp@-DxIyQN;eMaZxNwgqYRZHOOj{cg}2&C{x4-x}xJ7;)SnOWWD! z+c}ce*+1Gb(b3Uq*ocMK#NksqY*6@KKQ)~vNT)SuwjB?F`P)R#)1EilWQtcmDEkdG z1)`touHq@7y{mWO>2Npf@URDuOaTZk0Iy{$hqM4YQ!U-2t>CfFUiHp!p2oq|*3m2! zK3yk4Y-jH(YEYsl;J8y9uc}+TEVv@GYB?TCReh0Gx1-)`dffKYzPqrpd=LORJMOlR zskoH*-XPKP$G)NQ2=bQ}c)$7`28S-x;_l0~Vm<9qr0tQd>>M3Msjl_Ije3Sx`&A`7 zHR%T4z3L%Z>zp}mnZfI#!7ml&ZYttR*}o~=)^+DF>NYd%E$A4u?d+x0BI3XA)kM}S z;o#TObX@!N3CH%etOAH@K|BBu@f1Kh7GQ$_U||^han{g;C+N3N2Q*L6U!D%Do%E;P zqY@qZ)v^cFI|o4btp!!J^e0tUk)a*f-Bjw`Z1;m)og+Vwd)*|5?2n^U0y{>wAgI8W z5rLL2hmP*5K8a2c1DtSO08NzlUDOXh+7D6*0O;TkryC6?8jUabjW1=7uSoVR;P-_3 z4&*rWP>q$Ebmn5m6l@J=lsH7{9D8!CHC5w}a-K9*W!IqvMjMQV%(sgDwQ&A+d?$iW z0mu7@wWb&u(fw->RXRcjSv2q?IwBSe7XcuB0B8A4uVzovbWPL6ji(q5P~cDC8MOu( z&iu@-46v)(vn@+sE+o$Kx^n3KVc)xpp`HCUR_7W!+1NR0$ryp!nk0e)vAaOqa6*kK zg0jFl)i_*@DG0+BFyv%v7=OAY1^{%1L^}Y=;e_<-^Cl1T2;*@^shRBT0m%J$jNy#N zXl0Um>cMT#Np_o{#$>+z>~n%qN&t?y#^{|9mL(uU=5$ix>70VbSa{sj8{;XWk7(ga z`~`t&TmdMq0L;vIIn{sJL}NVLuagMBGg5s3g4e`bQJCyIVyHWtOFMbtH|w-khepo& zu6JwJHhZjrQ5Um!cvEs3OJP#zH@x$ASsjfuP-Ha#7YU?@TOR3J8(W{xk{TG3n0Rcj zbm%G=zpEmm>VDR>@}J*g9>#Z42mqz~F~xFx&M7Z6zlgsv9*x4?$L zQ>?$SP`yB$=Im8)*D9mHk2g~2lkvWHaUHJ_xCq4N2Q8prAgJ+TQ^;gTIAGfPVR@Q% z#fN4vIVKgKVORKcF!#9E1p#3b01BXhF0vbYyb%vi$3R^hoevu<-+`1NZ*7XX+P07YnF zE5WgAjW+^}BWN*GlZ?_+p~gSo=l)DV;JVl1m;`LzYk^+~fU!`7e$GTTkB51=d&zV& zXrA@ajs~KP;+^ZWk1g4FFw05MW&pAhbYAj2jIa;LjgdgLzM4yEo7#=pY1cdM(%lg@Xv# zBo{bk%>zAi#$MzDL!AF@>FGxICEeHQO#Feo&Yr#4OqQed=W#2ay4TOox__g90$b2c z1c5RJalRRNuFin%vUOe$`04*Ih4P;ez+pjC*d++udIXUFD=s0?o+k>*od*_|1!V+a zoA80DzMficTpE1;o!Ys_qA>%_EiCiiFQcFIdThIk3H<$|F-_KmgX??~If+w_ z!uewiLn%+@HGAYKh>d)+|CUa@~+=m_Ms;Bdtwij{D z?rCIo#-8rlp2%zF$^`u(!zI(lE>cb5tG;HPU?5aNyozLtg+x(`H`)zMHB~}tNdPd` zvNwi_Ayl6_8S~{8`yxD!;_zp&=FxPZ-Ag+h(>^Sfs3LHPUcI&US9(#MUfU-7nhL!G=Rr&kE-v4avHaG1 zkjv*VA`X+a*5O`3VwTr_ZU>sf$l`YtxLwcRilp-|#(*s}t;+RMJ@WS^~oS z@7#4~4VGuD4&cyeT(+0XY_+>&U$){&on=9Il3W74R&OWR_bhPjIKQ%1#)}^Y*a0O( z>StegQ*ala(Jf6s32@+bFJ+P%K9H8JXG8EF3l?rJNx01d`G9rq_5E-_}~yS zSCt*yRkRB9%NHDSn@HEI;h}<^L-`YmysZ%e2^(%gqQWFQZvI`Z;0XS9=a_-t=AoM1 zw|1X6LuikV##>0oYKu}{EFFZaNdNuSP?Mfyct&ZH>E|rZ^`rEnVL4xaPIQYf;7o0EiQjE#o%tzSxJQ zAW#d<5c>3-gS07rq<9Sh#8F4qCV>bNSFR)o3vlZo{C;A+FZsrdhOZXlA$ zLg8t@k~|;NGF-F!(ER#To{E=oyD#bSwf>J#$cb}*>^s7AcY3RI-~iaqzFA*|%`kIt z(=19mdC8*wQwWH{C_D|IUUd?<%@J6(AV@4nN*ck>H;AD!qHM5Btmd>(t5E&8(nY?L6DW!22J1RtQbU>gx9rHxycO z@Og+E*)(oGjP$8o@aQ`RuBUIL2YCTRWJ`$WNiLY;%b!6|a~4G$wu2{MG8lny(U*j8 zr^27ofj~F{3JE8{wXzN@v}5RS!EakKj9712C%wV}o-GaCP<&@;TPpAu?N;ne*|4|2 z65{V>vy?f_9Slz5aNg*(Gc21y;%&C^x_}DQD?#BeekCYcDMnar)kM&wXTeWxl#Sre zXuvNEfTTCsIYQG>s>4WHX-;uzRs;mDw+mB30%$Vx!mT0^1Z+5}it@6svQasTw_5~G zdA+n;XnDey03fUtFG)%q_&_*?YbDf2J>DAr;s_ouu}2cwhK4?iyo|L`QJ{G^(l01t zLCcZ2+JqU(GF$;vVWRR7u>%V(W9RXwnASXhZ51H%%7sRJc4G1z8E~b*7W-4CT+WqY zu;LW}*V-r~b)ojB*eCDF)k|~A4%7gDh}f$wDO-7@?GMtVw#H()V>KODE2bL+h~4H2 zEY@m84xb(;vmh&sv@!i#mJu?SGD{qRX+2s1#v~?bU~`+ zqTeNJf6;ts3I~;3L|BeBme^M>tN&RZWj=GH(5Qb6%Af5Qy4XvO_INL}E>jIvynNyWXcFQvqyZADwPK!^x=1J=)R z34P|g4Fg8X8>ssl`?kHHd{|>S#~mG;@#^eQ>TB1L( zz7K=1X%uLDpP@U{>4;V(jbtX`O_vou&>!W}bHoJ-)IjH=jbZyolpTTg0{+%zGA4iw z`bY&B9V!qY5KK&QAxDRQ#JOsKL)2;+bU+5|xfLf#`IBPj{CCy#{qwi6c28e2;J?8Z znEH~7BRm^SjRIhwZ1oUhtX#Wa{~gUS_j)s^m-jYzD}+8TU*|I;gvCSF5TDmA|7my) zvls0LY7UR0B(_7HQxqCC>5%sVpxNwcG{EyjT8Q=5nv@rW}nxEZ{3I*2UUInKeMP-kyF=t-dexdNsjt)D?n zK-e#1Q5-?iHt_-@nAsIC^NJDIH%{S~czeZqtLpi$sMqJPE@6U?~wYEgQi$Yy5j4<3cceS@5%jOBs4k7KYG@c%^41 zq`K8bhK0HCW=Mx&TXWoBXp;Sz#t+^_O#8`AU1fTB2EFQ&38qofyhxP*s8sHE2eQXr zE77w2!JixJ^Fu_Ig$5BqIZ;H+K|wzYt)Bk(1TEW`PtdUTgcmyz#YiF_%!&jtQ=zus zfnLC+^1p~+Rf63Qq_HLA)6me)tgyVLj%+tvzWg9{?~u|40QQZ97D9&SCWsLh{F4?` zE87zS03;cbHhTrB08oHr5D+3oDuzmqX)!Q^h}(o>-^;{qnqYfc2eT6=xY5Rt2tf zxlIWL9#M%GwcYl55=yWTKSm+3`~Hq)sJT~{P_1mlA)pjiLEs=Si{DQq8UA7lBF_j> z=nMv-<@t7dpDYb170H=q_Y1K~g5S4&TzVS6rMb02`0&88OHj@CN?v4yCbb>50^t<0CHc`{ z?O#X{7*IB}mnd6aL*S+3$)MuB5+9@+C@|C`C~LaZPpcK-8LJ%KN&9jYim`=-Q`E-# z#e(@R`vUHV==H+I0~Ph@&P)j6GlQG=oV5Ej_%r8sX zoJu{;A?Q3WRZ1XKOdu4V9#Z<{)kt>8zh}KiUW5`F>iM3q_kK*~O{{nJP|HZ6O(d2% z3Tm=aYMegA2Gm%J)mYBfSgF!j?bKKs(^y~A*f`PHyw}*mp8&_hVE?C!AOrkAU4;Ap z=^`*VbSkx54MF`_3}mfvvz`bDk#ML=UBSTTRzAD6bOENJIE-X4S^Ap~!--69;>pVE zEk`2xcL>F*OO-dHhGAQ(gP z-j3WxHxehUpr7kc{z8q-tjWjaX-oY`(WsntT+eR?Epj+3T^;}J8g@&P2S5GWY_Zl6 z5phP}_UYMDMf?+Wo#V@zcg?|;SXi-hm6nxXJCvHaJQ~-k`{mtC#>Mvza;u98`O18L z+o!c?zs=GAYk#_-D7Y7Ynt3_7;q!rcU*i5|<7dBWZ_{Dx?cii;-P;6d{};c`)~q8i zH8%8;H|KFq@w!<7#jX*@OkegNbAIfP4DXmmmaz3{gdT@32nIfhL$XUy~$eC$jl34gOy1+&{ zY;Zo#wi-qgDYMLpL`v$ozqcDG=g^8~-+lp4B&vW|eo^=>%obC#u0-XxKW=RvX_5Gc z*@)ps(&!uNQ&9^IU9|(VEJfFh`~(}`>oh%qZdG`;Z5X;ViTpwTjjE5@GR+s4Kb*#y zcGH}k=D!9~&7D4-QCp`!`S!;~?p?+5+u}VoZo3$5oT%B7AnDKwrOFBPo{V(~=8hW? z8Glq!LKkdTSps#WLK_C#^xZ9iXKV1+?p>{|2?SNHA4!D(_eMY{>=;p(?> zCmEB&c{Q%WoO4gTiBOG`+z}qnSJGLs^RGl5b1t3qV6pduyoq#Qh7S!`S(~0ompf)N zos+p0aBjj%#>u%(@+Yd~?>EP!=Os^TiZ|GVa+S1IRdTpk>kRt&hWm-%AWwfh_^(aoQnXD}*_=!@R< zmG7pfByJlxhGgy;#OHAo8^OvZ_5EM2pVlm&x~Bfp9oVCH9qUwi{J36--IX}3oUk^t zS4l+Xqg@k|OM!-Z$A}&Zuk*F;6VB56{&?*66WOYBd9XeL)H5&~a6UY~L!(JC!WAQ* zfBGpd8aiqJp}p+&|8M>J+l@kdWp>lhjz5vKVtyr&uWl#fS->q{KArh(TWgF^%P*PT z$DNOb!{_in?Z)@~o?3m`YIoN}D^1_LE(P;40KKXETFqy3Vw3W z2qVdyA~IxD5bN#9!H*C1-sxEx0*Q9hbUO!A^qWDtS`=^xC&P|?b0sk7LRH0gMK?kn+=aU`Us{5 zA%`G2`@Ocz?>@o46bVE{DR@wj#*sBL{>@w-dp+}C#?9D1N3ktt;^o3wVl%3r;Uhpj zm?k-oR`x*l*ajTJo}5qA1ZC~w=&`6T30e0sQ&P6=lf{{hVe~ImQEVTy&#ex=wqO^! z9-d5X|HaW+Tf!J8rfLFbfx{JT6lm|XBJbw%akR@`yR`?&!b1w01-G;;)UqFq6N2LJ z4^%ewmFQPybIGeYq>szBQ?LG`Hz5i5J-UqG1VOhCkJc0njG5 zbnfLGdrxO={60(Wa(nUeUkWc+WNs?>d^$h2hBaSyminbms*t0Ndwk@qfD9c5rf09+ z!V$gcAq9X#*(8Cy7UpexRK0u^mCNboi(iNFjVU!%DLm_kONH#8nZ{Gz3UD z{34X)1RZ6BPuV!N=4RB=uIRXLplb1hR5=7gbHW)CYwYlWYV~$PaL_H+$!~)I zu??DrH72OjSu91*pc|^AJtoBlC;7bV^FyB|chM}o<-8fuUvLfmKK7H}muAcrpirhr z5bl8v8rv>Ai+EU;ei6|;E>$v^RSv{?I;KC_I??p)chwG_mcqi^#bttGB4>t}$EwPc(BT3@^xD5`=j+f^oG3n&@<%g}UT< zcOVoC4N^eI`szCyl<+<*kMZ+>eIKxSD@y3>;h164b1CfGzGl_1Lrj&yGRRi6pR{Xz zSz2NxDa=YzRZY`vO?K5FwYEis_evn-?-~LlD?+v#c;@!GkX`_TC7d^96xFg4lRq$! z@Rj($^xe+J?9>o67h(UIc70PA)k_-uEG|gBZ(mCKnSYFioE)ys4}tmI8wQvXhoIZA z=vRxnMM;l}uc}05sQQwYQ(M!(p3|b&-XlYCj$-LFw5ML)8{d08?fv@B7cM%;xr6oV zqaWtk&u%A+r$4ExFjLXP6^OC}0V7Wyt~@+~349pCi}CUfU#Gdx_558)kX~ahW9&RB z<2q!=o_)2+Cgk<`75>UBL+A93SDJ$3)y8DbPj}7hjrWMbcMM#NuK*{LZa@H^J0SpaMmn_!&-%QDX#HU)Tn0T9yrrxy;%@C+bu z4p9k(=A>aw)%wR%`qT^Cd<(5g_5s267b&d6j8|GsqN`TD! zn9CltRpNT6Hs36hn2Gk`m7SFq3m41;a;Ptn*X1p@MS2u zTy!iJ@qNpS&|P)*gAdUWVaqMS{;mE2oPY+|n5I^LUIZljkF80#V_vG`Q%}cdLj(&` zgbRTLqb;$HRKd!O5m*8#2fzfmna{uW##Vmfhn+pEyg_U^i&y9sI%ou=#fq1rn zN3Ea}7>F1EI)f!#pyN-d2v2Q74)!7Lp-??U6g^|8f=W!*ft$)r6xn;P%EV}mGP&4L zuLVj1%?ST>(U{LSM4w>Vj?I%5mfgN0`PSfaYS4Y@?iV*wuBEJ1dT0)yK=_4 zibCgZ!dcSwjei+0VA6u~_T#9;J_*1A+>7v2hC;$%pd%{6^(g}QG{OHeVzr>N^vGKq zFiI;bhZ3Kf8VO>=$>RK6XpI~XH7>XzDnZlWQo9i9lGc0(l-ze)y7q4MN*oj*apQu! zOj&kO+VPaSTDN-IF9kW?gwNc>3fzYOBk&@k(s7;BSzv1Bi_uc0!7KX_RJQp4$V^Sf zNGaL)=%o+>K$K!s{2@ACQ#Ij?GfJu~@@G@R!;<&lMP>#pEuX^%R;ptbWp#T^YU!;w zJ_zS*jq^i)an~o61%REE?OYK_mR{k5*j}F({g@7drXthl%G2jnb0W$Cpf_#3Ik%CCH}O|gUrVDhbIT(k^Wb}Q0IxH!wmjuR*|{+z zv^Nx4SC<9N|BSWr*)-n^%#oWcmMwh=VG?sZ)v~ihyFOne9BB<2g$47ATaZXB@TOmrs7<0Gbs_U&4`yg3%SBnQJ0)w|HH|=Da%Bm zULLn#-|Oeri6Orax;0W-NDHNk)Mu4Ps!V_rmqZ9QA}NFffGE*eDo(f0rG6i62*Pl_ zC||^~_!J207eu$E$LPa(GmXQq(^-?g%+-F`(92D=g=OO4E2t*t*+$B7C!BjlBK5xR zU?P+sY+&juUjxeg8*Wq7p1n5536lx(Ul9pqD5oH0#O`bLv>Zta!ikF#%UO;pa9ydW z7KeYhAbNp_Qwj6JYKb#a%SajiN{KAAtbLihUl;}}y09$^S1BSwh3t5KwahCD;4U{j zEc&%?fcm6w-A0Pj?kvruC%0H?^M;5h(bIP*DNrnD3SBbCS+IOqQEdQp!zH<-ka<^D z>a-k89aZ=<11mfbx+|J2bdVd7UUorMn-rNK(wb?Bfayim=pTMfIf|MW*I8U457n;o zh)kV(PZ~V%W%^CY)M3(Wdc~1ldgO2g$H=!caX5J%G2I_LQVaIl)ZiJ}7?HcGdrWyv z2s-5JG9Xf=7N}lS&K2-}861wpN*t-J!-35lMOi&BZWXF4bWJ@ftc0u4x!mM0+om69 zemiMTKRf#Nk1{nsCRwDCLyFn^88qCq7g0K6)d#*z*~&mJHOLs&Vj1E{1>%G|V=4cw zni@#~FGjtyc3@B{&NLt;s(8XEnjYC!@^Pdo#Y*#?-dBX?0iw zaC)X%T4Flzvl2mKSxu^~;XK^BB6@b0<#&m=n5|B*S;1}A_YZ(((~f!!BZ^G@&9kF6 z!@ImC+VaCy)KXT*!Dy{|MM_qBV2CfWSN(hD2oxfRvN0p5pmf^wHW$yRT&(OSdfLO? ziQ>%e<@8H@LDQ;ygI{bdtF_9roY|vI41Q+jRU_ObC_$i*-ql*!b!^|DY}C)2-970O z^HqcZ>S_L!vo7gNpU4^yDS%`JL3*zRhsb(e`TP^==Nd&PO11Sr9s8$c=zW#logs=0 z*730^?tAoXWkXSV3c#allRYBi)Pe%9_qeYM9am z7v2M#p&az486ouP<>_qZsTw4bY%k1e?8>6K71U|OuTiWABr51&kBzct_qX^qmfVjn zz8a~z{}gXjo!B`B1rrM8Llr??=0?8awlzg#%@Jc0%2gv8xC6?1eYN-#l&d7#Srph8 za7RY?mI3kZ8VNsuYB62r!+q*PR@a%)cw0xeQf%Uadik29!@R-dpEVN8j7dskVqK)l z)k>SVSVo}{`fO}aWv%5>a$1P4j@F;Dp*GR)4U7{A#$b>GpdPSkAc?0WTs@CieQMU! z2tJ0YeYdqb{F;=;O!#5lZDg7QAmwb7S)oDUl1vb*E2sI z{66fpE{fOSTvK+BioNgWJ~f~+ns2uy4n)x#8WS6zl3EFnBrw9C%aVF>cKsPF^|u@B z#_uC@2z}#UZpO7}mv79!PUC4zEEh-8+{rK{p=!-zuMhJH?C`Hchh;BCHAVHjKV35W zfKBEC0w)4|2}ra9Np|yW@`S=1Y{PVy+^`}-(fHB%kt=I*1hi7ipOFN3;H4YSxIC!u z7_Y$ufngh>nhI~N(k%M@G|faOgFBL7LI8e;$)c#xX*Or8+tygWOLP#H1DYm+pS{6` z_T)^vtnASo{4F7sNG%aN^f>TLS~Qp9 zZ2Y)anxw@9{zVpUcR{3!#7TcmjAT@*mptj+0L{ej0)hVr#aaCSpg81~pgEM3-LIrJ zUpADGMLt%~xj;UKOwf)jvXZGk9wX~WcRN|25=^C;R8;0#JetNzZ62yBXr>;)tKre9 z@5b^iLn`D7zv9wLIZqgyL_w|5Vz%@}1Vu8g_U`L2#?Q~=%3SSdQiI`T&h+_ z1zqb*jES)h*XHn~LZh4;%t!MDHLY+&zG)U!zQY!N5NbZ@GIrW2j(xlHY9*gzvXS7w z50{DkrtGC+sd;lhgL=5Tmc+N6Gp7`Htg8REZk^4|T4AtjBwI}zzKte*4amB%KF?i@ zNjsmuHt#RejhB)7^dq487nBp>RI2T{5d>ydnbt33(?eLd$&zoYkp}!E`PlrBM&Z05 z1~W00)E3nWC67KZbElxTHZh_ZSupqH&bY~we?D@M9%3;)Xcp1^vv?~?7R$0wfm!M1 zhm6u->1MQNvDXJf<$@O$-sj~amI}khVx~x&WgT4)U1I3lMA;S2nNVh>+qZmXxJ8!1 z+!ak9QeF%%S!o~6Q>6F^6}M4%ktP-I#o=^&>*g7`3UekxsJW9hqr?vml2%pQ_ln7s zxDjD-E(4{ZpRbzOQu4X=f981o`L&mwX~H#ML@kC9PySl)qlmaFbAc;0J&|3EQPkBG?K2YW#$mC2O*9 z$g^#FDE^}<>ihcReL*vhiyu!CHMZ4O#>hGBS9jOU23IAIVV}_1sL1w-c)#dM z0F2@ z@7-aBurb4)#_2cDQ?0se_?bf=&F!#lrJDDSJ_~`!8Cr(?c7lY|(tR`Iq3Z9u_p_{$ ztMfbfCpS55g!je=ZWzs5SyJt;lM9Yht%tik&9@C5Wu1@eH@;u*BXwIdAzFdBVu&6G zY<`nQCzi&LO-wc=S2eMPCs8M2#H&Y+@T@n8@q`iEnzA{Bpv(eWi1=`iT{QpFkrli1 zE6iG@k2*jr^mup+?=M!>KLjuE`W>w*#!jW5nc@v2aE@so@wV%ve85ckKQqltG&S*K zZ?w|x0C|IrB7}RFGJ|XL#{8g>UOfIIVhlzlb`!(n_$J07l!bVcYDj!!I*$I;2O8D- zAvU|05xs_A$wFFlc^(TB2<3KZ4>^Y9CM>XM4KbW+^6*dhw1Tj@Jn7uHO_e3B;~5*M-jX#~I$S=5q}d3{pS zKsFnLsrM^O`Kxqvb}`#K?;!=;*}$w$b2HCJ*@O@rGaaa4gvboieaaI_Bz`WM^PcTmSi_dFgGo9Ut<4oEKK-)4w1Ia zOSY9kvprI7%2UgI2vrZGery1(_9#S%gfNqHCjD3Tz63|b)ITV$n9tHyEyh;kLpy6B zZIA7*1B#Kmo*-rfY)sKiZ7!{#po~w7`(MS;TygWKbm+9Ld{HL4UQR$?0-sxP7eH#h zkX)wx(T;Wi_g#JST-C+<3cbFgxzZV-Qu(kW(OJ;c&M;O0%XlQe(oFnk*&xQx#>4zm zxC`dODb*qLm1gsqOM}lBYHclfxEYR@274R1=%I{8`_V6txpwXQ@F;)FuORc{Kh>~o z+r0$&&erI7)W>KXBl@`Y2QbnU&5b!9&D~;F*Si<$Q7?{}-n@bTuqyfn$v?UvS;~ne zchE8zN1NtPEe0bB88^`|Cvrpm&DX*$%@1du?uM(EYRxUJy!_t7z8mX~El9aHXD09= zmOm)g8dkH9?t)3i!BxzOr>Cbrwpk|qBzm1t!dhQqqt)et#h%*V)xM;EiFVlgd$m%W z2v0}Wf|lZ=S!h^QH<*y!Cb~=<6p=vAf@nO2bQGSIK#1w957Z5mz0dg0gJ%DlGyEsa zOtqnc9EbJ$J%9zl`AP~UiE!KdzG2);d4(cL68rnY@0i+p^+!Xa|5k1~pA$Bu553xZ zo|Q31&TyqJWTe%8mVnWqe&Z^~nhyuUEGYP*&SdT?Rvt@4nRpgd@}LEQpYaVCO(>ia zCdUqxB<`lAa;xR%#;CIg1-|Bx5hu~gTZd7FFizbJl`3|TNO4$v*DFC2lf1Qwmb{y{ z$`z`z=G_s(i}VnG5QxZ9PI{BD3u0Y%QfQQfo0INNvDm&Su>Dt}B+)TO9&i8hsnqT$VW{$JrCw8V(f>e>A7EqgRE?-F~}5k$->kCALjv(7P}% zN~VjFiYNxgr{+sM%)jIlZ_#5&mIHUqe}Jnjkds{H$vhm}#J8h;`rW;@2;AuQlD_}?UVtbJ0uwCq{-Zr1 z&&LtY0x<{;tlryjClL--C%XSJ_S|aWD^uOALElA#tL7Zc0OV07>2MB&Uv_}_DlOBH zC<9i=HmtP=_R9XjdpuIEh$N^LM8zZNe@OeKKtd}wM;4bbo*xdfv;B$?5DWUW7t$;r zHdcW-rSL#Fi&yV~eU&vzI2ozp0fni7AlBh@D+nYR3iV*FAh26Mhsz8G%4vn6kQg)k zgUi1BqC7myG*+!F-UvKDW&n+h2HkOpILM4_l4m~uJfhLVgJ+MG%EhPChVvr=L+;#K zJV>H~$rljDXfVyDKLYKG31%5lpK?G0)!8tP4=sDft~>{`b4IUwnUmj zT&{6M;~=@=8%%}Br0>o&LqZ>AB?*Vv_r2yt0~|ujKq7!6MC-TVv5qvD4UvhT6uCSX zk3q#X%#pX($jfseDxCtpk7v>cp{5BK-l}BDsR;GRSRU^bGQ;V>GZ{}pTlYW|`PAl0 zvg~>c>jG#fDu6l(HKwErkeRr7)2_DTNEA8J=OK|yj`Aw}XjI8czHc$P5cP~zkC4Gx59MdGur($_(Cuo4O#s3 zsRaF#u+t$>To2M&1G`H}%UVe5H_2;_N;f_LdBxR zWZ}^aK9%C0)XyjE8_5M(VAm!B!#Y9_o6%ELmNyb;#=Ku;5|KU$dP2>7Laskj%4(Em zws!{~Q1Vx*echz-s&^b~)6l;_$RBvf2htKRMnaNa^9U7)xS{I&s@#btYp9h6S z4awh7=g_2wx>PH`?+u+~*Xn4L{?2At!KF-Nd#OV5&*yAXI?}}A5GWjkaolxOBSCRY zPurTb;&eHzks%&++DFZK4H({HCMQsf5nA|a4L~ON-PMi#-zUod$GGG1{~33X@eQF! zx&X(%o^@{&T1e+awDZ$faXdmswOIyu&2StpD13hE^I(dYXn|e~<>a-fv$n%|D0bVy zaCyf@^QtC${emlDd>pYCMAb6YZTa47d#t~TxQTK->&q^tm1Y$7A-=kC<#m{OYKm~I zm&*|4R{dgljI+3A$%&>1oVhYv<{j*(BUSbv;}=`bj?8Yx%CFoD>F4^e>Fc{?-Q^m^ zRo>nvJvXe7&R5RwbHP3%iQVRpg{B^K?b((!v z++1b(lTnpU?;%@xmVNX)z%1FQ6e;~@{gnYyCC5kslo0w|zPoXHy z{v2<~LqfWuK{juJd}^*|`Rb{2?OuZg+8i=|xct84HO?H~?JV8A$6Tuq5+C}@*`?hv z)l7As!IhQ!-@6j~p!CAY_O;+VCeDpeLD!+uEPke#kJ?_4*y2jtL%N?szI5W@M8d)D ze%Z6v*wqK+n3qaxK3#0p#eT0@YEo+gB{odl)nwwPB43?Za;&HCBCMW{Z74XktY{zE z6-vK3Q`cbs&+b{`e7+gJ^~9m$nU0l#+z*T!UCGIF$nB$~?uhoBpPldJkXz5!CFb7h zK>Kvvu7Xru4EkWHk{&v^qYdd`UeA&9uVxk>SCq8g9IYEF-_RkxRyjB->pnXoIeqTh zKzx3@-@o;O>gg0G&L-;&yWg+WxdooR&_Z1Dsy!?9w_e>#DL;8e>gVWA2&!A*X|rJf zO2iNJ^MdTF3;H|TK6d;9W|6!Vo6{pZ-cqY+E&+bY@0_?ZWIp)q(f>9RZy^>WX0D=M z?=?D{dnnc)|Lw+~_S4+4DNB5XkL5nt9=-60J3Mo}J+yF7*K+K-kfoe~tWV;FXXz7; z9ZNPj=ah81cEmatpzjljX~i`o?Hf7VrAHt8(T10G{t42Pe)-A>9sho|hf&cOz1-b6 zb(W<*b<5DEtCRTRn?S=K8xuPD*Si4KBRv7lO_y68Bd-76ZzZ4Ki6C?5OXwEg9ve9x z%w4qmVUG)X<>JT$D>_v6r0%{*B1JS5K)B~|Nap*Y^Vve9a-xk-EPmct?OC3#7Ja!x zvvB|&6&k0sDu0@qxclj%6-ef3TS_$G1Fd?Gjhky@Wpaz+1)pM}2YfyUi5&^$!kuD8 zKUIHv$v~9nT`^12*Q+_rtQK1FL+6!G_ewpdUDZ$1;u6r(ID1Mxw4FZ*`R&`Q?PdeK zo1Hd~EtK#EMZ5Ru^x*a>xz{`WQ<3h0gF>knUTu^0UCQ}ScN`A(r(rILfV>1x-~Xi* zqj%xt=_e+F z$jYJLe}86DSFcM&9CVw`{<3y+xvDVkk$8(SJ(xTOOI_U_wPz{j03kKGcNK8P17lI| z&RU+o|Mcq0NtvuQ;RiAyih8DReq}oFy#@-jpS38-ju%)`0a8`m4WqhgJ_}6Rp9D2RBEu8;lePf zGA@!co?k0Fe}DaoS?sO!t=lYcY%jpIk-8FrBLO05V*@5ragEbiCw3iVmZ}VFl{hb3 zDr<^0)37e?EBqhZHsou!Z#i2*YwkVUFlx8PriZ?*b%=o6yoO_WN=ZppyJ4g6wXP;> z;+3U)L*iEc@JzY?O#YVPDT#kF^F3Vs6sXz{z@eDg#kjAAiMnAsa%lYe*5k%`Y9ttc z*~FQL+L-a=hx`S#IJ|6#sz;dxzB_B+a%dCzS^Psy!^*Bjdby_`&tDOZy=ueK4Ngih z{G3%(oN@2X(l#F69{@8xYQ+Cf`^5XRURcV)hN*l<(lb|k)gxm?jv@}%-6iq#%4@!+ zJ6(R>5}JNBAE=ML+Gq*&fd*>2+4#G@%?_@QRdnC~JCyI3`BMKl32#L=m8ZhaP{6C8 zsON0LEnKK-dETm?pFbe^+!{YE7TU+(5A4~rs3s?!?>M?L#9$Hy!u z?VW^Q`6of@kbfzY;H+%R}0Rvtvurpz=Z5^_J=ASGo%Wc^2h4gZtk}!TRl+d z6CG`gT*{sM5IO(i)W61PM@Ji<3|>wLjRqoLam2r^dHMiTn@8SX$#178_mPU_TO1FR zzp4CIoM4{x2yP+9a$NKqN)#?b-8oV{D{tDT@v0^ullkketE|ACw%5v54eP905U!%g zR|vx25Y~4qg6+VAY=coJ*+P@CdiA0610GQ5Po4>;dC#JiAKw&47A87gHq`B5T%gN2 z^A|HNdapBndj)gOnT3~0e2?p3zM?de7`db5-MLxL%-wcpp9E+~L#~MRvMp0!Xfg@r zRdR$|c4!qj+vBP$7Rrj^QLCz8=2jKU*v_>*g&6&HZ=k*;_K{5nf*id z6JIh~Uwrr&y0Em>g*dwpxN*&Yc<)~!|00?4JAZD87WGYq7R4IKj{GF3o%!YR$t1M7 z@(Pp4ywcl?=RGzgNrySzFChbf!aHW7F$CEjlmyYKbfltIkEPH3mGI0LNNjeF&<*=G((3J|8KuN*h21Vmzhc2 z4c6J!jqXS9Q4!ChVEz?8KV}2E<~(~feKmPVdJd$1c@o25M3Dlq^A1rdJd!-vH||Ut zG_3Goh)`R|X`Vd@#t$p9SW`$2BX&S!>0$M#r^iMZWa)vm`*Jq_J-Z zQEa|Z#XdTJt^?oYdB2|478rOeQDG^XCi?JXh_@KFGMdGY?0S~$bMafW7EestemK-P z5w7KH;0yZB6A{)LbIDj2@J%R-j-AGX28pZ|ZIPXWaUI5BUMg#cSR4pNre}eXF=Qqa zvLFu`BOYf9L@^vlMkisK5H%$o`z_P@!aVy#qA4B;i;H6f`fF&LuIe ziw@eMS(#cQnNgJ~+Tx%|AdAsDpp%(=DZ!hw!Kw(~^vcwmSut0&K|l8sVLaJCw0uzw zfyG)$T7F5S>?DTtI`tZUk(_>4I~^rXHpvE?pwfLPIW^hj#`T;=lUyHZsJS%!KP#BX zItXMzCNo|+M19SS_Y&s_+wt!a(Ho75(5hmc1f{ZiWYHdncgp+r`h>3;XTS5yP6DN6 z@)j)9#j`&SC8lT>)Cea5Vj<;P*kHvhhQ7?OKTm+5x$X#t{d&PZjDZ{v<(R}k z=tG6i7IKiNT!??}B`Q1@1l6D*JSM>bR+uT1zvo;ZP-M4wWV%T%M zS&Z9uR0sl1%n$VmPvP;E$w^J~OI%;eSMw|Q>X*Heo&DA~y3`@*Ja5(jCGEE(Oalb9 zlxDibkc20Tp#BJuQrRt?BEwh+NTSU2FvrBHtbU>NrYY2R84e7C*%rYBD-0K3e!oKA z*=Y1Ym7)Nzb-ISGjcr{OY;G75HU7b$u;dmDhLKq(JQ;Uy**8AHPu< zb*R%~7u{c!Qi(){R+URlg4u__+NKb88tNVZ6{bOi2@rkLGLUJGm^8E|wv57DQx{v- z8dE4!1S!{nNYLPg%V-f&=>w(W!J_g3TFOJe?%HlPHrjOEab298a3C#094dM1hxPEovfg+UUh;oStwJ3tLRYTzc6yy@T{Q&#Y6w>Kf zzB>taB-Fk-Oj&FTouDN5dgaOb`uFXYlpyO#D6E(oc$rQhh1!=ai5GlQssm{is8v)( zAEv6CHIq%7VU)Tzu?bry&z$_hyQZxAgu((-h{!UWXk~9pg_l-C*+8I0BFx$h#z-hK z1yJmrDfi7_HY3j;)Id9iS?>|7Hl@MrlzI&U)ZVn-i5c4j!g9qVyGdjU?kWo@J_>mi zsJWgkX4-U?lpj9)EN8yy8>%j2q~xnf-Fip6T5G%3Q7Wlv$?kM6MBlol(~YS>YWOAWRw?!h97x$FCL5P{-e1Hm1^?@7ZSGcmn1EoaEq zyOR*2ZtawKo?BJp=&+EG(&Gju%^3cWa(Y&p>)?x=i`UO!kYc@A|u&wcIfU$-g`<7RQcuefY%j_If$hXU+ z$Zv^p%xO8!1$lAJzmD3kJ9lg60>#ui@%HYEx{WgJh@F_ycp}7VlI1ZI#eA}qV~JW; z4P}i713z9_j=nMvtmZi`HK)N9s6CI-#TwGo)1&=de0jW1QBf&Dl6wBx;z^qwZPymr zFPdhn#Z+cV)+s61sU5egpY4ay3EevZjQPqSY#T|ZFwq;cWxf|2N|(9^f(<+CZ5!B~mx%Zq(m&dsrbgX$xLs(#Ec&=7l5wx(Z)`eAqfBIIxw$!HN6 z5;_^Cx_J>r!o7y2>y=IXufYIhd7OryN_idxTVX>E?McICq?ykT)h^9j$j6o1NF;ej?SLSjfJNZVG@6 z9>J}0Ua`3JT#%Zspz93WukAUOh6nt?TDiBLaA)eFO@Q8yyrGTQq`tV`^g?$4{p!Ta z-#Hv-@eEV7F5Rp^^Qe8McFOc&_x$MK{JXaT+Xyw{sM>If?%A1sJ`90^I6kbuO@e+O z9WOJVzWA`GOlP2DaeQwS-k=QsxD0=2&PpyC5qVN9n%n4}6NRwwf4E8t(0g|AEQc!@ zP?H>tk{F9s?tfc5rAvWXJYlYw9296^fhMEA5ju8#M@6tL+>|N-8cdd0m`m)u8~+Mp zK3%pre92-UKw!jfIQL|3hOq9u(_%NTQk!4&jE{MOj3l-)ZnOY5M(``pwVE;+n=;QI zi+eYipFgHcCj(K^xFu=8g;G#{^iEIth2tdn$NLX?9S9BgmKMtRrd~rw>{44o5B89` zgH|KUNVbf9m@<3wWNE}ZtCx|~0J0VnzVm)m0W^;CW0boW$KJ-jTgdIA0r_A1|fs2hZ*V35li#|)#!SOET1RCtL;PNhE+2br~Kx@*vb>3^cL~Wya zGM@vD2e=Xd_LmD52~!0x-WOT`XGm;X%O#~pb*KK0ty`>^x~|k)uzJv7`aq-X%P(ev zGyMWthwjN|k)v16aaGb(Z)_IOCzmU>m$|IRZU`1 zMbit`HdPw?m0q~`FS|QUKD^1{cJtzZGIodUi@`ar>v`%or20f&e2BfWQTlK3)Y94$ z{=Dn?ja3=TaStmhHw#jz+TtWO(m%wPQ%N#sSErXC`gkTA(ATmnKen)6d;Y=xjrLc+ z4D~&=o_ce=KM9JCAGBtG7NZ_A&%vidu8D@Gd#CpDSkR98#e+ToV@Wn74hcYhY)@k{0;rg30KToLPt z2YB50jfsHM&B^icKdU$__i5NwHJE9$awoB>hblBf5*5_npI`@x8>}9G0-cLPhcE1{ zdw|=D;?pLSnv*fBGE6xuu#)b>vpopprx-}`FK$~}?Y$!}wZ%3|HW7Uwixnp8fn6`& zmG#(peqQGEPu})m918^skpV-uSuUu->C{gvQy^9<8)rB8)x@8)cX@1^WZ;cHph@|= z8vJ^+TWCe~Z|e5m7uCa0w?VQ!ok$sEvGDO`FkQ@p!gCT11p~_euqU)4mes;hoagN3 zCr|cZSw(J^8c%(`9?yHp)k`eWwpUoF^@*^fjOl;Ti#oe1fDygWVL2DN0NlBer|7+9 zj!F>i1*`r;{WW2GTmO_1y|^E1Fnu+I=dEm+>D!IBNxNw*R&CK!1aGZ|L=m&+PUjps zuhllExPMe_c<^PsOm`Qnd&ip*z0l=O>(yg_y(?lqFFj<>K4%&qFu!d(DCu{-+&tTk z@c*{?{x9&Q6sd(xFtJ@8BwF=G0;lErMH+1SBhXS8O81jWG(rV0Jvi2CETtx&VNEpO zmo6I)mO>ABnaHf_<|x1yYojkzOcWA)oH&?0%GFB<=m$Tt)2ehUnC_m5h@Luiy;7i1 zLqN;Rxrb~aVzWE!?W9wx@VdN|Cu8|4MM#nDbWX#wg-)aGSgFfiD-&5x?^Eu#HCtP~ z@bg~09{a%W+P`WaUo& zZlTM3hFh?#grU#9H&hmVIH$68ArSZ{o>kI6y@xqNAq2PAI znPmd&08^26tzR>{ChF7v5(_WBtbJ*yP zjhdfA0kNHOUnJ&!y2*47;Zo(7P;+MFs@LA|O5ejUf`T=@2oXkrd_!8REB z;eh=dYfjZvSQwN4Tm0 zgDUk3w&AL$HcE`Pqwd8^RkIC6hwc1QhtzK ztzPrSIrd9o!?y1ZrA?gM1Otx)PR2_ zo*e6A+a3GzJ71Bwc&#|~N&AlV-S<~q#9Fl#l}q}roYYInkbg8Pqkg*kL0$*Xy&9hW z4oI2wApP^5#%E0W5hBC5`tRSHWSV|7#Xpt$>d|#=C1(DdF_%a^@Dt8ws|rP977`k4T}!FCeDk2RoHK zn+{rP5^n-BtRNzj&=WevRMkgk1L(zNlGp>*t2(l&7A?_S!Q#_dpLc5QZaAAb4$oR` z%+xBqm$>l4u z@w;d0TFURMI6t_QYtr_ZtEuSk*&Ej{!c0kE{*R=qPTf<9v(;S8R(J`6CESt^y`QV< z#Ac;czCY9tAlkX2w-!50#s_kQ8$Wxj>GHB^HC10KpL|j4@G188nvu`5nRHjRw769F z$*Y|6lt|xa+X=s(hqY9xoVRq8G%VrbY}LMB&FxjAF9n?D)*6Wj+8q)5_xZEM(W~=8 z$pZ>Q|7hnP!F9ZzEzQC;xtkX9&plQ}e;U28T4dz)MbSv+!!{qDaZ;+kv}lcW&Ew{; z_I?=fd&4sR-<{Uq_cv5{J;EID-v;D^m}I=-ZBLl3zdpL2L+DuV?(co1jAD`WIoY_H zqR`gMp4e!AtFkM=lZ7$fYt?K!% z@_S*z6DJ!Kh8)y|U^?hU&5ex8vF7wD;@(%Uin15$(qT%Y^DN$e-96aZ>b<&0c&BQa z#RsJloEQzQFwpGS*5hj>?;(?#UH7HFTl{RFP-c2mig97%I?>N|hBw4Em2KKi{%9F# z|D3>e1NZ{LbKTx=gkS&LC;@#$-oBuM#9 zk68V{&$vwAikdO+1RtLMFV{Dn)vjzUH7;_qgRV-Sqdt1x*|q6re=rQk|IS%4<=Y#V zFTp&tOF`!23EWy!h0{B|{2$i2c-5IoT}d_aK^yG#hvk>X)(3ANA!n(9S=UDW*lbH$ z?1B+4jsa%UX?T`FQs>ggm;Sbtzq=BdietM5Kl3zCf4r!B;cCdQyt`gb>&~Fh^DWv%dSg&pHNcn zD&JSegLeBq@OTC9jg=YLt5)o^Rpn^jg1F_a9(|+(YG?-Sxh3xnM?OKW`6k=@ne90i zKKDDx6;$=$uD1w6go4l~`)_`Gz%Rfu+LeIy|YN+AY3v5&C6WT!@d1WeM{Egf*L_E=LLV$R?q zbLPFUIA%s7@juu`h2Lt8WU2%`j1F^O3p2U<#4O6s(jNQiet5t<_<5H9&(@&uwvf>I zM?Z|5XUIBNxo~Ia0COI%36B_TOU!7NI|lralLWa$fCvLn*~-ZI=vZc}utnc6X265< z?h`IV9AAY8mji5fGOBegWb1YK&9=B;W1p6CHK!T81}-Lfjr~PMJhmt4jErykd^{w^ z9f1p*Qh-iZLY2rc3o7ezQKW5AWTjYgMHVRu;dd{?J1x`iwA_Df#0`fyfufKQg=orN zl!s4>l&EJ6QVk)4g;-F3-Zabn1`XL#~V3D7SLY<1Uo67qB9t>nUx}P5G7{flS0yp^8(}*ITfq!D=%cE zJOGMmsmJ&<`$~u~9(MM3hTAVf5zZh2I?DzO7PgB|AuoWX z@EKeC>6VTunY;m$MXbHH0A!gx$vysMR7REMst~r8o`-;JWFp8O}o)HGT!d@&KraxsWFf$s7AvJB`OZ_j(bwy(-slD3?vTkY}>+ zUlp1803u9=^#W)S2IQrJ3Dl@Jiut>QY%A^TAA@mGVz@Wbz=at0rzo5wZ^6@K&}V6K z_x~(}N-_jdr>7!|FyiwVgbcD$f#f9~JF^dAUMOrufj~u>evXBU#6rKSlA{=KG9Io@ zVIGeuVgxe-C`DMM{B`c)S;y?qwtNYB^m|@h6S>$&CPt8%*iqm( zJd$-8c9R5h=c{nvD928MZ~$0z5q8cASdqpMv@?r&pfg$!xoYUYp~9n~TzU-nJRM&o zLxzo3!5$G12qze77%U6qo1wA^v~W^Z97j%mp+xj|CKU?_z)k|_<1YWG&E}k44R3&) zTLa^1AS}M3EvKeq1C~w$;U|$+gTOZe`WRml;*^=dTok2L`WIECgfC+BCoKptWfHWS zj3Snmo}iSXJF1>nKDIVV=~zoI*r=lIxzeYqflo;kQ?_^->jVG5PZS82R^H)Xzb{d7 z6F}mZ!Sp#`g@ERjE|DSRhMQLMSXD;lP{50oQ`LFBG`J28#a;wY&ne30LwL|iKlW?hH|{+FT~8}$;w>EV zFNxZyo#cBaL&-D3SB>*BA^oce49IE5YQUUvry~e(s(YsCIZe4NaslmYP*5!q$T?Lf znadv03J)PPIbCkTf*|LOaear(=J;3!1s?Y=!Rl5{Nj#gWhG~)>drdYkbs%Jm8ptsi zZcz1VM_bog$hKIpnc0bD0^29;=OWhaY+&YpIqvV55ptC0MY;8iDQ1KwTNwdei^sq( zS0fN|lRX)zbG6)z88u^LI&@9_cbR)9l!#xbv@58VB4&LK+o&8qcY* zKn+0tvF-)@9nQ}6?N0TjBVK1bSl;wA*H6MP(fNudm^;IqGLshJZ=E_h0~+3$vhIRl z2F%4Bl_`doyEg9n+~SE>F5!-_!daOCcEeqgl2nKX1ffQ4h;=ukwa>(XW^10aVk)Q< zxURfbHv!{cgpj8)SLMPkZBQUinavCEUSenUGTM~dyky!IK*BiE{C_R^e^IInY0I}0 zcka>vq-4;R&brHlNW{{o@Xe zAwrHy;igpPd!s#;%dgB`sywNsdW#Te(Q2{w{6A*d_p;k0W6B<9dWsmIh_!Fs8ui3{jXjF#FkQ4}&oj24&>x?}L zxqx}SbGiD?Qqa#ou7_4`e|&&El+4+s*AJ#(ntG0;NvKEcpl(-<=WxIKBD4T_MP}0O zrl2cuj4Mai$o)h&y_Va|jl<@{zl_}inH)Mabr>?_=p-=w9KA+@UpS)TT`XRHtmI-V@zBb_k4_rtLB*d5A|1Vl$jF(Z#o@XN?j&y>695Oh5D4+{{13HEfsVH z1QJ0wNe2+;Cx-F`B0XK6r0+_UO64`JdnSU~@R3pZF@ z8k>Fi@PrBxm?5LlXNZQ!<8MI72&t|kOqbw|7Fkh{$e8V+h;H`|yJS8a#){3?-G%8j zx}TU@asv>=^G#SyX>IEE?hH;4oZH{ejGw4}su%t1Z0Ow^_C06RQs18p#{*Z2aH_|w zu>>}Ni2F{~;lkZ+2V(T`f&&!vP6BtI`57ZU=flYfmYXXQ*)2aaqD2H;#PUD&od)<_ zfdBv}FfJ5q1t^gCsUQqAU)$A_?~96a`?Dp(hr`l)u0Njyc*h5b@nT6w~=cm37_m{zCt`OuF6C?Y_MjxKDEGU0lzwOF(KZZy9A8U6Ia8epz+|tpBkn(@5M36LA`rF9c zJFjmLx-V_=d{`OU{2?5&0V&?3OTWL;vsrhN@w|f-aA^x23dR&OL9F|I!7#e?@>jv1 zzpnh;vHS_Q2A`8b3KwU2*?#=wHO@eaB`` z;^qV4RPn$q{PqdAtv*XOC_{$S2l(r8A@3MJwW3cGc$B3H;`CqNz^P1^)u3&~Fv#|I zd1-g)?%(=0NT1>@WucubzqjEsD456gT^egP5wMfmxygDyQ3j#By<@2QE%5H{iXia+ zuO$A*6Z=^t4EJ4%`=XfH&-KF_o)Tg?MRZP78Z2uhvZ|V%&~7XpN)^+U<3L>y(awO& z7^NK~mFtGe34HAuYO)^-mcGWN$0I8|mJf^5P)0R|9C^OTC>9jAfHt0Dh(h8|VC z$zZKH0YaR5~>GA68M8WSjn{!gWyh|m5JN1k?H!TZ-HNVQb zOiJq5#Gm}C7ti7LzTY2O|0=Kf?81cCod0U=oQ+++%Wq3%^NtqhrbpAC18)SJTo0*m z{{G>H?3tef=ig?OiplJ`rVY5A@oDec*=+R8y>@(fqHXtko0O5f%Aca8mE}HG`o|v@ zT@Qa7u)DcQzLP&(FIYOQem1yszv@l!Z?6+_-4?UIUWhJ}1|NF!l3)Wq#5;e6GM2*} z4q8)#?|RG41b@A>zf4O0VndEX`-ure#NLm4?u>+E1o2>4+8NjACJDa>YOS+&(vA-OiWJ(z9-AF8|Qc@}|{zbB>3Kzlm@v z!Dt;HiB{UMzpnPCy;A%B(*;rgRHcn|HK$+3DN)5ie4ky0EqE&&Y*0>4YWc2o|Bp3i z9?gl|zTN9n73z4%Q4{wfMAVL8f4(;eY3%;6%($w1y17#ewL}ix`Oiz(3Zo7tZSuHbW=Jv*>=8MrIm-fc0f)!KD2lnd$(SH;os3i-@miW z)y-3%B^Qh0nIwM_=vO4&y9#Fc?P{k#IIqQL*@)KU@pESt4k(8x$fyg41x^k%mz#4n z33iHZzm#Pi{FPXbyG-fkDAYaQf5~nC>4*xuxpeRJ2N$3d{PV#tE%o|iTKzSNXLHfs zr+#=k8))A6-EVmmQ}?#sKcP|A&P||s%+XouVSD?_uID+ig62Du5+!qwuLu03b8i)Y zmHC^~*}kKa^6B*$hW5ssTcx^}`L>;0t!n*4zj3VmdfoD6+e?1E$|twTZwv1gv<~>r zo#{xy*0{2rSME6nbT!^JJo-UsvYguxV()LvuIzT4-BiBT-)Zu?d#SyvY{gZ{XsFQZ z@;1VtK6e*>-EDh#%Gudz+TccDz+SVBzSRp+)g!+>?o`#bBY4lb&I7TY@dLsRI*oqr zK&XzsxRpEiyJMHUt$1F!>-$F#zjrmAE&y|iV)4}=wG{NfzDQV)_10(Zo}%4nuFMAbWBOI&?%7Fq zyk@`7W(u+w@>SaF&nfNSIwgGthRIO~#Zy1BeA&-Dn!?{pD{yxA?O{`zG?+n`RMhdP z|E4{S*KZyivzu3cF|V(zR9ejUNc;=@q55;rg`wx(b{W2 zr8HsDXSzBE$f=CyGg-&+j_T)?#^Y?f{KsVnHB?QT9DmuBL1IL81lHa>)fbuZC9}eq4Jd@9*a3 zV^?u4(3I@*oUO9OWZkHVS(g&pQW?xyWA)n9sI1_3mD7F6bAG0MbEVt(J3NC3^Ts<} zXJ)(tUY(JUTe>su|Gdd!v&M>_&p0-bGfy^uO=={2{{F9|;9s}bJ;#oiVrfefQr2rg^P%F)4GoE={UAOSe&2!0Q%YV1? z$=hcReM;tyz0GYpEme;uP-d^Tn$M=yJ{56r1`Y*RK?uM8(|RO}#kD`(@Bh~O!jG4z zhqDDs0=14KRTC9*-t9x-o3yrJ`8y*XAp16Bsxcb#!-h?b8vX+ zfDGq@TKA~#C|iWs&5!W>Y0{jT2Mi1O#20l?~@G#@fk^kEl z*MBFz)tl9(&p&Ozjx(s2ylA{4J{p&* z*2l9JgnZ)pgiPEEpJZy6d#>BK>EERqh&O-mmfZi9^@bR?V9j{ zd)x_Bcres&`CBjet3=9=#hGah-yqJbwD3Z^Df+$}j=|zjVypo*`hE{%X6d+{bTO^#hy-L%6Pyj(bbaeh_P&Ir+w6tVJd$PeeH! zqosfNU~@cd=I7so33)1qIX$pSNbOHZp~`Gi-9Ay_!-t*$4|=K3_z>u=MmLmu@T;!P z@oKJJ(|ewNX$~TIZRz8`+Ni%AMjw6NLjTc>?Li;OC_B8a{GL0t%y#@OAMBf7MI`6S zau5?3L?;CA+J^-AgzUA3jJNv#L-@{X20SW4Y*5gj?zrIx%<|wi=x9!XZ@{K#NQVqe zfe7BU53?l%PuvYwYzw)v78oS|gml*%>;Rf4Af6Mj{bY2PyJyk$$Hj0#fhfBq0?6ts zi4zxgVU6vYG+fRYthgUxQwdUW2(!xyxq^IhRqRQvX!yLnuZTN%QX0$HtDf;CDIhGX zn}fcGgIJ0&&%mHQGFWbqH9{;>!68IBI>cc=?1;V>672KjS>}`5dsq#3Mu8*h4Gp8W z7G7}4%IcF#$76QWeG)^HtRMo%yR3=?ph^oE!vm`{iJ(e;RYEi`DNar+`kn%l)qI>% zTeMPEmYksut|W=-Jti?eqQk3IsZE0n5MwB?3fcZFh)n ztBmW4j$W&b=HZFtLp~w21rKP26A93)`S@u9w#_}tci!ZyCV!c4EPOk*e4n*!KZ#C> z<+OGPk;boQ z%Wzq0ij+8L#wx0#^;WDyQ)Elt1+L4-!G4jUSJ4;Asmj?Is@ZWX4v8AtY2WrE z?0mxlxFhx2{h6e}9wZC`j6Ez0XA(+I}aAM+aApX6oV~`k-=g>wqrI} z^&s`LSQ;Taxg$FIPmQv@X_LDv*Zc{&O<{7RD#3i1|8RoW|FD8L^P|9joWXt=Oa8({o*Z=>d%plje$ z-PzFYCs?ciyJ~((0>9KgD;!_UOewzzKwX&`uoHUTs@T)CVqLq`p{n4gVj^c%vcM3l zEe#cVm>1z+#`MXNgnCqe6)I{?W;qGt{Dc;nf(>yqo*M$aG%oF0C}$(W7&}WgLN%u; zv@)modTd2sc82;${xUt{E73E3_*8nP?fK2=`@oUI0u4gq+qpluO9W5)xX z0$uWCAyRxYObQD5pyLwYMIz-o1A0q8o8g0E%%JAfTwn<~hjOsIq%ZsMidS?EQMivFjHhd! zGCzY_QxKBX1j%Y-F2(H-a!VRx%J|wk5wZY62;Dejh2bW-G9rAFK;U7eSvLoWvCV-S zu!?QhA-3~bPUcFCu={GT9s0AY5ct(-s2%%krI99KO6&RMCf8UnDxg`*xtZ}mjvF89 z20vOK{U4W3lx*3h!SG99#>~^Svt>FviPju(LK?V?2j25?K0IQz2{_AI&EE||R9S`b zFgHNV8Unw!DoJ-X>sG5eHAs+Q>Qtz55%VL)UX|LWG1+#DAK`&-$D?7!ajxe~@66u^ zapF)v!O@;22qtpVMM_gWf9Dm+&L>N)0rVOOL>JcC$;?;)TM`k{WMslo8^Nk(H5OwU zSJzIi@;C7KGzDiqnJZT%)w~SPa4fzY&_FqQ@!pvl5Z8H&_ROdV_K2|_2DS9+mfbdE zz2jW=>wC(+nC@RQuEWl5fzdneMQ~eU!J;Gur3YC)dV$iTT1>(jY+0v>*^+=@2bg{w zwY`e#GGc}~;z84toIlQrCrNA^JiR=n2vaI*VWf!>6l-1T`Xu^lNq;SsmBX}wlH1G0|B`{trQ@O4 zA8<>cCw>In--$5neA=@QLiSU8?8Ce3tl@MV99Mq?LhCK~<*{+81mGV{q&RXNOYm_eEpP zhB*{Ht#m^APkl&hZz`WCycpnQzR>XHW-%f7)Q#b0@f$Jf`De@zlNeEMP8pkre0Qqn za?E5Nw?uQ+TsOBUtoF|O^ZjJ8Zb?vl&0a2AjTmkwz~8WyEXz@QWt>6GqRdg-OR zySq^V5osw&NkKqS0hJQjcfZds?>qC%y#K+ObLO5o_Z6Qj*{|n@mZXEtK)mSi4n0Gz& zU|93_EfRNj!_cw!)DF1xhJv0jSUy~ko=daNkhab=cafsa-9Tj6B+*RCB-uR9coq8Y#fn!9~1X+t-hgug&Txz91a>*QvyU(N7>^_RJL2@8(NeinXR7O z|9bEPb!;==*i--Gw6W~Nl=jQlhRJqKuG+yXKl=NGDVx=Lh4Q9{c(TXp{k|mb2o3-B zCvmU$Gno)?k8wHt-U>;m+tuNcy5TW0m+|!LxcStCVIQAho}2bm1^@4Nbq6}rZ$^2) zizGqx?C+>O=sfW7CLd;PuL92f9~BOH28_su<(72021K8J zu(W)2GVop3SaRJV1m9k^sD7(Lc=Ish8{Jm*=_lT8xypaj{T?igqMvzmuLO1(EY{Cd zzN@_!(Km2ZG?-2&6G4b3Nhu<}PL;4XEHv=mGTUQ+`MLwuV3`&$QgZ*K{BG!51do8l zC2JU=g56}-UAk$6kGGKtxcKJVmAY)?&wr0zaVHuYy6wX|tdgaRFnEK?KQB9fwdRV5 zk(I9ck;Ew*=ewzWFXn+4jfbVbOvUq9`c4bWNHWsla>j&4hi>s299PP?GCdD6%` zIHb+z$Mw$6P$g?Yo|imw6lT=WoQjkyzE+W%ER()1w|Aw-)4bQt{?*lI!AeW^e#aOA zB3p2JrKjj+kJnKUnG^WchE}qT8C;9`7E#};ajhY(U%4+~ckCxQ#O{0F66h_5PxD|BtL5p8}Kkz~x@BF1`7+Z$gw zJ!@b1*pFX14G=8H-XVn~hG3r{Gk|(KgTK_O0FFk{t0#DnFT<|YjDC9qtD$@uLl3cI1H&%aqG&YJ%pgWnD4w`2oU&b?-ziW zlNf%Z164FN zDcU4SifB~q!r{i6!q9P2MR5>jM&aF4oUT!RMWhPvW^{ z@ErO*EorYM;0s)De2Qq>oiRy?zst>_azl)U#AmSvZCY_+0rCN%O%tPUH?!N zbtmkYSUjzmtiP*n;ib#3n0a{)&`ZpC=uEf-b_+UyQ=4{6j=S5+J+F8%9A>6qYMT_X z(^I!}yM!abUVrVY(MG>D%Ms}P*Sv33VCkus>!3jf^TYmr2>sCJFSN!z<{97NAUD&{ z3dT#$!UyEs*Bo063Oji&oe6Wt>nUE8I}{X?B;qBCa697r1TW%>^e&ocP`#)DiTn2X zUBj>K-cQHw&W=ZHuz2|IqC=aXC}v_YxIOu+@4CvDO0v&!Q;s2bs}o!BYx&=ul2?P) zA;o7l0GzhT^I;;l_^T)VM%I_AHb z*&_^lQ=n{T0AaY@X3vB0Uz*7PA8PW`fcS7(xDYPiG`y^rkqkmd)O+dxalcdD9<8@4 zV!~DTyxnI`KOiJC;9n=WALk{z7vL3%p;{td)5nndV!Yb}hoYVDqP%a|g(~`h32_^T zO21x^8>6zDrWorNbD`Fb-3 zzKM?ZWdtYg8Yf73teyta8xu%G#0=<=ti{mbx70)f*fsonMpI*8-c^grAk3hJ*h-q# zE=$pYAD$ux8=i)w@%b4y5r9PQ;d{Q+{n73pW9#jr-&T6y+5=L2LWR+k5AA~3&ef8S z045~x>3tC7IF9BxND&beTI}ZU3E@QoP%#=4R|HZjJnB*Synev6E1`7h^=~hvir+h- zGS+(}Hn1!9?OC+!Uc?PM?u{72M7=W#xpbc50eq%>jVDW1ABpJ<}$&+o1!tp+V)l1O?LBkuH%pT)B z$%nNTOF=;45RBi3&4V@+aJvY>w27r{PNEt}CuxQlYgX(9!C`trrvXelv zQM8wmbegJtS`cI63qC-%3rxS1=mbqZlJ%kRi~u z7$(oDOQitGWtu(!fpN`A_XFc_*)7vVSxDEd^X$DPP`WLFDSHkn zt3Nn%xiCmGze)z4W6L70U&Jtw5-Xm&B5%XGpKBCnUVp`5^k-0-pf*e&YIy)0L{zGSAmL69 zgMx|a@H0tN&2Se%T6fv9=~GI1ze<_Q@&M)vgXK^Lk}46=;TW@GFVOgv zR&ncdL(f=4^*v&I5wQWOO^O!6HmAX5pnnoW9)yI0GPZ4pm ztxQf-45kZhfNr%Syqf-&K!}yw?yfS+Y9nmC+j=e3U|(c=y%3v%w6uSL$KQb0oAoh6 zZHA$4PruPouhQL;1O6Vh+l+HZ;pmLyifvar{-(6h475@!7ZVLo*@s8E!LyYFJu1y7JYC?aRMut`O%0z@_&jxf&9%cRAL#JUbJNa4DXYWN}Rv5 zTrD-QAM|6}vQDft;<*_LxS4(xj>bv~uX~QRS`msq0UeYzG&_1FNDsYu#$TIb+4wY0 z9>I32kD=-@E-G1G4r+}wjUu~CkBf|R?~Y$aRMFSzMU#Ah2;VZ|{nSeo&D{Qt-rlT+ zICoaXl;P)6YKR;CP}aH2RzVWW*YB&+V%Fv9Hx$eGZ!FG?hNa*vP|u+4x%?hO=ax1B4J+)lcOMRSc^wDPrOXc{!V_e@FLFN&xj>yUc$wJ zgY67W;{TLA2|wkz!HNBsex4M~ZclFME+>M079<_U!)-wcHz`xmQ_SXaKpR+<-%H{- zC`kf18I3Z}RH4aP<_bgBx-H4`#xJcJu-duCG*}K1b|y00I99jOytH}Yi=umc>!Zm< zp@deHzIhT}s{*bc8cK?}B#s^4@>tH=EJe z*_Oa!{{YfEB&2cw%lpFAa)><87hyvos!XeN(SW)kD!bm&Q2}EtXELL6*XNRY-{pM=X%ObN$9T12@HD$0d) zlGPNF&u|}4p5(dBKkUYO-FqHzP?M$=o$ujdbJp&z|DqzV;H}~=DpX3)B{zibsv1I*P{EcNA3ebB(IP6&_tAc zACC;zx$iR-I{8l?o~Sf#IQ<a}tGX?d z$n5JspZ`Ih_nqJ#z6{(sHErxf%{l&jd{zZ>xp=Fws4H%mX&Wr7Zdyop_k24tukXz8 zR<~5cr?CeCD{9dSAu30o%~iaMrGGVxS%J&Y^`*+x6qdQEPel3B()vv za4H5*PObpUZ$i$EZ>gt^&~Azwa6HIE6TopdVTuMAmy35!4OIi780rX6-^VYHd^mie zAELHrwrM=7)FfXd^EsDoTqN$j>$4V>+AKv<{Co?OdOZ{7Mn^5$g-zR5b&Zs3Bj>ni z)8OtI!g+n#PTky-Q<_b3WP7xQ5)x2Xjuqoi=rbqzJ9r;@8kwr0Z2h~ZQfs5CB|Y+R zgB4*i0*g#ZYWRc@VyjkR=hR8luH(^gA($7{vsqMJHu~P0?<`JLv!NChTn{)W!-_c5 zH^MFN7inryKS6}GcH7(=ss`MthVqoEdG7`c>LQ|Ag8IU^*`I~!;pTtko$WvcPrC7= zpQ_xFTgNSULN9uMnE(avD8Jl|IU5Cz=lrR^^W9%Ln5fo&6ph#C^U8v1C(v-0D1Z-S z1~_NKcXom%>J(6 z&fn7f3FI<9(5PGKOY`7!GHcS4^;%}wD^O=IIBn#a00H3O3e32JRQL-PCN7jGd= zSgyM6>>7oe-B0u-=9SU^$Y>N>j9w{na*(01JUXDoKWUG10RekTXTkD!62z>?3r7n+ z?FWve(b>B1sDul0MB-+IR41Y(4f6O&(Uf`_$K z8c3FTR9UhYqc97%1|DnpIY0b#+Oyb3|CN*p{p~3W64Y}(De6_Py#_y*0)maf1~t$} z*sEXM;ql~>6thplP#H+SKSB>?Ym3fI&j+udWaP}7#DCY%-kwc12G`52eKqW)2DuDA z_%XVgbI-~fzw@rEg%ebe*O|&NbsVtxEAGY}5Ew<(g8Qz$1H}3EoJ%%)sW(T`OS$#G zY042NpWO^GhCtEjt}gr&Y-|C3?>`-5kP$jxILNw3ZYoN|EFNyai8=ACCV|b54;UX| zb%=oJbFw=ppKxUAr`*Kn(9H77-Fpm~vh3$>1Y_QgT4`~FOYI~@;AFnm)U)^~Hwsm8 zMXl$--6+MYQT3N*NjY_Xx{AHwWloWuT5ryT>ab{8^5F*0=P-=K)E`mr`YA_QzmmMT zVI}&fmIZZ-QWflf>XrPv_P0$}IuH5XUwd^$lRg|oxM2kmDBZI~YIizx<6UW87jJ+YQS({L zQSyWH6ik1>NIf97ka>NgW0GRu`Ddb(?4}u^8K?0}o!lTjxC?_k!y87c zP-ux-$#oCscdD3o!V$+dtE9y2)R*t;*TSjFigy_JyJye(UV;f5*{cF35po@+epuNz1(Ua$ zlrMcdx!J++P1n!Qg1n6_l_}ZwK@uX!v+hOn1KtNR{bzdd)qmLr&W`&w@nHYyTRU{b zCWDVOJ_I&ShaphrS48Znp9_rzGYY;zhnCdDDW3~|cs%dH{~%g--cXX^;8V&yCZn<2 zxgXJlwhcabB>|gxPfZ#%22J z=|cthF}Ic?WxMR5jNzJ6!NQ9eFqfDZRyAi+C=|VO`Y%=}l_Cg8J z*sA^rU4V+c-?V-=l!Gx0;fgg_A`8NhSQ9!xcrnZ~4a^7%*fR3d-t)Y3&Ktx{M0>?R|YFv!?Tjt#?NW0#1) z#-tE_QXFH^8}PX8vw zwR+f@RM5E*Hi{h5X%{z_>7Ne7w_r%(`ALVd@wd(+iIx2<>?p79!mWKB?^bD0a6m!# zLGNshH_2a`Cxg|n5pB{zNyVtDyL7*z0( ztE-5(cZCsa zSwKJdK|es=WlPy5L2^NhK}$Jf=!(SrrLrA@2B309zE83e>15LGnWPy@ClN-9;`w3c z=Y;tyVgnNhEr95H?+NMl>FkJceMh`)KvpLWs3QSan^X&XJJbLvaVUgn21CG~OU6qR()e@YCL`(xP$KhwC% z!{@q@0mBIBFc^{#G*d#wNq<`vpy^rDrlADBOIClu*&=BCMxnuo1>7}Fp(zcbkwRc@ zT~))B#Nh_&MbeDspC1C3MYaVthK ziji7GF90$(H1UT~X!Q~G6$)?%Xdq2K4F@bUO=tyisB$qmqB!>65yHFQT*~3;Cj%mq zWU$XPRG|jOqYqINtMnWz%fX<`rBwB0p0LsFuB94x$sk(A>0Y6AKcZT2 z1|E1L?HD{N){G~dS>slvhZ%EXZ5P||P0uaBsCkU-UPlRrI;_86NXs+l9D z4i$=HhSAWAvB;wu2)VkOayJ@52-aHw1q(vawS~xyl;fC`g2O+@pRiElMuIpd68k3n z^O9P4wRJnRmBfMsBHjW?Zsst6SRmW<#`&@-h$}7HIGV|CU$z|$wc^vypBCb=@btgzG5N4@!rVt@|bX$@i|XabNUvNplT#s%H5f)uQSZ z6QpTw4HnYB5@gfPC^inWmCLx2c6Xd_k=}A>`|62aX?p0#UfyNoe81EEb_M)O;_GHO z2AZo|^ZRJ&wf2pNW!1mF50C2e%%;b8*DW^1Sjy6mb#vNwCaPyr`JLO?mDPj~{~%v3 z8CxW?EwqYbO8~)&*a9)?;X9ZDa_1DH-55A<_N~OD|B~Tpkcb{)ez4Eu}?&<8r zC#>f4>#V*HjbhjJb3ZTYYt1ZtaFg$MuA^!WeO|WW-}S{;MmLqaF^|w1dpx#=kC!Ij zBVFr+mp)-km^?R8-NuiKFpy_mY~~>U3WFD{ml-rpC2Tt!kw7}XnJtC?=_g~6g*MK( zatp65C#h{?{V?8QDGSfwkJ4<03hp*#NxTc5^pCHS)2-4BX|P*L59!l(({J>2ar-5R zFK-0jClz=eUh8aMkzvft$PuVp@Y^~z^IzHy`bD+OMxJdf(~Fmqri;7={tE{j!Po8N z2E|&=jC7?pX}mL{aVZCxYUwnZ3%c1h^8#VPoSHM0MjbXxxt=u4jD???DGFk(sqkHv zRr8U5*sEpo8D}%>t(nX!c^=2!s;@HjGbrGJ-xX@MT-z6HU0d7#R+J*la;uI};hT*M zAG{ZKpG5D%S^x9cfu)GUsc%sS!UXocch|w+zH0Wxj!Sa_1zqFor}S+2(8eb?L zbiauD{Ub+I^|K3;i#;9^|D|92MK7oNwl|%d3X3*tlQNr+c41Db!6*Hx8(Z5*2rG+lS3I{^#Vv>nqoeM*)2& z(|47e|EOv|*L^f~`(}mhq`dWtWEZb<-mhiLW?kVCVV1axgwhu~9aBD*t1o-GEb(k} z^K93&ZtJ1nf=nvznPs(X2BfkWk3>8?^XT4S$NFv&mMBI06$;jx$C6(v0HRuv>xd6+ z8HdfCnjZ3(N@YHDa>}hR+sn^arr%rkj}7%4zmr|Oezrco&hDc-JSXD+?8n^$W)PpKOGqv#Gg( z2;|dyMhYcZoLI~pnv^yRvH5*GjPxQ>&5A#BDH({UsbPWr^BRPGNr(ysZ(rhr4SPpI z^{iBHf7)=ZLhVYHMJwFFp4#ybgb6lH)x1dQRM zUCOP&1|i43O|99gdz03BBClXE$5XsR()1CDM9Cb;Y{n66-AoeFZy8zGd5`gh9>&#V zhsv>Km=@xOOJT)vkNb2!w@P{9a>d2Lwf*LIkb5|tCns0E_ZYo9X)+3m=8tBiZLb$| z)^;kP->uS)Z*z~TdCD%tld7qHN|X70d_?TI3C{X%E60?O?j6-pz*g^d|rxRE&U{d>3 z_3!ctDMR;L)kgo1W*GqlE?@@M#mQRx`2kG^DCC`}<9C*4Z$vsMXgqjTKWa{nceLgx zv5DF-Jbk*|AoZ#==qQ})GL)FV{$sA@kD4&XM_)cK;?gZR-r>3oA8fBUiEt3;kqUy{BWp(^c)~x#? zC3k@^5YeW+w0(#o^GK||sy8!owj;0YWYHymFY^Q-zS~p`>Kbk*l}P?(*2+P5{|F_W zGYwbe>*PAycl5aR%9MLiW=_S$BTpQ@H=rmt`C*|EZ3O#D{hKKfvF2WEe~2dIl3KWL ztPGYY4wcl|W{gLL_w5P4BrO_PKYiWV$0rVjpZ9{48ShDRvf3nkDh1|kJO=mj?4=&A z&cS`W-3>O}ff1Ti9myw>QwtML3q4W7q)LSAad{H>*CL4)=DaZQdg4FBd zpr6@`d0^@Hes5s?Q5<)aCr-!0-wNm*f0VtLqkSMR<2IVcfA6yM*Ik|X z6u#2F@7N$oI=J)q-#$%JNBO^N8{VQUenP+cy)u<$^4pImyJ|0`H&M}<>frCTm41^i zU!Uf_SNH)&qrQ4wd&8SJSZSkpMUR>>)nwN{!^y6=GF;E!So%Xde+Fz7D+ZQ|_Otc* z25O`oQ-8Uw#69EcwoChT|2KQibRUw4ZP?sx>3g;Egy(^kQ+kG0oV)ejUn8pI-c zQ|Skkdr4@>H~y~wz5dz%!SgygBm{D}aUB{>+=+qp;sV*Zpm;I(FXf=RQ){%hUqKoy zxCmN!<|lgQp4#LsE#-fkFW4nB_$DJL6%@wJ2&&%mS5mhTZ-ey~(c1SzE^U3`WPyL9 zL*7WaNlJ%bSB6Mo!?SsP`+PhzFk~N4l-*6RF&{PvMhmZRBwX=i+sn^9)6Zx=jF77f z14!tzLQ5ATo!*DCV?Dl9`X7*mIoXC;VWW7Z!>Ti(%16PW3tRF8h5?@_Cq}uf!UkVRhuIsa@%pC4Eoa4#QW6 zWjqsTqMs5h29OYn0e<#4g1el3K$eXim>T?QSeIoZV1tWd}ES=NZPa`C=C{%H%F1e`6imi_KhqP*fU5*OD4&@Lep-rEw zVa&`272D@2eruhW;Rh({zqH`bk64a9F%IK0E!p%-+q}qEkEskXtqd})6qW@h&@l1r zJV;LVT`jQm7^Gyn3W}@F@Gr|0tof8vU6fN@!d$bX)sRI|U6@mCg9ELOK@9wB9-5Zd zm=?|GCukf;ka3V&6=Oo_X%>z02p;^rYc1I^={7^wA8gbfLrEQDU7UTTphHQBLmhoU z^JG6I(>Ud_KH!NihB#0Lpi3(q>S6tmSRBZ^sU^|BY+JBQ>~D)iK*Q}@4V(Ct`fLY? zO;gK{8uBCN#*-d0j(~DfhlnLEFf<|&1Bzr5w<-9YBEAevW;eOo<}a9J#RydLnwAJU zR0h=4`BHSy_cky0H-GAePvSZzQGh3kI)kZ27+uPSZh`4{^5=qpP4dL17H|F5;J=-i z+*Ub9GJhD@rk_lSrF=oQoNXF%{}zZuD`mxlO!0)68Ih?ZZqtW@rNwkp9+OJjCx!Xt zH(>K4P?i1;VZ;7)fm(G;u#T}DIMc0O8?%nd>1N_)Dhv`D9eC;*snUvqnJKWQGEA`fokG(4$4+$9H4fylowwd|YJd%_xp&``UM zfFZ(LKoXvW1iJ~x9KyG!N!bqv*sccINFY3qL_%k9bv)qiQ}T7Q&REA97Z9w;Rs|!27`=sBu^1GNI3`x?CzpBu5*rh2s-0%im-NoGxenIrHc0eB(eg@7r}y1vAw!CQG#U;F9@&dI%X-e<7B9!0(0 zgTh<&iyD}9Rr}Zq#ctB575n)W!-ACVvYLRJ9SDX9GdMM`$)O7@=mTF-(S0l}5PvY?oy`WmZ)+#E0p<>L25$)w`+p;k5Qf~!|BdtpaEgXqDm}zF zNG{K;DMRL2KMjWCwL?-#$(CEw0JrQ3~sS@OECTb%=P| z_5rEfEjjRcTB7y_o|)FnG0lW^!Qxe&6@1q=5Z;!sY8ANJQ@`7nznhbYkOMUG{to*j z%rf9e$^E`eyxRJZ51Rh|MLuDTTYiscYKreiYia_SJ$g~Zg%ZcIGxeK_F%(Q{Fmy)` zc=HTUucMnqvW{4fDHX2-_tfdV?tj(S-=4opmUz%rzdKd5Ych3Uss~7e5O&i#y{K%! z>BHAxuq}qnSHCl|sML{a$(jAzyxCxj-ro1TZJ6t)F2(h)>wA5Q@Yf=&Pm2(*(UPu` zO%TMiJb1>WuDSlzxc#jOio*pG_F0P8-(d|0IW8c#HNX&qI7#>sr1xdw@a+Kl>yOu< zNPY6arY|jc^TFl<(}YQ~64a<43jE{s9;ETxXG#))BOahMbYsaT;i0+ihq2WAu7AZ|VRKoVY&P8SVa7dVQ)C0v^!6 zxb^15`Q?R6(h1$`KkkL|(?~dp(oIb?;5l8c493wNFBldc#Cy^l7`DQ4p7$PVJyn)i)Grw6V!W0fdJZc)2@cI#v^S3YQ@6!kWmO_?G zsITbogy0dHgaJWjMJKrV*nn+Ub8xM9;lp3EH`l#NZQ8nLFGqoQg}k7*h=Os#7CFr% zHGn6k;+EErMX^62H@~$sz4bQajqg_MT{!a{OD0L1zJ9mT=0rBhs4HVb?mOVQs;!kj zux;%uaqGdnqsGL372?kmJIix>529OxhY>_=wq;pb@l#iSZZU{B(2ECicN=hz)EG&b z(-kGHv_92+Cc|L0zoCLHTtl7u2<@zj5# zz$!7#p+WAjqfB|5P89k7A(m{mhLKZqKJCISDMeCpX~plmmZ-*ciBL_PH*%^*uuH}g z%ltkuX*uWLhxS{!R*2dJ}ze|DNH;i(-<+NK%NV*E$l;P@( zCXH_D8~PjvQk&th-NDBWG&bDD5#lnwMb0M!AB&wBG}ANCz)5C~CUZf=R`5+5hK-0e z4(W%n7WLSa1fJy)7Vq@-uH~ofGb3z?(lkrYl9{0<+YUFfBk5yjC;4!R5*#sev0dm9 zmh`h{zS{{rV_m5j3o5H})m#3{?AEppY1+9m*F=1I`YFa=@=2cY(|Lqfd4wagT+XD6pfT@PPwXGtw8kONL%Vn5$!C2+!u4=klQJLtuu2Ip` zU0`1F8vpUDe>D9_L7vZR8GECsmK?#PI7siexMV!uR3SK^4kDEzQ%Q8{<9u&^+`78)#ogOp`S!eo zz1JT8>ciTjZ<8YTFyoUgTM=9hzH{cv!VkBQqUL}oUy!!n=M?Hsr(gEm*=TpfE+{WG zGhvTp@24d-q<#KoG5-B^Otj49forE!uWI?+kJFEGKV$0`RllzH9%O5CUzypyWoi1F za(!?-BF*;T;y7Jb=iiarPu8Q|IWCv-k_pSHqd%NpFXffD9;97wa8}Bv_HqaOlf3-R zKs~qnLn+~Mr+h~5`jT~=YLYvO=A`*0a*${foL(YF6s65aF`>C(^Iud%&jBI7VHO+; zka4_xi|1Hxgv6xg`DiWwJhTx@B(?Lv&>l`zaGgGCKX?- zfw$=bIB9;$D$f^d%v!yH4|B(8Y?08Z-+Kj9OpawP{;%gFd!lI|{!nO?GGYH-6wW|^ zwQidN`lA0&q(Kbla*5H@h+1QxxX^VgIosL8o|yc}r#?cK|76dTm`Dwyu5Qh+h<2(+ z+%ZJHkuKBpZH8%vjXUX1SXmoNG5nIG^tdl%<*nEG{#$htyt`q`L89Kz%mUSVK4TB_I)0Z$9t%hoSEnNN+VaQ| zHG2Mp!Mcn`1$Gm>2;H1{37J1N#y14)odj`Mn$}R2ceF+fnIBs+B=1@ZbL#W>7d{m9 zLcWB@ne53fib3z?|Ddz>-lmwcnEBIa`)cahUf2i0iu3}RbXGJ2%hK!;2n?Jc&qxK| z8n!$V2u!E5l!|-YEm>R+3KGzgldZn-G`h9QOYo6%^;i?@lSUegh{q4LR}1hBcN-fd zx%8p{#IgjcHSc?rG++?9yhr0ozNkAk6#dc@56Vm?=2T!o)T^ zdBCFgc#~=SV~M(N0uUjzMcAp}3n)Ye!$W+6nHi`2*uYL{w7*HEIj0?mMVW(61DaLIvmlDB>dP*g^)T zCo0+tR#o4IGD%p5&7X6Ev$RbR1W`Y}pO}^EL`hM8i$l9>Feyt6tT?g78L;%0_3i-q zWgK?6fD*t@zgKzfTiB^~XCR&9Wn#8Ka@}bb^$ijC6mgv`E{1IUTl>=^u9T-+cRIT# z848-Px}QwmDqC{QNRN62Xqx-V-ay>ML~_QB&^!#QuafqR&Xf;UU5~?9QUhYvO&+29L=Dv$^cWw8XXs zfrLHv2R(zFq)k-}XSSi_C}s!Ykw=+hIA6p2Ao2qdt>LGD3;yj#S?E2ZeE%fGgb^~o z;&xu9j)mpJp2gm&Ke&KNd%aG%CG7@}2(Nq$_0vBcpCc+&a}<(ULRdI93p9)Qww*)e zGpqYBB$XTaKYh<~274QP8pQQ}#=g&8L@xT~DkuI3!9nQ9@Ey)Rc}LVYe$^MJy!gXy zC6XM8fBVJv(EbM6U{A}Ib%*}L8O`Q*w-TbY&o`-M`T`WJj1NnybGW?@)dm+8YGiq( z88ksO*YtOf+9-51dY0ZNdbZ^LJJjCvTKSxhc`7E!ix`O+2L~e>Zp+`D0Lb~}iypci zUG3XEmjlnf_8#ENKkB9L+wyqjT^bieEU|2xc_(@e`5jJQ1kAAP9 zb7Y^12Gb`xbU_^Y8ubKa_;hFM&rRE)ZO0GiZL8~l*Fs+5&jtz|7gq~FdklZM&Rwa~ zlZK#hV-2YzVrOh$b3YF>NJnkM^*~((Pm>JC>w~!7!BYAm&C$W1ql3dci7Sx-D|?vT zMSqHY|5NMG2)@uSe4(^<0ekNuDg7|{bX^D%8kXV5+w2PBN8Hi?z)gW7jI@(Te-bwm zjvJa?{lppQV2UM-f`)HfWhlKb1py>-f5~6IDwKlXmwp7JID*keLv>J;oK+9`=D=JW zpq%&2JOf6=nQUVGi0d@> zeN2E|unIXgs4IHEGnC3M+Aj-Ri6QnS5JB+zk=*S-xIQB_z|txv5UFPF{hn}WLwE-$ zAzg4gjVPFSEDS+~Y=?aigZtwAsf|Oa7~_KWqkVj%+l=Enq~kh#LqoCA;aSna##mM? z@#ufF6whR7gwGMu4doNb5mYJ_USWl3*j%)UX zs9+NbdSvHP+!y0G@2b$i{kY~X&#h_@cxE>BN0s3XYgW?(Rg+<>;@BDL?q*K4arF znm~U*@VO((5k|vrp-^6YGjs|IkjKzK_!H>2aJ1qiM4_?W0GyYcn5`d@go&dbN#qkt zVPQ()^-JMAz#MiZe$z;~*`014ll0X--}GKtfY?}a6ML1eFI7x8RX#MCm1*hKXsLlo;lJt% zK6I&5Petx%#gIK26jjNKhKgx{MVJdmFwQU@8nhU|k3`5~fN?C4CI&F+GfLq7Zn`v+ z(TZmV+n4hX6c~>ImN>3?9O>~v2^|V7HU^2*uT0z|x6&$g&Z%&|tf*e8sUEAb7p$yx zASu|h@V!` zJ%4Z#xiHR@=6RQC&_!m$0HuN^Kp3MRnJy`;sa=K(No)dSsfQcIfGM?#Y1z!m5juHGdX0nAo|n= zD^11zjTxZk@784ZT**an5a&(!`ZTEk4r1ckEaKWMqZq@QJwg-%%sU`@0ZNgvTrs&e)mrjDUeBR8 z5P)mr9d8%dY|~>gT*px8t+pAMk^CRJ-ukP_Kk)zF#)8=3+UVZs4gpDZgtUZ;v;v}( zAV{YJr8_01ySovjOF)nm-e4dS3QB|Q-se7_bME_x?{{Z^z|MBIUDxyVd_Epxc$>eO zpuO+e@t~rU?FNbQEHOXu=AX1X3bc}Vz|jCu$I~*NGiid!YJ$NSHx$DBS|6zCvqF=a z`J)f=g;EL-PX)-j5R?mBW0FP2DN)0Ph5n@kb7Q+mP{QLQ^4nA$b{FMG0J8E7Yq3v5 z;_;$yO1P4HpQS4a*>I)yVo*eRuFEs4Y*ZEp7kdNppL+aF-XJ1UqaSr0ONI zcF*{?&H6u5wd>A}u9c`P9d!k7xOb2%L!@w>TyBx~Ug5yPv%YU5o6h9Gx!xFN3xRK*OUN(K9h4D< z3cAtrIfEAMfaJ4ym#f$oTKVo`<^9#pyJ?;N_rl#-JK3LB8geTuT?hz3$zH)4#^{zA z$s4w*p?@ycGL3iIvbqzwF_09_CsS=Hwp6F!K z?f143hUAo?YD#F<0QB=b+FJ?i?-HmW32?rE+E7q^k5ikJW3Q#sNQFgzZ{>(#dOsZj zL7JqdHgPmh!-Via`z1|^#b}5v1_z=gGa%7d0|u;+KBVJNzjsE+u88chitI=5%RzFs z&`?m}XaLoHfzg^T*YD9Fq} zs1D}#H$FW@(SpJMZ^rWf1EKtXZNG$dDbo0iOLrt;qwysbtM+BM6IXu}gOuZE0ax5m zFeh)=w{%yY(PU<{y4q+%@jy7lx+7aK>8WzEuzY6IX!6(h5%PhgV~~4~r{9Vv3K{t| zS4ieS{rl0k13RtB1i0f>HabU z&Pv!F76-debyEz1tC>X;%LL-GOe{m|@3tmOGH*c^W0RV!v&|y*xxEi1Q7`YMIlDPO zU1Jb^Y^QCh@N1nmQJe_--Cc5TKEE=T=FzF^*$_{z%ggvArnBYA)!}*Fcf_@mpzn+( zT5GGaskVY=-g%GP_oYn&_x>?{)K3sB%L=uXNng|O=_@lDhTE|HzhuBJaC1oB5lTqCuHZO*G`}M> z^{Zn-`lE$o3}#Tb&{3)z&o6pZ!u^PSp+&}cHDU3g@I|eft$Co=5`z}fbJl6dPjJOK zOSEa+@VAoaDkYqlxfc;vjH<_n=p44Rv=y7kO}Y|2+r0V3AGR(Buv(Z8C9RAUbhzkW zv5LRy&uXtT)8{$+D%!2oVVzVPC;Qd>)fLCKlBtDzlH9S0uSx^)lmB2>-9_hGC*r0= zSW~k3@jkO;ZQ@_l=vB>5@vnc)nKzn$VUFy7Ka@JuwBG%1Bg37jbjk*Me!duyukZ6$ zB$%dUr|mzK;#w%*bK1l0%>Vx38{8kMKjrHUA(tRra>wq~6yF-AbA1W*U0?R&>RV_o z3fbkRfa?q=t_+6W?n?UnyZ0%+_?fFs?7b+WG}DbNTRpc58^+OeZRyfu%2M2l&HiJCDEjt%d+X+cTN1D1>osvFsfnv^l4*8=o4;I7M=Y}o*``H? zr5=^&MjuY!Y8|+9T`Pn#__82t4aslQ8=2&+&BG?V{|XD~`*5?Z5%DYLhrr^`aM3C( zTU#Orx8zTv2K&cvi7PxmZg~kzhecpAXa$EEE~g1ML66CRRXhz3%S@yhT>(Q*DYk_$ z6-oX#!2FiVGf&iDr|NnQGa;{(G$BLg@X1rP``NzF~6qE$K>+jALu(j_+%LPa@9 z{B9BmrR8sDHSd39J&!Jb#edPAe^d)I3#rP(6tO$MxHoRmukfj=I#g5*l^AaU6=z&L zN^)x%xBO~*(#hQ@x~9pb*#lqb(oD3a<-ji=cNn*q#6EW)-ToM%&e(FpKGJk=<3CS&eGsxVU1AA7d_~1p}&L|s~2m>LyXN6?3M1cRQE(c$}BVFZqjAS zHW-~(EFb0EB6UP;K#Yqw*Vt=&Yy6DJAEBUhA0$IJ%$GS<@XSv>)tSo2%uneG!`Ij7zb zdz8H#nH5~Y;6+mqiGTDqzt8UGj{KDOJR&tY+XfT9IJhz>#^JA>$$r(;X6J<_&nAGh zYtKG9GmK=>M#CgMu&8N?urgKA?Ymj$b3}PApNHLvCK#EqgSYG zD+hKW8TegKQ@mmHhMZzE;_a1m#qB`wJ=v_K@#DsvBjqhx&gFNHf>s|>$R``}VLrBT zd}0t++z z()EIU>z3mL26agW-AFsM&zUBWE9HUp>?1l}PP(w`w>7nn_MkbgG*ek`6`FLn8cQ7SkixrOjB_k zV@A}jDTSH;1u_tBl5;p^%y!-4&?M)ECjG5nF<~=a33=gb`ODw0mkb?&CzGf-@YqRT z&;!9G>I{DSU$FltTIx5JZ+T?@_iR-1M4|XlAt7rPijuz-o$>V#dGP(9`%1$XpE-eG zdLIz|s(lFJL0w4OI4MaP_SrTT_pll%K#pE@j#9kz>|0nnzI-SnU2?f?!tJL!ctiag zcQq=EZT$~KzIW^a_RmZED{waPY$a`fxivk%JoB#nK&x{iZKl}8lJSj` zd%1oah+CdFr&M;du1goRFDK+>cH@xiqdepdFR5_<{UOT_AX5fOxzSXky~vc^zQY6E z5FS{(-&@)F2ze;; zlxOB8X6EIcX-*!e3t^qQRJ^&k9Ma?0?@r^k=JM6_Md(co_3b914be#U>fx zW*td^29dI)@82=bnn3>P%Q0`?5q_xKj8%{U#Z65&B-3jjlVAq#*X70(sdXMYCh$9_ zuh|V~_`@S2M_?~ z7VLmtv0|EQQ@kxf-k%4e2f^OtAt^@)Gj@J-T5)9>ZoNRz*5d2>6K@h?LKKe?w)4bo z+38+K@f`cdo|r}iN78P@6D1O+$&(|K(TMUe7Gh!;Ydy51pSGztfj$qUa)u}|pvXFj zrIU}FZY7Q2f>=g^ceWBmHsb!2Jr}Y|dcEoYa2_vdkN!X|t`y0tAP*Q#(<+;SN!BnG z2M{j+DFGpa2`PSU;qJpucb3TNc_X?O!P4M(V@5|ZV^@hV%bWp>jVA3AldwnfXjr6? zbq3%dm)5FIYfq7)Tn_qA0!-p$b_h`Y@^E8lPeB<_=m9|l%^-gVT1JV`yWv6kPwn^} zoFhu?#UxSUqw(Jj6DIJ$N?lgx8O=MAF}W-m>O)CkolV?NXUNZn;xZDJ0M1_8$Aq*w zQh<|}!3KgJUyrcKi_S5H2baC^Ckm$CaP#Naj5mc_tu@>8@QPl@CP<^Rd1lhPGSEK> zm=6w26N0>AhG1qw8c{3fUP49|Gva7Jm^?EtsFf~O&ap$owL#uJ>Z$fSNs4HST&duE z6fT!#26_0CDLyhAmYDndAeY&y;7Th8PI6xLl0Z0llq`kcMrpYPT)iH|Cu+Le+iI^A ziM3>)#Y_Qd=3L@_es4QEb_sc;g}KlwK-K3;DT556n65Iwm#)Qpeg(=7AX*R^>6RWL zR7l~7*56NL!+Bn>2o*Z^w+>UMDRJ#+Kz}I9pDaUrjHN^Cv$Pd}$^9(A?;o!IYBFD-bX%29HsQnB}=)OUQ-Lq|KolHmS759|E{HE|s{L z-A^HpVFAZ7RM4?ipjYyWCiY8T2>}ar=otg<_;W_@1k&)WvRkP9l2AY_2ML=&vb4w$ z3U9N{X`ToHRvieoUa-0ql+D`lFe1r9&b_InFu9e^+YBI+PpCBm)X!;_@IbRR@Szu@ zr$Ec4K-*bC*JD=O$5Pd=P-VuFQr=teaJxX-Dv#vRKC*g_pe%u~B4)L#B}gRHI*Nz; zaesXZ1=>T>(NEnEOV;YsF>W;)=k?!(%3$?1keQmLyrQ4WHCs`nlg(t*Ov7)|=uwi4 zEEsZ~m<1LfG#+nPo-0(*_tMyBko5P`(ib!W zu88a7tqT6NM?&Qx7mdpP8IKbv%keo8m1%aq&HR#7#5@{7KBuO%de?Ifwo1A~`ASV| z6op&mZ*&3KOpBW$S~Lr3Ti;qi3R$xniXrxWaZz*HGvKs19+U!XNtfl z0^Q$p8t??IxCk({!Xjw^psKq4cX~f}w$h4JdG9xVb$e+1wf=`+&CiZfht96BdNMX^ z3JEq1yI?RyFhu5}a%H^Zoe;om^{^re!xYtZ*ohWe0fe&N!-C0Z-O^b$2I3_9NmJoF zB%DnjOhFcnlh5^`EIUSdsM*3wVq{ z8A4G{ZrcaZo`K6GjY#2!p=eTd3nC~b)G9g3a}f5tYJyA|%ADWBFf)jo(ATsk*G|0q zDi2-oj45^DJ!FHN4c(u`HGx6HdFtaUiY9Qq1FSQK{NTyvzH!PB#DdKP#Xyq2yY<7-00wGV zG;Q_FcQ-PaVLw^$N47g3DbVjd@j#{l@BmNi=tnTB9Q}wrqDhHhk`e{z?*O+}fz!)A zyTlo2--LEB^13RDm{2L_j8?P+KI#Ib!KG0*9peo^cG?8FKpTKXH(bt*#Y{4wXZiCf z@m%=hY&djd+UDOp#mp21`Xln-BN>kDxBGOD;iT)Nnw9@2NaDS~8;d;u%L4j8$1&J? zJ2Etbh~~3=)mJp!*HSd=3kJhc;@T5(z?-3XqMZ66d4sJ+vH*;WE+gs5*c;YINp$Ik z-zMV4thb?Be@Z5Da59@+a&!W>L)DXoJR^2WhBIyl-c325vNKEB;xd1Yhc|nB>i;CE4tDL&(2^Yi;@ZS5SpTo9!v3PLX_4mztp2~G= z>fzbyiPnPD8%skuqchtBMCi?jw|0*gln+@6PzL+^s#lVqK0o_*bYf{Ta_}P$-=6X& z&mVgGG|vw8-p(q#KTx0`g#O|EjZmm|#Oh0KJvt*(?bLM4i)#?0$I&yfb89vh%0*vn zAMSZ@$FY2h_=4xZk}A4=9Jp8*euNhMl6Y68{Bf%AQ-S6CSRwnlK()=JB1hZnt+vW` zHs!pDT>k`eY#-lDDS6`_^5dRZ3HrXBHYu?&w&>Q{D#?hUXytdY+mxBje^^k?XCBXR zYDZu(Y{wb|2|C;@Nc1x0D5P@C>o%B-k7pJBYR#+E|S^yyfd!$zvOsC-e~@b~c!QVo&un zZLY2deWS}Yh@qb9pt0vYk7PgXta(*Ch-_C4NR@w?Y6 ztHw!g;37lOl@+(U3PiRCp$^wr#NSKJrXCIoilS0xsVW1uW~9T%4@=LkBV;D1e+V3E zNJR@bHn%^s+?iB6$!u1Wh3}=W+~ig|T(B&(J?_k`nDiWS$|RQ6sha%sfIZiO7Nvk{|f z)Jfa9#KMmCZ);caJI&q}xZiwWn#+jOusbu0G&64%a?7MjeSZ%;E>{}1qLux3$Vl<% zsQ@@qU{hCvEi8&Y+>3XS@F?&i~zUy_pj*u|wsda324q_HyX-9Z&tad|@t# z{{-iDRQs~p=|3I7M`^q7budL!PP5!e*SU;%;3u-teykiG_HY`v*uN(BwB=n+I!&;* zduGeBr>r`-j!T>^TW!KljQXLP@AYSfl($P`#F0-DNB>l=$ht|lqij?6_a(Vz$~d1-Eg_x371M!QP0Qfn%U zWppwH196jv>>SAr8oUf!1V36sQ{ZdzKHGc7P+d53$^5m%{LvucPPc+nutcq zw@XUt5m->d4X>_m8Y(7e)~Jt(kzElC`hv<)>AxeYvcC#gY9;K{Pd+dHY~z0@fHoYu z%bmGfEK=Xrgo3yQ_o-Q8q(2gj;6+|7!cv5`{(5h2QISRi;yl~ZU2VRb(w4}^M+ zg>Hnar@LdfEWWGm{~Tt1Fp#r<2~(F{{@~q1TWFO0tzSWlEsD}M{#;c{%_!=_OCKvL zEcq7vnboU|`IHjrWUhF~5^Vljy?ibogN#Eb_rRqNAd=XDRAzk+h zC1OuSWim}t@Pys&g>&J<@lE*bG?soiPxNT){^t1bff_Vu~~>*0YJGym~+Xu3=A%z`Qd*O-*5dBe|aHn{)>8XU`?j(v24`+;SQfZR2aV^ZSb&WV5f5$42u09;a?6klcd>-tiQ@I%Mw43?!fv-Wa^@=C6~ zjs)&61qh|%`gEz7&==&UTpD_fi5e5%Fj+ptUS4X~DBQ)OaBFk9OiNB$=;)K%jwy_9 zxBAi%Io7O)Ab_(tI+G;nAx15Z=)>!h?R3^}HTsOfYW}1iWZO*&@wP0$3?6HcqeOu7 zxxa%d;xGE5Etxz8W8!3z?JU1(jJMAxzikW_&2jkrq9X_U`H#+v?8hQ~?yr4RG??GV zIPxQx;ZF$a_teYgibl?t7azE1x2}GpEnWP0H?6Ae>-mNi4K#7EWF^-d$JYvch1dX1 zjfSz?97=smQ&_mzKvOhxuKWsU;W%?)*Itv=X!O4tY4d|BE4U8dDop;b56Km7!S;=L zMnWAyvPD8Cvepj&wGHQVlQ=!SOfy8EA*sywze|7dtV;~HWKq4fb@_2#-vyVgWfrqG z8^U~17#&biIaq#l*tv>`gv%7U1@lLMseXHgjC|kOEoWTVHak#QD*UTE`125fDOBbXJ^v0 z$CDMsefBkG?=dm1nWKH@!~ZM~@dQvsh)KjcL|A@*a!b*5LC?>0HhW$3xW&70_e=KQ zq_j3=G8V@{@77C8mXGOSpb%xBe$`cY)JwuXr-5&U9s8e1#vtfxn)&g^xWD=&X?h7R zcB;1PJIfvapRH5JzdCaUK8O7w=9p#=x|a^HGFq!CGvU)*J}2hvwsbGL*Iq~;a!}n^ z#1ALaK!B=s;=k5_{sqv4Yyin5e1#xj@c}#wo>Qg1A7ukSX`qCLU)YrfHaxKWTV`r8b$F}+*r0~HAP*9zfS z2Zy!khm#W!BLS&IeovY2geUt@D+KDk0L5F4V2i%zOT%}U9zfTnp~@2E%KK!utY8l$ zK$TWeRdP^O(zs{Z>*1+a3f#ML_#xUZ$TW>FrZnqF;q=d9wV zq-^LGQ382V2BLJ0zh?!y6&ZgL5kKz=owq{4NjRM|RHYpB>w0J`pMwrx2&NU7tqX$W z2Qh9Ww1E?0U`Y3zqfL^B4nIgY0ZEsT$h;J8QU?x5eHCI)Sd@FiTkaD%?34B*W@Pd1>80VqD~XgEa$QXuavTONuW; zCT@kNq$f5duONWO1X(9|d!gNkdOxXp%2f)ii!3GDF)pMVFaUpC}(~ z_|v{A;=WL*GWH-$2OTcH@s@FrN>c-++Y1pP)l=5t%??@P=`UIHvQqfp;Iv|Krg?nr zuhSMh`RuYtwVlRxkO%>)oABad$Z<+KhbbyQPAdbylzvkyBgr&gQ?LNOkIKNqe?wF2 z8N8FijLXy*D}Dl!EQP#-dOlyMb*(6f1n7xY*2QSJS)1F(`joO!u8K|TlT82?$TKwA*umq)!to|?xTwr*#u`soq0 z&j=rmkvgZbFa?rbO9lFn$#agUy$IqyInj!OG$cJ=Dq^pTG57&ZC1NA`PO^B0? zz5A~pg_oVnXC2EIL;4V>y^cm_G-e&el)c3d$CB@XO6JNz$_^PT@&$`{#4(;^pK^4J zvV}#u7E@sS&H?ET;Nj0y@P4H=IG1E?u4$suH2@H^UWIX8hMkQ!P5YGC@g zkdSNZb&i2;rT`&eq8B-2RyX2T#fO6u@gSIT+38RC(S9{@ENOKtrojN-fTfxmj#)L! zW~fYS{s})7tfAhhQ%D3;=hslKH&}Gk?DHq|^HR8}h2sLF@DMXp+ z67`TSQTUn=Gt3_yu#6ON1EopSPb)OsH*dbPod-lUWZ!0dYyg(rX%=T~DN(3k&a7Zk zs7=<2uXY4!NPz9|z&WT%#lO-iEgE#KZoR&ay1t3A9t!O(Y{)=9P;7f}Q3Q~BI(5+9 z{F<1`;<(P5U*`?Bl@*6$70-lANQN$0e(jt@^4oT=XOtdIg*2otT6YxY^*R7{trOU2 z3ey5g0d&wxRYOMFYyVDUR5O~g-ny@W$`DM}NAq)xCU&JcM!01+vuoe4QI0C*zg{qv zHF9pDmgr|&{1@$~P`6|blwSZt9e^8=Dcov+fbkZ-eCbz?3NsFRI!U+e|t^ zX;u`#cqfe$9VrCx-UlwRU64fZfH{~p+Tc^Ivf~|6 zlrcyJ9K<_txbUn@+)7|@A-LU*tHWA?KI8t|a zU~>hziMwV0VTRYsjZRJeLWaA2$>tD&U$M+o)T?Fe%wBqrCV0CKU#zzB?6t) zxKS$QF>z%o!Hv#d;jZ^Oz3^wfiNfu>6~$QV$Qc4v7vOGwfM$^3(9;?DHa-B`82GRc z3@ZVY2AGm(nEJ||DAvwW{OFw07$$gZ0NhXIhOk9q6k>)7%=$|xY5!-|9PRwc2#d_? z^U1NZPC#ame?$P%P!u*pEf_qhgYKOC+)5Hc-D;fRmF)Y$=9Gj3P4uEGq#bE0bDaD9cPceV6@D3*J&HI^-U(u#Wum z3`IvYB%#Ps(K}2}_h}UG04$=wC7UDGkc=MvafJ=d>2YBlPv}=0k#5@(XipR;UdTL}CKD;0#N> zC|9rOGfaMAh{9SMZhG4i^V|Ta+{Y}}JtQfxF!kiz1q}?~(XR)Ke992_*){qN5FZZA zM7k;=jXhCx%hlxKU@$%ynfT@Nn&`U3OtqHvD%AQv=nRZXc~t?Y{nHW+xNJZ-V0>#r z;sho0^P*6xtAU;*Q4LBE_YRFcTMy{nME zZ<=we?goILEZ`>sz_0+>I$-L8-Z6k!=jpWWe+yJc%EfY2r+bEaZ@XyuQ3XR06lr=EM4q(^bW7;4gGgLhp)a{ z|2Jh%RJ4gWXs5G!$@CqP6pTBYd+_8gJ1kk9XHw%o!W254(F%Uo9tdIZLL`O3(CXF! zSiJ?h@R}!|HlhOl&D*&0HbfYR36$8|1T$o`=4gCtN31)^jLDfNxS<7$0_6QpzTQz| zAqkThXY6@mcSntiRpAPPk7ItWw!BFT4IRLV-Dj}ndG#5V@jW(5zW8mb7dvx7wp)-< zi78$F2ydG25aPRrRa7Z+g#Y}{yRp|{3Yls5k}~-9*q)|iRb_+}%=0;|$BHs6&Fp`s zrhYh0DiY63*(`u?EIVlD&M!E=q}wFQ3gt}qjh34$JNm6v)|V9Jrwzt3y{RdC(Dw7q zM4$bxa&1R5RmEi5;g4HI%i%vsAMlAq(^SvAb8F+>j@8PDsFZ4>u8Owl_NG6BHR1XV z+UPKNWSNlO`X51;PO**j27l88UD%-2B--SZ89p8$E!_*`jVUx(RcE}ym%t?{jY^JE+X1gzL zPkLqj84zfV0j5xUe?(?!B=3k-;Z&iXS~SA!N29_)mCl3lL4HjM5AXR;e>%#aOjv%@ z&&Kzj^WH#)zMhKSb*kK6-erR-D_jGnhK7*1kzoeigrqn7PvLX`#`3^h?Hl zO#Y*(oLDt{g(9R0JN0`?SU9RtqaC`W->QoB7PI#bR++6F5LRY zY#U7VZRsBalj6l-j0^JF*e3%Xg>VCaXYth{f=r_n68AfV6TGlWcp&%Pdf;^?YyO(O zF}`fFT0Lqu33H_!Bsbfmul-y>YH^UhLPWV#;{s_( z);IGw5K1wk^59#PloF2$xnSBO12+$6HJby55XFeo#5iaJ2lx0EgD|E(8loFH>vgR| z#_UP5n`FVBZQ1Sn4sLJ!}ohHGYP+SB)h6_mFQl)8H3U@ii0dt3{+%ezA6Y^9dAo6w`;SRbs4SyN; zJopu6%Jn?eCB?heSE2oK#8#tU#o4g4+j1=ilZK=c(+zx?3N48*C3l!lv&To?;@adc zCY84n!!NY#FcJ5nDSaTOlqS$ekHjwyW}>G!27FZhv>$TXCMaW>DyGA)(-p`KmZR8~ z5y}aDUW}pfLTH&CCx}@aQ3;5p9asCE-mCq>a23%vY50mDK zO;AikvYw43rp{cGl~>~`op!jCsgEi=uo1fVie;wlnoLptchN`k<5qED(qDQP_W78) z6gMRkULwC?bry*f91%KNiMl_ye-Dl-rEsL|95XV|z8ZVb(;!_WiBi#5<&1b6Mhagd z9Y8iSv0{gLu?JAr@#}S8joe?Q_C1yLX8{YJj%DxYIw~SCjaDpeie{I8P?mHco_WGu4x6|YFi_crvPhcOrcceYmDJTX#N|<-o^`tJ=$X!FOd5otx zTy%a28*ry|_~!Wh`O}Sq)ZT{#X4CESwMXfNrXIk)SBvqo|N=(b$ z^z9$-RJS_a`j&3d7ZX*_<`g)muqid{+u;!O*F)!WmF_>uZs)pZ`CqfPXd-&acrM*0 z#QyE+{&+L{DiW>K;Im!-_Ncm^`oL56^KY}?ejg@RL#UaqR#Sh0C3tj?^RoW+sl|iH z@jx)#<8n(YymM20cO!me|5r#_SlbEpLHJ6*J~g6if#&%szfvDyvPC8#_xAU&>f5h0 zJ|J?X-V1uAye(z*4*CDqP;S@)%8_>(^uO{0z8QnjU0d+aikxp|?#RzaKalG%diQTu z{PMR94V(y=O!-Ns;Er$WzdVoBJ3Rbxb1(UkvRo!MkfsX1cl*cbcbKGp!l^mi_z74A zL{(=6;j4p2)j^?n5WPYHiXgZIaHQMee} zjmr`O>YxZvFmi+t-%BdBk)E%Od_t)rLXmAy3J?|NKG?{>u};ccB@}on0Q|@2C%zEq zV(+((@KHcOkJdxP$X{)S!%XV#1R8kAmc3GFd)58sRWc*k@r{3)-RtWFIG3_r1ksg- zQv$;08XBJ$n%WlnAR^MxC(_I$^sZ)Tsy&2zA1p>7=VoLhWoU3TbT8>i{pO2~PQ zKR$b;s6>#LPk4=8&=V}xjk-HBTjA|?5y{3eawaihJ`rc$zU1qaS`pR>BM8H-$gK2G zD!JHsflvdv*u}+I#D3^wds~~nD3}|R)*4kh7r}IZ3@Nd1KMo6#^$B?cZ%dE%iU|J^ z4oe5c{5lC55bzxm@Etw%kJR)Z>y02vP(b=XOm5Z@`{24f@_Ccc?2%aG^w+#oV7w0O9VPGqq1xpsOlJi4vy=t7P;<>hou7#`_NjVju_|@JyP>r20nFwp z`UT*uk`hLa3kl<+XvDgQFougcM0+wNA4kOh_z}HI7jvCNJJa+HwU2NJkAP7iHPe0j zJZZSGsp+H^*w07K z-dy=)wV%C#E@i!<$c{CF)!{+J>Ff_n+4tI@X`{K96d6~(ssGLhb)@0O60X1(*lJ@} zJ#;jEYX6lXT1d_vHj}yaGqb)u-n%@gpFB4x@?{`_qNI*Y)fWI0NWqUas}W4MF84w( zPueeA#;;IrIgzrB0O^Bn*rz?{%Mm|7C2)JzjM#5Dwc`6ZzbBo8<*nc zBO_K{!=9{@JpmxpgaqtNfw~}dohu;MQ3%Z=C`Oe`mX|DSl|1VpxH)DcBq6Lmw98s( z-%O145}=!T*Tq`j&@0gQB(BWPg*+Jfe4Xqj5qPB!6thN!cbUL{Xp~E1;c-zuHvx#^ zewMHwu;L2XoTHQWY2sahXHg|iew0)r70(^BG31qrKPwzqGF;3mDcb-}SK3P~(0xvy z%ZwSSlY)j*-Q#=OP(tI>o+Q%>NLzt%Bv3e>OrNrRUg(WJfXIp}pp&d1P8Z~}R1oph zoWYf~Q6)7(!8*MaE}1os%Qcjml~vl6P<$Tyc?sf)D6AXh!E%K|M@_w7r2}Qdj#=$r zv)bnEiocl+;=;8@V|1W>%sJ7I_VZF6LyIPC+)jNtE}jn*aSqt`l2bc@8P>_>&dcao zi}hXWAC7^*`{lp}$yb5KMpyn+s1@+16dtb;q-uOP-XLLKTQAgbdQR_M2fRC{k-eaC zzQ8C@Vdh+4*k)*)%{dN5rA5Ip>KApmye8U}Chvp0p~(ASEb}q&EW8hQ27$H|Ww8 z?yC9W=Y}WC+AbbZY^FYFBLzH@oxqeo?`dWWwHpL-(EC=Wn>2l0_3xWs>0Z!jIO$;D z>I86?=;u0E&qJ!tX%@kb1hxwrVT*UiJ0J#Zza}e0CZ;K8j66ny;$0XE%3cgW4-m@dFDrZ3$evg0_&^lXqZfYKtSk0>!njvj=A`*{1{ z@L&kex`$?_1C|eR(G_m+2Sh~x;fgk|iTZn;1!GhdE4|32Y2`LIm)vU49!0LNnbqyf$NdZJ2y8!c7)*dFq_qYQl zjraAWI5SWQ9n;xMBn-fn$7l|^+$_+1251|$Q4=ckQ37Lf4DeO)Jr_DndfGQ58>4zj z)3OVEa7E4QqC0khVoFS(2$1j@h_wjBxlK?@l6{*1Dl7op1WlDvng*l@DLMWic%Ti5 zE{UOyBG5j~yCws|FeiL?wo}PfUzbTc$s{=m37$q8l8)1-3_k%b2BmWc<2GOzT`-t8 ze?kt2x)y@8RzOo)0w{~A!U^^Zwjn0Bk5n_0LjPEP1dQ1v!sXBdFNv4H0`O65cN&<1 z@hXG=8IluwA=ev*L{z3rV|wLc(Mw%yGgOM31^_9?mIIT!7!Gjgd~Ds#YZoC!hMZzn zfHS}b@#j!tFXL!|)7H{<$dE z3My+vAUozJNb}8okXSy7+HjuAZB`dIC$>S}`;1cvgwEIlBwQC2t-;C^7!aXDP#NZH zJp|yOm_S+|ys)1ofX4#t>r2QD(oX~OcNZ-;ex*no=^nt&Vm-%D0ChPiMI=rEU~rBG zqzi?ac7qh`%)4S;6Q5<(d|*{z|WLLP%9tDTwrzPc9G zH-o~#eI-G(`PBY65G)b#T~zp2HS<6q@OD3#0Wyb_od37#yZD%#U9%IISw>u~b8djB z4O8fFW?zsSOfK~9RzPgR1_uY`e}{CoARlF6JkOb^iYX;=T~a>O9blbOx83L zN{?Z5VJ_Y%Y+ktbL*d5B!`L@=>a{NZhogOzt&Bv=E~Qe}gU`&5hO;=D{YJI~lW(pD zoe!R_{$9wv!6*kAPR>DL5&UkO9J znBTzTcr}L5jQILbVK&LCV(r|isH9;g6K2?(>_>uM`18!P_+U?7v)}OIOFa3GTY>6l7jf!o#r&Vl6Lb+JRlY50aB@{`KkM9zV@;oo9wS;vG3(OwHcHGS@EbXAD zsB-8GW0`X*eevmTZF!{Zcb5anN}h@kj*RWPpWhvJ;>VvVZUuj|@wdOb!RM4(zWggv zFkJGQ&X##+Hq@?5V1_EOJEBLhw4i7C=kM2{F2XfE!W*oHUEL2%YN~|90yf$?n4cvK zcqv#Iy$`z^Q2Vw3b9uM+X1%txSnN=%L-+Qt0=L`3U$-S~86G+P(*E$?ZnvL@YP7*o z6v6O!p#Mt5#>Ty)NTN%)c=7KoA)4L)K8h>vUDHlWV#?{i>%E)QZ7Tk{w%mf#)^+yX z&*c{j+t;3X)K`3GV_Lqguhuea6;pFYGa*dvXc!&4DfOp9HwLOxjZPomQ)ut`z z#982-e>dy>H7+OH(+!yAT8;VR+TFU&uR}Riy#-W7z6PL9=yGh*k&o_ z*y^J_Xz=rF$Q?dd=Yj;SFVY@=Xq?Kg?Y^+lIjQpZ6Si~i`kpQ&*O=c@nzQyd!7bPS zi@2|T@@e@&ThU4_V^8|vEYZ{V0+v|Yv#L3z9~f!!E)Wp!>Sb5-{u8YUvQ)Vl`?JRG zfMq>?&6|G}weLY0!#RD0R$hCe=UMev2Zr8!GyaJ@=+{^3Zn2ZDP04T{flA6jd_=DtwO3DW+cLBM9=#UX^*WOC zl`mkUGL+6#MI(kP(V@Hwc{vrpCmvKk^i&(*EsM~+UjKR`)x+ItK3h}fOm@;Wq4UfeZOmS+?<{XGDh+vk8SQ3GN1%BjK@C4ad;$tLB=S( zV@^)!2ra47MM*A<-t~OiRE)R$srAfpnxfh*Ye~(~P|!v<`@x^L$phbHxF3+ZB#m$N z2OV$tv&_{r(9v7{g9#0cX_Zn(mPtzr-^lP$3;NSoz+&`+bG-a+@Rdj5@+YHarpv@XM-A3{BKE&|%;uu5l}MW|KMvn9afeG6 z@`qMGlZrOod~w{8HMsY*v~y*#i)D#QoRNvH?2KFb@FXbFz7`}_s2vBES9zD9GySotw(x4Isr4ou@f+p7WgNoZtWZ_ddJd*L_{DmqP^7 z-=Vcsls9|4YPL5sYv|F{U*3O>(8{CMul-(o6@01N3jKPx*!%ASKR5WbW3148thZ=r z#?tZyp6*x2|3!b`L+FKlmG@|FcbAUd_4vY>EQ^#l$xBtfR~1TUG`9PH%5wJd zbDg4SC9?3Bo`h^^^##bR2fkQZ+kdWH$?^b&kh{QxQ=vV?5DrT_!obCPD$7i4aa^sA|ZQ%mOg8Sk^(JTDtNza-zW@mp}D%Q1S+#clo zmvF}*f_o0oloR|J$?@rUteKV7G1M|zyZ?R7oX5Y0?ZfjG7Oj$&J0 z294BBZ0aW5Aw_VfPG4A|H^+#nhY8-ACpY-@?=K3i@R_bCEW!m3BG~x#JwuPqFPef< z?i8pPhK_G=1&|an&D#9#oOu~qe7c6N@dY9`a(;FN%H3BW7_-+y|2CQo68jLc$0h(Z zk=DYkvV7Q=^Q7zIi%Pob>r6-8pN+LcukEJ#{K&$2&B-zN?ih$Xl5w(nZU?goAgn>? zwo$L{3n5moL7$L}l^5_>DL@Jw`m__+z5t>&Kb-Ibgemxq6~3{N+gX21P*#C&vV!5B zjPnrkc9e`E2GPj(36y&p{`byTxmo$%AW%ZM;wd+?Ok1qPOpUtNzI1ZA~1m&QTm924?R>(Xc z^j&3N!hSTCdJBeta43fY+%%p#RDEuAh-R7V>=5A zC#I$o2m;{Fq}VJ2=q=~yJK`)F)1YbRuuA8Uk7AMcnG#F8?w@4n=~loGm=e!-1x;zP z*H_^xNkC8)<9lb4!ax$lED0%qx{?AW2*kH+a+>dBJBT>M81KK%8!tOqWIF>{R-w*h z1e%Osr6%Msg*4n@(qKeL4mYqMi08fowgbY=_~%*yuL8~vuV9Q49HVE4l}oI0j^wfi zq+Eb?1Hu*&SI?AOCl34t2!3=jzHOiLhzXZU+`&opExSQ4>5p@C{-J zClbP%jM9)xQR0Z<Dull;2z0VwJH3zXjC3x@3JFMdC;sl*usP+1)!#G7Mp@n=}`voJES3{setL?$sH z$4Mpgrb`Hc2~FL@y4nE6v3NhjG&aXvO)Zcw3K!JP6TZoxZ{>&l7%zRtN7gevV;{?q z@iCzC(Veko5*g3uks17q6<}OuY>KCKYAG@CWtE%$SmwO|DI_kf3YBI(h&}ee?-41Y zn|QTupnxu5!KNRK76WmIA>cDXJlWaj_z{<;^ZHc^Av@`;6u1VpkVbmM7JF+fCkLdO z`P?OB0)+nJ0F+APv(Dz~XeYBZ=Zo#+S{7p~%e`B>ZKOH9t_*<8nxOYrf`4+AG$J#S zVo}+ekB$vfrnrhkC5jj-g9;Gy(`Mj1kr)&JxtN+p02iN}$uGcX_ce)zHl?2f3I!*! zlTDvG&WMm3IPl$j8RN+6Y= zatwXL7~+f71Q z++cvPB-i$#3G;>2rc>Jj?`^R)B3#tOvGD`a{D5y0pQ0VRgC^l^?#WFRfvYTp4O>9V z4p+*BLm1;A@gkjp?P_)v-&R}Y2Hi)DE0m3oagJ zam4|6uTP-x11caw$rrpWKs59)IiJ^Iywt|x*!5>1`C}{OX z5+JNpZVC_4U+%G-09tG&{S~}hN&vyO%J>0_d#3M-nNgev{&lU=WG~)w8|xd7CzDx< z?D+l}6F6wR@eY6w39Z@e12}d^=yzV0m!Li%Mv?$-67J7`c@VFe8z7kQL;-CP+B_I9 zK$|k_kM{(hty3D3!>|oJarg9=-9h(s0sLsLJr?Pi?Wg(7xW!(^jEfWd5IFJ>dnkwd z2O6etK-~wREX7Z)BJsw@Sn|rSt35F30pvD~c%a>rct*YLN6T)F`u8yci2pxD=l`p4 zZ@%+Cg}aC2)0m-?l0$!}B}4H<7X9Np&8zz1C(fKkOQe(;BykFbc>A}MPo~S>&`!^h z6F!wDZ#BCtF(+)ApsJUeuus^am8b-;>N;O`87vaY=Rg~JyUxXNw?gh6$cw+M6!Q#z zya-6>RG81D!k1D@F2?eV4zka)*V(lDZ@s>m^G4`$ci_<)T)NZsI_)H(_Hhv2b3OZ< zpXxgo&z~j(3S2RIT*RCDli54wuwXNap6$$Q_fBa;Gg2DsX7m0mhSY858u?Ws4=*ermA|&(%@Gu4vOV!55O<}Z@td&pJA7-iE((;hxLy=Z5) z{n%*$9j(<`xc)xk2oMIMu`ov!*EkfrL&Df&lSrsDtDQac>n z?MmD6-+lsJd6MG9qTeZ#E0ymgZ@J$uHR`FV7c2fE;3L0f_x5p3MV^!AzmB$*zNypR z+j>k-(znvTKiK`+Hn@>D-F)ioUAcTTOPRbD-!G1zhQD%q<;qNJUdec?H}KwRtJGG# zJHYRCy!q^Njs2(o;f-Z!+7Ro*dV`Imx@`~r_&m$maJQ^8fyDVSGdhF|ccTefoWWXLhf49Q~5+kPUnO;nb~B`<>_e zr`b+#1`n8?-r^ESs7uX05<59UA1mM(Y5yshy3LHcPTk=WpeJunwN~_n_P>$Vm}paj z2mT(Fz3%>}yR~0v>Lejo{`LIphTB<|O2_KISFNt{?tW)M(L27RDW4CmkV=&M{4)7k z_pXe~J3VS;^I`kVP>+ z0JH6-hp(UlTBkO72%=?VF-J$HfS<}-qSJmRXBJ*uR_<7>dlCJV<=HQ~efD6qxQB}_ z?+iRGJLS4CjG|lWXp+F#g9V#J7V{JuCwx-Fq+XOH-CZNh;q|NK%)F3g#98jDVKp;( z#z#v*BA4LkTE=qgpdwQMSpX`zgb)~^NG#O8RE;8np3 zdhbS3F#_*|OO@4F*P{F%UN&csw>CK4KNOqCR-$b_P7@U{iGCXX7V?Dn6xRGRy+T^V z+d5#9D|I`TW2y{yD`vd^PmA(s`nXKI#5lj=&&XSk_&C!>32P|XoZ|)IGd|rH1$^d` zqmOR$1w~xE`j3g-wYdyjZ2i1H-Rx1=&5LUA)0gD`LD@Ie4bO>~(=REfRuxj&tRz1w z8m@Om+i!qHlYN#XS;MUp=4(m?;P#WGFhGh7<9>2}NANW4gBnM`PU6`NbX^ z7@0AMP8%X+(Kl`x`U%e1`o1ja_uSG(e&)&EeO|bzRwDOp)3^_~Y|r|t+TdXgQ11G> zB3P^JEOK!0Vg)|`fw!wpo6&_Nr|4Rkp{RA|Ay5I?u3PI7&zv$+)y+(+7xVnWwy-~t zG{|0q9HyB6Qb`hxT<2Y-YAPdml0*}9GU^}3+Rl7tyZes4@q;wwdJGWBckIAkON!$B zz#rpB9^mY!>WH>A#X;)AIf@ymY9RMb_}JMjefAA_M&RTqUAseRol3*j0rB^(*E^S+ zMwE)S6Mcz(T~DL5)o7q7KTR;R!?w04vu3Jx~OHdF{=4PH(4>$%z+f3Ql6-`=g)(GVmuxB~bH=UW|? zzulC{{_qA=H+%5&t@{t&E;$~u3e8W?>VH+fyMKp*$;ja3TYMoC!ZOkC+`&96t_ zK+XC0^~*E{rcAEyrFO#t$KaoRcj9&8vIQjSulx1#+?n-RM+R7uE6VG()MT^gWLewK zw$!a~hpG$?-$NE>utcMl`MHNE?beIwEEvVy0@)0dbs8@>7H!BY2=)!vZr-|hp?i)w z8`7fo*9B_dJ@K5?C-DyN=C2CdS3IY{!W9GeTu6*-oH40#pRKsg`>HZt&qc}XQ6_|` zjHj%3G|j>c41XDf9)7-;Ui{WY@<;c=i;mNYZeKrYTKPYgV?ro!oC1LvPWstx#kxDC zn^(8ja+woXOTDfth(+JC>vK0JM2G!wTJMGYp?CQ8TlL=$?c`m2czZj8$Nt89%j_iyPaa!JQLP!dtGzNy5KFzM%O>!pemYLMc?sN=e0jWQ~a(+lOLon z^}drhe=uS5{YU6^m6ciFZT~0I_HthZi06`fU+|nFQzG)#INmE?@~_^=tWXhnNgav3 z@Df&8mj0Qpc|0c)?(+^U2<6f|AWC}tY>0ULC70oU%Fyt={fp~cX@_Ug+jo%eU-sWw zvUJ~83w+NjN@`C(r>X-IAP@YC0e2Rwf?>1%Q(v#iv}tO6khv7F7Ifm5!;ik^9g|yG zqkS(GlxRw#K+J4j$fqi#>d+5UwBB|5-2km&I^TK0{m&P}#U4@(f0UjBh6%8p@AP0> zXU`Ze%>q7lV#rQPXh25L)sOV10B{uuOeCB6!k%+_J^6#tp9YVSgMl%xN=`q9?u;yl znFZWGj5vp8dG=)T8(W^#;%EQqhZD)heSZ^zl7|>+v82mE&yc{!Kmax7!5Qa>%t}xO z(w9zsAhn0JkRzM#VlOn^p?5flfM6$@kobA8$~Tb@?w~crQ15optzoKxcbvFZ%@3Oh zUo&w`05@0@=u+bSk`;~*i}nw5IYWZldxRcjfse_d`lBpE1`#NIu0$GYPk|DtJRRr^ru+ddsrd{!Un_eVw(B-i}s<;a(jG5AA zCg2x4T^x%M2sI(W<#%G)J&^L65J?(*&?=64A>u$R!cRG-HamiSIs#_>pqWT{&q%4A zDOjQ(sVkna>184AX|*3L11u-VsmjXG%5~M-i*S+HboLTi^8mm`n$y zp*`WJJ(-~cv1oc&nyF)&>H(I)e&yT77aEZN#8C14U{y^B+9uAAhGjL3cpx58?fg)6 zA4{xufhxl<@FV4DU{6pge|5ZwVXBZ}*fcfOax?hvE6$^Rtmg{eX@#vnlj8$GP()Gl zvN_M^0AIxLZDb6L0+Ay^C5xaSk8I*LM3NM`G@t%pKifwUkh%nklMH)cA zFlDkT1GxG4k_9h}RaDnXaB~*mOa%mr6i(alPWdw{Zlyt*gOtb9PKRaTErAEyyvLGr2_hywlBD7{@~Re{-9J+^Rgh05aDy^qw`eqzWQj z1jU0udPSIh??+>?U?PArG=vCKVT_JHyHRnxJy-}N5eMdF#+i~Zqy)+`5MF4&VbX<7>@$-5~+kA1skCPQn?iT zn1@8HpL8|bM=@N29B@k+2$B>&&F~X?V*afb3WqbVlJ0Qv7nRUA%Y?Tf)EcN6bFq0~ zdZuJKAX%M4h3S%^Mk_1?5+s(&bh`&BbeQqpg(7xXffA^Uway)+uy9l+aE75UI|XJ! zYUcuRV$-FV=2{uYGHlqT@@*8?HWq4IgfJ>jm8>SpRliuQqt;|g0??~8Xn`d1njBaT z1icklp|Fy2cM<%@As8*6DSY6?niWRP&c%9${7?l>0Nj{q;Q2OI^{`;i2sknV_Wl6p z7Hc8kI#hEO1CX@<%9voy4eRkhj!l$ZG%6Rjwtk{lK4$3O>hWwoHWOSRiESJ0MQ8tiI+Zv@rm2ORJ>CqLymKrkk(> ztqtW?)~YaDxg_Z4vPtf0ZmD|S#o{ScOHOxv(MrA87pNu(=Jq*9lZpW5fi2ZAO|e#~ zamD)!oHRM$Zy=^Ct`^%8>O zFFoPMDiy)oi|RPsn&&2(*;Knu9>K3ua~>4IMT~32dXX+YNq@*_&Z2IO(QZmr@Epj! za@_g5BtBFUS8mtiwvClzsGI0CzJ~6NpLLVzcjI`8h0{#_M5MQ1RYUExIt7&e@h@md?A0G%Io z8r%pMs8VZg{79D~DATxa^W&4%fs{JHR+>>2O14k9Bqoi4OvgXtK~q7fJ0O%kFu@B# zc}+uVroc7J5SQt`?-WWOG1?gh9^n4u+dv zoEPDpiT>ss6Ks%pdw!#$|AZcHu1UsH698)fj#R9I){yC^g^qi3@1i(di&27%I?2is ztohAw3c5XbX(aK|g%boHIe_r>rqCmm(Zr>%$DVZ3G{o^Ov6)F^g=dwEd(2*Le&#Q& zvwaaY(*c{w4Jd{|npq(@L;*=CK$Ea>S9vXenc0zvb6|-$vl!=e_U*m*pp%4p7Z}`e zj9vtWq@YmE%d#KB!2edLuiglPQL~U$j(;nH!K)KQ;Qyk=`rpin(Key53;Q>%YQ_Lu z+K;RO&f$3C8SSP1+bv@Z%;D33%RG3etxw};60OhZ!GK9>Jw$$d(2_VcgJB}f$P8HedSE-llLH* zQU~{LCuF*0e%-feoth;`&@oqcBuTIHrM@I;;Ld)_sky)O3~kr>V=3UglH&Z*_Wc*b zDWdm-Np*(3qjwe&`Kccte3_B8?Jsz&wn#hITtko2&IedB9d$8{66 zUkCeBIH}9`7yJHAj=sIX7ufAB<`6RNnOS6RCSmZ}ilL3bha%VB%sR5ST;>ntTJw8n z{gZv@x(?6h4|cJzevLCVR>Un9de=PIBE+Qx(z$9As!J}r)$Y8ClW^?&{#m^CJv`%c zjL+z&XYD!Hu}uhDqO*o){aaGs?l6%NpHCH zg_|;e!FZ5G+CDHk&eiYRyUa$dqfFzJ?#$Qlly1&@#3n7oUbRG{?dCF75=&re@i z{?%=oqIPKX579Yxu#(qs*iS zT7AowGEQD|)j=O!zon{uCtw=y#kS)g9jxd+W!=&0Fiw2;HJy~garyHDPw9;}aERy|I=lYXz~>x-KY|2dmA;^-aWl!M&1#US@3g3MZIF*4MLkc1*ljvU_m@L$DtQa59Yg2uXo-sf0WIs zd1MF(i@*#!oT?f+r{%2#4j2mI>Py#S&f5wW^K$w)mTl!nutI=do$&X$>84Jcvts z^lpkteolgG9b=wLAFDXf^ndK%68VTY@QGMGPJgbZ?U|J3U7=wn{^T(KMQrBO)$^}V z_+aj#;&t?mlJNs zWFNj1zbvc$3<;s;x!2a*51EN7Gug)BNN6HBqTQ|aXrufaC$wi(C_cM z6274zYWz(qs0WuMfL=qYTMx^n)8>U9t}!Lus>9bc#qk~UGwD#t4vr8rZ)RAd@8d7_ zUsNyjBz3g;57%B$SGjO_{8#knv%WwHMQi$n1B?=taYqxCb*t_!V-u-0S+ojKzs4TdqFJ;W~{r%PE2hp_5K4(OOQ7mo$t?ThO<*w;OiZo&HDp@ zV&02g+x;hK&$PWz)=?y?Uv@)lo*`#08I6}GC3OF7vI10%p;_l@KeE}A%wNQ`)f-I=;2 z+8UJHyV0ZnlF>F&m(=NWz9{B4gxEnvarRGrI(KyN;>^GA zV%js7ZB+-;&bPfDo~>KO^KtdO<3h@uoB7D>u`nw$(pElNL}qs89?2Z>u4JdVXxMCU&wcUp*C-y!iQAgz8)YqN7pc#Aj2^pqB%z9nFq? zDCMf$SJS4gC2xB-XZmeIZ?U22tWd+AMd>Bp(;+TV2&>wUAq+TNq{Y|EiDZ+=s8 zwC_+&@peGp&a^{;}QL;eW*SzB7z#joe~TH2%lkhy8d2>^LY>EREt(Ehy; z@uo2D%@A(?FmWLCyIANBXQ)6|*!MeO5|r@A7rYr_dVhY*j3yc)9t`)7n69wpUL~Eq z#bCbzBLLenlAxn}5lh6Rl93Fb(Fq!uAcv6N4-+#8=P`&9@JF5j!p~5ntgE7|#Y3+U zqs5g&d6W?Z8rXy>A{8L_u3S{!kJePYSqw8%-M7(KG@SAAOEC^GB`#VW{Q1koiJz( zwRectZ$jqIgCBK)0#;aGkRlY$#w{}4JUL=o#fWD3C6~6b4NUQ@W+1gTmQ_EN|FAS5 zif7Y*j9Zay)L8&3KOH~HlrYp4b>y6oLP;2;B+Jv_)#O;lF*UP^_&x^d+(sA8la`cI zzWLzZQ*f*+CsxV$4QEDXqP4<_rF(!fN%1DuScMfRBbQ*nXa6U1VFV_SeqsWJk^rIy zq)|4L_r=pBr{UEgxJXkfFDTK(0L;R`*N~WYG$TZGQ%HvPoil-uFa%@=k*u9@@&L;> zp7B<}e@Kpn!DvBFCmB=Xd7Dz@NDzVtOnDkEO9nrvhRVvN9d0J8EhPV&k3U-kk=%wb za+5Gai2OFZ@PABPIk{wo?eIrq5TgTZ>q`1dwv5bx6kl=RP!69^B#EBRxO#wH@6uWk z1B|z^WwiesS>h_m`bF6U5aPWY+=2!dCWHUWeMm4yc^og7#R#zmRUWAV*z-! zA}Awsy5RwP4?<8-P^m3w;sV&f5OgYqeU&}18M}Q?2m&@jf5z=C2BVjdHa`jh01R#$=V6u=y*iXkZk{5YYc35>` zw@c}h?nmA#Y4Y5RECzmE4o-!``$2`eIn;R<)M9`)r)u0f3bLYz=U2slk|VKdfuyPE zOLED)McH>HkPkpM*J)T;$5RIUS-~~>LCS^;@MgP8s2OI%jdVFEkdQ32AVW{N7T)Gg zbDpSp8e8ZtSvtj3rVO(A#-AT+n9h)}Jq!fc_zC2=2)z;+92s{a2WLUX8}l=&xyZ)u z%xfTcYc=dTsqiW}IRsVXd{}pHqEurd;h2H;HiCPrg8hu3jkZiU?qXB3TG-|ZFd1Jn z&y_0qU%d3CrU7n-p*vvw{Bd#A@}Epl31-A)TFLKm_|>MmLdi5nk^uRVwLg|Ht_n8- z<$EvI1X0l}n#`3RkdML*1)buN6YQ8G+z$ufn-OlCnIK96jQN>a$jHIP)Cb!bcNO@} zo;vx(I*3~9EO+YxeI9Z0Wl9qNFI_6sFPkuWu!KYgoPg1OSkQ9q%qq% zlm%_q1gvzV2JvRb7dH!lw8Vw@z5ox6aW^z^mRkgrTnl`lr-YwbdZou7s0(UBeo#u5 zp_9>1;?d(|Btt@0=JB{hvl+R{^u(-hnMwVmYSKn_!{4SEcc z1|YmNdQ;C=7NPCQYssaZ&NF>n#h~>ZGnRy$uGEG}sq)iW@R9>;WYf*jFs2&wzB3Sv zRx?6Nz6a6#gs261=gEXz#U#P1M8t$7_jvyDuyNeI!hlvyrpnBc*`hswO(G#m5oaie zW6Y6~X?SfA9(62ksye`ylEN(ygM&fDBAod)r#}sklEYoP3sm#4jjfzeG8je(AXcqa z)T9AGu0W&D)v_Eor1r@SfRkIvuFIqOT1?!uVfyrNi2Yb}0&dg+$aMta)a3{w)IbjN zkr?&y{h2XfmBpx*_23oWQ5}EXWW4sQQaKNsCE(<~jxBmLK__Q$?hIs{@nG8-c5whw zizYgx39j2%SL!4Zhd>Ol8bwR_`c4ip_DiIxG2Z{XEb9MkTwpAs>i@x2!mtb>cUP0+ za4c5Vscav}(oN*iiGxYBd@!JhTGZGFOy4lhL|@GmmM{~Y%7fpldD%TH^ddA&=5GJ!?qtnNXFtK!0jt07q>p_C-e-%$&kWb%+^$w8Krek)%lxc2 z;x6NLw)&u9Z7u|wrO%klOunkKn)Jr}=DYX0%h19eqt^MOE;^J4ecZZdv3l{o(EELh zgP)Y=Lgx!j^Bc6ESJxV2L#}rER(O?q(f8h-Eq<^bFF@C)=c^j-XvNrZs@Qvb$qU-~D}m80d`%vtnMz|f%R%Gt+PigYj|V&*a5?qxe% z;wZjZ?CYNrMWC>@46SL&Iti~uZRRr-#~iwd@LhCS<+z$alZSms95qw=U@X%S<(T9& z{=vrZ%mb#m6dv#v-(^g|kRY|l3{`&5keTy}ky%A2K1;kI2&xcxal zQKVrruTpOCgR@0h?t*x+Q1sSjb>=yG+}8OMgSqB4S__A%{!y#0d6mK1ep}vMyN4f} z?i>!+QXMQiSz0{bEtWK5&D{86B{jZeb`U((-jeSh)ZEs2kXoDH?!LucUxhHstLYRi zo^+?OK66R!fs8Mms%0I>tMmA$zf|;Opd}}-`KGqMU0LN$cS^@U7oMhW_(jHYfWuB* zr1JP=o@^WUPnNre+|b2qv30?-v@0QQh z6Q%sirrviy%%hzDXWy)Q%4)o>OyXPksR?iQ!dwoUm{(s}x^_j}$(-qd3E_^Y$=xKNGELfewZypQ61B1=MT6Z-G*H`#H{&01&_~Eai%JZDtpQ0kp$$stn zc<$~u!GnJs+kbEO`)d_K59+q7BoXR6j;Cc0y*u3!{GQDoCED&*d13eVBJ!4hx?Bt7 z!~Mp0V>AvC&B?h(-!8$<>s0n$5Bj#8N1y(>*h%UC_sfF%_n#=R>mp6ARbzrwmz{t6 z?BZR_SKF<)%>GAU%tO)D;b%a0DkEQrSW~q{b@x5YDvsC5HgXrTFT%v1I5@i6ZFK34YU zj!bC!7|##laQb6+>9DVB0(0U9-*n}!>`S2pcos%=PnoAMqYb}FFI>>7nMu5uBYa{{ z$%uR}lKSOotjML&@jQ>8ob?K^++X|#P_Ji0D}|hNoFsT9b5lYjaks2>B}}qE%!a4J zRxkSEk{+Z#g+70HTlvx0Ipaflk-p0)$>Y?Ek36TnqlYfCSgjznm;`~+O{nPMG}L!@ zH9wdsQswV7inR}1#kl9H?0^5-qcaj zoQbpiozFi%n9_nWGCB<_LaquLD78FGO;RY!`da36SzrBq_r=)%;3~q*GjXObOO&5U z=pFG>*qeToi3HqG3O#fA2IDCOFKhG5Y0c%n{#~K^mW(Mgx>9X=7O->HwkF$NZt!`P z{w|%Oew+D9o<4h}MZ1=5eT`*@`wIBy*fXmuEZKo}NyG^%5$MqA+ z=0jVZ+0|NG&dK?aQyusa>X_jtX2L`bTe(zoo$d#5ldZ((w|lLxIbU!)I~#2o(L4X; zT4fkKPVrBj%ayx#PZ-<5=<^^VKMh6Fs39gbcnbE)j2(CFU+7Ow2)CS!df=5}U!}a) zO&W)v%B)>>CJwf%Cdypl?7iA?`gMnEy9{K+En-IyM7*giX%QZ@u*(toSV^n&sJ}*g zM$)h6xkz-#pQD63KgP*i&IH!+&5XM%CnEgfQ*QR6J^*9h^}L$eeen@S_Ig}^t>Yl$9_BI=I6RKZVQ zrf6z8?_f`^By|1z;PHdX)%wARlanqo5`W3w$;IFXw^hN20;o9Ig~@?2kNBDOB;l46 z&pHa#;OXaLw{#+{lRoNEu+35PE4I{CG3DTc;IZ_w?8ZCJ33Cp)xJGGAaDokp5*-aH z8ramA%AJW%Y6G2`4tqU2Uvu{MndAYUPo|gVQHo}kXVQypZ%Hfh0?doXXetuntTVQfQ`1ESS#ZSeT>Uh403 z+*ZY_qQUDncerNaZ+kZe*MGU5ytLfS;CBs?z9M857YhExw2Xd<3>2a*)S1zA%IfdO z7z7E`aO4>n4_=!H-07z$`os;RnRI_~zwOrFYZU8uqg>Z~dr4fUm-8r@Y0_?i4}0K` zS-T;oPy}A5v;QeO{0n3%eL%h%snc>gxZ)nOKV<9f-}U0+_iM%vm$$x?rweJ{la`9s znKXMa>GD6y)ZPk+hHAo2I$V3$SqX76BTwG=x0C?tny}q%0Zp9PY01l5JO8O$-j@G& zacMV_w!c67cKJrYQt$_~d;kMV63(PRLv379aU1cV);Gew5DpM;o|@Ya*p@%Jk{KK!L-dU*eDshNd|~PtYdySIiLDHzZ2qsvm8fa z6aJWr7v9HSR1TGL3>VS5WhIJI%X*lF^qlPUWtER8yx@I{QQMz}ek&uZ%?Ij?`Q4o- z-&Ky(rG%K0fCd_9Iu-0RaOX3-@e7b|tYtuEXXwFb)Ced7D<1)Oj<6=fO)|*K^f$q7 z^U>N>VV}feysFU700<$E+ItC@IRk}bfEh6kITPu>A8DR#a|5g{F77L1?W^%A{3Ay& zgux@(54crCQJyzldJ}Bk86ET{+G9TW!9I4P2?{P^n4)6g#R(>!NY=0zG&oT}D-kY` z2-k`uq{b1$ZVH-(T2){>GTkypqgsgZ{kvEh5>{Ib=U^S7KnlLKZ|dAdc4dkQ7K_vo z_tA5XGzFovs0q*afvtAl*VznzUJP@gB;lNAt;aXJ#{gDs&5&6374X09aHKMZzE=xHNYfwT5mjup*9uIJm8eQl$LB z@G$gT46f5U;wLeXhm=eePZb_dZhZq{&`ZG8sY^vf`7Pj#17LT6RoKdqPRXEqq$zh_ zVm5ndgJ2q~3_WKBa7YC6$Vc#uQ&g;zca?pFW3yT-O&M?zbWtj}2{S$hNF4y17V&s_ zs8cNO3@LL9a*Nf_&28bKwhQ11APfCcJW%*E2ZdYQbf){z<-nwYATq9o&xg2 zB=YS?nObvribbxX{G41=SkBw{V(3~>#@tF^8=*kX8E{E|x=`RiL_XWff~e%rbz|mS z0ILPyAg2&J0KsYb3pe4ea^ax4`MwIaif`ifFSujF5R5s?$MHOc?SR3p0**u2|G|3z z`MVPNQ`^PI0l)%3AaaO(wvC^s8lnkb++}+-K#5!I4N21)!wi1$3@{C+AD#_75&C1X%V`ktp75J8g@{7%- zef$-{${0<$E#7w@%gSAR&Lao11EV`s)2U_mY_U_Q$^>eHnQP%Qpk#qRKVSkK=vt0h zMJt&>o`4b2Mkn;!?)}H$AQRGPAoL1ISF$+k04BRoLF0-pft#v6};;%7N zty!Q}me*9GECARx)_$UVOd!0u=7f8hR_;JT@*!4cp%^mGwD}pt>Hz-Y3jVm&uzmPw zQ}uti4O*S~VKE~Ydd6KpCt2_J8K|kLfq+4dB`~5VGol#;pUHlk8?!XSMwXy%26)dVb}XSwV9I`QYGftRn@!VlrtAOo2LV1g;TZai0jJA0pQTYiPP zUZuBjmb>N6H2S<7P#1{7I$~J0q6EyKb17&UDaBpG{5n+mL0l_uP>p(T<53{S#tqPw z!o8ku7j;D7!ElDC$Xx(QaD-e8l34G-@gEjf^k4+-%4fJ+tn(^u^V&6oFb|Iahaf=u z2%w$rT@Nm__JrahAirG_D-|WgjgxW( z1moNP9bz-=@K^J?-x6_b9)K?iZ%YFVx4X}$0n;l$5I^po6i~bb9L06xJiDdC@|6WT z90iy=CaF&*3ri;%P>+(KAmBL(BO(AF4C_M-ATa}vCs}%Mbh8>!$2P&8`xsBmlcUF< zm)P9MI1!_Ii2P%;7Tjbt*XI@6JrdrgsXxmJ9sLV}9_WeN0SkDV$IzazLh_!5gX!eQ zOvf|<`7Wg$M7KZ{s{l=UwGdHUFSyDiFfaflh~007O3E|wmv%55P7DwbKx6X<2Gax) zj%K|`t*5w@mJImQi+R10S_4&%HwTNEi1HA*uu|12K9&@OlNJ-i6HWwsAy#SqJ3R=2 zp$m2W0^0yL4X^}o=EuPABWwbIu(BVK5_lRS58^3~!R`z)@=}7?KOC*P!PK0KqzeM*=qajK6V7xB@Noc6lN5aW;(A|>WhtER zt+mTtF8{xxF;Kh))Rk7oItDM*CC7*jnD~qy8F&gd#1YT1-km3{=_T@ODIODBoJLbc zgtT>Ceij>LavKgVTa!ff^01dW7_#X~{rvM>|DbUx#OI+G%$X`Sam@e}osiseRYI_oEV}7tayz?tBV#(J4I1tE4mhx^zw0nC*{a zd3|dmJ>G;N5AUB7ugJqUk6TkLqlG_RSFVX! zkFd;Fsmc*gjCClL4sLS}f)#C1?RZ!t8zrGYswQWjmoO+3Km7n zstf2M*X;cBjYQ*ATKwFob188W<%#t*l1+uo+_#(Em;-J$G8~+xwGnZCqOAr!M)Tzb z6Z#+9rNb9x8u-q2f6?j3cd*2+b5v#&e#o?S*Z!0{?9%%izvu25U|T!$_;8TtO5c$= zOil~>hNrNvzCTc|>u9IeYyhq`W!?uEOE34rp?Bp6Uaa-X#O%Fxl{NV^7_c>Rt~lYY zwtNk5>f~}}4serIt!>l!+ADRzDfQ3Cr#;!fTsf5!^fUdtF7)YC>%iXzb?HYCnfivN z6YEOFFQEfpOGh@N;@Wj>=@Eb4R7Tf$<(|L9D`I@^NMCVQJ?e2s>%~2(k4rxB^V(ShZp&!TD2NhuSO zTJ_f^9OBMF6<7YzpU=cZ4Y4;Sjn5C(_y@jA%HoWNg7lwsavbaQBror6OxK?~@Olcx zn*S=KKkc2ECRq1PE6$ygUMMeBefwwV{AG>l4ehfbzoGe;dUSf18=PjxE>-q#&b)Z9 zs{d-~$Nf)5b}Z*S4|}gasrsbWKGP`teZ3GIr9-PLzJG?ER-pOZs&Ava?q=}~haaYI z+L)q+LkZ~XOi=Ou9O-8fjM`75bS7R|B6LIlgZO!_7PEwFj|_fsh*WkABnt+>Rx~%AN3>I`0wU|yLm<_h z`XMPIu@7-vp;;I7ZBO}gEMKRGjGZ((XRd6HUODy$Kh{KeaQ0R4f1a2hS9va+D(6|I zf74|QUvi7mBrSaGTBQ&EHkhbe=OlMs*EsRRPl3^!<(E>o`t+F7Vz(?-kfB;euI6c+ zPaG;v?jYGOZ#JV=nJ%cP^PB2|eg*K)ROo6Sj6X9;O`mTQRs59m%=OxtJZwpc5;B|~ z7aaW*$`9zmAUMjo-^KV~kiG~*VW-KY4TL!BD%(o{KIdX`IHCkhL{-MzEPs{$L`!J2 z+FVL?w-9<~vZW7S3zH!i6)94-5I4E!a-X+VoQ3N{d3zva5wF62$D8^;s!Ud4=4E?j zchrrI%-t0KRI?6j>X2)2|Cx-GpVe4))?CSFn2fO8H_n))j(IUtMJU2dr!pokS0??g z_DGaEcc79l1A`Fl6D1&bo2+18GaAX?zF6Ww3CMlNnr|jwbsbPI$`yU8lQL>=uErGv z={i(|W2CR2k+Oc``noaruhexV0Nhm4cJ}j732!?icu;I9X}WsXUPx_Wk$#)NE<7yb zE^+#0?A>B)(c8NoTE@$((XU%a^-eiRHN#^_SK=Oe^R4e2cRj{b#u&l zsfIVDfvkC3!;5bp;F(zK6MH(1`?43_&N18QH?Mm-8S%*BqTjI$+6Vm;#zHEwm0AkJ z%x8i1aM_LS!uBt8}Ze4qS*=`5$NaArqeRnA~pG`)37bfoAndZ^2`v2fGdWNZJ zwtjh@2TCBk<{oyK`ff*5KYVnVTmVvR-cAyaN(&!$lH>gmqN9pG<^GJhQyffG@mcjx z;(Nz!hSzJz0dDJ$DMuVugG*lt#rqq-usep(08q2%!LP|!qiF~PS88Oqc%#S8OiB9I zCq9Lg4#eX@W~>G}``J49H|W~WNgdsG=Ot-hv(4AupJvUNw_j}0Um1R`-Uk|VjkuJy z)U>Iduo~a1w%1RAh7)Yeu(R8bO=dt zCFUzD*0?LUH0o{+5RgVc<|Kw!;HqD+uK%2kKh;_g&00{FI-nG!_}!Xx4u%WQ2Yv6y zq=I1N9C<&J9Pmkfo=FbRse_Ph$ltMm->U&XNl~CKA;Orb=Qgf}5rK827;@?u>InQE z0DNxVwY=E(g|xlKUwoZN6=Z@nMh4tH!tx`!4L%8;3k+inj!xL_Hz(*dPwG!`8|0$r zkJ1lt^YX{3*Fg7V0+Zq{s-q(F0mc9zAHw}h1c=8L*e@4S(t@g5?_Z8Ye0;SU`Hed& zg$$1!fcm%WQ|O3~`1~hhQVi}L5EcTT5rS_)>ikD83|Oc}&My{YZQRxZ$H_45hM~Jd zf=J85gI2?x75rV-f|C1#J}E>DlL07Gm7L9kmuLaN0>E>V;+0oGL^%#IvyZr5h~O~_ zO+D&4n6v*opV3i+8#f?(L0(pz+JPdd2>bF8$Gr3Du!@RbBOA_v` z^R{CLBa1u8&MPRnKFIkvyze;3O(=Fn5R9^uFp>fByj2;OQS!OPS%Bg4!XW5?XL1SJ zbEqQj5*eizC$10(BMX&^jAA8^KNtM;gC!*QIO;6`c+LX3Mgqdhh2Wq0wBMKOun0Ib zqC0fKvF8Ao!L&hvx(SP8;f`Jjy<`zM`r=Of;uF-AH^K- zDV!z5&nihmDDV$PsK|Jd=xx%EW3;Q0D7rf|s(LU81-#@E-BuR;ion_=&D=%TY2e7b z&xOg)7TatG5GCj}>zVL%ET*qLVVEUCFDg}AG1YxMwJtOkGa_+u2hs@xKBILh;kr?_ zajR=dwI@juWIn_gt~rsR-tSSKDJx3=xF|TI7F8L>Xp2ea;Hr}&AAwEI3YZ6gr{?_v zBNEV0f(G=SUsi+;Yph;{*ZcPo*@~$YP_%J(cOPU%;vyUetbx)AOM6KklCK-bx0Y2$ zp8iod{=h5V$TQm$flm&DEJAP>Oo^;@Ka(vOgl9TwlsdJCxjdKir*H8S>VaLDnJ#3h z%Tbx>cA1%X3EC$KIqSI;m8s-BiRC&ea^$!K&lVR17UK@^C2NR&S=t$CJm*B3Sh-JA z$0tKwbUt;|&V)Sh0?If9A3#Q`#gf06s#Z_t_pGu0?_>_Z1-haWdwF4ICXguQW@i?x zcVt3N3bM(SK~k!W3t;a4!gpytmtz4=>uD?}Ap#=VhkED|0AMZ*O5IP<`~EX5)d133 zeDp2_n+-4uDIrlWaTUt_8db36T|kZ0kEK2>`6f(lkpS_8fu9}Njc}0v4q9q|)+$+% zy?&7v297u!{81k@cc+Lku}rQr%uOf5Z4|o$frBGbjs`__QYgT>3tAzMrMfTJ1Xmb$ z;?gQXH|NP|g9?#w)W>C>Gyh~Bh-np9a9WD%qF!&g2T}-ppF2qEB&aSQ zJN+JAqQAzO7Q2WWD+7U!zgh1LLFLZJPJw|dMQVeDU6w*~CNm*6JHWTBsj-u}?<;cC z?CQQLRu5AE?tH2r?(wIQh&m#TCNwELN1?2Uz)C@2?btWVZ8p$| z+M{~9tBN$@yOv+D)x}R{lAkuBK$>pZns`N<=v=F3b|6Ai1bJ|v;tqtZpYWCX^P#_) za@1)_1}8`#+VADRvfdca7ICxDGDOiNNZC4_-}EdYw-JTR(n8B%&`eLJWDH_D*3_YyblnRv_8vIuJ- z2to($W=4+{7)_Q8oHlYRrw;oxg=N=;IW#@D6Tk&c>ktA3btoQ;+L*g*KRWCm!?OXE zuus2^Myay|>Dw6+(_QJ?iQiB+_keGy3^{p##5_(P+&?Wv;BMs`TD64bd0YGT^s zmZVwUk6lVv`0^eyc-r|bs(X~ZX*dgF^-wnr=%P#Py?DTv&xc+qQ)?wae;bfJu~SKc z3BR1wJ>J*hR`*U!bpr=**6Vu-7kbYPAjFUOl+*Nv2>iPTeAU~5&r5Z9Qkbt@tA#du zXA*D;Vxc^S&}s^*xNOK(|Inu&L#OPJ=bYfA3s;e2=$8TXH5GBJI)s0kz;zRZobLlJ z;1Qdlt5?4w6dtth8hrJ$?mY+e{0wK67UC%eS*C>!?hrD=p_bid4u)(|+0d|`kWXUJ z736c~k^^xyAczJKRKtP9QmAMJs7_PQC5Ukhh7q<$i2NgdfeOT%0~&`zpbCdR15)UB z2!p04{{A376o!7_faIw_X1FIo8j~MIJD&NQ^^Y&rD@o=X`NJ#GtG7<7lqW z&>=}5%-GC`rHM4KXW)wTtr9yr74rCpKdGM2Mh=b}V z7CNs2O-z8qqp`K>+CsBzbkQk-$LM&krEu)z& z1)70s!(LRWKvy3jPw?gF17J=LIv54h+NlGW4arY{*ynrDQDr!(!V~{4fCrXO+*bp{ zncw|em81B-!;$|bnEv0S5*~99p~U~rV$6r1vzVtL#)P7QU{X4JD&YkSg(OB~?$5}i zy~#8}%E6pW?)}Mhd~qzXawxNM7L4b#naurIp@`45=Px;tl}xEByL24eg0)niV6qR^9N3=!*+grPuWvB_6UQjGgQCM{``{x}k=|?y^(j^f3bB3GS*9ZqO#RvkmUq zjV92K+Ou}IU+d9<*e}!?1$W~U;*L1Dn)!bYz&vfVX|B(;Pm;?BF`_hHU7a_+4@AC= z%(c85cb%|te*f@z@$J{Z*b&|z(FPlA;CCAlnr>ushnGN}$u}sUskyyu$oF6zW6NY+ zq!cNg(=OCg!^yG7IxU<)yeL27)_;8G^-YCLp{*Dl+6o+94bvEo(y3r-3vpd~VY{$j zZW>P$z_J(okXZiGLX7i-cBD$kM2F6 z!A7ijkh$!XnvnF19Au%aNdsPrlT%~B<@egFuu3!dSn(rRavNhmBQ#4`Ux|Jsv)m^J z>!#FBlQ-(n6;09YD0j&;tL%%<{rbVPH%#$Ri%9eSb76-RNl{9N7^|IoX)n?~qZ;(y z{;Vd`KC3KE`18lzL!k`ppNt18OPMLZhVNTsO`Q7a|9Go z+>>wA;uZRmg2@)O!wKCQwQMSe1kyk2G} z@8YZ5WKg}b*5lzLx!GzMp#&!J^sRQ@W#~!0r`H7W@Gy9z)_oa#R@5L7g^~03&&aWB2q!P* zo0n-k5yGK6xO6-HC~x}v(Zk{JsDuIB5BV74`}BS%Qwn!9jZ&WLH7}`C=I-Qfxq=lY z{6}HC589Z0yO(h`@IU0ccx_?^yI@F9LJ&^cE)0!pJ^~NN9EjxP9)>^`FwGKzUWn1* zS1wn1R88idOAty!vgSP&D-F!TU-G9ELju@vEU-n}I{Lx1;qU~sr&N~?FNLW`QuB5up8mm@oF#T z69y=Mc)^4>E|c@0!Jjt@!&aeE_`e|vFIme*SSgs)YMR%#o)&svbzUUKr|e^e2=?_) zE$aXBDN>m&urIF656pRdu!x zs+iO&5*)G7uYBKCTbUFhLj58KuwR(ITtUmUT%!KoYq5xzC|A|r7RszY=Ox!td9{V2 zbEW^T+W1qQ*?0uIcV*&d`-B8Z>L|04*C{E5d^z#wM%)Qlx}OBDax+G%jn3`A#vT&a zX)RaT%2%!Shqt_p=B@szU#K6&z}o^+v~klT8FTc_-;54vN^#Om|2^ppUT^0c4_=fMh<{F^4d8E;L> zN1if_T80kiS0b0JHM2?55@}~cm$|>&fa(Wnk~Yr#fe(xuo0%Thm4jD5y`P~**R8re zzf_%lsbLXoY#uMW)^_~g-(^2YYrITMVc=4}A2D5eQ^5^-#T(+VOmg#S0LP=>=PJt> z+(bI4;Q}Dn`?J;dmyBuck>{@8J|j@1wI=cW0;G=!ox;;7%>i+}lbqUNJNq;!=uz)O zoo|I9Plx3*ANfo}?eD7@B14x{m-^X~O;5>081?2WL;#>5!kEb8^( zr6JP)QSEYddCtc|h7|(3ckc@Kr^{NH5*dFRD%7oqEL36(35sifU<#S__+pb%a{7Mn zW!q?=@kNMJj)kOe=2Wf3-}IvXXJ7eH3tB_7zB?zG@w1JXcO&f6XPhI&m7)p4Pc~l4 z|I8)c+)hH$E>jt&52)rkH=1T!TW!R_aw&t+N!By|{;I^w`dPgPrQ=0tgLYe;cc@p# z0MC=jb=I!6^}^hSW-#uV6Y2fN<`XYPp-b$m=Zfm4keo2Z`CL$J<}T=tr-M%QmqL>; z3rf$q;;Q}B_6X$?-+8r(l)(Xc-ThnxGT>8x5jP^prt2WqRG0#_9v8!J^R>m($Vi`l zB9HFYAZo=e9o-iaPfv=H%e1y2MI(Fm&5U{D`1@*P&3Rgr;SR5)5XHO;0w6>W_#_ko zbC<}@yD=0BXB3&|$K-c;x;Y+I@DcZeeEcIo`Mr3AH@$@O%AVA3Md$ndN(*lWMe~x317d*^j$a8#&hmE2jl;bvWU?Wc$s~$Z7r~HjD=1cD%Z)I*d z8v?b*Ymwn5x4*Rqy`9nD=dGfk+rpp8px({8sy0@9B(@PAwrkg(H4&eJNkPdjUPomC zloL;s{_)N|Y8J7)EFLC{M+fpl< zPQW|NK;ks0*)6_7dn5z$GA#bs@H)(TxWKag%457gT<8|9a1~yj=1E{1p=%QqH1^4F zEu!@(0tbc}14B#92fl(uKHHdA5uZ@t=m9XjYG@Q$L)cDY_^)f#UxYYWB1hhwPP=^ifOg-*#8)_#@`0L%6SRudL zWg;0X^+Xx-o~G~-FZkR15V^l0yaHw@o;bvMp$%i9w%lOli~zK|(Di&YNp8&Ny@c#D zR3smjpBBdi4wO&uVju%haN~vX1Xh2F2i!usk0G}h-d`|cXICYz%A;<^jiz=0WA*4y z?H@@ZBYrUZMj+DzfZ)i@e00hB1Oj!mXZQvHkQhP+G*pjNa!Fk6$JvBoQeWZID?-ZQ zScGOkyap6tK`J?4D&3tgenI@r*oRgt_p}Uj_KK)0TA#ai^w4~i(0V)+7-lj8Bi1E7 zI6X+k))$Q&kjxF^28_*~W2vf|Kgk`kdB}+p76M=6; zW;`=WqFGaLJK{w8<6dz`vOzMFz0q23F#`HCGr$y!Q3U^()AEHwe&2fZentJN`+1B6 z;{);-C;^Ab)dQGydTtSGk?ab)uu6T*>3^B}|d0F@5B zDSpDN_hfpCPz<)`3QPX7Fa)11Np%2w&CV515K`#vKByP+>qlBm~@Bk#|d8#4A#C*AW+YlEIdb$^%QZ0K~86qe!{L`_mQx8}gqmpNYCg z3mE=eirNDSQS9KtJ>kNGD472Nz+oq?wgN>*9Uw^yl7ykwl>;q1^EMTVHoQv56iX*6 zP#F+;N#OW1bwCNc7@U|N9#j^c0hJSB?S_OWq`Ok;YSM=%jY4q#mZ#;g6doXjWv0|I z#`3|T`Cwi33LV;P(|gpq_PiXdB5wPl2XM@*`O>M(6fGDCRB!PWOml5h2Ej@r-5{_L zuwIC;lnqPbNI{Q^hSN=!yIG zou!WV8B2gqqVol>VpWpsq4wAwtVl!?imLRUZ^gt1A9AS7qzkrHESkd-Sa< z7zwS#7&myxz9Li@z!_cr%CxQ^t8tR8QgI&CkPiYuuq-37P_e@|kRk?4dfIy zr1@1ac!NZ}CQdWXEAr$diaL6BkY)lJQ&*#4WnDcB2w0B=f?*Uu@c4b1dJvTuqPZs6 zRWKCK+?8UNYrpknR9^rZY$x2U8RwY1A`4bs%UY*O31;$b6hTCLv1jY&gWT)If7fI8 zskhsi1s=~6VbVfl44}slSwRDc0XCtSK^l++K9XketcrggcJS33t=uHx^=H*&f=eAb zs}jJ~31Ij6PC*z(T@}VD42QfGJJ7xzDv}x}N=cd@Bd6bFeH7VtuMnoxT|oZc+E;lX z0o`W4d)5FJ1pteY*75Al1^mUP!2 zQo9QJh6F7_IKB7iEEd^rviri72@gc+-Xf?r?gzpO0#R@M?1;??rd>m$HG|%WY!ov8 zHR#(q>*+(3F^CD{=R>(o`&AQ%C@!%_mEq)_gFw({!UqrQP@(%G`kO@Ft<4^y=%IJ- zN1S<3kk8q=IzD0Hh?!kKj00+{STMs}ixWGz?OsI{-Th%yIiyJPy>#ETL6@LP(>uR0 zvm7Q63OW*i#{F7(GjYs&I^L2zHr01{qoZjTi6u7lXLQS)mv4)0duqVTaDqXsqXUYI zoQFiEN-*S%{jl#SR52%bG~tjLP!Z!zpqfa2#7=vhe5V+NS}=h+fS(K;7i90ok8S1H znsN;5*dr3SEt=dg=<9%Ew zyPd+G5}R$nA!>cZcSTU@(L(JI(^49}@vC9X)x8R_;mSTUT*LhEdb#0g5&o^oT9w%! zKW({_QGRidr9VOxyGM7m$D^nF`-fhSstdFZvOjT52sn0b?V{4Kk?kiOs!I26?S{%}!4~23sZP;szW2>Phj$d;~@-*}C z7mH|ePH0ih>o_grHGFN-c-{*wn${Fs)b#cwL*&Hd?r^?RznOj^J%o(ybD>^!RVAQ8 z;OD^z=@KA~v{1Tph+fVr(sk7DTq2-nJhw?5rP?NO@R1LDwP7!?mKM5upT+gx$9X@}&GG?Aa2L5QSU&q~)I-<8AK zP0ZR4+#M?8+1FB_>RoKirx&+hc^HjF>_Wvh;v`iZIoAn>(17B#P2c%Bx2Xeu=!ij3 z;RGKM_(};-5dcxT5oAhyuXLBQ?V|u{kx<3ZW{CNkM$Yb?!&f2WgAm7kxnHb)gY4eF z@$;lN_CD^8RS#&^?joHIgwzOp5fCi|i3vAETms4rKge^1I1;jj3<^B#3X#zqNF4N7 z8!y{R9Mh;Bg+Gz0BXf?U{k;qJmuqMm zJK%v0AQU(naK>BMr&-ZA&z0(db2eanuB!@_jVCZpfGGC=G<7`R>0KLcI0VySfy~gi z)p&M$e+^1NKX4Ns2mC@pE`U-OC%?LWez}|!Em(P-ZG(!M$y zLP24B3!yi!|IP9JH~qy0U*B(myh9fP9O3VmqJ5X2E-@wNeoEzCmL{Hrxnj#Y^UL>5 z_Z47<57ekSlfC^sc9emEih}#jZ~`a()&lb!K!i3kbZn&!b%Kz5ei)O7Q7g z_Qn4fJGuh^m`DKfe=1-=C<#Yo!rEX2DzWr`cBGLQpsU&NaYFuR9PJzFN0#3&6@#gj z^&QKT=*H5xUwo;~;5{77qO{p)uF$fY%O!F}rB-S_o(q#V*b~VlvVNPR5`o3Y+PtS( zOcS&HD6eUuRRgICmSz2TDwAvSBAPKK)nU5DLVQK#jIVaH(1lfr0W1BNLY^bY!|<%F zdU?>}A^Ner-O4z@*TnqqDVeieiImj_F6;HB%p8fUh>RmI)5%;6=*zaBC&NZyKCo#P zhu32(uhk|!)-QW+u9nR$&mUXIOJ_n<_sn>fs`avc4!bl~LY?L6khIPM!$g(tK;gEL|&d--F$cX#UG&@@NtGHRUbuQ z{JZ=0ZrS%xBAsKyuwkn9@9-zt@qG6ulml`<1C;W2;eV;i&BD`BvnYDK&3# zcmz+=e@F=(mea?>7RTtrLv{s=BGxqCkt@YkpzA5Hm9kicQ_zeP$B=8Jt+?fy$?t|q z>UjN-vPiN!%(RvZIm{GC6}C1*AekrkMNikv`S^=Dlt#dwY%H%kvZU9S0FS# zE=q7Hy^GEIOjCSV{8`Szt~goKp0zl!F^VZitG6u6Lx#VFBzOMZL|LAuoFZd%;5~BU z-P_3?mV|;xorr?Z4j(J+O0v)k8wbgq(*t9pG`)uhcqF|I(i5K3nH(Z~xf9zRI;lSl*E{pv*$yD_7 z_tfo|9Ou4?&MEp#Li^#pmdygJ6-}025w3Y+WwGY!Z>{JAweK~~iVeEHfLWU^;#C@_ zm~3jU_!eDeS|&`=sajXq`>KEUK0ZxVltJ0OR^=&poS?NIk^Dq<=H8+_3v!Z3zrPDj zga6)gsjjnC&6@sFc$UU*JA9g8kIB8_Gxk)yJIJu9?Km`2u5Fp-{yz1pJDuyvI$jsO zlV(srymU=>XY}djXETtiO5u~iK5-12h^}?kh)Dd^QJQCM2jHthA^ySmSxEQq=X5oS z0TuAl|2Hb1~nrUU~^02)1VRd9O)kM+n7wh&fS#5H_y#k7!hQA(8O-2o~F0g6FL9AkDL5)(SU_ZFoK5K9uq9 zAC6r02^QW_#?a&Wg@G6Ym6Z7!AF~mo3%kA6k4mP-2_E&(uT?vQf?l$m#;zoz8M5>R zW;h&bNlBk`EJ;ctN4Ru82ftLIIh@7pBqFHNnwwXT=eQm$s|fuJ3Mz3pjvUkfJs*ER zbU=N%+V_O}E8X|+Ox4Gg1}jEHXvcu4{rY&A4^tpnJT4RVOqBQ|n$aut=Q zt^3RCy%pU%wveAG)8- zTIYv~Zw(IJ(yxb^U!B>#%^D@j<5bO!SGnODYvAy+_-Xu?p6W_>TzDBn#|5+zAvpXg0|A#YTa?-omxIWeEKGxE#x(WIeFf#?xc9{6|#2waLOv>ag&YVyGcLY}k|E!I&W1vzv+JB1)rsk2pJ{ zsYI63zF=2xRG=|tm&4*tT0|3BiPt8Bz^%ruG)zAfN|aC#|M?-7-?k8rBJ%9@!rRxl(+HomSsjO!wsyi9%NNm$Cc{?8|*PJgbxBC*WE@;~EknzXEu2LWwft%3lEWw!*wdD1$ zDW6U)ICGbmKR{y5mUt+X(nBmxwoFd-yi(i!)v45uZKr5@@eR`0Zk~LNvT_sj7PB z&*@<`4|HAa3coacKBA6wZL_^{FAV%=+B&wi>f($@!DXq=AjjWP)8?>fQ$8KD7TLpH zZJkdw{u@rZeH_NzxdXjG6wq&VyFvKBKh!F+*>k5fF*W--8t$zu(B5o>)?M#{zk7WXe7fn!??r^Xb>De-8cuw1)kNnr z!KTFRL&M0tdiCeDE`5LB%0yvGan4`nV()L?QvmXmE=T!zFVa;Gjq(qNp6rL;hIbOL zIv-;sAMZDjPq%NoFAI7fW2(}6*Nen<`P2;B1Oy8O4N;VNQv98EC7w4^4xYAq={}`L z9ueqzNNvw?ci~we=HC`|UqSCBA{^&>-@8>WJoP{gh#xx{h;=O(FFlxrEJTzx<5XosVgme za_;y#D}=vhiEz0M<|hMH>V@FthgTj4*9b*d9EaDfdD)H!580ql!6P(})b~c;neL12 zz1AtSWU5?LUcvA=7ZChb=Hq-68z&S+#1fEz8Rl#wFXa{`kS?Q=A{9O2wj&ViYZ~Lw z9^R%GQyUrIycYa(JiKN+o{KEv@Hx053$gWz!8-zq*xK=y`h7%)NApQ6uA11i=!U%Z z@Ux7ZI`)1*3tA$J(5K3GWO2of&m`M#HmeWf>7k^=^x+~;O@ClufvP_cYr5;L2fJv$&Iim}3EEUp%LD!$e^8UH(k`~*aWD5a3dvz+0vov9Gv+LexBgjy%ASU?} zF&jj(#M(ACO{_I?JXcE&fMS31G6FOC49G<2#CndhaNI;k%A9b)G{5Htneh8oDZ^vO zyAD4iFdAb;S_Nxfv-~NY)vk!>4HEo&I=> zCea^W8})^`@(c6i^CJMvlclzZLY$pa96*^ZZ5g|MnQedJtIAT&jlyH$!m19S1On}e zIT$%6wJI#Y3yh3G@Z?x$2iWvQ;%xR5h?wwX&5tPGWN0S9xa@HV7Az{HLobFs;c4TD>z@(t z@Ts{=&+iJ|?aP{cs#>zDnk%dBJL?`RtM2UU+U`p|Das|DQC6nu6IjK*k>1Ft4>iG- zKXjqtU?^c1VbV#8wo=JAk;LyQoD9EDQbEs({QvA6I| zc4bR;RR#4!OLu+83(9Aqt0BxHDx@TnngQS=?7KOUi~fLSWr}jQQ5R5ECt}bhlHKOt z)z-saQ_>XkQJK3X9JM2Est+w0r+**A61(%=gNj zIY{NsBJA4yhZZ8?ApAd~br{-J*{!j}@rHL+1k0_@ATkM4l-Ql;T4;Aqc3Yrmw=X4N zP_*n`w10T2&x5kR@1d(}3J{_Ud<$!yJ^TDBTDK<5qp4s4AUf*5Ed zYlE&s?-pC9K4mS3RzTN^oZlci|2?bPrQ@ zqiyw%7!0FU_YXVt-=Fo#`*mYHwxLh~LLmSR2&x2NU?wJ;hGI0oLe=0ejJmCSV4b?ugnxaiuzufeR-BDL50DGM4;0> z4}I%#wG(hRY5RwKhtvE<(x#sWY7-fLBkG3T7=FX6^_VgJ0O>QJ1TFfgef>DX>GxF3 z>rz+q6cdVWZwyUaLhu+-7d6Rx2V3kQ2TtdYhe7V2gTov{5RXpHCRSEA;e}*J*{fb+ zzvlQ<^chNwYs%p)Vo)dyB%hC_U5_i{IFYF`E1xrR_&9!&Gn=c@y}mQE^N7JthUyHT zbcGC{XQOY1RnrJ)z$oC7w-Il#Yd&{W=+ljbkP{dh^zLSlc|Q*N`b`sXE@+8_eGy?r zU4~KcRZ{De7onSz&JR%|hG5g%*$|6fM;l zFlaucqovoem(6e*;xtV-gWcyc?Vvh6O)koJ!!^U9M7WgcixK_;z3(&s;@kXjiT?56 zG0fz8OeGkaA`JEU5`fDeEe7&cum^V<0+{BWNX-2zfV)1ezMhtYyNdVDk zH-M`KO&$hXO~6gc!JXk=->03Op2u15$3duqwDZx#`hof4n1?k$5n7N~K02Rx8JDe7 z#kY~0M>Nj**3PK(>XGIq>KujB0mE$W@QDt*Z-Y?(ErQ%F`?xK}fIZ&W-bKaQAS#KK zAqDDx3zIk9t2N^=Lu1p4EH@?0=X*JA#COenAQ>%+tor^l=N~|eLcoowEP?rk8#4p} zlmr|QaHA1$?-RiHW$^&=@hBn)piDlReLW`lsgTQS$r%Pz{DBOJeBJ9};Qk!PrI=1iG!beW{SR}D7Pl&xI4}SfneRCQ0%CyfJ zu(nJB8sjee{PTylDd>q4q=`V4uH84R#Vv=Qme-$F_nwy5;%?L*C>n1+)AWImLmlqJ z82IcSYnpuAWF^(^*Pjc$Ysot)V!2HwXyh5{#|@B%qb8o3MWrp9nz5H^$IM^0HjMZB z=Fh*y@9o*^YW=QY5|!I-S2R`_c)~ET1E4Xgnpd==yRpq`KXdkXz5}>GiU`z4wVlJ7 z{r6w?6~7z+Oty=kP$P5EWFUvHYS1y&hOxc`_thSX+)Xa!yo)-JA{Se>C;Aw3(os-F zF#mxNowLTTdu%D-e7t7s)z9=aenv+x##`PlI3ZiUaa8=LYC{)2XpF;&dD+xfikh z7<6$y|Ki`m#mfBqD2%1!`qWg>*oYLQ2Z~)i#*K2RbMy$s&JXi=pjbV^C?Az!{-RiI z2VruLr~^L6Bc3npE0G`_Q_vM2B&HSmau|S1k0C03yOw+y%oB-Er&cVavG*owG~X_i zDZdDS^0icvQZY`SFJ(@fz#@u$bt=8f@^|6H8EIXh@TbGamaj*1UnGODuA-7z>WuhP z*?JV4PPDSs0##dtuMU^Xbz1agOZeGTGtJEQUQQ)tOxm!hD$xt8e&T^PqST%Cpq334Ix6JD+>z0Ty_XiBp*I>d@;wx0V^9?5k>o^t1Qj7(EQ zZt$_MRIOKO&h#!n)YTgG1g8oM=&m=}E!R4+E9Mz~rZ5@m_4Sn4j<6BxZgAlM6bvmX zOk+`@yWsFn2z)Kzoo3stZel1-2$o<4OCN$?NDHB-d{Quo#Q?sDiG4juvQ^zUA*3uFh8aah z%Qv#5Cr^mMf!K{Ak)uLtEj*Cr&JDM>6Tyo~-YT(qV$RGsfvsiRs_WQnV}!$GG>QHs+*U7)Nr=#@oG%VDaPWql8C6ch6veu0mDW7 zG!9h3&>W6A4B=!I5Ua!wGd>fgdRjD!JumZh63gsY!`v5L_?ihai*bU=Cb7`cy<&oQ z!Uea>6WRp|;;Ciu&sBKr72!XBEi;a|Et)Y70p})6968=9{q-5)ooWRn4%2_`AFf*S zFqu-QvrK>2@x|KT%OcZC3BXABPUpQ9+zh?nn~X*$N;XD?iDe)NB7`W|a(gG2zLuD2 zy!wqmN{**ZNh=tcNY?mqMo{!i66LK!J+?r5=UJ`WJM&1o_;wxrO%LCdlENI#lE9@O zdfN&S{Ofj)v0*H@v`-p7E~$B5FY$)6OUzt{@da012Ej-S%+(C8COg@S;2X|wjopje zHlahFrF7z)SK5JYd+G8)16-Tq=h2Rzs=`~8y3Sj^t>m6FPYk_lH>&DRvM@a;R30v) zo}qea`O@>r`?%-=hHzJOC$jI^0kO)GoEd@7s_piPI_jqc#doOEmB~DQ5q((n&|CN- zv=`;s_WfylVDT?w^%=?2FT3_Juaz@{uSu(7R$w(BY)jMqYnPrR;xknI^>IUMXq|V* zvF|S7_sVe_WUm$qj(p9JsCCOW?%NSIv2KIie}=b%j3C7iNQKYenwv{dlE7}}jrgd9 zYcN{$2H<%8=Z2qdq!nRk5C+%8o_^Bg;xz?!+TxeQ;%mLS3tR!J(gvhvx3b?fWCPnL zK`ZmTVgM{b z@s{ZADzwu$biI`gUWplpXi|iT7~n%ntU%>2k(ruiqicUG@p3V zj7n_8M=0}!e922m7-rKZc?~5uGv|1RwMncJMp)!Dw6f6$OGOVVI4pz0GH3Zp&n*u) zO}vF@mwv?i)3eCKNcw+y&t>o_DS0ws<;sd^eJWYpV|bsT@#=$ib%x`lWPonk>qdD7 zqpXqi$JTU`pFu#X`~ey`pd+m}r4)89_2O|O0>(!R3G#xOQ6k_&0{JKbBcZCkkfj2G z7G&y77CXi@Jk~-sf!<{AN3oJQD_K~bkhbFCyQ1WGRslyIh8^GZj2sl-ygcxQN`aZL(yG-r{lc%?#0(FLlxR>bL1|{veA`kZ)}&S@cF>=})N9 z2!@~x(wK6i6r}dI9TNO<@kQ+5vaEuqt$s<;nC>3w-!iM+VsOJRcW4X+OW*Er;*e>e zq7A{(Vch0UITYz;yi1I{Sy6P~d@TwsmpzQCzx(@FF9d4He$Z~DJwE5|6^x000{8dmg;+mJ&G}3%^I~Ec^S2-L>l+ZZb2k3V4bN325Jg~%P%s`bK%?GGB z4`R#Z4@frR;842UF_ZSzzZH5O!8qn)JpY4Ybk|3*n)={8W=Mmt^9gAYivlk))4c_c z=P3ji=Z1{q`?{5EEkiC}h+GqSbBDJy-FgZd1Y;WwXW|I{3W5UpfD;V{BhnZ%uVL*x$PdJ49 zSO(+3cNq1<<@=f#o#;Vhzcb>K+lK7x`H%G2E@WTA10g%mG(CWZ5_0Fh_-vTq^DPF! zA~&viDJkjem((&w$j8;8r?H`5M&8y$V)RF$0O5wvw`p7pIeGRE%H!bQLBOByq)h(( zOX~F}{oeIQ?AdmID}R&s=OIVWg856}6TzFd3Zsm|D@s_Ntz^SXSXO_S?Jb5aW;Q+I zb;TbrR-|ZQWca}YE$TFE^mV`TV!y@&=#aKAa|ne>I1FoxsO}GJ^#_bhQ%ZXPM19wu zml0e=0i@=UD(nXld>pv4kZOA`KtqKUwb%EXT=rZG*=yknEQ=@48tmIaVkgbn}duYft+%i z43^kGQQX};km@&*k8jMg2kXsvkA+ZRVlpz0(r^BPC2lb!yrgq&hh=hPl&WQvyJb`y znK&I^$*7m2Ps^y|j0EPxj*n%Wz2ddDqVOTII*udt8fet-Wp#68^@|Io*T7Q4P`XnOQ;0ztP0mt#fQX^j(oxRFFdmy0OD{Cu+)vKIan!C`&S^`oP*0{% z0q_AwUL6NLuYKfNNy=449)B8GKM<0G!1xq9##2pp+%N9kEw3XZ@8@0z2mHS>WViph z>nBoh%pV9rV!hPPfE2t5!Fi#?AkaAK}&Y$b+7`Rq8GBv)c zdj0aiF8!@Dwb1HCcKYYI0P0=yNygna9nmjh=g2b<%MSbqT2AA;BD?xRx8zfrkrGzo zNQyEDI;5#GCpMIAuqk|y8~~>HL<2`jA~oeYD^S>pX0H@7^<_Yf%=!3#csk3!CjYo^ zZ^SlWg8@o|bc2*ghyqH7gwie2AgFXVqq}1uT@Iv2gOtQTx}-r$KxuK;e%JN7??2&u zaz6OJKgV%|sqCd&>eansQ73+DXBi=D!~NZJDL6_G!OvM?9fRM-jZ9Hty84m&iu3VI zB=70hL>D@70&6k(r}&(iw8{3??u;wxBySHYz#J)D0|=7nj{`rqax~H-ZSx_u*JROl zM#Wk3ZTI)zBt}Kkjupl5j&MJMJ1Z}niK-HmBV3OqcT2N(40lU;MtFFGTP3b-qnRqa z5(^^zcXbP&v0_zAGvA6h92DW$ri0r2M)y*c>K%RZsylyG+q{DP;h*)qbTRzpFXbJb zQc8FoxxV<|`D_IC80|Au%P<+))HLnfa6j5Bt#WA#xm(mmt<*{-7R=d1J2#$h#C)zF z6aV(GY?zZ||I0*#!%=R-Yx;*?eS!+*;iv$WgUsgr%7fZ{)zI33sgKKU@`G}E+eWSe zPkucQ0P!6)-q@huf4jqb!qp=>cUaUr#3U`$B}v9`(zViiux3O?Pj`?>so!2ZFztGH z`u)4IsK)en0;!~70rDKwsQB%{@6i(8QKxzA*`dgS#Dk?T^OiBrzv}^;(36Uif2}qn zA$YG@`YG73cigotF72n^8FgjO*Q4}TCbg_AJE&GOrtF(1Zb#xSIvi}jHEr8_*0e53 ze!08YL1xGZAl<+0d-VnWR7@;HNcp(5F+MiEIzqe@7#6!xuSozT6TEAb{WJBrAk>-b z@Znh2r&MdmOMK{7Y?*lPM_cVt6TM*LulASt zL(_QbYyf6FKw#j%(i*1R=O=AT?Jbw>`{Y6{&d)9O8Zw&plLUi7ADJ)UEnl*|<3or= zl~z+T;$<^YzEVM7XeDA=FZ0 zfHs0lEO{~kt9RZ9QA~(ZN2I)*_EdtH$$GDDU{?YIYAm z@|C7fvG*iNNDRboyCLDp!fvMcs_2qkLg|T*h!gs%m|ICjRg&`;tB(qqU@14Bl7K)= zQ+z@_xxVBy#}em1dK78SFb-dT!#+u4?%`aFQ)Rl{%)U! zsbHTlOx0u;gq4asCmLfV{+K;w%_&0UfU%=$h?<`cec-~3PaDQU@F5dl@p<=Te8d`z z4%Q>0sH$zeEKBw^F;6Nlz2%yRBKGv-7Ta$XU30UbQdYxxx(9Pg(oY+ZP*c0}uxt_} zr*5G$n}bM(6vkHMb;!6=r53HxE9nt@(#I`wH2fA}@kZUBSgv>tq;Ej+>F1wpzS^-` zTf~^XaLzjd zYK9X#AyA)|9uZ|@qp57Y>TSj<16|+Qy7b2C4;D)Q_M{YP7w;Fe&Qt|>qs@PuQ3iQ9 zR(fVg&g`mswi0S`yUWl|e9Dag9id zwI=3k?WIUZC8fn!e6!MH3TWZ>9;UaVNYLwNdHT*?Bi9JkblE`@9>z}Ct@d~4F}CYb z)6a)s$D~KCF@sfBLg6e7hU3QzLy&kEWYzK(x2R#OPNyr9u*OpUgMM3x4Zq-@wIBiG zo2kr;vkaNG)q7?0qjok&^b!CBNlR8&N1CX)Zkz)H{bm>0dR=~*^j0wE^{|r7pCs_z z9*C=LICA_vl@DsC^ipDKCcIBi0EKU3d$W*EDjGHExca*&bCj3!KxqPLqxWNeMhx^A z1#n8WIGvxqlD6LTdlhIC)SdE0~A$6Uni@t?W|J-=OJ zj$mnE^9Ense>xY@?ff*o~Hm^2pyGZ?XDRzB+CUU4p&yTSBj}V;{DT=#QUy zH@9ycjSuP(b%th>nXC~sH*}NduR8d)joNS?Fm1FiMH??K`D_#CW9$B;3@vugXn3a; zv^i#?O;#UHw9Ph;pQrJmcPyH{$K5*L=1Ax4vi=i6X#cgp2$+UD{NtGFk-5zISNv1D z=XZ*N=KD_SoDoy9zk*&p?!Ef6Cn02isTklMC{grj0vY4a$V8^k$~vs6d&+EdJY`rGRt(hYB;eYz8m#hcd@yn43yG~MY_U75`X`#STc(Z?p7zPh{>HXr;Pm;<}eU5(Y>&KR6 zpM%F1YmX4~L8 z+hF?nFq*xvpONAFbHT>*;jbaVK8YdPYk;mnFK*=sK8Zj9gNQzA@07sw93ZPmx$i5%DI4*~e+SLUX;K zR#%C6%1KD>Xa|VLo2w8SqR^{I563}m+{ntR;#sp3TI3m&@ezJ{McEEWxP!#f(gfep zBnq{rh+QW;O zBpW>8Gv0;YX$uz-g-h3YbabSH-GEV70uTz85(GLJ1{@IO@X_UHTg3U$7MxroI;o?1 z%R@RUq9q@DXIyB%JSpf~2yJXhGi{Aj6V4*4Odq#Toa9Mattf<7X0zNBQmJOs>=)8U zXPZhDnmOb#tCrj!A(0&h04-p;LWB#bqJ!ZgfoPEMP3hr6hG=$<#K4+5^@ z7e>X3Ql7vzeRmIkk)&JzQOOf5Q3kD4zCLgMTx*_{QN@GBLgu!T)aVj}?27E{3P!q0 zQ{K{?wvr8RQXH{^)9ucl5Sq9WEUgs@NLBGKX7CZ`c&L6l;YkhViSDqC?x6L)xqkgr zs5sb&EKw!-olVTiJUxi6#H_M}g|0Apq=Lz(B0st&&!{rfp_2NhB44$#AiAQ+p)!xR zqL7xZy4ZKE1EZ(ea_%^GoKT6PWnK@Ht)-KtUT4sR`!as9ef{fa8st_?P4 z0aH354v!D@i95dEp_*t3cp!mPy7U%Lqf~dG zNarbeMO=|uSuu4}Gw)NMu~@@)TVv{2Z$Z*zVqE`5t;sy5kT3}JXbCEKTW^ZQmpZ|F zB1G-)Ry6P8Cfy82Jfa3=icuS~n17mlnnp%vvgb&dTZ3%4$gHPQX;@ZN+nzz1foH4{* z-5r((9p%`y4kQU4?pZAu4)Mo;(=gy}EusV8&R}Lp$Qn3Kt8HzmgJz`TvZ~{K1@O_g zHf||Gb7m-R43c670usAJg#fB3NTpA+$5O`SK3XI?2g}*fIt>4J*_^URguHN?}b|@#eKPtA%7!K){9hQm(h@Pl*3;?&u zHN;NhVxtl9w$TP4mb>%mQK+G{vJ)d>#}@ z8Tqk)AFP$8!C>NzdrIFpeK`ApszYjCTv(U5pezdlxAMylp}O5TF5#_IY3xEruo z*s*%JvU*};+&O3q-zCmp3l*-Mzsr^T{i1OK*T!G+1aPY>DnD0XTtLYj) zvQ2*oxva)v7Jm6}O5|_;S_Yp9Z9X-dvDN~-3c{~7>ShPby2b#lWY%0|(5^LGKqev; zWz;{2G&2*vgJ~D%KwMMXc7%Q1s5Y}Z4%(O<#MD!tUQcq~P-ydq+xc%TO zPz=@MKentyLJ$(yBIvyHAfFEB^eM@oKtNZwYxezD_eJ9iTX5x(_}zy1O#>~`w4?oO z83HFHA+AlQ;=0xBzxT3pkEnJ{=LA2|g^&(DMcxT=M?vrZ)21OEWU+6+W!g(@28|Fg z_!7wypbn~FVla6p7S*NS@x8V7;BfW(`=3NIS_fzGo8lP!AWY$r%j!Cu_+uhb9fnYu zX)Pvxsgj{NPLS}`KYY@s%h}B9#a_$Pwac#6sQlF_mct{cW6SjxykwQa-i%@^;P@YO z(=Y#*|I_RQLYRTjFaBSv1{ji7S+6Cm^I4`psWAWtzY_Q14ol!`gxKG1^v&#v1U+N; ziBFd&G}kvd^oH7(SrE`shLwr-_NgOt>C;ojcN8L;zfC?b7}Kb>1d+ZKIuLmFOI! z_=iXFSnTO{eFl~Sa#Qo`t-FhvB6@0O(CINj;nB4k?B+n==IsxBD+}mWs=CqQIrjtA z{zS5M1+cp;m;*`Ji6pE_{N4JQ*ddXq6A5(JTDD4C_SO0;fWiC7(0jD!z&U(`WICSD z-6-v(#4*`3=Eu95$La5`Be43s&+uSFCuDC?KnUQb*!-dZd!63}e0xIX3xGWp5e=QY zI1=nL|IAuT3H!=Sga?2(Swq9v*(Mp3ZSOS&O|xpCEU05?Qw-cT6cE&0M$?%|8%hZe zgdNxo&o-W=wrkZ|FC=e2%;4AlT5OoIqy9?L{djBP`|eBRv%u5EMm~K(lm2`KG@y8u zQrIAf#cqwD=QV*|ox|6~b6)+==3k^((7>2XE^N?H)I}^!t%-v7T@n3st!7*MA3BBX zSN3AvQ{@^toc7rOAm&~IRI;9JIFW%xT3oXAOesgq(q_lx!-w5KPlbdgHBWAHlZpKM zXiMvt9f4LO%7i<`a@g0kMl~U0bhCVhOD&K8aI@{0cgQffDC@?N91C`IDQ7%2y;u8T zMy*06MbUtQxgSxeO|_7Ds`!Uiz(*{>jQ#r0J-I*^l?Q+B#=oDxEHl3M^M{=acTZ!3 zb)P&hy^F6!57_m0TyyP^j!+08J<3voKiYSLh|Yq09pvzeZKu3wfAMo!dsIDXMqsKD zFP%fv!8qz5{T%>HOLA@lh@_qxYEjv0C4_`${ z2?*}yj|wW%c0I>J>433ybQkr(<#xSo%9}?<4 zFQPQAj#q~VD%9k8rRcu?52;VoI6B0X)woLd>xXx4|Cs)*oq71M6I!AeM8R7w8%!h- zbu?p;mZa4~*k#g#Pew`#UJnb`_#(fYtFu4>>Ni{CXa&=V>*EJ+ zFX{&0pO4$wVx*i^PV#@;wKgDArbv^C2F5KYNjIX1^#q)0)Z|3VG(U`14Nps1aXCrM z+LsorS)>q)VCG1a%ahU<6Uhc0A7WQE1&-6oCc%k@{frqVOz+~SO*}+Ps%`U0u&+D{ zDX?mE%Dv;V?g1YQdt~{=0g8Hp?+(riz$mI`2cphwe0*@m{#H>CZNIuIV z`}5$wwyw#e)*hIC-Pb3jGTW?ctAe^;7)`WJQRj|VRJu{0(>)nnNwG#V=EcXR8gj{`Dr*sl8b&C(*a|nR@Tm;1x(%y(wO}*Dv zUMxG2gUx!_W$h0cU8*d#=g9`+F*1mv8$|mZLVm{@l@eGS^>9fCW^prE-#5w{BuAVN zB(rO0oOEK-E@p54N7GQEj7R1oOEfSbJ1F2&M=5mD^UQ~tk1KPM!|oM}D%`a&@CpFR z+uQh~6%-=;$C1M;@r7~uTh@i6Se1X*mMnY_L7;NcTlLW#c0#+X&I%HaAQP1{UT+Oe zZ1|4tbGZ>wF%@7s4G_xLx-IZa7D3UFr0ir(#A#(1qKT=?eN+f|_z9SC!>B3B@gc|D z?4!CXkM5J0Sawn0KO$VLa5mvWHRif&T#WO8TG1QyC=&&6En+9-K9yIq2}-6t`9z5o z!fUtgp}2?I`eaf*jCTevYT46L`AuF-c87<2Uat;_W+0P;`Dk$rsF%Yz_VF9D*o(Ng zsrU`L@rdrK zR*7~}I5nh{?KUE-Xy=ic(#%Vp&7??tgAhWcn>|%xG==5gH^tEk2t5*81pYl%EE5~u zL4f=w&lk{GX*1szO9_zatyFYb7kX)If`_u8N=^G|N|$M9l|GXBJ|*dxodwe$RaN%TSl@ekn`zeOvDqhk3gOI? z1P*x6z0f!!XoI6!s;oN)0dw~={+=+~D0i`pI9BzY_j^2?}EcN%4$r3$s)m|vrdh}&+WP}p$Bv$nR3|FBrJro z_C)sa6Hc>NAMZ=Xtfomk5#Fhf^_Ghb91YmVxIzWj9n_L7DXn7DI)%r- z3B_5ZlJ+?dk8W#56_{kQ>_5kZUz0KuJJZlzUrw0Xe6-<=rm#a6Hyt=PsEsgCvQWlP zmXhE5=+JB^gZhv`gw!7fJp{O#!SW4_9_P1J1`-y^NPA#8?#0Y}(d%JisQO z6>2{;eQ~#r3LRbbyg2WV!TFjfRS5Z5Uy_(D?jiSHQ8#by{;O=d_aOt_i~5#k^(d-pQ5(*p4<9-H>{#4Q!?Gx*lpFQ z?51#o@{o-9o=$iCbI*acyRHI!7sfhyG?uo=6Ocp%&s?d{cKEY`4oN|j99NBC9|^vkzftFXWpYZrW7~?kV&fuGS1$u zt2N>G(^qppA|8i<>L|3khNLRRTUsjs%2wSP93-Lu4Ux}3VW>GpW znjn6gzf7fbw)v|o zc8u}PEf*HOwIXAzsnQt7MYQuTRzlyoTBQzO{p>?!3HG%}8Mo^W?{tHaEG2}X4bl7e zDw%2vAQ`9E7G^yrlHc$7i?EqbmG73C3DoTv&!PnS1bu1FoMuw>-!Ruoxod~03g_{cS}hxui5wsA@I9X54Q9g<8k$;_j4A*=PiGe_ut zP53L>+UYxR*{c7JO!sL(+bqmF^`#-pi_w6NiiV}P*Qe|jp;qVAmah~nr<3f{7|RRa zFvw>jBdNDVlB2*$G|cLpMI7`Yvq@Y?tLOH`g;pZGJDG8N6%ebazeK4;3WBTypA?=Q zx8$lT(zR?)DwrhDF_ z8CyubS8gsg5n}6Mm7HOlWf$+woE@0Y!{P3jUrwDT?5~m@g_w~{DSFYSlJg?*ed%s~ zfK;n3($B+(E#pi;TsO@~FOxfcpblbR^3*|px2o6_7YnWSet%PzS#VRaZlibn%383$ z&ZyGYgNf_V@9AWvQ_Z(2ALhy*ZkKFDnnz%skiq!NTsc-eKF2R>t8MmWemiq1DDM&) zsz%-3bgL#(;WiGP?({v{maAm+Va>$;03EvY{WpsCqYG90s(kN*8f}u@n2MH`M`jggcA8OV>jD?<1bY@-`KkLmMy%)VJO#vYai(Zv0E~h5PI;O<()LU$|E)>lv`T% zXw3)*n6Y7)<6*1_MhSoAteW?2j%Xq?-ybn`w3kVvFTZIV$5@O# z58WQGUMNfk-uc~NnxAzHA9fW;?kq29itYZm6!go#pHv_f&E}mapKSYdJQLzGsfN6r zD(k+gdl~$zZ}l(x5Sr+M)Kz&YrF* zk96*~UB}m?A9FV;!fEh7lWX=zR>c^dViTKt?mZ=Vk5*>=n;s<#YI=weFkz;&rG7>` z6(3@TF4_~nlxAG)55rd{p&==kpm*m!9NkJWiPHqwXiou{NUP6r7E$wf;_sW_&Ti$X{ zSZMo(YLw(pQ&nGZ+@tnVMU{vo3lZgI5kXKgz?jO@s^GhKXsBK?(MvTf|9&Bt%z6a&Wm(Ygf{nkNbgyYggNakaV?T0oJlIqB2&f6V zNF5TtrA;wC2`L%{mMcD99J2CB4eux5;mcakHta;jgTK+zu=!BL-u#XPx&4&0j!xA* z>n(abW>4|XK|9AK^%I`yHm|glX4sU@yO>nV``+TJ(U^OW@CdA5z8==))_Ywhowxt8 z`9?#*Cp9-EwV3jI)_CMNEV@H=!@;0(?4@c$8TDA@Yxn5al_QOnCPzE^a;jQ|oGFFY z{-5~R&dHj_cPjR7!3xxB@;gKK>Y}$Rtjp-;T6)iGQ)enoYT9Nj0%ks&36#k(E@&;f zV^9@x0y@l9FC%3=9!YlCy>(Na8^O*})fzNb+X&~(b;e($1>ASKXL_v|94ln}K9s)L z3{9~^nk@bDt;>_QYNiWjvrQ5u)o%}^ma5D=n!S!{Z0ClQ>p5ThE2+QJjYCrg)Oysw zDr)`ej8_glJnOF-Kbv+Pd|&mL`C{tN;VozQLfhp`aD3+5!`Z6&tMSVovVkLy3f~p< zfH?ZYN=+bQQD2v|(cf8POxLh-@oL~z52Yc>(Y6x4@DHz7sL9#?$k@DX!=uc7Mo1sX zKRwC&0SWCa(-8E5->2uv8GU`KV18%?5%unDz?E=o95?@J>UG`0-UZ5$=ym!~^=NL# zC(+lh5s)nO+eQ#2tMrm^KD)+#buoLNv(2z6^&_KKe_S-;!-2VLbZT6_wcGWX=nk`V z__%Y#2lx9jyOPgnCli}Q(+d688Fp#frT=lWaB;y!FGVW_j{X=DJMT>2%jW2c68)r` zXQ%MlXEH9nvDnec^iC(M>3;2*O~>%2u6TJrn`C3Q0-IB2yBBk{rbngek)4ug+wio8 z$EOF3me-236HD8_N`4csX>~PB&$C}31auDbS{jxqdv+_cIu2iaH(Wdp@Mzd&v{zBe z8dw~6w`rzx(Ii>K;K?=1YpugWq+hI+8+xGckGo1pKUsZeCem0a;Ak*$)7G5M@7Y|f z?S+>s3c*H34;})`AktePtbI%0>slwE_SH%4_^0Gy={>9T*ePR z92(BuiJ{45{YrHKBCx^_=J#h7x4U^({4bI4fy`G5%hp-LV<*HzlZ`P_bdIB@o6!&2 zrS>C9Uj5kY*$Dbi+|-`H$quJi)BXq0cjtWzNBa@qvSz*qcy@4Uyt%P?J+b=Qu{Q?# zaUTA6AAjPeht|mx&9{7(-t}blZv4FCan8zF-c@y(lOrrN=ZN3%gAp@XPD|NaLNd!c zx!QjuqjUc*(kA>WMJH~@9PfU2gKy^rG=Dl!Kiz!LF!WP_X{Qgld>$kxt9}DNtX?v^ zgboH=_+xL!Lj!g`HA`ai$tH9W;{)fQGXELGINi@5N*6=5e~-20u*FKopS~Li43HO^2P17=)u68-S>QRE^w}( zDDj|Fk2mGEcGZJHFl7pIyTJPr!RKXxR~f-Kk8!yS+_D?|nbYH~zH^GAHL5U(mCNbG zmf=rE2;WtR5>2qEa_D85cUwl_^4$A3)V3Om9$!Glxx!)bmt>$_u$5kzZBm$(SC~*{ z7_C>}t*2-1oTm=WJ4@;S*_MF8;1Fd?Pf#!^kA&|Vi3m}HhFUDQJyt=QN&=s z3C^q^ta6)WGWHWgresg510V3GTKLZ?Y-x6VZ=^xBxz9sIRIIlCl9Z-BqP;7~) zj0*GH3(>-Dl*&;fpm%ySrW|%o#Bz}f!ZrcL0(qq|Uk1agBx3pOf@^6)?@L6D40%5= z`0x@$)7>Jfm`y}U#2q|5H|4{S*Bs65+ zU#i7Pgy=Pl1q_=au_KS)fe_2lrYa!PmAsP_hSSxD0V$Xy`r)M5|7tKvAzA=FSJK6k z$YAc!z~Ka+=44ZQ1`8CEmJs}p)(Z?ZsS_Yoo)+NBlLeK{Qu5Bq_?fP2~CudWSEsno13YU zs<@D)DVg-)I`yManiVAG#w$&mTNeV(sr?DRpBSQXl9@~kSR%?z<4KYm&P&BEVtBn@o_eQT1o7P#pI+JYkatrSs?@UAeln?@AeiQ*3O%${ojHfWI>M7e}gS!q1T zZb{_UbCX{Ks>6_P03r-CK#Ev~Q?kIoJDXVFAE;l< zMh*ri6(6jF#6Q4SyFr1jB>^{Oe9WI zo0et}I&2r2z+J}A0)XLCB^G7H!9-lAP>o3#I~`zDt7xgaGSH{!vr$pIVF`dY)$V7B z`C_`&KEU>bY~X|pgCv`lt)TXaEkUHUP?zP7(7th{A_vgB!T~kBH9BpT%aS!UQZ;qF z`Gg~RuW$1BqqACk@~P~B9s5y3M51A(;bFaSs&UBM5#G-Wg$t_H#`j8ELtT-*a13#uPYgqc(|M&vY_(l;4P zH-U}61Ur-j)3tsXDS7e@z=FYpSweeNtE3gkbkn8>%Ea%7KKl7Vx<$>iSlSO)c;®usM#|8+M$%bN-%|4p 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 Hy8QnDu6(%i literal 0 HcmV?d00001 diff --git a/README.md b/README.md index 39fafd5..6843c25 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,9 @@ Alternatively, the content can be viewed in a web browser Duck Typing; Type Casting & Checking; Input Validation) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/01_exercises.ipynb) + (Towers of Hanoi) #### Videos From 76ef753ffa95b6bd45e617b5b609e04384b63a53 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 15 Oct 2020 18:35:12 +0200 Subject: [PATCH 057/142] Add initial version of chapter 04, part 2 --- 04_iteration/02_content.ipynb | 1300 +++++++++++++++++++++++++++++++++ README.md | 5 + 2 files changed, 1305 insertions(+) create mode 100644 04_iteration/02_content.ipynb diff --git a/04_iteration/02_content.ipynb b/04_iteration/02_content.ipynb new file mode 100644 index 0000000..7fb3eb3 --- /dev/null +++ b/04_iteration/02_content.ipynb @@ -0,0 +1,1300 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/02_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 4: Recursion & Looping (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "After learning about the concept of **recursion** in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb) of this chapter, we look at other ways of running code repeatedly, namely **looping** with the `for` and `while` statements. We start with the latter as it is more generic. Throughout this second part of the chapter, we revisit the same examples from the first part to show how recursion and looping are really two sides of the same coin." + ] + }, + { + "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 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", + "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**." + ] + }, + { + "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 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." + ] + }, + { + "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; must be positive\n", + " \"\"\"\n", + " while n != 0:\n", + " print(n)\n", + " n -= 1\n", + "\n", + " print(\"Happy new Year!\")" + ] + }, + { + "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 [PythonTutor ](http://pythontutor.com/visualize.html#code=def%20countdown%28n%29%3A%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." + ] + }, + { + "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 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` 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!" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "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", + " while a != b:\n", + " if a > b:\n", + " a -= b\n", + " else:\n", + " b -= a\n", + "\n", + " return a" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gcd(12, 4)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gcd(7, 7919)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### Efficiency of Algorithms (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5.32 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** is 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 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." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "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", + " while n != 1:\n", + " print(n, end=\" \")\n", + " if n % 2 == 0:\n", + " n //= 2 # //= to preserve the int type\n", + " else:\n", + " n = 3 * n + 1\n", + "\n", + " print(1)" + ] + }, + { + "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": 8, + "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": 9, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": 10, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": 11, + "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 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 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." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "elements = [0, 1, 2, 3, 4]" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 1 2 3 4 " + ] + } + ], + "source": [ + "index = 0\n", + "\n", + "while index < len(elements):\n", + " element = elements[index]\n", + " print(element, end=\" \")\n", + " index += 1\n", + "\n", + "del index" + ] + }, + { + "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 ](https://en.wikipedia.org/wiki/Boilerplate_code)** away. The variable that is automatically set by Python in each iteration of the loop (i.e., `element` in the example) is called the **target variable**." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 1 2 3 4 " + ] + } + ], + "source": [ + "for element in elements:\n", + " print(element, 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 look closely at the underlying effects in memory in [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb#Mapping)." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 1 2 3 4 " + ] + } + ], + "source": [ + "for element in range(5):\n", + " print(element, end=\" \")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "range" + ] + }, + "execution_count": 16, + "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 use to customize the sequence of integers even more." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 3 5 7 9 " + ] + } + ], + "source": [ + "for element in [1, 3, 5, 7, 9]:\n", + " print(element, end=\" \")" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 3 5 7 9 " + ] + } + ], + "source": [ + "for element in range(1, 10, 2):\n", + " print(element, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Containers vs. Iterables" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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 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", + "\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/develop/01_elements/03_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/develop/07_sequences/00_content.ipynb#Collections-vs.-Sequences), we formalize these two concepts and introduce many more. Finally, [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb) 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." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "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 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**." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Achim\" in first_names" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Alexander\" in first_names" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[0, 1, 2, 3, 4]" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "elements" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "3.0 in elements" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 24, + "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 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." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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, 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": 26, + "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, 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": 27, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "last_names = [\"Müller\", \"Meyer\", \"Mayer\", \"Schmitt\", \"Schmidt\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "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, end=\" \")" + ] + }, + { + "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.\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", + "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": 29, + "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", + " a = 0\n", + " b = 1\n", + " print(a, b, sep=\" \", end=\" \") # added for didactical purposes\n", + " for _ in range(i - 1):\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": 30, + "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": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fibonacci(12) # = 13th number" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "##### Efficiency of Algorithms (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Another more important advantage is that now we may calculate even big Fibonacci numbers *efficiently*." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "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": 31, + "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()` 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." + ] + }, + { + "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", + " product = 1 # because 0! = 1\n", + " for i in range(1, n + 1):\n", + " product *= i\n", + " print(product, end=\" \") # added for didactical purposes\n", + "\n", + " return product" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 2 6 " + ] + }, + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(3)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 2 6 24 120 720 5040 40320 362880 3628800 " + ] + }, + { + "data": { + "text/plain": [ + "3628800" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(10)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index 6843c25..911350d 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,11 @@ Alternatively, the content can be viewed in a web browser - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/01_exercises.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/01_exercises.ipynb) (Towers of Hanoi) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/02_content.ipynb) + (Looping with `while` & `for`; + Examples: Collatz Conjecture, Factorial, Euclid's Algorithm, & Fibonacci; + Containers vs. Iterables) #### Videos From e9b166a618230ce9c7451dc9caaab46ad849bc11 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 15 Oct 2020 18:54:15 +0200 Subject: [PATCH 058/142] Add initial version of chapter 04, part 3 --- 04_iteration/03_content.ipynb | 991 ++++++++++++++++++++++++++++++++++ README.md | 4 + 2 files changed, 995 insertions(+) create mode 100644 04_iteration/03_content.ipynb diff --git a/04_iteration/03_content.ipynb b/04_iteration/03_content.ipynb new file mode 100644 index 0000000..8cfb19f --- /dev/null +++ b/04_iteration/03_content.ipynb @@ -0,0 +1,991 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/03_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 4: Recursion & Looping (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While what we learned about the `for` and `while` statements in the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb) of this chapter suffices to translate any iterative algorithm into code, both come with some syntactic sugar to make life easier for the developer. This last part of the chapter shows how we can further customize the looping logic and introduces as \"trick\" for situations where we cannot come up with a stopping criterion in a `while`-loop." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Stopping Loops prematurely" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This section introduces additional syntax to customize `for` and `while` statements in our code even further. They are mostly syntactic sugar in that they do not change how a program runs but make its code more readable. We illustrate them for the `for` statement only. However, everything presented in this section also works for the `while` statement." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Example: Is the square of a number in `[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]` greater than `100`?" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's say we have a list of `numbers` and want to check if the square of at least one of its elements is greater than `100`. So, conceptually, we are asking the question if a list of numbers as a whole satisfies a certain condition." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A first naive implementation could look like this: We loop over *every* element in `numbers` and set an **indicator variable** `is_above`, initialized as `False`, to `True` once we encounter an element satisfying the condition.\n", + "\n", + "This implementation is *inefficient* as even if the *first* element in `numbers` has a square greater than `100`, we loop until the last element: This could take a long time for a big list.\n", + "\n", + "Moreover, we must initialize `is_above` *before* the `for`-loop and write an `if`-`else`-logic *after* it to check for the result. The actual business logic is *not* conveyed in a clear way." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "7 11 8 5 3 12 2 6 9 10 1 4 => at least one number satisfies the condition\n" + ] + } + ], + "source": [ + "is_above = False\n", + "\n", + "for number in numbers:\n", + " print(number, end=\" \") # added for didactical purposes\n", + " if number ** 2 > 100:\n", + " is_above = True\n", + "\n", + "if is_above:\n", + " print(\"=> at least one number satisfies the condition\")\n", + "else:\n", + " print(\"=> no number satisfies the condition\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### The `break` Statement" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Python provides the `break` statement (cf., [reference ](https://docs.python.org/3/reference/simple_stmts.html#the-break-statement)) that lets us stop a loop prematurely at any iteration. It is yet another means of controlling the flow of execution, and we say that we \"break out of a loop.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "7 11 => at least one number satisfies the condition\n" + ] + } + ], + "source": [ + "is_above = False\n", + "\n", + "for number in numbers:\n", + " print(number, end=\" \") # added for didactical purposes\n", + " if number ** 2 > 100:\n", + " is_above = True\n", + " break\n", + "\n", + "if is_above:\n", + " print(\"=> at least one number satisfies the condition\")\n", + "else:\n", + " print(\"=> no number satisfies the condition\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This is a computational improvement. However, the code still consists of *three* sections: Some initialization *before* the `for`-loop, the loop itself, and some finalizing logic. We prefer to convey the program's idea in *one* compound statement instead." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### The `else`-clause" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To express the logic in a prettier way, we add an `else`-clause at the end of the `for`-loop (cf., [reference ](https://docs.python.org/3/reference/compound_stmts.html#the-for-statement)). The `else`-clause is executed *only if* the `for`-loop is *not* stopped with a `break` statement *prematurely* (i.e., *before* reaching the *last* iteration in the loop). The word \"else\" implies a somewhat unintuitive meaning and may have better been named a `then`-clause. In most use cases, however, the `else`-clause logically goes together with some `if` statement in the loop's body.\n", + "\n", + "Overall, the code's expressive power increases. Not many programming languages support an optional `else`-branching for the `for` and `while` statements, which turns out to be very useful in practice." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "7 11 => at least one number satisfies the condition\n" + ] + } + ], + "source": [ + "for number in numbers:\n", + " print(number, end=\" \") # added for didactical purposes\n", + " if number ** 2 > 100:\n", + " is_above = True\n", + " break\n", + "else:\n", + " is_above = False\n", + "\n", + "if is_above:\n", + " print(\"=> at least one number satisfies the condition\")\n", + "else:\n", + " print(\"=> no number satisfies the condition\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Lastly, we incorporate the finalizing `if`-`else` logic into the `for`-loop, avoiding the `is_above` variable altogether." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "7 11 => at least one number satisfies the condition\n" + ] + } + ], + "source": [ + "for number in numbers:\n", + " print(number, end=\" \") # added for didactical purposes\n", + " if number ** 2 > 100:\n", + " print(\"=> at least one number satisfies the condition\")\n", + " break\n", + "else:\n", + " print(\"=> no number satisfies the condition\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Of course, if we choose the number an element's square has to pass to be larger, for example, to `200`, we have to loop over all `numbers`. There is *no way* to optimize this **[linear search ](https://en.wikipedia.org/wiki/Linear_search)** further." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "7 11 8 5 3 12 2 6 9 10 1 4 => no number satisfies the condition\n" + ] + } + ], + "source": [ + "for number in numbers:\n", + " print(number, end=\" \") # added for didactical purposes\n", + " if number ** 2 > 200:\n", + " print(\"=> at least one number satisfies the condition\")\n", + " break\n", + "else:\n", + " print(\"=> no number satisfies the condition\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## A first Glance at the **Map-Filter-Reduce** Paradigm" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Often, we process some iterable with numeric data, for example, a list of `numbers` as in this book's introductory example in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List) or, more realistically, data from a CSV file with many rows and columns.\n", + "\n", + "Processing numeric data usually comes down to operations that may be grouped into one of the following three categories:\n", + "\n", + "- **mapping**: transform a number according to some functional relationship $y = f(x)$\n", + "- **filtering**: throw away individual numbers (e.g., statistical outliers in a sample)\n", + "- **reducing**: collect individual numbers into summary statistics\n", + "\n", + "We study this **map-filter-reduce** paradigm extensively in [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb) after introducing more advanced data types that are needed to work with \"big\" data.\n", + "\n", + "Here, we focus on *filtering out* some numbers in a `for`-loop." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Example: A simple Filter" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Calculate the sum of all even numbers in `[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]` after squaring them and adding `1` to the squares:\n", + "\n", + "- \"*all*\" => **loop** over an iterable\n", + "- \"*even*\" => **filter** out the odd numbers\n", + "- \"*square and add $1$*\" => apply the **map** $y = f(x) = x^2 + 1$\n", + "- \"*sum*\" => **reduce** the remaining and mapped numbers to their sum" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "8 -> 65 12 -> 145 2 -> 5 6 -> 37 10 -> 101 4 -> 17 " + ] + }, + { + "data": { + "text/plain": [ + "370" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "total = 0\n", + "\n", + "for number in numbers:\n", + " if number % 2 == 0: # only keep even numbers\n", + " square = (number ** 2) + 1\n", + " print(number, \"->\", square, end=\" \") # added for didactical purposes\n", + " total += square\n", + "\n", + "total" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The above code is easy to read as it involves only two levels of indentation.\n", + "\n", + "In general, code gets harder to comprehend the more **horizontal space** it occupies. It is commonly considered good practice to grow a program **vertically** rather than horizontally. Code compliant with [PEP 8 ](https://www.python.org/dev/peps/pep-0008/#maximum-line-length) requires us to use *at most* 79 characters in a line!\n", + "\n", + "Consider the next example, whose implementation in code already starts to look unbalanced." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Example: Several Filters" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Calculate the sum of every third and even number in `[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]` after squaring them and adding `1` to the squares:\n", + "\n", + "- \"*every*\" => **loop** over an iterable\n", + "- \"*third*\" => **filter** out all numbers except every third\n", + "- \"*even*\" => **filter** out the odd numbers\n", + "- \"*square and add $1$*\" => apply the **map** $y = f(x) = x^2 + 1$\n", + "- \"*sum*\" => **reduce** the remaining and mapped numbers to their sum" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "8 -> 65 12 -> 145 4 -> 17 " + ] + }, + { + "data": { + "text/plain": [ + "227" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "total = 0\n", + "\n", + "for i, number in enumerate(numbers, start=1):\n", + " if i % 3 == 0: # only keep every third number\n", + " if number % 2 == 0: # only keep even numbers\n", + " square = (number ** 2) + 1\n", + " print(number, \"->\", square, end=\" \") # added for didactical purposes \n", + " total += square\n", + "\n", + "total" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With already three levels of indentation, less horizontal space is available for the actual code block. Of course, one could flatten the two `if` statements with the logical `and` operator, as shown in [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb#The-if-Statement). Then, however, we trade off horizontal space against a more \"complex\" `if` logic, and this is *not* a real improvement." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### The `continue` Statement" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A Pythonista would instead make use of the `continue` statement (cf., [reference ](https://docs.python.org/3/reference/simple_stmts.html#the-continue-statement)) that causes a loop to jump into the next iteration skipping the rest of the code block.\n", + "\n", + "The revised code fragment below occupies more vertical space and less horizontal space: A *good* trade-off.\n", + "\n", + "One caveat is that we need to negate the conditions in the `if` statements. Conceptually, we are now filtering \"out\" and not \"in.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "8 -> 65 12 -> 145 4 -> 17 " + ] + }, + { + "data": { + "text/plain": [ + "227" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "total = 0\n", + "\n", + "for i, number in enumerate(numbers, start=1):\n", + " if i % 3 != 0: # only keep every third number\n", + " continue\n", + " elif number % 2 != 0: # only keep even numbers\n", + " continue\n", + "\n", + " square = (number ** 2) + 1\n", + " print(number, \"->\", square, end=\" \") # added for didactical purposes \n", + " total += square\n", + "\n", + "total" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This is yet another illustration of why programming is an art. The two preceding code cells do the *same* with *identical* time complexity. However, the latter is arguably easier to read for a human, even more so when the business logic grows beyond two filters." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Indefinite Loops" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Sometimes we find ourselves in situations where we *cannot* know ahead of time how often or until which point in time a code block is to be executed." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Example: Guessing a Coin Toss" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's consider a game where we randomly choose a variable to be either \"Heads\" or \"Tails\" and the user of our program has to guess it.\n", + "\n", + "Python provides the built-in [input() ](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 particular event occurs, we `break` out of the loop.\n", + "\n", + "Let's look at a first and naive implementation." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import random" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Guess if the coin comes up as heads or tails: heads\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Ooops, it was tails\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Guess if the coin comes up as heads or tails: heads\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Yes, it was heads\n" + ] + } + ], + "source": [ + "while True:\n", + " guess = input(\"Guess if the coin comes up as heads or tails: \")\n", + "\n", + " if random.random() < 0.5:\n", + " if guess == \"heads\":\n", + " print(\"Yes, it was heads\")\n", + " break\n", + " else:\n", + " print(\"Ooops, it was heads\")\n", + " else:\n", + " if guess == \"tails\":\n", + " print(\"Yes, it was tails\")\n", + " break\n", + " else:\n", + " print(\"Ooops, it was tails\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This version exhibits two *severe* issues where we should improve on:\n", + "\n", + "1. If a user enters *anything* other than `\"heads\"` or `\"tails\"`, for example, `\"Heads\"` or `\"Tails\"`, the program keeps running *without* the user knowing about the mistake!\n", + "2. The code *intermingles* the coin tossing with the processing of the user's input: Mixing *unrelated* business logic in the *same* code block makes a program harder to read and, more importantly, maintain in the long run." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Example: Guessing a Coin Toss (revisited)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's refactor the code and make it *modular*.\n", + "\n", + "First, we divide the business logic into two functions `get_guess()` and `toss_coin()` that are controlled from within a `while`-loop.\n", + "\n", + "`get_guess()` not only reads in the user's input but also implements a simple input validation pattern in that the [strip() ](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`." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def get_guess():\n", + " \"\"\"Process the user's input.\n", + " \n", + " Returns:\n", + " guess (str / NoneType): either \"heads\" or \"tails\"\n", + " if the input can be parsed and None otherwise\n", + " \"\"\"\n", + " guess = input(\"Guess if the coin comes up as heads or tails: \")\n", + " # handle frequent cases of \"misspelled\" user input\n", + " guess = guess.strip().lower()\n", + "\n", + " if guess in [\"heads\", \"tails\"]:\n", + " return guess\n", + " return None" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`toss_coin()` models a fair coin toss when called with default arguments." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def toss_coin(p_heads=0.5):\n", + " \"\"\"Simulate the tossing of a coin.\n", + "\n", + " Args:\n", + " p_heads (optional, float): probability that the coin comes up \"heads\";\n", + " defaults to 0.5 resembling a fair coin\n", + "\n", + " Returns:\n", + " side_on_top (str): \"heads\" or \"tails\"\n", + " \"\"\"\n", + " if random.random() < p_heads:\n", + " return \"heads\"\n", + " return \"tails\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Second, we rewrite the `if`-`else`-logic to handle the case where `get_guess()` returns `None` explicitly: Whenever the user enters something invalid, a warning is shown, and another try is granted. We use the `is` operator and not the `==` operator as `None` is a singleton object.\n", + "\n", + "The `while`-loop takes on the role of **glue code** that manages how other parts of the program interact with each other." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Guess if the coin comes up as heads or tails: invalid\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Make sure to enter your guess correctly!\n" + ] + }, + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Guess if the coin comes up as heads or tails: Heads\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Yes, it was heads\n" + ] + } + ], + "source": [ + "while True:\n", + " guess = get_guess()\n", + " result = toss_coin()\n", + "\n", + " if guess is None:\n", + " print(\"Make sure to enter your guess correctly!\")\n", + " elif guess == result:\n", + " print(\"Yes, it was\", result)\n", + " break\n", + " else:\n", + " print(\"Ooops, it was\", result)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Now, the program's business logic is expressed in a clearer way. More importantly, we can now change it more easily. For example, we could make the `toss_coin()` function base the tossing on a probability distribution other than the uniform (i.e., replace the [random.random() ](https://docs.python.org/3/library/random.html#random.random) function with another one). In general, modular architecture leads to improved software maintenance." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index 911350d..2e7f606 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,10 @@ Alternatively, the content can be viewed in a web browser (Looping with `while` & `for`; Examples: Collatz Conjecture, Factorial, Euclid's Algorithm, & Fibonacci; Containers vs. Iterables) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/03_content.ipynb) + (Customizing Loops with `break` and `continue`; + Indefinite Loops) #### Videos From b73df81315e7e1d52fede4cdb92efae9261cccc3 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 15 Oct 2020 18:56:35 +0200 Subject: [PATCH 059/142] Add initial version of chapter 04's exercises, part 2 --- 04_iteration/04_exercises.ipynb | 403 ++++++++++++++++++++++++++++++++ README.md | 3 + 2 files changed, 406 insertions(+) create mode 100644 04_iteration/04_exercises.ipynb diff --git a/04_iteration/04_exercises.ipynb b/04_iteration/04_exercises.ipynb new file mode 100644 index 0000000..04654bc --- /dev/null +++ b/04_iteration/04_exercises.ipynb @@ -0,0 +1,403 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/04_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 4: Recursion & Looping (Coding Exercises)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read the [third part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb) of Chapter 4.\n", + "\n", + "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Throwing Dice" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this exercise, you will model the throwing of dice within the context of a guessing game similar to the one shown in the \"*Example: Guessing a Coin Toss*\" section in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb#Example:-Guessing-a-Coin-Toss).\n", + "\n", + "As the game involves randomness, we import the [random ](https://docs.python.org/3/library/random.html) module from the [standard library ](https://docs.python.org/3/library/index.html). To follow best practices, we set the random seed as well." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import random" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "random.seed(42)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A die has six sides that we labeled with integers `1` to `6` in this exercise. For a fair die, the probability for each side is the same.\n", + "\n", + "**Q1**: Model a `fair_die` as a `list` object!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fair_die = ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: What function from the [random ](https://docs.python.org/3/library/random.html) module that we have seen already is useful for modeling a single throw of the `fair_die`? Write a simple expression (i.e., one function call) that draws one of the equally likely sides! Execute the cell a couple of times to \"see\" the probability distribution!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's check if the `fair_die` is indeed fair. To do so, we create a little numerical experiment and throw the `fair_die` `100000` times. We track the six different outcomes in a `list` object called `throws` that we initialize with all `0`s for each outcome.\n", + "\n", + "**Q3**: Complete the `for`-loop below such that it runs `100000` times! In the body, use your answer to **Q2** to simulate a single throw of the `fair_die` and update the corresponding count in `throws`!\n", + "\n", + "Hints: You need to use the indexing operator `[]` and calculate an `index` in each iteration of the loop. Do do not actually need the target variable provided by the `for`-loop and may want to indicate that with an underscore `_`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "throws = [0, 0, 0, 0, 0, 0]\n", + "\n", + "for ... in ...:\n", + " ...\n", + " ...\n", + " ...\n", + "\n", + "throws" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`throws` contains the simulation results as absolute counts.\n", + "\n", + "**Q4**: Complete the `for`-loop below to convert the counts in `throws` to relative frequencies stored in a `list` called `frequencies`! Round the frequencies to three decimals with the built-in [round() ](https://docs.python.org/3/library/functions.html#round) function!\n", + "\n", + "Hints: Initialize `frequencies` just as `throws` above. How many iterations does the `for`-loop have? `6` or `100000`? You may want to obtain an `index` variable with the [enumerate() ](https://docs.python.org/3/library/functions.html#enumerate) built-in." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "frequencies = [0, 0, 0, 0, 0, 0]\n", + "\n", + "for ... in ...:\n", + " ...\n", + "\n", + "frequencies" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: How could we adapt the `list` object used above to model an `unfair_die` where `1` is as likely as `2`, `2` is twice as likely as `3`, and `3` is twice as likely as `4`, `5`, or `6`, who are all equally likely?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "unfair_die = ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: Copy your solution to **Q2** for the `unfair_die`! Execute the cell a couple of times to \"see\" the probability distribution!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: Copy and adapt your solutions to **Q3** and **Q4** to calculate the `frequencies` for the `unfair_die`!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "throws = [0, 0, 0, 0, 0, 0]\n", + "frequencies = [0, 0, 0, 0, 0, 0]\n", + "\n", + "for ... in ...:\n", + " ...\n", + " ...\n", + " ...\n", + "\n", + "for ... in ...:\n", + " ...\n", + "\n", + "frequencies" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: The built-in [input() ](https://docs.python.org/3/library/functions.html#input) allows us to ask the user to enter a `guess`. What is the data type of the object returned by [input() ](https://docs.python.org/3/library/functions.html#input)? Assume the user enters the `guess` as a number (i.e., \"1\", \"2\", ...) and not as a text (e.g., \"one\")." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "guess = input(\"Guess the side of the die: \")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "guess" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: Use a built-in constructor to cast `guess` as an `int` object!\n", + "\n", + "Hint: Simply wrap `guess` or `input(\"Guess the side of the die: \")` with the constructor you choose." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: What type of error is raised if `guess` cannot be cast as an `int` object?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: Write a `try` statement that catches the type of error (i.e., your answer to **Q10**) raised if the user's input cannot be cast as an `int` object! Print out some nice error message notifying the user of the bad input!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " ...\n", + "except ...:\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q12**: Write a function `get_guess()` that takes a user's input and checks if it is a valid side of the die! The function should *return* either an `int` object between `1` and `6` or `None` if the user enters something invalid.\n", + "\n", + "Hints: You may want to re-use the `try` statement from **Q11**. Instead of printing out an error message, you can also `return` directly from the `except`-clause (i.e., early exit) with `None`. So, the user can make *two* kinds of input errors and maybe you want to model that with two *distinct* `return None` statements. Also, you may want to allow the user to enter leading and trailing whitespace that gets removed without an error message." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_guess():\n", + " \"\"\"Process the user's input.\n", + " \n", + " Returns:\n", + " guess (int / NoneType): either 1, 2, 3, 4, 5 or 6\n", + " if the input can be parsed and None otherwise\n", + " \"\"\"\n", + " ...\n", + "\n", + " # Check if the user entered an integer.\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + "\n", + " # Check if the user entered a valid side.\n", + " ...\n", + " ...\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q13** Test your function for all *three* cases!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "get_guess()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q14**: Write an *indefinite* loop where in each iteration a `fair_die` is thrown and the user makes a guess! Print out an error message if the user does not enter something that can be understood as a number between `1` and `6`! The game should continue until the user makes a correct guess." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "...\n", + "...\n", + "...\n", + "\n", + "...\n", + "...\n", + "...\n", + "...\n", + "...\n", + "...\n", + "..." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index 2e7f606..209bbbc 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,9 @@ Alternatively, the content can be viewed in a web browser [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/03_content.ipynb) (Customizing Loops with `break` and `continue`; Indefinite Loops) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/04_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/04_exercises.ipynb) + (Throwing Dice) #### Videos From 5958b386876f690e3d858f9448f0ad709bc77645 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 15 Oct 2020 18:58:27 +0200 Subject: [PATCH 060/142] Add initial version of chapter 04's summary --- 04_iteration/05_summary.ipynb | 79 +++++++++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 80 insertions(+) create mode 100644 04_iteration/05_summary.ipynb diff --git a/04_iteration/05_summary.ipynb b/04_iteration/05_summary.ipynb new file mode 100644 index 0000000..981bfec --- /dev/null +++ b/04_iteration/05_summary.ipynb @@ -0,0 +1,79 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 4: Recursion & Looping (TL;DR)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Iteration** is about **running blocks of code repeatedly**.\n", + "\n", + "There are two redundant approaches to achieving that.\n", + "\n", + "First, we combine functions that call themselves with conditional statements. This concept is known as **recursion** and suffices to control the flow of execution in *every* way we desire. For a beginner, this approach of **backward** reasoning might not be intuitive, but it turns out to be a handy tool to have in one's toolbox.\n", + "\n", + "Second, the `while` and `for` statements are alternative and potentially more intuitive ways to express iteration as they correspond to a **forward** reasoning. The `for` statement is **syntactic sugar** that allows rewriting common occurrences of the `while` statement concisely. Python provides the `break` and `continue` statements as well as an optional `else`-clause that make working with the `for` and `while` statements even more convenient.\n", + "\n", + "**Iterables** are any **concrete data types** that support being looped over, for example, with the `for` statement. The idea behind iterables is an **abstract concept** that may or may not be implemented by any given concrete data type. For example, both `list` and `range` objects can be looped over. The `list` type is also a **container** as any given `list` objects \"contains\" references to other objects in memory. On the contrary, the `range` type does not reference any other object but instead creates *new* `int` objects \"on the fly\" (i.e., when being looped over)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index 209bbbc..f431c9a 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,7 @@ Alternatively, the content can be viewed in a web browser - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/04_exercises.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/04_exercises.ipynb) (Throwing Dice) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/05_summary.ipynb) #### Videos From f557daee31c5f3fe3a1fd0970ca65d98b00d919d Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 15 Oct 2020 19:00:14 +0200 Subject: [PATCH 061/142] Add initial version of chapter 04's review --- 04_iteration/06_review.ipynb | 268 +++++++++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 269 insertions(+) create mode 100644 04_iteration/06_review.ipynb diff --git a/04_iteration/06_review.ipynb b/04_iteration/06_review.ipynb new file mode 100644 index 0000000..b40647b --- /dev/null +++ b/04_iteration/06_review.ipynb @@ -0,0 +1,268 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 4: Recursion & Looping (Review Questions)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb), [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb), and the [third ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb) part of Chapter 4.\n", + "\n", + "Be concise in your answers! Most questions can be answered in *one* sentence." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Essay Questions " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: Solving a problem by **recursion** is not only popular in computer science and math. Name some examples from the fields of business or economics where problems are also solved in a **backward** fashion!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: Explain what **duck typing** means! Why can it cause problems? Why is it [not a bug but a feature](https://www.urbandictionary.com/define.php?term=It%27s%20not%20a%20bug%2C%20it%27s%20a%20feature)?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: What is **syntactic sugar**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: Describe in your own words why the **recursive** version of `fibonacci()`, the \"Easy at first Glance\" example in the chapter, is computationally **inefficient**! Why does the **iterative** version of `fibonacci()`, the \"Hard at first Glance\" example, run so much faster?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: What is the conceptual difference between a **container** and a **list**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: What is a good use case for the `for`-loop's optional `else`-clause?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## True / False Questions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Motivate your answer with *one short* sentence!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: When a **recursion** does **not** reach the base case, this is an example of the **early exit** strategy." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: Any programming language **without** looping constructs like the `for` or `while` statements is **not** Turing complete." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: A **recursive** formulation is the same as a **circular** one: The terms are **synonyms**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: Formulating a computational problem as a **recursion** results in an **efficient** implementation." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: Whereas a **recursion** may accidentally result in a **never-ending** program, `while`-loops and `for`-loops are guaranteed to **terminate**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q12**: Before writing **any** kind of **loop**, we **always** need to think about a **stopping criterion** ahead of time." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q13**: **Container** types such as `list` objects are characterized by their **support** for **being looped over**, for example as in:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```python\n", + "for element in container:\n", + " # do something for every element\n", + " ...\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index f431c9a..bb5114f 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,7 @@ Alternatively, the content can be viewed in a web browser [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/04_exercises.ipynb) (Throwing Dice) - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/05_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/06_review.ipynb) #### Videos From b3213e40458e9e8d7c17d6b2355a2fa7e5149391 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 15 Oct 2020 23:32:24 +0200 Subject: [PATCH 062/142] Add initial version of chapter 05, part 1 --- 00_intro/00_content.ipynb | 2 +- 05_numbers/00_content.ipynb | 2454 +++++++++++++++++++++++++++++++++++ README.md | 8 + 3 files changed, 2463 insertions(+), 1 deletion(-) create mode 100644 05_numbers/00_content.ipynb diff --git a/00_intro/00_content.ipynb b/00_intro/00_content.ipynb index dbe3f49..d3e1fae 100644 --- a/00_intro/00_content.ipynb +++ b/00_intro/00_content.ipynb @@ -768,7 +768,7 @@ "**Part B: Managing Data and Memory**\n", "\n", "- How is data stored in memory?\n", - " - *Chapter 5*: Numbers & Bits\n", + " - *Chapter 5*: [Numbers & Bits ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb)\n", " - *Chapter 6*: Text & Bytes\n", " - *Chapter 7*: Sequential Data\n", " - *Chapter 8*: Map, Filter, & Reduce\n", diff --git a/05_numbers/00_content.ipynb b/05_numbers/00_content.ipynb new file mode 100644 index 0000000..0a106c4 --- /dev/null +++ b/05_numbers/00_content.ipynb @@ -0,0 +1,2454 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/00_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 5: Numbers & Bits" + ] + }, + { + "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 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/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/develop/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/develop/07_sequences/00_content.ipynb), [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb), [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb), [Chapter 10 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/10_arrarys/00_content.ipynb) then cover the more \"complex\" data types, including, for example, the `list` type. Finally, [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb) 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/develop/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/develop/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/develop/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 add an [Appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/03_appendix.ipynb) where we 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: 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/develop/01_elements/00_content.ipynb#%28Arithmetic%29-Operators).\n", + "\n", + "One way to create `int` objects is by simply writing its value as a literal with the digits `0` to `9`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "a = 42" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Just like any other object, the `42` has an identity, a type, and a value." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "94857615440928" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(a)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A nice feature in newer Python versions is using underscores `_` as (thousands) separators in numeric literals. For example, `1_000_000` evaluates to `1000000` in memory; the `_`s are ignored by the interpreter." + ] + }, + { + "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": [ + "We may place the `_`s anywhere we want." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "123456789" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1_2_3_4_5_6_7_8_9" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "It is syntactically invalid to write out leading `0`s in numeric literals. The reason for that will become apparent in the next section." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "leading zeros in decimal integer literals are not permitted; use an 0o prefix for octal integers (, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 042\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m leading zeros in decimal integer literals are not permitted; use an 0o prefix for octal integers\n" + ] + } + ], + "source": [ + "042" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Another way to create `int` objects is with the [int() ](https://docs.python.org/3/library/functions.html#int) built-in that casts `float` or properly formatted `str` objects as integers. Then, decimals are truncated (i.e., \"cut off\")." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(42.11)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(42.87)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Whereas the integer division operator `//` \"rounds\" towards negative infinity (cf., the \"*(Arithmetic) Operators*\" section in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#%28Arithmetic%29-Operators)), the [int() ](https://docs.python.org/3/library/functions.html#int) built-in rounds towards `0`." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-42" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(-42.87)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When casting `str` objects as `int`s, the [int() ](https://docs.python.org/3/library/functions.html#int) built-in is less forgiving. We must not include any decimals as shown by the `ValueError`. Yet, leading and trailing whitespace is gracefully ignored." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(\"42\")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "invalid literal for int() with base 10: '42.0'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"42.0\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: invalid literal for int() with base 10: '42.0'" + ] + } + ], + "source": [ + "int(\"42.0\")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(\" 42 \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `int` type follows all rules we know from math, apart from one exception: Whereas mathematicians to this day argue what the term $0^0$ means (cf., this [article ](https://en.wikipedia.org/wiki/Zero_to_the_power_of_zero)), programmers are pragmatic about this and simply define $0^0 = 1$." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0 ** 0" + ] + }, + { + "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, `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:" + ] + }, + { + "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 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$) as $2^0 + 2^1 = 1 + 2 = 3$. So the binary representation of $3$ is $00~00~00~11$. To borrow some terminology from linear algebra, the $3$ is a linear combination of the digits where the coefficients are either $0$ or $1$: $3 = 0*128 + 0*64 + 0*32 + 0*16 + 0*8 + 0*4 + 1*2 + 1*1$. It is *guaranteed* that there is exactly *one* such combination for each number between $0$ and $255$.\n", + "\n", + "As each bit in the binary representation is one of two values, we say that this representation has a base of $2$. Often, the base is indicated with a subscript to avoid confusion. For example, we write $3_{10} = 00000011_2$ or $3_{10} = 11_2$ for short omitting leading $0$s. A subscript of $10$ implies a decimal number as we know it from elementary school.\n", + "\n", + "We use the built-in [bin() ](https://docs.python.org/3/library/functions.html#bin) function to obtain an `int` object's binary representation: It returns a `str` object starting with `\"0b\"` indicating the binary format and as many $0$s and $1$s as are necessary to encode the integer omitting leading $0$s." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b11'" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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 create an `int` object, for example, with the value of `3`." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 16, + "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` to obtain an `int` object with the same value." + ] + }, + { + "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": [ + "0b11" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Another example is the integer `123` that is the sum of $64 + 32 + 16 + 8 + 2 + 1$: Thus, its binary representation is the sequence of bits $01~11~10~11$, or to use our new notation, $123_{10} = 1111011_2$." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1111011'" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(123)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Analogous to typing `123` into a code cell, we may write `0b1111011`, or `0b_111_1011` to make use of the underscores, and create an `int` object with the value `123`." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "123" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0b_111_1011" + ] + }, + { + "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": 20, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b0'" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1'" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b10'" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(2)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b11111111'" + ] + }, + "execution_count": 23, + "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 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, `789` is encoded with ten bits and $789_{10} = 1100010101_2$." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1100010101'" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "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 coefficients are one of *ten* values, and the base is now $10$. For example, the number $123$ can be expressed as $0*1000 + 1*100 + 2*10 + 3*1$. So, the binary representation follows the same logic as the decimal system taught in elementary school. The decimal system is intuitive to us humans, mostly as we learn to count with our *ten* fingers. The $0$s and $1$s in a computer's memory are therefore no rocket science; they only feel unintuitive for a beginner." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Arithmetic with Bits" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Adding two numbers in their binary representations is straightforward and works just like we all learned addition in elementary school. Going from right to left, we add the individual digits, and ..." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 + 2" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1 + 0b10 = 0b11'" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(1) + \" + \" + bin(2) + \" = \" + bin(3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... if any two digits add up to $2$, the resulting digit is $0$ and a $1$ carries over." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 + 3" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1 + 0b11 = 0b100'" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(1) + \" + \" + bin(3) + \" = \" + bin(4)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Multiplication is also quite easy. All we need to do is to multiply the left operand by all digits of the right operand separately and then add up the individual products, just like in elementary school." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "4 * 3" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b100 * 0b11 = 0b1100'" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(4) + \" * \" + bin(3) + \" = \" + bin(12)" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b100 * 0b1 = 0b100'" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(4) + \" * \" + bin(1) + \" = \" + bin(4) # multiply with first digit" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b100 * 0b10 = 0b1000'" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(4) + \" * \" + bin(2) + \" = \" + bin(8) # multiply with second digit" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [Further Resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/06_resources.ipynb) section at the end of this chapter provides video tutorials on addition and multiplication in binary. Subtraction and division are a bit more involved but essentially also easy to understand." + ] + }, + { + "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 `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." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0x0'" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0x1'" + ] + }, + "execution_count": 34, + "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": 35, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0x3'" + ] + }, + "execution_count": 35, + "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": 36, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0xa'" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0xf'" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(15)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The binary representation of `123`, `0b_111_1011`, can be viewed as *two* groups of four bits, $0111$ and $1011$, that are encoded as $7$ and $\\text{b}$ in hexadecimal (cf., table above)." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1111011'" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(123)" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0x7b'" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(123)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To obtain a *new* `int` object with the value `123`, we call the [int() ](https://docs.python.org/3/library/functions.html#int) built-in with a properly formatted `str` object and `base=16` as arguments." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "123" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "int(\"0x7b\", base=16)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Alternatively, we could use a literal notation instead." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "123" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0x7b" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0x0'" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0xff'" + ] + }, + "execution_count": 43, + "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 numbers like `789`." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0x315'" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(789)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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." + ] + }, + { + "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 $0$s and $1$s in memory (cf., [Two's Complement ](https://en.wikipedia.org/wiki/Two%27s_complement)), Python manages that for us, and we do not look into the theory here for brevity. We have learned all that a practitioner needs to know about how integers are modeled in a computer. The [Further Resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/06_resources.ipynb) section at the end of this chapter provides a video tutorial on how the [Two's Complement ](https://en.wikipedia.org/wiki/Two%27s_complement) idea works.\n", + "\n", + "The binary and hexadecimal representations of negative integers are identical to their positive counterparts except that they start with a minus sign `-`. However, as the video tutorial at the end of the chapter reveals, that is *not* how the bits are organized in memory." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'-0b11'" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(-3)" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'-0x3'" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(-3)" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'-0b11111111'" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(-255)" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'-0xff'" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hex(-255)" + ] + }, + { + "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, 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 practitioner regularly. Yet, it is worthwhile to have heard about them as they form the basis of all of arithmetic in computers.\n", + "\n", + "The first operator is the **bitwise AND** operator `&`: It looks at the bits of its two operands, `11` and `13` in the example, in a pairwise fashion and if *both* operands have a $1$ in the *same* position, the resulting integer will have a $1$ in this position as well. Otherwise, the resulting integer will have a $0$ in this position. The binary representations of `11` and `13` both have $1$s in their respective first and fourth bits, which is why `bin(11 & 13)` evaluates to `Ob_1001` or `9`." + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "9" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "11 & 13" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1011 & 0b1101'" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(11) + \" & \" + bin(13) # to show the operands' bits" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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": [ + "`0b_1001` is the binary representation of `9`." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "9" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0b_1001" + ] + }, + { + "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 `0b_1101` or `13`." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "13" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "9 | 13" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1001 | 0b1101'" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(9) + \" | \" + bin(13) # to show the operands' bits" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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": [ + "`0b_1101` evaluates to an `int` object with the value `13`." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "13" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0b_1101" + ] + }, + { + "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 exponentiation operator `**`! In the example, `9 ^ 13`, only the third bit differs between the two operands, which is why it evaluates to `0b_100` omitting the leading $0$." + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "9 ^ 13" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b1001 ^ 0b1101'" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(9) + \" ^ \" + bin(13) # to show the operands' bits" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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": [ + "`0b_100` evaluates to an `int` object with the value `4`." + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0b_100" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The **bitwise NOT** operator `~`, sometimes also called **inversion** operator, is said to \"flip\" the $0$s into $1$s and the $1$s into $0$s. However, it is based on the aforementioned [Two's Complement ](https://en.wikipedia.org/wiki/Two%27s_complement) convention and `~x = -(x + 1)` by definition (cf., the [reference ](https://docs.python.org/3/reference/expressions.html#unary-arithmetic-and-bitwise-operations)). The full logic behind this, while actually quite simple, is considered out of scope in this book.\n", + "\n", + "We can at least verify the definition by comparing the binary representations of `7` and `-8`: They are indeed the same." + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-8" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "~7" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "~7 == -(7 + 1) # = Two's Complement" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'-0b1000'" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(~7)" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'-0b1000'" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(-(7 + 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": 65, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "~7 + 7" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Lastly, the **bitwise left and right shift** operators, `<<` and `>>`, shift all the bits either to the left or to the right. This corresponds to multiplying or dividing an integer by powers of `2`.\n", + "\n", + "When shifting left, $0$s are filled in." + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "28" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "7 << 2" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b111'" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(7)" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b11100'" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(7 << 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "28" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0b_1_1100" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When shifting right, some bits are always lost." + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "7 >> 1" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b111'" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(7)" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0b11'" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bin(7 >> 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0b_11" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index bb5114f..1afc65c 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,14 @@ Alternatively, the content can be viewed in a web browser (Throwing Dice) - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/05_summary.ipynb) - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/06_review.ipynb) +- **Part B: Managing Data and Memory** + - *Chapter 5*: Numbers & Bits + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/00_content.ipynb) + (`int` Type; + Binary & Hexadecimal Representations; + Bit Arithmetic; + Bitwise Operators) #### Videos From 4d8d6fe240c2abfb46e23ba84b38ba62e31f0955 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 15 Oct 2020 23:36:37 +0200 Subject: [PATCH 063/142] Fix wrong numbering and missing Binder link --- 01_elements/07_resources.ipynb | 7 +++++++ README.md | 12 ++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/01_elements/07_resources.ipynb b/01_elements/07_resources.ipynb index c73cc49..9d46189 100644 --- a/01_elements/07_resources.ipynb +++ b/01_elements/07_resources.ipynb @@ -1,5 +1,12 @@ { "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/07_resources.ipynb)." + ] + }, { "cell_type": "markdown", "metadata": { diff --git a/README.md b/README.md index 1afc65c..b553332 100644 --- a/README.md +++ b/README.md @@ -46,13 +46,13 @@ Alternatively, the content can be viewed in a web browser Variables & References; Mutability; Expressions & Statements) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/03_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/04_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/04_exercises.ipynb) (Python as a Calculator) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/04_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/05_review.ipynb) - - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/06_resources.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/06_resources.ipynb) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/05_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/06_review.ipynb) + - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/07_resources.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/07_resources.ipynb) - *Chapter 2*: Functions & Modularization - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/00_content.ipynb) From 1923297b0d76ac6b2c4865afddc4ddfdd1c0eb3c Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 15 Oct 2020 23:44:16 +0200 Subject: [PATCH 064/142] Add initial version of chapter 05, part 2 --- 05_numbers/01_content.ipynb | 2180 +++++++++++++++++++++++++++++++++++ README.md | 7 + 2 files changed, 2187 insertions(+) create mode 100644 05_numbers/01_content.ipynb diff --git a/05_numbers/01_content.ipynb b/05_numbers/01_content.ipynb new file mode 100644 index 0000000..af18f07 --- /dev/null +++ b/05_numbers/01_content.ipynb @@ -0,0 +1,2180 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/01_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 5: Numbers & Bits (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In this second part of the chapter, we look at the `float` type in detail. It is probably the most commonly used one in all of data science, even across programming languages." + ] + }, + { + "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 before, 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-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 in the first place.\n", + "\n", + "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": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "b = 42.0" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139923238853936" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(b)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "float" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(b)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42.0" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As with `int` literals, we may use underscores `_` to make longer `float` objects easier to read." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.123456789" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0.123_456_789" + ] + }, + { + "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 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": 6, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42.0" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42.0" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42.0" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"42\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Leading and trailing whitespace is ignored ..." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42.87" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\" 42.87 \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... but not whitespace in between." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "could not convert string to float: '42. 87'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfloat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"42. 87\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: could not convert string to float: '42. 87'" + ] + } + ], + "source": [ + "float(\"42. 87\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`float` objects are implicitly created as the result of dividing an `int` object by another with the division operator `/`." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.3333333333333333" + ] + }, + "execution_count": 11, + "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: Python uses the \"broader\" representation." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42.0" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "40.0 + 2" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42.0" + ] + }, + "execution_count": 13, + "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 **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": 14, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1.23" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1.23e0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 15, + "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": 16, + "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": 17, + "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 unsuccessfully tries to look up a variable named `e0`." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'e0' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0me0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mNameError\u001b[0m: name 'e0' is not defined" + ] + } + ], + "source": [ + "e0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "So, to write $10^0$ in Python, we need to think of it as $1*10^0$ and write `1e0`." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1.0" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1e0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To express thousands of something (i.e., $10^3$), we write `1e3`." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1000.0" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1e3 # = thousands" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Similarly, to express, for example, milliseconds (i.e., $10^{-3} s$), we write `1e-3`." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.001" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1e-3 # = milli" + ] + }, + { + "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` 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": 22, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "nan" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"nan\") # also float(\"NaN\")" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "inf" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"+inf\") # also float(\"+infinity\") or float(\"infinity\")" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "inf" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"inf\") # also float(\"+inf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-inf" + ] + }, + "execution_count": 25, + "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": 26, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"nan\") == float(\"nan\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Another caveat is that any arithmetic involving a `nan` object results in `nan`. In other words, the addition below **fails silently** as no error is raised. As this also happens in accordance with the [IEEE 754 ](https://en.wikipedia.org/wiki/IEEE_754) standard, we *need* to be aware of that and check any data we work with for any `nan` occurrences *before* doing any calculations." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "nan" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 + float(\"nan\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "On the contrary, as two values go to infinity, there is no such concept as difference and *everything* compares equal." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 28, + "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": 29, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "inf" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"inf\") + 42" + ] + }, + { + "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": [ + "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": 31, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "inf" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "42 * float(\"inf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 32, + "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": 33, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "inf" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"inf\") ** 42" + ] + }, + { + "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": [ + "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": 35, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "inf" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "-42 * float(\"-inf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 36, + "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. So, if we (accidentally or unknowingly) do this on a real dataset, we do *not* see any error messages, and our program may continue to run with non-meaningful results! This is another example of a piece of code **failing silently**." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "nan" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(\"inf\") + float(\"-inf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "nan" + ] + }, + "execution_count": 38, + "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\" 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 `1` to `1e15` and `1e16`, respectively. In the latter case, the `1` somehow gets \"lost.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1000000000000001.0" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1e15 + 1" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1e+16" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1e16 + 1" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Interactions between sufficiently large and small `float` objects are not the only source of imprecision." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "from math import sqrt" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2.0000000000000004" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sqrt(2) ** 2" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.30000000000000004" + ] + }, + "execution_count": 43, + "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": 44, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sqrt(2) ** 2 == 2" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 45, + "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 benchmark the absolute value of 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": 46, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "threshold = 1e-15" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "abs((sqrt(2) ** 2) - 2) < threshold" + ] + }, + { + "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": [ + "abs((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 arbitrary precision. To exemplify it, let's view a couple of `float` objects with `50` digits. This analysis reveals that almost no `float` number is precise! After 14 or 15 digits \"weird\" things happen. As we see further below, the \"random\" digits ending the `float` numbers do *not* \"physically\" exist in memory! Rather, they are \"calculated\" by the [format() ](https://docs.python.org/3/library/functions.html#format) function that is forced to show `50` digits.\n", + "\n", + "The [format() ](https://docs.python.org/3/library/functions.html#format) function is different from the [format() ](https://docs.python.org/3/library/stdtypes.html#str.format) method on `str` objects introduced in the next chapter (cf., [Chapter 6 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/00_content.ipynb#format%28%29-Method)): Yet, both work with the so-called [format specification mini-language ](https://docs.python.org/3/library/string.html#format-specification-mini-language): `\".50f\"` is the instruction to show `50` digits of a `float` number." + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.10000000000000000555111512312578270211815834045410'" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "format(0.1, \".50f\")" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.20000000000000001110223024625156540423631668090820'" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "format(0.2, \".50f\")" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.29999999999999998889776975374843459576368331909180'" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "format(0.3, \".50f\")" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.33333333333333331482961625624739099293947219848633'" + ] + }, + "execution_count": 52, + "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 `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", + "\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." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "roughly_a_third = round(1 / 3, 5)" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.33333" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "roughly_a_third" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.33333000000000001517008740847813896834850311279297'" + ] + }, + "execution_count": 55, + "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: Both are powers of $2$ in disguise." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.12500000000000000000000000000000000000000000000000'" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "format(0.125, \".50f\")" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.25000000000000000000000000000000000000000000000000'" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "format(0.25, \".50f\")" + ] + }, + { + "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": [ + "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": [ + "To understand these subtleties, we need to look at the **[binary representation of floats ](https://en.wikipedia.org/wiki/Double-precision_floating-point_format)** and review the basics of the **[IEEE 754 ](https://en.wikipedia.org/wiki/IEEE_754)** standard. On modern machines, floats are modeled in so-called double precision with $64$ bits that are grouped as in the figure below. The first bit determines the sign ($0$ for plus, $1$ for minus), the next $11$ bits represent an $exponent$ term, and the last $52$ bits resemble the actual significant digits, the so-called $fraction$ part. The three groups are put together like so:" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "$$float = (-1)^{sign} * 1.fraction * 2^{exponent-1023}$$" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A $1.$ is implicitly prepended as the first digit, and both, $fraction$ and $exponent$, are stored in base $2$ representation (i.e., they both are interpreted like integers above). As $exponent$ is consequently non-negative, between $0_{10}$ and $2047_{10}$ to be precise, the $-1023$, called the exponent bias, centers the entire $2^{exponent-1023}$ term around $1$ and allows the period within the $1.fraction$ part be shifted into either direction by the same amount. Floating-point numbers received their name as the period, formally called the **[radix point ](https://en.wikipedia.org/wiki/Radix_point)**, \"floats\" along the significant digits. As an aside, an $exponent$ of all $0$s or all $1$s is used to model the special values `nan` or `inf`.\n", + "\n", + "As the standard defines the exponent part to come as a power of $2$, we now see why `0.125` is a *precise* float: It can be represented as a power of $2$, i.e., $0.125 = (-1)^0 * 1.0 * 2^{1020-1023} = 2^{-3} = \\frac{1}{8}$. In other words, the floating-point representation of $0.125_{10}$ is $0_2$, $1111111100_2 = 1020_{10}$, and $0_2$ for the three groups, respectively." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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$ 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 $1023$ and separated by a `\"p\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "one_eighth = 1 / 8" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0x1.0000000000000p-3'" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "one_eighth.hex()" + ] + }, + { + "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": 61, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 8)" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "one_eighth.as_integer_ratio()" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0x1.555475a31a4bep-2'" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "roughly_a_third.hex()" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(3002369727582815, 9007199254740992)" + ] + }, + "execution_count": 63, + "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 power of $2$ and thus a *precise* `float` number." + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "zero = 0.0" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0x0.0p+0'" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "zero.hex()" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(0, 1)" + ] + }, + "execution_count": 66, + "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/develop/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": 67, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "roughly_a_third.is_integer()" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 68, + "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 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." + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import sys" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "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": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sys.float_info" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index b553332..66d7135 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,13 @@ Alternatively, the content can be viewed in a web browser Binary & Hexadecimal Representations; Bit Arithmetic; Bitwise Operators) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/01_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/01_content.ipynb) + (`float` Type; + Floating-point Standard; + Special Values; + Imprecision; + Binary & Hexadecimal Representations) #### Videos From bf168d49706a404b6e7ab550b76e116ac23cf15b Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Fri, 16 Oct 2020 00:00:32 +0200 Subject: [PATCH 065/142] Add initial version of chapter 05, part 3 --- 05_numbers/02_content.ipynb | 1515 +++++++++++++++++++++++++++++++++++ README.md | 5 + 2 files changed, 1520 insertions(+) create mode 100644 05_numbers/02_content.ipynb diff --git a/05_numbers/02_content.ipynb b/05_numbers/02_content.ipynb new file mode 100644 index 0000000..89ff220 --- /dev/null +++ b/05_numbers/02_content.ipynb @@ -0,0 +1,1515 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/02_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 5: Numbers & Bits (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In the third part of this chapter, we first look at the lesser known `complex` type. Then, we introduce a more abstract classification scheme for the numeric types." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The `complex` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "**What is the solution to $x^2 = -1$ ?**" + ] + }, + { + "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$ 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}$.\n", + "\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 on the y-axis." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "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 $\\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`. 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": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "x = 1j" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140084727448400" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(x)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "complex" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(x)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1j" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To verify that it solves the equation, let's raise it to the power of $2$." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x ** 2 == -1" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Often, we write an expression of the form $a + b\\textbf{i}$." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(2+0.5j)" + ] + }, + "execution_count": 6, + "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": 7, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(2+0.5j)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "complex(2, 0.5)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "By omitting the second argument, we set the imaginary part to $0$." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(2+0j)" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "complex(2) " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The arguments to [complex() ](https://docs.python.org/3/library/functions.html#complex) may be any numeric type or properly formated `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(2+0.5j)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "complex(\"2+0.5j\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 10, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "c1 = 1 + 2j\n", + "c2 = 3 + 4j" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(4+6j)" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c1 + c2" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(-2-2j)" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c1 - c2" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(2+2j)" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c1 + 1" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(0.5-4j)" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "3.5 - c2" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(5+10j)" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "5 * c1" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(0.5+0.6666666666666666j)" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c2 / 6" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(-5+10j)" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c1 * c2" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(0.44+0.08j)" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c1 / c2" + ] + }, + { + "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": 19, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.0" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x.real" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1.0" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x.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": 21, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-1j" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x.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/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 Numerical 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/develop/04_iteration/02_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", + "- $\\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, 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 of the respective following sets, 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 data types introduced in this chapter and its appendix are all *(im)perfect* 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 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 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", + "- `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": 22, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "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": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dir(numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Duck Typing (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The primary purpose of ABCs is to classify the *concrete* data types and standardize how they behave. This guides us as the programmers in what kind of behavior we should expect from objects of a given data type. In this context, ABCs are not reflected in code but only in our heads.\n", + "\n", + "For, example, as all numeric data types are `Complex` numbers in the abstract sense, they all work with the built-in [abs() ](https://docs.python.org/3/library/functions.html#abs) function (cf., [documentation ](https://docs.python.org/3/library/numbers.html#numbers.Complex)). While it is intuitively clear what the [absolute value ](https://en.wikipedia.org/wiki/Absolute_value) (i.e., \"distance\" from $0$) of an integer, a fraction, or any real number is, [abs() ](https://docs.python.org/3/library/functions.html#abs) calculates the equivalent of that for complex numbers. That concept is called the [magnitude ](https://en.wikipedia.org/wiki/Magnitude_%28mathematics%29) of a number, and is really a *generalization* of the absolute value.\n", + "\n", + "Relating back to the concept of **duck typing** mentioned in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb#Duck-Typing), `int`, `float`, and `complex` objects \"walk\" and \"quack\" alike in context of the [abs() ](https://docs.python.org/3/library/functions.html#abs) function." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "abs(-1)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42.87" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "abs(-42.87)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "5.0" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "abs(3 + 4j)" + ] + }, + { + "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": 27, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "100" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "round(123, -2)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "round(42.1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`Complex` numbers are two-dimensional. So, rounding makes no sense here and leads to a `TypeError`. So, in the context of the [round() ](https://docs.python.org/3/library/functions.html#round) function, `int` and `float` objects \"walk\" and \"quack\" alike whereas `complex` objects do not." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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;36m1\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m2j\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: type complex doesn't define __round__ method" + ] + } + ], + "source": [ + "round(1 + 2j)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Goose Typing" + ] + }, + { + "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": 30, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(1 / 10, float)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A `float` object is a generic `Number` in the abstract sense but may also be seen as a `Complex` or `Real` number." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(1 / 10, numbers.Number)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(1 / 10, numbers.Complex)" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "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": 34, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(1 / 10, numbers.Rational)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, if we model `1 / 10` as a `Fraction` object (cf., [Appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/03_appendix.ipynb#The-Fraction-Type)), it is recognized as a `Rational` number." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [], + "source": [ + "from fractions import Fraction" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(Fraction(\"1/10\"), numbers.Rational)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Replacing *concrete* data types with ABCs is particularly valuable in the context of \"type checking:\" The revised version of the `factorial()` function below allows its user to take advantage of *duck typing*: If a real but non-integer argument `n` is passed in, `factorial()` tries to cast `n` as an `int` object with the [int() ](https://docs.python.org/3/library/functions.html#int) built-in.\n", + "\n", + "Two popular and distinguished Pythonistas, [Luciano Ramalho ](https://github.com/ramalho) and [Alex Martelli ](https://en.wikipedia.org/wiki/Alex_Martelli), coin the term **goose typing** to specifically mean using the built-in [isinstance() ](https://docs.python.org/3/library/functions.html#isinstance) function with an ABC (cf., Chapter 11 in this [book](https://www.amazon.com/Fluent-Python-Concise-Effective-Programming/dp/1491946008) or this [summary](https://dgkim5360.github.io/blog/python/2017/07/duck-typing-vs-goose-typing-pythonic-interfaces/) thereof)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### Example: [Factorial ](https://en.wikipedia.org/wiki/Factorial) (revisited)" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "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", + " if set to False, the decimals in n are ignored\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 != int(n) and strict:\n", + " raise TypeError(\"n is not integer-like; it has non-zero decimals\")\n", + " n = int(n)\n", + " else:\n", + " raise TypeError(\"Factorial is only defined for integers\")\n", + "\n", + " if n < 0:\n", + " raise ValueError(\"Factorial is not defined for negative integers\")\n", + " elif n == 0:\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": 38, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "factorial(3)" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 40, + "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 come with decimals that are then truncated. By default, this is not allowed and results in a `TypeError`." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "n is not integer-like; it has non-zero decimals", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfactorial\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m3.1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mReal\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mstrict\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 19\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"n is not integer-like; it has non-zero decimals\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 20\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 21\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: n is not integer-like; it has non-zero decimals" + ] + } + ], + "source": [ + "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": 42, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 42, + "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` because they are neither an `Integral` nor a `Real` number." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "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 20\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 21\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 22\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 23\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 24\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: Factorial is only defined for integers" + ] + } + ], + "source": [ + "factorial(1 + 2j)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index 66d7135..d9adfa3 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,11 @@ Alternatively, the content can be viewed in a web browser Special Values; Imprecision; Binary & Hexadecimal Representations) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/02_content.ipynb) + (`complex` Type; + Numerical Tower; + Duck vs. Goose Typing) #### Videos From 586941fdbaa6c948d0aa8b92ee6c10866cb34416 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Fri, 16 Oct 2020 00:10:13 +0200 Subject: [PATCH 066/142] Add initial version of chapter 05's appendix --- 05_numbers/03_appendix.ipynb | 1504 ++++++++++++++++++++++++++++++++++ README.md | 4 + 2 files changed, 1508 insertions(+) create mode 100644 05_numbers/03_appendix.ipynb diff --git a/05_numbers/03_appendix.ipynb b/05_numbers/03_appendix.ipynb new file mode 100644 index 0000000..3db9422 --- /dev/null +++ b/05_numbers/03_appendix.ipynb @@ -0,0 +1,1504 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/03_appendix.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 5: Numbers & Bits (Appendix)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this appendix, we look at the `Decimal` and `Fraction` types that can be used as replacements for the built-in `float` type mitigating its imprecision. The content is put in an appendix as the data science practitioner can live without knowing about it for quite some time. Eventually, when working with financial data, for example, knowing how to not use the `float` type in a bad way pays off." + ] + }, + { + "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 ](https://docs.python.org/3/library/index.html) provides a [Decimal ](https://docs.python.org/3/library/decimal.html#decimal.Decimal) type that may be used to represent any real number to a user-defined level of precision: \"User-defined\" does *not* mean an infinite or exact precision! The `Decimal` type merely allows us to work with a number of bits *different* from the $64$ as specified for the `float` type and also to customize the rounding rules and some other settings.\n", + "\n", + "We import the `Decimal` type and also the [getcontext() ](https://docs.python.org/3/library/decimal.html#decimal.getcontext) function from the [decimal ](https://docs.python.org/3/library/decimal.html) module." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "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": 2, + "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": 2, + "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` 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": 3, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('42')" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('0.1')" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"0.1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('0.001')" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"1e-3\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 6, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('0.1000000000000000055511151231257827021181583404541015625')" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(0.1) # do not do this!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With the `Decimal` type, the imprecisions in the arithmetic and equality comparisons go away." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('0.3')" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"0.1\") + Decimal(\"0.2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"0.1\") + Decimal(\"0.2\") == Decimal(\"0.3\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`Decimal` numbers *preserve* the **significant digits**, even in cases where this is not needed." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('0.30000')" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"0.10000\") + Decimal(\"0.20000\")" + ] + }, + { + "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": [ + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('42')" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "21 + Decimal(21)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('42.0')" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "10 * Decimal(\"4.2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('0.1')" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(1) / 10" + ] + }, + { + "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": 14, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.10000000000000000000000000000000000000000000000000'" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "format(Decimal(1) / 10, \".50f\")" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.10000000000000000555111512312578270211815834045410'" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "format(1 / 10, \".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 by **failing loudly**." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": [ + "To preserve the precision for more advanced mathematical functions, `Decimal` objects come with many **methods bound** on them. For example, [ln() ](https://docs.python.org/3/library/decimal.html#decimal.Decimal.ln) and [log10() ](https://docs.python.org/3/library/decimal.html#decimal.Decimal.log10) take the logarithm while [sqrt() ](https://docs.python.org/3/library/decimal.html#decimal.Decimal.sqrt) calculates the square root. The methods always return a *new* `Decimal` object. We must never use the functions in the [math ](https://docs.python.org/3/library/math.html) module in the [standard library ](https://docs.python.org/3/library/index.html) with `Decimal` objects as they do *not* preserve precision." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('2')" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(100).log10()" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('1.414213562373095048801688724')" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(2).sqrt()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 19, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('1.999999999999999999999999999')" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "two = Decimal(2).sqrt() ** 2\n", + "\n", + "two" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, the [quantize() ](https://docs.python.org/3/library/decimal.html#decimal.Decimal.quantize) method allows us to [quantize](https://www.dictionary.com/browse/quantize) (i.e., \"round\") a `Decimal` number at any precision that is *smaller* than the set precision. It takes the number of decimals to the right of the period of the `Decimal` argument we pass in and rounds accordingly.\n", + "\n", + "For example, as the overall imprecise value of `two` still has an internal precision of `28` digits, we can correctly round it to *four* decimals (i.e., `Decimal(\"0.0000\")` has four decimals)." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('2.0000')" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "two.quantize(Decimal(\"0.0000\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We can never round a `Decimal` number and obtain a greater precision than before: The `InvalidOperation` exception tells us that *loudly*." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "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[0mtwo\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquantize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"1e-28\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mInvalidOperation\u001b[0m: []" + ] + } + ], + "source": [ + "two.quantize(Decimal(\"1e-28\"))" + ] + }, + { + "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": 22, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "two.quantize(Decimal(\"0.0000\")) == 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`." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(Decimal(2).sqrt() ** 2).quantize(Decimal(\"0.0000\")) == 2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`nan` and positive and negative `inf` exist as well, and the same remarks from the discussion of the `float` type apply." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('NaN')" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"nan\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`Decimal(\"nan\")`s never compare equal to anything, not even to themselves." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"nan\") == Decimal(\"nan\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Infinity is larger than any concrete number." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('Infinity')" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"inf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('-Infinity')" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"-inf\")" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Decimal('Infinity')" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Decimal(\"inf\") + 42" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 29, + "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. Here, **failing loudly** is a good thing as it prevents us from working with invalid results." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "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": 31, + "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 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)." + ] + }, + { + "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/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." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "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 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." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(1, 3)" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Fraction(1, 3) # 1 / 3 with \"full\" precision" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(1, 3)" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Fraction(\"1/3\") # 1 / 3 with \"full\" precision" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(3333333333, 10000000000)" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Fraction(\"0.3333333333\") # 1 / 3 with a precision of 10 significant digits" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(3333333333, 10000000000)" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Fraction(\"3333333333e-10\") # scientific notation is also allowed" + ] + }, + { + "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": 37, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(3, 2)" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Fraction(3, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(3, 2)" + ] + }, + "execution_count": 38, + "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": 39, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(1, 10)" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Fraction(Decimal(\"0.1\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`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": 40, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(3602879701896397, 36028797018963968)" + ] + }, + "execution_count": 40, + "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. The result is always a *new* `Fraction` object." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(7, 4)" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Fraction(3, 2) + Fraction(1, 4)" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(1, 2)" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Fraction(5, 2) - 2" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(1, 1)" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "3 * Fraction(1, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Fraction(1, 1)" + ] + }, + "execution_count": 44, + "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! This is another example of code **failing silently**." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.1" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "10.0 * Fraction(1, 100) # do not do this!" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.10000000000000000555111512312578270211815834045410'" + ] + }, + "execution_count": 46, + "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)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index d9adfa3..4d3340b 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,10 @@ Alternatively, the content can be viewed in a web browser (`complex` Type; Numerical Tower; Duck vs. Goose Typing) + - [appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/03_appendix.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/03_appendix.ipynb) + (`Decimal` Type; + `Fraction` Type) #### Videos From 50aa1804623869dd2fe292c9df2b6460abb4aa06 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Fri, 16 Oct 2020 00:11:18 +0200 Subject: [PATCH 067/142] Add initial version of chapter 05's summary --- 05_numbers/04_summary.ipynb | 85 +++++++++++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 86 insertions(+) create mode 100644 05_numbers/04_summary.ipynb diff --git a/05_numbers/04_summary.ipynb b/05_numbers/04_summary.ipynb new file mode 100644 index 0000000..45c7c29 --- /dev/null +++ b/05_numbers/04_summary.ipynb @@ -0,0 +1,85 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 5: Numbers & Bits (TL;DR)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "There exist three numeric types in core Python:\n", + "- `int`: a near-perfect model for whole numbers (i.e., $\\mathbb{Z}$); inherently precise\n", + "- `float`: the \"gold\" standard to approximate real numbers (i.e., $\\mathbb{R}$); inherently imprecise\n", + "- `complex`: layer on top of the `float` type to approximate complex numbers (i.e., $\\mathbb{C}$); inherently imprecise\n", + "\n", + "Furthermore, the [standard library ](https://docs.python.org/3/library/index.html) provides two more types that can be used as substitutes for the `float` type:\n", + "- `Decimal`: similar to `float` but allows customizing the precision; still inherently imprecise\n", + "- `Fraction`: a near-perfect model for rational numbers (i.e., $\\mathbb{Q}$); built on top of the `int` type and therefore inherently precise\n", + "\n", + "The *important* takeaways for the data science practitioner are:\n", + "\n", + "1. **Do not mix** precise and imprecise data types, and\n", + "2. actively expect `nan` results when working with `float` numbers as there are no **loud failures**.\n", + "\n", + "The **numerical tower** is Python's way of implementing various **abstract** ideas of what numbers are in mathematics." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index 4d3340b..098db41 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,7 @@ Alternatively, the content can be viewed in a web browser [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/03_appendix.ipynb) (`Decimal` Type; `Fraction` Type) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/04_summary.ipynb) #### Videos From dfaa1709353824ad2113742cc59b036539f81ea0 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Fri, 16 Oct 2020 00:19:15 +0200 Subject: [PATCH 068/142] Add initial version of chapter 05's review --- 05_numbers/05_review.ipynb | 327 +++++++++++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 328 insertions(+) create mode 100644 05_numbers/05_review.ipynb diff --git a/05_numbers/05_review.ipynb b/05_numbers/05_review.ipynb new file mode 100644 index 0000000..db49191 --- /dev/null +++ b/05_numbers/05_review.ipynb @@ -0,0 +1,327 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 5: Numbers & Bits (Review Questions)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb), [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/01_content.ipynb), and the [third ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/02_content.ipynb) part of Chapter 5. Some questions regard the [Appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/03_appendix.ipynb); that is indicated with a **\\***.\n", + "\n", + "Be concise in your answers! Most questions can be answered in *one* sentence." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Essay Questions " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Answer the following questions briefly with *at most* 300 characters per question!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: In what way is the **binary representation** of `int` objects *similar* to the **decimal system** taught in elementary school?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: Why may objects of type `bool` be regarded a **numeric type** as well?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: Colors are commonly expressed in the **hexadecimal system** in websites (cf., the [HTML ](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": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: What does it mean for a code fragment to **fail silently**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: Explain why the mathematical set of all real numbers $\\mathbb{R}$ can only be **approximated** by floating-point numbers on a computer!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: How do we deal with a `float` object's imprecision if we need to **check for equality**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: What data type, built-in or from the [standard library ](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": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: How can **abstract base classes**, for example, as defined in the **numerical tower**, be helpful in enabling **duck typing**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## True / False Questions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Motivate your answer with *one short* sentence!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: The precision of `int` objects depends on how we choose to represent them in memory. For example, using a **hexadecimal representation** gives us $16^8$ digits whereas with a **binary representation** an `int` object can have *at most* $2^8$ digits." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: With the built-in [round() ](https://docs.python.org/3/library/functions.html#round) function, we obtain a *precise* representation for any `float` object if we can live with *less than* $15$ digits of precision." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: As most currencies operate with $2$ or $3$ decimals (e.g., EUR $9.99$), the `float` type's limitation of *at most* $15$ digits is *not* a problem in practice." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q12**: The [IEEE 754 ](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": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q13**: The following code fragment raises an `InvalidOperation` exception. That is an example of code **failing loudly**.\n", + "```python\n", + "float(\"inf\") + float(\"-inf\")\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q14**: Python provides a `scientific` type (e.g., `1.23e4`) that is useful mainly to model problems in the domains of physics or astrophysics." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15**: From a practitioner's point of view, the built-in [format() ](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": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q16\\***: 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": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q17\\***: 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": [ + " < your answer >" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index 098db41..48c9171 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,7 @@ Alternatively, the content can be viewed in a web browser (`Decimal` Type; `Fraction` Type) - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/04_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/05_review.ipynb) #### Videos From 57f615a33253eb01d9c44abffb9c77c059af3340 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Fri, 16 Oct 2020 00:21:40 +0200 Subject: [PATCH 069/142] Add initial version of chapter 05's further resources --- 05_numbers/06_resources.ipynb | 313 ++++++++++++++++++++++++++++++++++ README.md | 2 + 2 files changed, 315 insertions(+) create mode 100644 05_numbers/06_resources.ipynb diff --git a/05_numbers/06_resources.ipynb b/05_numbers/06_resources.ipynb new file mode 100644 index 0000000..87343f1 --- /dev/null +++ b/05_numbers/06_resources.ipynb @@ -0,0 +1,313 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/06_resources.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 5: Numbers & Bits (Further Resources)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Working with Bits" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The two videos below show how addition and multiplication works with numbers in their binary representations. Subtraction is a bit more involved as we need to understand how negative numbers are represented in binary with the concept of [Two's Complement ](https://en.wikipedia.org/wiki/Two%27s_complement) first. A video on that is shown further below. Division in binary is actually also quite simple." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAUDBAcICAgICAgJCAgIBwgICAgICAgICAgICAgICAgICAgIChALCAgOCQgIDRUODhERExMTCAsXGBYSGBASExIBBQUFBwYHDwkJDxcVEhUXFRMVFxUYFRUVFRUVFRcYFRUVGBUXFRIVFhUXFxcVFRUXGBYVFxUVFRUVFRYVFRUSFf/AABEIAWgB4AMBIgACEQEDEQH/xAAdAAEAAQUBAQEAAAAAAAAAAAAAAQQFBgcIAwIJ/8QAVxAAAQQBAwIDBAQHCQsJCQEAAQACAwQRBQYSEyEHCDEUIkFRCTJhcRUjNkJSgZEzYnJzdHWhtLUWJDQ1Y3aCorGzwjdDRFNVh5LBxCaDk5SjssPR1Bj/xAAbAQEBAQEAAwEAAAAAAAAAAAAAAQIDBAYHBf/EAC8RAQEAAgAEBAIJBQAAAAAAAAABAhEDEiExBAVBUTJxBiJhgZGhsdHwFCNSweH/2gAMAwEAAhEDEQA/AOMkREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCEU4TCCUREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQFUdEdHqd89Thj4Y48h/5qnVZAc1pR+hLE79ThI0/08VrGb/NvCb38qoyiIssCIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAAiq6E7mnixjHSPcA1zm8iM9sNDuwz88Jq3DrP444h2PdxjIADsY+HIFb5fq7b5fq7UiKqsRBjGD1e4c3fvWn6jcD4kd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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from IPython.display import YouTubeVideo\n", + "YouTubeVideo(\"RgklPQ8rbkg\", width=\"60%\")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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OVfTdF7GINmCsLIdz60jfXrt2239Ctd+POs6+odQ5LMVopYIMi+u6OKxw6rRDUr1zz6bnN3LoXHsT2IWDogKw3kCw2Rn1W21VeY6uMpWHZN227ZYrDDFDVP9Z04jlH/lXH4KvKs95ZPHrS2kMNNWlo5Szk71mSzcmhhpCu4tHTq145X2GydBsY5buYSHTS7AjYIOx+US197TkaWnoZN4sQz2y+1pBBu2WbV2P+IdHVcXf/en12G1UF2+s9QWMrkbuStO5T5K3NZl7ucGmZ5eI2cu4jY0hjR8GsaPguoQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQERAgKeP+Sy/Rfh7kMlxk4+zVj62ZmnZw/8GPs6X8vZvr3W7dKaKx2ObtDCJZXNIfYsNbJK7f6TR22Yz7mgffv6rkw4WWXd571T8S8n6fej+LP2x/zfl979FYiEW+daeFVO2HTUS2pOdz0tj7LIfzW7mH8rdx/VWmtQ4C3Ql6NuF0TvxSdiyQD1dHIPde309D8e+ymeGWHl2/S/W+U9Sx/c5fF88b5/3PrHVoiLD9YRNvgiAiIgIiICIiAiljSewBJAJ2Hc7NBc4/kABJ/IoQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQF2mko2vv0WPaHMkvVWva4btc107A5rgexBBIXVrttG/6xx/6QqfvEas8uHmLrg5We1/st3SwV+aB1iCpPLXjcWOlhifI1rmtaSHCMEtADm99tgCtweXGTHCrZG8TbxsHq9QsEprcGdPhv3MXLqb/AH+vwXy8teajDLePc4CTqe1wNP47XNZFKG/e0xxn/wBQ/Yvp49aHidGMnViAmEscdqONoAm672xRy8f+96jmNJ+IfufTv2s8t3or516T6f8AsnLY+q8v8dkvVjfvq+886s8ffX/ihBWnzdmPFx9VryzeOmwyNfOGjrGJkQPL3vXj+NyWqPHn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P+1d9y1iiDYevPGPUeTyd2+zK5GnHdsySQ062QtRQ1od+MMDGRPazZkbWN3AHIgk9ys88G/MOcfi8th9RxXc7SzLXNYX2zJYhbNC6CzH1bRcem5gic0N24ua8/jdtAIgsR4L+YytQxDdO6lxLc5iIhtX3EUk0UbX9SOB8NkdOeNj+7HcmuZsANwG8fj4ueYKjbixVHTeDrYijgclBlazpoIOt7ZXkEsfSjg9ytGX7l7g5z5OwJaAQ6vqILW6k8yekLtirnZdKST6moQMZVmsWR7DDLG4vik3Y/eYRyOc9pdCHj0Dm9iNeeIXjzYt6vrarxVd1OWnWrwitacJWyhkckdiKXp7coZGyvZ22O2xGx220qiC3eT802k5Hty7dItdqSOJoit2BUfHFM1vBj/bA3rvawdgem12w4gt9Rqfxh8cbGX1Hi9SY6F+PuYihUha17hI02IJrM0rgB613+0uj4nuW8t/VacRBbex5mdF5Aw5HM6ObZzdZjQJmspzQufF3jPXn2kDAe4D2PLN+xO251d4deM9TG6ys6okxTYoLnte+Mxzw1sJsxhu7HSjZzi8F7js0F0jiA0bNGmUQbdwHi5Wra7fq51OZ1d967Z9hbIzrhtqpPWa3qEcOQMwcfzSFOV8Xa02vG6vFOYVm3q1n2EyM6/CCpFVLee3DkTGXf37LUKINr+K+uJdW6vgyOJglp2b1jGVcfFK9jpW24zDDA/k0cRvNxI9fRWT+UH12+nh6OAZIDZzUjbF/h2HsdQtIHEndrZbfEj17VnhVA8JdZu09l6uXZUhuS0Oq6GCy57Y+pJE+Jsh6Z35N6hI+8D7iPv4yeId3VGWmy1xjInyxwww1oi50VeGFnFscZf7xBeZJDv+NK78iDDUREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEUbpuglFG6boJRRum6CUUbpuglFG6boJRRum6CUUbpuglFG6boJRRum6CUUbpuglFG6boJRRum6CUUbpuglFG6boJRRum6CUUbpuglFG6boJRRum6CUUbpuglFG6boJRRum6CUUbpuglFG6boJRRum6CUUbpuglFG6boJRRum6CUUbpuglFG6boJRRum6CEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQf/9k=\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"xHWKYFhhtJQ\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The video below explains the idea behind [Two's Complement ](https://en.wikipedia.org/wiki/Two%27s_complement). This is how most modern programming languages implement negative integers. The video also shows how subtraction in binary works." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"4qH4unVtJkE\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The Intuition behind Floating Point Numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This video by the YouTube channel [Computerphile](https://www.youtube.com/channel/UC9-y-6csu5WGm29I7JiwpnA) explains floating point numbers in an intuitive way with some numeric examples." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"PZRI1IfStY0\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## An Introduction to Complex Numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Below is a short introduction to [complex numbers ](https://en.wikipedia.org/wiki/Complex_number) by [MIT](https://www.mit.edu) professor [Gilbert Strang ](https://en.wikipedia.org/wiki/Gilbert_Strang) aimed at high school students." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2MBERISGBUYLxoaL2NCOEJjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY//AABEIAWgB4AMBIgACEQEDEQH/xAAbAAABBQEBAAAAAAAAAAAAAAAAAQIDBAYFB//EAFYQAAEDAgEFCgoGBggEBAcAAAEAAgMEERIFITFBUQYTFBYicYGRktEyQlJTVGGTobHBFUNygtLhIzM0YnODJERVY6KjwuIHsvDxFzWU0yUmNkVldOP/xAAYAQEBAQEBAAAAAAAAAAAAAAAAAQIDBP/EAB0RAQEBAQEAAwEBAAAAAAAAAAABEQIhAxIxE2H/2gAMAwEAAhEDEQA/APP0IQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEIQgEK2cnTCJsmJlnEjSdXQm8Ck8pnWgrIVjgcnlM60vApPKZ1oKyFZ4FJ5TOtObk+VzXuDmcgXOc7bbEFRCtNoJXNccTM2fSVoaf/h/lWop4p2VFEGyMDwC917EX8lBlELVu/wCH+VWusaii7bvwo/8AD/KvpFF23fhRcZRC1LtwWVWnPUUfbd+FJxDyp5+j7bvwpqMuhaZ+4fKbC289JynBo5bvwqHKm4/KGSohJPNSuBDjyHOOgXOloQZ9CmbTPdoLU7gcm1vWgroVjgcm1vWjgcm1vWgroVjgcm1vWjgcm1vWgroVjgkm1vWjgcm1vWgroVjgcm1vWjgcm1vWgroVjgcm1vWjgcm1vWgroVjgcm1vWl4FJ5TOtBWQrPAZfKZ1lHAZfKZ1oKyFZ4DL5TOso4DL5TOsoKyFZ4DL5TOspeAy+UzrKCqhWuAS+UzrKOAS+UzrKCqhWuAS+UzrKOAS+UzrKCqhWuAS+UzrKOAS+UzrKCqhWuAS+UzrKOAS+UzrKCqhWuAS+UzrKOAy+UzrKCqhWeAy+UzrKOBSbWdaCshWeBSeUzrKOAy+UzrKCshWeBSeUzrScDk2t60FdCscEk2t60cEk2t60FdCscEk2tRwSTa3rQV0Kfgr9rUcFftaggQpuDP2tRwZ+1qCFCm4M/a1HBn7WoO0wB0VO1/gmQ39yinBbPICLEOOZNB0A3w+pSOc2S2KV2bW5tz1oGxSPjNmyOYDpITpi0vuJDINpFkmCPzo7JS72zzzeooGu3o+C14PrIPyT6cttM0kDFGQL7bg/JJvTPPN6ije4tc46GlAkOdxG1p+C9XyWLZKox/cM/5QvKm71GS4SOcbGww21c69XoBbJ9MNkTfgFL+LCS/rCkOYJ8gu8ph0qNIZLl3QmqSUcoJlkRDMOXB/FHzXP3bfsTP4cvwC6Uw/SU/8UfAq7V0dNWOY2phZK0A2DhfYrKPGoQVNZehuG5GF7muZQtc02Iw6CjfdyI8Wh9n+Suo88sheib/uSHi0Hsh3JDV7k2+JQn+SO5NHniF6Jw/coPq6LogHcg5T3LD6ukv/APrjuTR52jpC9D+l9y4+rpv/AE47kfTW5kaGQ/8Ap/yTYPPOlIvReMG50aGx9EH5J3GXIA2ew/JNHnKF6ON0+QgLg5v4B7kvGrIdsznexPcmjzgA7E4NOwr0XjXkPynexKTjZkXU53sSro88wuPinqS71IdEbz90r0HjdkfUJD/KTTuwySNDJTzRqIwQgnOiGTsFOFLUnRTy9grd8c8lj6ubsBJx1yZqhn7I70GGFFVejTezKcKGr9Fn9mVtuO+TvMVHZHejjxk7zFT2R3pqsVwCr9Fn9me5HAKz0Wf2ZW048ZP9HqepvejjxQejVPU3vV1GM+j6w6KSf2ZS/RtcdFHP7MrY8eaD0ap6m96OPND6LUf4e9QZAZKrzoo5+wU4ZHyidFFP2CtYd3VJqpJ+tqad3VNqopu0E0Zb6Fyl6DP2Cj6Eyn6DP2Vp+PcHoMnbCQ7u4tVA/wBoO5UZr6Cyof6jP2Uv0BlX0CfsrR8e2f2e72o7knHtv9nH235IM79AZV9Am7Kbxfyt6BN1LScex/Zx9t+STj3/APjv87/agzo3P5W9Am6kvF7K/wDZ83u71oDu7P8AZw9t/tTDu7kvmyc3235Irg8XcrnRQS+7vSHc3lj0GTrb3ru8e5v7OZ7X8kh3dz+gR+0Pcng4nFnLGuhf2m96Ublstegu7be9dk7u6jVQxds9yad3dV6DD2yg5PFXLPoX+Y3vRxVyz6H/AJje9dM7uqs6KKAfeKDu5rNVHB2ir4ObxTyyf6oBzyN70Dcjln0ZvtGroHdzW+iwdZTTu4r9VPAOtTUUuJ2Wb/qGe0CfxMyv5EPtFOd2+UdUcA6CkO7XKZ8SDsnvRUHEzK2yD2iOJ2VNZgH31Id2eVNlP2PzTDuxyofMez/NBxU5kZcC45mt0n5JqsSACngvfCS4m22/coiulAuprUu2f/CmhjbPeL4BmF9JKCWAsc5r3ktwZnW1hQyss4uAaGk5sJuApKQYpsGp7S33KJrWFpLpA1w0Nsc6BGtLr22L12kzUcA/u2/BeVQwO3y+KNwAPguGxer04tTRDYwfBKsI4cophBxhSEZ0WzrDSCUcvoTLKWbwkyyqK8/62n/iD4FdM+EOYrnSj9NTfxfkV0Dp6EHkdcf6fUfxXfFQ3UtZnrZz/eO+KhWmRdF0IQSMmDALRRm2twuppqibCxzXBoe3xWgZxpVdrsPitdzhTh5aLPjjYPW256igZG1r2ukkxHD4QHuUu8NbIICLueCQ74f9etROmbc+Ebix0NFuZNEgBH6NvWe9BLHCA1mNlwXEPPkWSmLepiXNG958JJuPUm1Ejd/kG9N8LPyjnTA6E+FGWn913egmvvkDo8Qc8crEMw5lC28eFzbkaTmzGysxxAQ4o3izmkAOzEk5lVcZGAxuLgL523zILFmMqWQ2G9utiJGnF3XRHEGvibhBuA5zyMwCq6dKXEcOG5tsvmQTsZgmnb4JY11uhDC2RrXvDcTXjFqxBQmRxNy4k2tcnUnQta5/6QnCBc20oB7Hb5IA2waSDsCYrNdKZKhwzBoOZo0KsgEIUlO0PnY05wXII0JTpKRAIQhAJ+9P3rfcJ3u+HF60xOc5+9tY5xwjO0X0IGpEqRAt0JFMKaUgHCADrLgEEKLqV8Do24i6M+oPBKhQLdJcJCkOhAt0ilqf2mSwAAcRYKJAJEqRAJEqRAXSFPjdgeHAAkaimOJJuUCXQhCASIQgmCljkAY6N2drs49R2qNIgVKJHBhYPBJuQQmoQWKN2CUyeQ1x91lGJXtZgDiG3vmSB1mFo16U1BNSZ5xfY74Fetx/qWfZC8fbpC9hj/Vs5gpVhDpSpUiyqGXw00BPl8NIEFaYf0mmFs2+aegroO1/ZVKb9opv4nyKuu8bmVHkFVnqpj++fiolJP8AtEn2j8UkUZlkawZsRstMmIAJNhnJXVkpIm0Ti2IYgSGnxj61zv1bNHLd7ggMQiPIN3eVs5lGSSbk3KEIBKNI50iVudwHrQPnvv8AJfTiPxTFJUG9RKdrj8VGgLqZkoeN7mzt1O1tUKnp6cyct/JjGcuKCSahfDT78XAi9rDUmxMmmb+hZHszYb+/Orc8xlbwZtuU1pvtzZlUp3BjZLFrJdAc42sNaCWsp5GhkbYXHAOU8M8I9Crx/oZRvrHW1gi10v6RguycH7L1G57nm7nFx2koJGQTT3exhdnzlNdBKzwo3D12TG2xDESBrIF1Ya2IeDVFp9bCEFdS0marh+2PikkiDRiEsb+Ym6WFoc4ANlL/ANwKhkotK8bHEJquGl1mCp6gq0gaHkNa4W1O0oG4ThxWzXtdIrcEO+U8rGyRlxs4DFotp+KgkhdGAXYehwKgjCtVMWPDLGWmPCPGHJzaCFE0QBt3F5OwAD3pcdP5px53oIUinL6fCbROxajjUKBErWOdoGbaksnsbi0yNaB5SBN7cb2INthTFdEUVPCS6Yb5K3k2aczfzVZr95lxRuBI0Et+RVEaRwzLoSTtfTDHUtxHxGwDN0qsKgNbYQQut4zm50DKhzX1EjmG7XOJUaub41jAZmRXcLhjYxe3rVeZ0brGNuHaFBEkSqVjoMIxskJ12cEEKRWcVJb9XN2goWFgdy2lzdgNkEsDWuglaHMbK4gDEbZtedQSMwOtiafsm6mxU2uOTtBPDKdwzR1A6AUFRIrRgpzoncw7Hxn5KqUCIQhBOiyEIBCEIBCEIHN8Ic69hj/Vt5gvH4s8rOcL19vgN5lKsKUiXWkWVRSeGgaEP8NCCGX9opvtn4K27x/s96qS/tNP9s/BWneP9nvVK8gmzzP+0U1pLXAg2ISvN3uPrTVplfeXS0cLpHk2e5x5v+gq7pxM4mdunQ5ukd6sTjBk2H97N81QQSmEkXjIe31aR0KJKCQbg2Kl38P/AFrA/a4ZighTo88jedSGDFnhcHjZod1JrOTK24zhwQLUftEtvLPxUalnYTVysYLnGQOtXYIo4QwCz5nGwOoc3egrw07WcufNrw96ZUVBmNhmYNAU8m/GTwQ1jjmbIAC75qm+we4N0XNkEtSSJw4ZjhafcEVQbvge3Q9t+lFXmnI2NaPcEpBfRB3kOsggQhCASoUjGNLDe+IgltvUgjVnCWU5kiJDXWBIOg6wqye2VzY3xg8h9rhBNE0Sg4WzuIF3EG9lA5rm+G1w5wpYN9wHe5XMDzhsCRiKhLi7SSecqh0MphkD2gEi4sdBuLJiEKCeFpbZ41h3QUscstriVjfU4hMgl3snEC5h0j5op3NjlDpI98b5KCSWSZrWl7Y3NdoOEG6rvfjdisBzCysyOgmdie6oB2mzrfBQvYweBKHc4sqI0NIDgXC4BzhCRQWpjTSyukdNJyjfCI9CikNPa0bZCdriPgokKhFNTMEj3MOsA9R7rqFPhk3qZr9NjnG1QOlmvUSuwtddx0i9gmhokY91sOEaRo5kkxaZnlh5BcbcynmqYJWsaIZGMaPBa8Wvt0IKiQp7978Qu+8mIEUscO+MBbpLsJ9SjspaWXepmlwJZflAIHxTNZcRPEY1Ow3cUj56kNx7+9zb2uHIhijdU8vGIbm2bTsU9RAZAAJoWxt8FguAPzQUpJpJQA95dbRdRqd9M5uhzHfZddQIEQhKASbAXJ1BBMhaXiRlHztP2j3I4kZS1S0/aPcgzSFpeJGUvO0/aPcjiTlPztN2j3IM0haTiTlTzlN2j3I4k5U85Tds9yDP0/7REP3x8V6+3wRzLBw7jMpsnjc59Pha4E2edvMt40ECxSqEiU6UiwqJ/hlCH/rChKIX/tNP9o/BWn6JPs96rP8A2qn5z8Fak0P+z3qjx13hHnStYXaLdaQ6Ugz5tq0y6Fax3BqVt2izdbvUO9VRTuLb44+2FYyiOW4DRHJg/wAI7lRQS7wfLj7YRvB8uPtqJCCYU7tT4+2FZhYXva2bA83zODxi/NUAnNF3tG0oOvLROZvj4nRG5OIudh161WZTytmEhqIcQ2OVed5FbM5ukvdm250+SilsHxxOwnSD4pQWHU5N96khxu0vdJdxUX0bJbPNBzB6I6U7w1zd6MhJvjeAABzqGZhY0OIYHB1rsIIPUqLFfRSsrJBdh0eMNgTY4H8FlYbXLgRnCMrXNaXuzlzQb9Fvko4uTQznynNHzUCGkmHijtBJwSfzd+YhQoQWnUcvJLGGxzG+oqSohFPTtfe5c3BzabqnjdgDb5gbq2446d2fETG1x6D+aCMxNaC0tziPG52wnR8VHvJ3tzr52txEeq9lLNLv8LLyAOAAc05r2zA9Sc2eIskjN2iRlibaCLW+HvVCMFqWJ/kveemzbKqrG+R8CdFis5smIfvAix+AVdQCAhCBwfZLvhsmIQOxXumpEIBCEIBSOYxgc0u5Y2aL7EkDmtnjc7MA4EpJYnwvwOHMdR9YQMSFWXOLafEy7c+EjZm+abIHCmDpL4yeRfTZBCGOc0uDSQNJ2IFs9wSrcsjI6uLD+rFnOA9en4qER4al0OkOJaPl8lRAlDXOcGgG50BSRb06Mslfg5QIda/OFM+aMyxzxgDCLOYTY7PggjFPIWvcHMdg0tBzhRtkN7AXPqVuAxwySNYXOcWGwI0GxzfFV5GllNE5mh4OJw230IGOleDZ1wdhT3kshjefHv7in17Ax0LhnDowc+tS76J8mkmGM7y/OALWadnSEFJ7gRnUS6DN6hpWzNxgvcRewJFtQKpTSb7IX2tdAxWaQ4IqiUeE1oDTsudKhgi36ojivbG4C+xX6V8bDJC1jWgnC7fBc+rouguu3SZejfIH1maM2daNunqRLuny5Fg/pmZ7cQvG3Qq+UKSpphwkgBr8zntdmJ5lTqL4InSOJeRo2D/oqK6Y3VZdINqkGwv+rCdxpy9gx7+y38MLlPlayo32BzsxzBzbW9ScahpmnIbZkrS0N2bPgER1ONWXgzEZo7WvnYL2TmbqsuPwgTw4nZwCzSuZv0D5JdI3xpALh4OwKF5ZvEOE/pBiDvUL5viUV2G7rMuF5aZYgRe4Meiy22QKipq8lw1NVIHvlaHZm2svNzLvzZp3tAfhwkjWSe669H3Nf/T9F/CCUdA6UqQpks0cMbpJXBjG5ySbALDQf4ZSLiVm6vJdM64fJLfzbb/GyoHd1QH9XTVDufCPmiNK79qp+c/BWZNEn2e9ZKi3Y0VZlGnjMMsTrkZ7EHMtU6Rr45HNNxg71R5ApaVuOqibteFErNBmqceqNrnnoBWmSyP301R2vxjrPeqqkheGSjFnaczuYpssZjkcw57IGoQhAJWmzgdmdIg6EE9aAKuUjQTiHTn+aSJ74zctLmOFiDoIS1g/pTuZv/KE+KocxoDKiRltFxmVDjvUTA2SN0sdy5jmnDzgqCR5lkFmhrRma0aArTa2oa/BPKXscNObNfWFVcwwzAOzi4II0EbUFrKsglna9otycNvWCoprR08UXjHlu6dCmq4m8Kaxx5ILnO9XKPyVSaTfZXP0X0BQMQkSoBKCRexIvmKRCAQgC5sFJJE6PSQebUgjQlSIFQkUsUYlcGAkPdmbfQSgjSJUrQXOAGkoGoRrzi6nE0QGeli7Tu9BAhSySRvbZsDIztaXfMqFAJ7ZpGCzZHgbA4piRA9skjSS17mk6SDpTXOc913uLjtJupLxtzFhd6w6yJYgI2yxkmMnDn0goIgSDdTQMY+VhEjY3BwNnGw07VApcN6THbO2Sx6R+SojktvjsOi5sm3QhQTuOGpjmbocQ735wkke6CaVjHcjERh0gpIZwxuF8YkZe4BNrFRPcXPLjpJuqJeFSGPBJhkbqDh4PNsUbZXsDgxxAcLOA1hII3kXDHEfZKaglhnMbXMcA+N2lp27RsKifhxcgm3rCRCgASCCDYjQVJJUPkbZ1rnwnAZ3c6iQg7mVKuWowRWDYmsE2FUKgtku9hJAdnJG25Vuqc1mUAH2w8HLc+jwSoXVsT4pGilgjvhwhrT7zdBTBcGkAnCdKRStfEY3Me1zCTcOZn1aLFRHTm0IBCFLTSNimD3txYdHPqQSVH6GNlONI5T/ALWzoXpW5z/yCh/hNXlhJLrk3J0leqbnR/8AAKH+C34KVYv6lgN1eVTUZSdTg/oYDa21y37jZeRVrzLWVD3ZiZXX61lpVmlL5PUo3NLHte1SEBIQ51rAlVFnJ8GOpM2jBottW7yFlF02Tqlkrv0kbCOcWOdYvJjSZHbLZ128nVMUE1S13JL4i1ric2goueMs1xsFapjhpKp/7rWdZ/JUWnMFcabZLk2ulA6gtMIgbqwy07Awm0jfBJ0EbFUaU9A4gg2IsQkU7ZI5hhmOF+qTvCjkhfHnIu3yhnCBiEIQTVOd7DtjZ/ygKxQYJS6OSGNzGtL3OzhwA5lBUZ2QEebA6iVHFNJC/HG8tdtCCzUPpJiXsEsT7eDmLVPk+jNbSyb44Nji8E6SCdXMqhmEow8GYXnQYxhPUFZglfk5hxEY32O9g+DzqiXK0ctOBjH6wC7vUBo+a5bWl7g0aSrFVWz1eETSXDdAAtZRMzMe8aQLdf8A0VAjy3Q0Zhr2piVPELy2+bRcC+chAxCmipXyRl4I9Q1kJ08TA6MxAhryW5zfQbII6f8AaI/tj4qaSN7nStaC476cwT5CcMl2tG9SNaywsb30e5T1IjG/4XlrWz3JA5yqOfHBJK9jGNJL3YRzpjhhcWkg2Nsy6FDIGGomc4sY92AG2guvn6BdUZYnQyujdpadWtAwLoRQR0zBK6ePfXtvFcEAevQuerr8NbFFhe1ksbAwtcbBwGggqCB9M9rcQdG8bWvBTadwZUROOgPBPWkkifEbPbbmIKIppITeJ7mn1FAtQ3DUyt1h5HvTRI5uh1uZXMpTufICcDmyMDwcAvnGfPzpuS597qmtcWYSDmc0G5tm0+tBHWNwOja4ASBnLttz/KyrZr59Cuy1znuPCaWFzzpJZhcq0roiQY2OYdl7hAtoLeFJfmChKnFS0ts+CJx8q1j7lC4gm4bhGy6ADXEEgE202CszDeaJkLs0j374RsFrBNgrZqeIsicGgm97Z7pXVMc9+FR3efrWZndI0FBFDFG8gSTNiBvnIJtz2V1tPSNp3wjKEbjI5pFmOzEX9XrXOaA6Rrb2BNrqw7HSMGHkvc5wLtYsbWQSyZLlaMTHh4+y4fEKoyLHe72Mtm5ZsrMUU8tnsqmOdqBkz9SrsldBIcTWP8prxiCoZIzezYPY/wBbSpIi6Fkr25n4RY7ATpSSVAfpghH2W2UkL43UszXuAe1nIv42cGygRhq5mEsnc7a3fc/VdVXXub6dd1aZDFFGyaoebuGJkTNLhtJ1BV5ZDLI55FsRvYakDEISIBCEIOhlM3qxfyG/BU1ayiQagEaMNupxHyVVAKVkWIAlwaCbC+tRLoSQ8HEMk7S0NjBaw6XG5PUgoOBa4tOkGxRdPaySdzi1uI6XJhBabOFiECL1bc//AOQ0P8BnwXlzgDSscB4Ly0+4j5r1HIObIdD/AAGfAKVYtuJuvK8swGnyvVx/3pPXn+a9Pc7OVi90uTJ6jLOKEWEjQcWy2ZSRplcPL5106KMbwRhu8OvzhXRufe0Bz5Wlw1Wslkis7CW4MAzJ1z1I1xhsYZG0lgsCVXkOJxIzZjpUrcRFiozhMhFtDT8Fjn9a7/HCviJNrKd8jeCRRggnE5zufQPgq7HYSDYH1FXWR/0YvnlZHjzMFus5l1cFcJ40JcEd7NlxfdIUjomt/rEJ5ie5BGpIpZITeNxF9OwpjQXHC0XJ1BSOppmC74ZGj1tKB2/RON5IATtYcKL0p883qKbJFgiikv4YObZY2UKDp8Gp56eG1U2OwdbfBa+dRCGjiP6SoMvqjb8yo3DFk6J/kyub1gFQXQWnVWAFtPG2Fp1jO49KrXukQgVSQ8sPjGlwzc4z96iQCQbjMUCgX0atKtsB4TM92iNp+FgqzpJJjbwidQGlTRGUyCCXELNcA12rMUD46sQx0xaLujDw4bb/APdQCbkxC2eNxd8O5MY0vNmi/SiWJ8L8EjcJtdBZrZHCpkaOTyietMNU50sjnNBbIAHNOtLLjqnb84sYThZyja5t+SiETt/3p4LTr9SodLPjhjjawMay5zHSSorqbDFJDI9gc0stpN7hQIFGlPmZvT8F7uA5XqOxPpuQHz2/VDN9o5h3qMSHyWknSSLkqBic9pYbHZcW1qWobgjjDmBspzkAWsNV0kX6SB8ZHKYMbfmOpAxkbpA8jQxtySmKwbx0TGjwpnYjzDMB13UrDIWgMZSPI1WF0EbKucjAZ3Bp2gO+Krytc2Qh4s5OmeXO5UbGHY1tlNF/SYHRn9ZG0uYdoGkKiohCFA4Mc5rnAZm6TsTVNTPDd9Y4gB8bhn2jOPgoFQLo1rmzUEU+IYiRiGvFax67ArnIUFv9FTQRPDN8lkbiDz4Lc+gDWVTUkVRJDcMdyTpaRcHoTnvbM1zhA1haLksNh1IIXNLTZwsbXSKWCMPcXP8A1bM7j8ulPaDIZKiQAMHvOoIGR1U0bcDXjDsc0OHvSOldMbb2zEdGFtj7lErVNG1gbPK2+f8ARs8s9yCCSN0QGMFrjnwkZwmOaWgE+MLhXBC+WolmrCQxh5Z2nyQq08pmlLyABoAGgBBGpREHUrpR4THgHmN7fBRuBabEagVNTSMbHURyGwkZmP7wNx80HVypk1jMG9SgYRaz3AXuSdPSqDaWRpvvkHTI0qWR8k81Y2ZxkLGOw31WIVEWxC4zXzhBZfExsZdIGt8kxuuHHmViIb9TwRyHkyYmAnxXDOD77KjNKZZC45hoaNg2J2/u4M2G3gvxh2sZkE53uGlayVr743XAzAkainQsE0sQcxl5rlz3aGgZvkoZ6x8r3uwtAksXNIuC62n1KDfHYcOI4dl0FjD/AEWob5DwbHTrC9OyF/5JQ/wGf8oXlwqC5sjZAHF7MNxpuCCD7ltIMpTfR1LTwXjDIWtc7WSANC1Ofsuu1VVlPTuLXO5XktzlceoqH1UwcG4WgWAJTGRtznSdJJTB+uJGgcld+fjkZtPkIw2ubrl1pIbYi3rXVKa6FkjbOFwt9TUjNAzuzMzga7qSJjrSFwz4D8F05MlgOBieWgG9lHNCynilEjhjcx1vXmXkvx2V0+2seNAU7J7RNY6Nj8N8JN811ANCcFGUoNyTYD1BTRzujbha2PncwE+9QDQluglc8uNzhB/dACs0cM1U88t+Blr5yqa7mRxghdfxs6z1cjXM2mV1KHQiwIc3QBoXHOY2OkLRV7mOpX4rttrB1rLh2e91OLrXckXBMeC7xbNjx36LKJIDcJVtzKhIhAqRCEFmE4YuQbPe4tuNIAF/elEpZNT76S4sN3EnPYnR1JKOXe3lptnIIJF7FRTscyeRrr3Dje6CTeXAGLRaQtcdllJOGzxMfDnLOQ5uuw0FVnyveXEuPKNyBoKYgtyljsnxEEYw8tcOjMVNA7EaOQkYjjiJPNm+K56XEbAXNgbgIJZXkYoxHveflC+sKMNJYX6AM3OVK6oEmeWJr3+Vci/PbSonvLyL2AGgDQEFiEYsnVFtLXscebOEuTqh8FRyJAzECOVmBNs1+lQ085gkxBoc0jC5p0OGxNl3ouvFjAOp2rpQWqnhbh/SaljueUO+CZQtzyzOzMijdc7SRYDrKqiwOcZvUpHzkx70wYI73ttO0oH1ZzwjUIWW6Rf5p8FFvsW+uqYY2A2OI5x0KB8pkjY1wBLBYO122JY55I2ljcJaTfC5ocL9Kos1kUbngxVELmtaGi7s5trTcm8iqdI7wYo3ud68xHxIUADp3aI2AaTbCAnyzMZEYKe+Em8jzpefkEEMbcbw0hxv5IuVLVQCAgYZGk6pLX6gq90KCxBTxysxPqo4jfwXAkpzqWIZm1kLj6wQoImuklYyMXe4gNA2qateLsixY3R3DpNp7gqIJI8B8Jjvsm6kpozKXtDSbsdaw1gX+Srq3S19RA+NrZniNrhdt81r51BXjZjfhLms9bzYK9R0rntlgxRkS25TJGkgjPovnVWre51Q8OkLwHEA30hSxObRwCR7A+WYZgSRhZtzbVRPJQSEtgiczemm7n4xn2uIuq1aTjbFHh3pmZgDgb+s+sqw5kbaZ4hifHK9mJ2lwDdNr6rqvDHHTwipnaHF36uM6/WfUge2gfDZ9QGXtibHjF3e9LDDUOrGTSt3sRnGHOHIAGewVKWV80hkkdicdanlcaenFOCQ59nSfIILNTGal1oZqdkIJLWmUdZ9aqNopHPw75AP3jK0D4paSlbU+K8WPKkzYW891XkaGvc0EOANgRrQXcoUwYY377E4Ojbna69yBY/BQRxUrowZKlzXa2iK9um6gLiQASbDQNiGPLDcW6RdBYiqCyWR5F8bXNPSFEE5zmOaLMDXX1bExQOSXTzDKITKWEMBAufXnHwSvhkZGx72ENeOSUDZWGOQsOkaU1WqqGSavqWxsLi17ibKuY3CNr7cl2YH17EFnJkHCK6Nh0aT0LVtaL2GgDMuTudpXRiaaQWdmaPUuxouV6fjmRmpW2bHn6U3AWtbfTpPSlZneWnRdPlzvXeIaBtTrBNOhIc7dKlCl7QNKoVcQnkDnWIGgKdzbN8IFRByxVjh1+S4hyohhJ021dCZDuarpWBwMQB8p1iu1OLkEBcuvyplGicDHKwxnRdguF5+uc9alLxUyh4roO3+Sc3cllQ6N49p+Sgh3SZULcRlia0ZrmNTxbpsryTiKN8D9dxHqWFOO5HKwF8NP7VTUsLoIg145TRY2VrJ2V8pV8TuEFrIzybYLE+9Mr/0UDy3MQFOuNi89Y51dFUVzRFSMxbbmygj3L5Wd4NO0/zApIMqZSgjbvUULx625/iulDl/KbGtIZBc6RgOb3qSYW6pjcllu37I32re9LxTy36GPas71cl3a5TgkMbqeAuGnMQnM3c5QdG53BYDhtcXKqKPFPLfof8Ams70h3K5aH9SPtG966HHutEbXCjgNzY8o5k527utaxj+AwEOv45Qcvivln0F3bb3pOLWWfQJO03vXVdu7rWxseaCGzwfHO1N4/1f9nw+0PciOXxcywP6hL1jvRJkHLUjy99BMXHXm711hu/qdeTovanuTh/xAm15MZ7Y9yDi8X8r/wBnzdST6Ayt/Z8/Uu4P+IL/AOyh7f8A2qR272VgBdkdwBz33/8A2orPfQOVh/UJ+ykOQ8qDTQT9laR+70stiyUbkX/X6P8ACm/+ILf7Mf7Ydyozn0LlP0GfsFJ9D5S9Bn7BWk/8QGf2Y/2w7k5v/ECK/KydKB6pAfkgy5yVlAaaKf2ZSfRlf6HP7Mr0miyzHXQiWCnkcNBF23B2EXVjhknok3u71B5acm13oc/syk+j6wf1Sf2Tu5emVWV46OF01RBLHG3STbvXF4+5PLrcFqrbbN71RjOAVnok/sndyOAVnok/s3dy2p3c0Abi4NUlu0Bp+abx8yb6PVdkd6DFmjqh/VpvZlMNNUDTTy9grcce8mej1PZHek49ZL9HqewO9EYfg8/mJewUGnn8zJ2CtyN3GSSM8NQP5YTm7t8kvNt5qPX+jQYRrJo3te1kjXNNwcJzKV9RK4cqGMHbvQuttx1yN5MvskvHPImvfPZIuPPyHXzg9SavQ+OORDpc/wBiUnG/IR8Z3sSoY88V2Gtays3+SNrwG4WtIvbNYLbca8gHx/8AI/JJxo3Pnx29MH5IMhUVz6qMx8PwtPiObhB6RmVGrkMk5LrCwAABuAANS3Z3S7njblM9h+ST6e3PP8aHpg/JXRhKYsbMHvIwt5Vtp1BRvfje5zjcuNyt59NbnHZiafph/JNOVNzZ10nsh3JiMbHNDJTsgn3xrWElpZbXtBUUrIQLxT4/UWFpW1NfuadnvRW9cY7k01m5nbQn+WO5Bh06J4jla8sDw03wu0FbQ1O5o+gdgdyTfNzLvQOoBBlKKNks9nWdmzDaU8EPh3yVrWjGMBAA15+hQvqJpHBz5CSNGqydEH1dXFHI9zi9wbcnRcqDp19XBU08sUT7MYI3ZhpIuM3QQmVpigaYd8LnOZGA3DmbaxuuScxIVnKErJqxz4zdlmgHmAQWX1DBUZQayZrDLLdryCQW3ds5wm0cjaBpfPaRklsMVtP72fQqGF2DFhOG9r6rpL586o2FAxsdEMD3ODjiu7Sb7VYOgAKpQyh9G21tAVqNwdIF6ub4zTzIGOudAKnu15u0ghV3Rb6XKMxmB2KO/rC3tRbc2y59bUuje1gIAtnV9sjZGXB51yq9m+VGFuoKdXzwh0Upewm4KfH4OdceSV1JUabi2cBdKmmZMwFjrhctaTFQTwNkbYsa71EKwSE0j12VqObVZJZJTYYLRvDsVtRUuSsmSx4X1EgdiBAYNAXQhDGvHKxOOoalG15Y1wb4xN/V61PpP1dOLhE7AM7hm5lysq1uCM2Oc5grMkgYCSc6z+UHOkna+94yLsO3asdXAjKyoaM0p6gunBlFroXnC8ujaHEG1jnAzda4oVugGKZ0fnI3N91x8FyU99c6SRznxQvudLoxdSQytex44LCGkDGcRaFRCmhm3tj2FjXsfa7T6tBVFuSaCNgjdRNwu5QLZXWPrUW+0jm4TBIwXvyZL/FOE8E9M2GW8JjJLHNGIWOo61UdmcRcH1jWgvl+T5IIonPnZvZNuQCTfpUDhRDwDUH1kNVZKgmD423DHOLfJe0WPvT+C75TuqGNMbG+Wcx5jrUcBjZjkkAc5o5DToJ9fqTJZXyvxSOLj60DLBW64W3lzScMkTTZVFI+Z74443eDHfD0qA315aGl7sOy6c+GLe8cdSx51sLS1w+XvUOjOFNJWVErcMsz3j943QNhi32QNuALEknUBpKmEEctNNLGC0RWzuPhX1WUVPKInuxC7XtLHW02KnY6OWnfTRYg64e0u8YjNb3+5BC6pkc4PvaQeOy7Xe5WN/ylwN8zqmrDQ5tnGR1iDdUjm0q9SVcsdHURCoewhoLBiO3OB0IIIDLVvkbLLJISwkYnk5xn+SShbE6rjE9t7vnubD1X6UQVLmVbJnnFhOf1jWrTo3NqHRU9I2WFpsCW+ENuJUVKrfhK5k4wkeLawHMoLKxUSFrnQh2NjDZt8+HmKrqAIRZCFQWUjxgp2AaZLuPNcgD3FRqaTl0sLx4l4z13Hx9yga2MOpJDrY9vUb9yhsrlCwSsqGOdhbveMn1A3UUsUe8b9E5xaDhcHDODqVFeyMK6UsUFLVtpXhthbfJHZze182xUWsMkgZGLlxsAoGGKzWuNrOvZNwhdOqoKiKgp3PhcC0vDrZ7Z7jQqEbHSOwsaXHYEEeFSm8W9EaQMXX+SJIZIg0vYWh2i+tOqBnjO2Jvwt8kCVcDYpeR4DwHt5ioMKuy/pMmQSHTG90Z5szh8Sqz8TG705oBBubjOgYRybak3CE5IgbgRgTkqC/HGJKffW02+cstIZe4zA396kp4mxVtLK3G1u/NBbI2xGf3qKAk5NqbEgxvY8W9eZV3TSOeHukc5zc4LjeyCy6CNuUaiOZwa2NzsxNsWfQnPijmopJIWtdKx4JEbbWbza8+tU5pXzyulldie83cdpRHI+J4fG4tcNBBQXZmhlNPFbM0MeOk9xROyaEQtp2OLDGHlzRfGTnz/AAVMzyEyEuuZBZ19ank/T5Pje3w4OQ8fuk3B95CDr5LdjZIxtuQb2Gq40LosdbPrWayNVOp8oRkeC84XDaFq5KfFyo9BXfj2IkjdyLhOLxvZuoYXmMlrhmKkIBC7xlWuWuJGYKGpJ3slmZxOcqy5qheNRWKrjVEIa0uJuqsTnxvuxxC6tRDjaRrXNwWdZce/FiWky02R2CXkuBtfaumyWKTPiWLdme7nKtU+UZoBhzPbqvqWZ0rYsljYLNzKoytEePE047m19Cz5yvMfEb1qKoyjNUNwmzRrw61r7iatr3TksjJw6ztSUg36nkpjncAXx840jpCptUsT3Rva9uZzTcLnaEVrJpw18ROi/wAlWecT3Ota5vZOjdvcjXjUbqASpL3KVAqEiVAISIQKhCECoKS6W6AQpoZadrbS05kO0SEKYT0Guif7YoKaFdM9BqoXdMxTTVUw8CgiH2nOKoqc6CfWrRr3DwIKdnNED8U05QqdUgbzMaPkoK6XE4Ntc22Kbh9V593uS/SFV553UEFZCsGunPhOa77TGn5Jjpw7woIugFvwKCJCVzmnwW4em6agVWKX9KH05Nt88E7HDR16FWQCQQQbEaCgngnNO6QOjDg9pY4HMUTVDXwiKOJscd8RF7lx9ZTqoidgqWjlHNKP3tvSqqDoVNq6nbUsP6aNobMzWbaHd655TmPdG8OYbOGtI44nE2AvsQXKOYmlqKfGWG2+MINs41dSZTTYzMyR4Y6VmEPObPe+fntZVUl0HWIhfRU7G2lfAXYmg3BNiQPWL5uhQ1znyUVM94a17S5jgG2trA6iqAJBuDZBJOk3VHSye1j6NjX2twtun7K50pcZXl/hlxvzqxITDQxR3s57zLzDQPmm1FSypYHyttOBYvbofzjb61BWQkQgEIQglDnAEAkB2kbU1bXiRTaq2fpaE07h4tVfJ0xDvRcY1C153DDxcoO6YfzTTuHfqygOmH81TGRUkE76eTGy2ixB0EbCtOdw82rKDOmI96bxIqfTouwUHHyYyKXKjN6Dg0NLsLtRstTTFxYWB3KGhU6bc3NkqdtTLUxvaOTYNIOdWZGuheHtXb42aWR0l7OaB60RyairEb2Ts5QzqOSmtnYV2/1A4KCUAEKZpzEHSE17ARnNimaKNQLMc4axZcmAXLidq7M7CG2K5UItiHrXn+RYzr/DdzlNIT35nuvtKauSkASgJUoCBwCeE0J4QCUBKEIESpUWQCEIQCEJUCIQUIBCEIBLdIhAqEIQIhCEAlQkQCEIQCEIQCRKkQOZI5mIDQ4WI2pqEIBIUqECXQhCBWMLzZtukgKxHHTxHFUPEhGiOM3vzlVkKh88z55XSPtc7NAUaEigEIQgEISINH9BbpG6G1XRN+aPofdM3xaz235rbNrL6ZZB6y3808VVs4qetjlNqsNwDdOzVW9u/wA0mDdOz07qJW34c7zrh/LKUZQePrR0sd3JtGG33dQ3XlD2Z7kGt3TN0vrxzxHuW5OUH+db2XdyT6Rk86zqPcrtGBflLLd2mrdUyRtNy2SOw+Cu0+6OjeBFUMkYdtrrRZbytLHkipcJW4i3CMOm56F55ha5xNs5z3K3z1YljVNyjQNOKKrZ6wTZTfS9HYnhEfWsg2BmxO3toBAGla/rUxpJcvUbdLj0C6h+nqCQgGRw52rgGBh29ajfTsvmun9aY1U9VG+LfRKHNAsAFx31kUJxSEjEdQuqFM0h5Y3XnVuOnwz45GB7NNiFjrvVxK3KND41+mNSCvycdJHTEnNjozppY+pSCKgOmjj6llSMrclHwnR9MR7lOyqyGTyjT9Mf5JGU+TT4VG1Tso8j+NSDtEJokhfued4bqPpbb5K21m5h2k0PXZQMyfkF3hUn+Ye9WW5H3OOH7O7olPepoBSblna6P2n5p4yZuXfoFN0TnvSDIG552iKUc0p70Hc1ufdo4QOaRXYHjIe5p2jeeioPencXNzp1t/8AUnvUPFTIR0Pqu2O5HE/IbvrqofeH4U2KmG5bIDtDz0VCeNyORHeC6TomVY7isjHRVVQ7P4U3iRkvVW1I6B3JsRb4l5Kd4Lpu3dNO4fJp0Szj7w7lW4j5P8XKFQOgdycNxNM3wcqTjoCmwSHcLQHRUzjq7kh3B0Xpc/u7k0bjWDwcr1A/6507inOPAy5Uj7x71dgYdwVNqrZuoJp3BQnRXSDnaFLxYrxmbl+fpc7vQNzOVdWX5et3emwVzuAGrKB6Y/zTDuBdqygPZfmro3O5aboy+/pB70OyLl9ouMvDNtagoHcDNqygzpiPemHcHVaq2E/cKtcGy9fkboad3SO5KKTdMfBy1TH7w7kFE7g67xaunPOHJh3C5RGippet3cupwbdW3/7nSO53fkmkbrGf1yid94dyDlncRlTVLSn77u5M4lZW8qlP8w9y6Zqt1TPr6A/eaulRZVq2wD6SfTxTXteMY2n15jmVGYO4vLA8WnP8z8kh3GZY83D7Vbunq3TtJiqIHgGxs05velfUztxBobI8C4a1js/ToUXGBO5DLA+pjPNIEw7k8sD+rA8zwtK/Ke6vGS3I0IbqBdc/8yb9L7qR4WRIzzX/ABKozR3K5Y9EPaHemO3M5YH9ReeYjvWo+mt0l+VkG/MSnfTuXR4W59/Q89yYjJHc7lcaaCX3d6acg5VH9Qm6lrjuiyq3wsgTDmce5Jxor2+FkOo6L9yisacj5SGmhqOwUw5Mrxpoqj2ZW040TeNkWsHM09yONQHhZIrR9xUYg0FYNNJOP5ZTDS1A008o52Fbk7rYB4WTq1v3U3jhReNS1TfujvQYYxSDTG8c7SmlpGkHqW7435NOllQOdiXjbkkjOZh/LTBgSkuNoW9dupyK7S+Tphum8YshO0v64D3JiMJcbQi63f03ufd9ZH0wHuSfSe5531lP0wnuQYVFluTU7nX/AFlJ0st8kn/y6/xqL3Jg4XCj51/WUorHjRPIPvFWuC+pIaX1LGqg4fLqq5e2Uv0jONFZJ7QqQ0g8lIaQeSgaMp1Wqsk7aUZUrNVW7rCQ0bfJCYaNmtoQRZQyjUzU29SzF7XEZsy5oOdTVobHJgaLW0qriWkT4rJcSiDwlcL57oH4k0uuU3FtTiQUBG7DMLK62cjxGHoXPdySHK0yFxF8ZzoLQqv7pnUpG1gH1DCqe8v8so3l/llRXUZlKMaaKM/eUwyrBroGdv8AJcfe5PLSiOXy/cg7QyrTa6AdEn5JwynSHTROHNIuLgm8sdScI5vLHUg7QyjQ66STocnjKNB6PMOkLiYJvKHUjDPtb1KDuDKGT/NTDq708ZRyeBmbOFwAJ/3epH6fY1BohlKg8ucdCcMo0PpFQOtZu83ktRil8hvWg0wyjRemT9RThlCl1V8w6Csxjk82OtG+P8370yDUcPp9WU3joKXh8WrK5H3Ssrvj9cZ60b47zZTINUaxh0ZaA5we9IarZlxnv71lN8Pm3IxnyHJkGuoqoQTvkmyyKhrhmY6UADmVqbKMMsT2MqoAXAi5nHcsNj/cd1Ix/uu6lqeC9xfdYBuUqUn1SpDudq7civgP84qjjA8V3UjfG7D1KC4dzmUdVXCf5pScXMq6p4z/ADiqm+N9fUl3xvrV1Fri3lPXJF7Uoducyk1tw2Jx2b4qomHlEJeEHzjusoO7kXJE9PBIKl7oXOdezM4+IXbpoBTm/Cpneqx/EsSKp+qZ/aKdwyT0iTtlFb7fxfNNJ/i70vCSPrX9RWBFbONFTJ2yncPqfSpO2g3oq3D653S09yOGHz9udp7lg/pGr1VT+0nfSdZ6U/rSDd8McPrmHoPckNc/z0fT/wBlhhlSt9Jf7k76WrvSD1BBt+HP89D2glFY8/W0/S8LEDK1aPrv8I7koyvW+dHYCeDciadwu0QuG0EJkk07Bd0UQG02WKGWKzW5h+4Ev0xVaxGfuqDYcJkP1ELulvekLiRd1BC77oKyP0vP5EXZSjKsuuKLqKqNS4Qnwskwn+UO5MMFG7TkWI/yB3LNjKr9cLPenDKzh9UOsqK77qPJp8LIkfsR3KF1Bkc6cjsH8tcgZZk8g9DylGWX+S/2hRHTdk7IevJgH3VE7JmQj/Urc11S+mn/AN77Qpfpp39921RNvSN7Ctb2kMfqXNpVMaaYvUrW9ppYgqmP1JpjGxWnNTC1VWPyjcVsw2PKqEq9lWwyhMP3lQctsBrs6nYThVW9ipY3k8yCayboKW6bfOgH5wVfya/fGFh0hUFPRO3qpY7UTYoOsI0u9qYBKAstIhEnCIKYNTmtREIiCkZBdShqlY1FQ8HRwdWw1OwKClwdJwdX8CN7QUODJODLoYEb2EHP4MkNKNi6O9hG9hNHN4N6kcG9S6JjCN7CaKTcnSOFwG9aX6Ln8gdoKy+R8LHOYQCPK0KNuVKljs28O+73FaxEIyZO4kNjBI05wnfRFV5g9YVsZaqrfqIzzRvQct1XogP3H9yYKRyTVejuTTkupGmB/Ur307Pro29bx/pS/TkmukZ7R34Uwc45NmGmF/ZTTQSD6p3ZXSflpwtembnF/wBafwqI5YB009v5w+aYqgaJ+uM9SbwM+bPUrxyqzzR9szvSDKsR+qd7Vh/1JiKPBP3Pck4IPJ9y6TcpQn6mY82E/NTMr6XwuD1N9HgX9yYOLwUeT7knBG7F3vpOm83Ujngcl+k6PW2Tpgd3JiuBwRmxN4GzyV3zX0J036YT3JpqqB3jRj+We5XBwjRt8lJwRuxdszUR0PiJ+yonT0XnoB94BTByeCDYUnBB611DLSHRNB2wkxU50SRH74VxHM4J6z1o4KfKd1rqWiOhzO0kwNGw9Kg5nBneU7rScHf5bl1BGCbWS8HzoOVvEnllJvUvlldbg4ScHCmjl73N5fuSFk3lDqXU4OkNOmjl2m2jqRab93qXS4Ojg6o0Nk0hYnjllHzNL2XfiSccso+Zpey78SzlXW1ICYQsbxxyh5ml7LvxJON+UPM03Zd+JMprYOCY4BZA7ra8/U03Zd3pDurrj9VT9l3emU1LugiaMonATymgnnXMEWa5N02qypPVTmWRsYcdgNvioTVPOkNW4ymcIxrTmDkjCqhlcdQStqHtFgAgtE6koaSqvCH7GpeFv2N6kFtObe+bSqQqnjxW9SUVkg8VnUg2MY5DbjPZSBqzDd0NW0W3uDsnvTuMlZ5qDsnvWcVp8KcGrLcZKzzUHZPel4zVvmqfsnvTBrGtUrGrIDdPWj6qn7Lu9OG6quH1VP2Xd6YNkGJ4ZmWMG66vH1NN2XfiS8cMoeZpey78SmVdbMMCXewsZxxyh5ml7LvxI445Q8zS9l34kyprZ72l3sLF8csoeZpey78SXjllHzFL2XfiTKa2e9hG9LGccso+Ypey78SOOWUPMUvZd+JMq62RjRvSxvHLKHmaXsu/Ek45ZQ8zS9l34kymtLOOTIPgqUjCXEBxPOT3FcF26itcSTDT5/3Xd6jfuiq3m+80w/l/mtstBvLzoaD91x/0JvBn3/Vj2X/81nTlypOmKn9kEn01UeZpvZBBphTvA8AD+W0f6EYcOkgdLR8lmhlypGiKnHNGnDdBWDQIxzAj5orRSvYMN5GjN5z/AHBMxMPj35nn/wBxcTjJWZrsjzfvPHwcjjLVeZhPO6T8aqO6G7HO7X+9OAf5Tu1/3XA4yVPo9N1PP+pNO6KqP1FMPuHvQaExyEaCe0f9KQwyb3+rvn8gn/Qs2cuVR+rp/ZhN+mqjDh3qnIvf9UEVo94fri/yh82Jd7w6QB0MH+lZz6aqBoigHNGlGXasaBH0Aj5ojQF7Bpc3tgfMJplZbM+/NJ/vXEG6GsHisP33/iTuMVV5qM875PxIO5E5t9J0Hxj/AO4kz6i7tf7iuIN0dUNEMH+M/wCpMOX6k/U04+4e9Fd+zz5R6XdxSGN58Qn7rj/oWeOW6k/VwezCZ9Lz+apz/KCI0BgfrjHTF3sRvLBpYwc7GD/SuAMsVA0RQDmjThlyrGgRjmBHzQauhDA84Q3wRot8lcAvfnWOj3SVkehkJ+1iPzUrd1dc0ZoaY87Xd6zVa3AkwhZTjbXeZpuy78SONld5mm7Lu9TF1q8ASYAsrxsrvM03Zd3pONdd5mm7Lu9MprV4EYFlONVd5mm7Lu9JxqrvM03Zd3pia4aEIWkCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQCEIQf/9k=\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"Jkv-55ndVYY\", width=\"60%\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index 48c9171..29198ed 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,8 @@ Alternatively, the content can be viewed in a web browser `Fraction` Type) - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/04_summary.ipynb) - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/05_review.ipynb) + - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/06_resources.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/06_resources.ipynb) #### Videos From 68fe189d89a78f6d069e4ccbcc3789beb8d0e81e Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Fri, 16 Oct 2020 00:28:15 +0200 Subject: [PATCH 070/142] Streamline slides --- 05_numbers/02_content.ipynb | 6 +++++- 05_numbers/03_appendix.ipynb | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/05_numbers/02_content.ipynb b/05_numbers/02_content.ipynb index 89ff220..e430e90 100644 --- a/05_numbers/02_content.ipynb +++ b/05_numbers/02_content.ipynb @@ -1180,7 +1180,11 @@ { "cell_type": "code", "execution_count": 35, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "outputs": [], "source": [ "from fractions import Fraction" diff --git a/05_numbers/03_appendix.ipynb b/05_numbers/03_appendix.ipynb index 3db9422..c470e82 100644 --- a/05_numbers/03_appendix.ipynb +++ b/05_numbers/03_appendix.ipynb @@ -24,7 +24,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "In this appendix, we look at the `Decimal` and `Fraction` types that can be used as replacements for the built-in `float` type mitigating its imprecision. The content is put in an appendix as the data science practitioner can live without knowing about it for quite some time. Eventually, when working with financial data, for example, knowing how to not use the `float` type in a bad way pays off." ] From 33f27da161bf6fccf3383e41df3bdaa73f9f8a33 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Sat, 17 Oct 2020 16:51:57 +0200 Subject: [PATCH 071/142] Fix missing static files for chapter 05 --- 05_numbers/static/complex_numbers.png | Bin 0 -> 22878 bytes 05_numbers/static/floating_point.png | Bin 0 -> 15371 bytes 05_numbers/static/numbers.png | Bin 0 -> 90958 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 05_numbers/static/complex_numbers.png create mode 100644 05_numbers/static/floating_point.png create mode 100644 05_numbers/static/numbers.png diff --git a/05_numbers/static/complex_numbers.png b/05_numbers/static/complex_numbers.png new file mode 100644 index 0000000000000000000000000000000000000000..807eb4647d1136f67c673fd2b81c74d3f813e08c GIT binary patch literal 22878 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 Ok(mi|?``_8;r|0G1$p%V literal 0 HcmV?d00001 diff --git a/05_numbers/static/floating_point.png b/05_numbers/static/floating_point.png new file mode 100644 index 0000000000000000000000000000000000000000..345aabaf1b3f150cfed2bef7739003913c8fd334 GIT binary patch literal 15371 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 RA1h$c55)T&dT@Z6{$EL>4blJr literal 0 HcmV?d00001 From 5eda6e8acfb72d0ef22ee6b4b42f3c548920960b Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 11:00:39 +0200 Subject: [PATCH 072/142] Add initial version of chapter 06 --- 00_intro/00_content.ipynb | 2 +- 06_text/00_content.ipynb | 3970 +++++++++++++++++++++++++++++++++++++ 06_text/lorem_ipsum.txt | 6 + README.md | 9 + 4 files changed, 3986 insertions(+), 1 deletion(-) create mode 100644 06_text/00_content.ipynb create mode 100644 06_text/lorem_ipsum.txt diff --git a/00_intro/00_content.ipynb b/00_intro/00_content.ipynb index d3e1fae..de293c8 100644 --- a/00_intro/00_content.ipynb +++ b/00_intro/00_content.ipynb @@ -769,7 +769,7 @@ "\n", "- How is data stored in memory?\n", " - *Chapter 5*: [Numbers & Bits ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb)\n", - " - *Chapter 6*: Text & Bytes\n", + " - *Chapter 6*: [Text & Bytes ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/00_content.ipynb)\n", " - *Chapter 7*: Sequential Data\n", " - *Chapter 8*: Map, Filter, & Reduce\n", " - *Chapter 9*: Mappings & Sets\n", diff --git a/06_text/00_content.ipynb b/06_text/00_content.ipynb new file mode 100644 index 0000000..7c9fff8 --- /dev/null +++ b/06_text/00_content.ipynb @@ -0,0 +1,3970 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/00_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 6: Text & Bytes" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In this chapter, we continue the study of the built-in data types. The next layer on top of numbers consists of **textual data** that are modeled primarily with the `str` type in Python. `str` objects are more complex than the numeric objects in [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb) as they *consist* of an *arbitrary* and possibly large number of *individual* characters that may be chosen from *any* alphabet in the history of humankind. Luckily, Python abstracts away most of this complexity from us. However, after looking at the `str` type in great detail, we briefly introduce the `bytes` type at the end of this chapter to understand how characters are modeled in memory." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The `str` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To create a `str` object, we use the *literal* notation and type the text between enclosing **double quotes** `\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "text = \"Lorem ipsum dolor sit amet.\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Like everything in Python, `text` is an object with an *identity*, a *type*, and a *value*." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140667764715968" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(text)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "str" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(text)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As seen before, a `str` object evaluates to itself in a literal notation with enclosing **single quotes** `'`.\n", + "\n", + "In [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Value-/-(Semantic)-\"Meaning\"), we specify the double quotes `\"` convention this book follows. Yet, single quotes `'` and double quotes `\"` are *perfect* substitutes. We could use the reverse convention, as well. As [this discussion ](https://stackoverflow.com/questions/56011/single-quotes-vs-double-quotes-in-python) shows, many programmers have *strong* opinions about such conventions. Consequently, the discussion was \"closed as not constructive\" by the moderators." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum dolor sit amet.'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As the single quote `'` is often used in the English language as a shortener, we could make an argument in favor of using the double quotes `\"`: There are possibly fewer situations like the two code cells below, where we must **escape** the kind of quote used as the `str` object's delimiter with a backslash `\"\\\"` inside the text (cf., also the \"*Unicode & (Special) Characters*\" section further below). However, double quotes `\"` are often used as well, for example, to indicate a quote like the one by [Albert Einstein](https://de.wikipedia.org/wiki/Albert_Einstein) below. So, such arguments are not convincing.\n", + "\n", + "Many proponents of the single quote `'` usage claim that double quotes `\"` cause more **visual noise** on the screen. However, this argument is also not convincing as, for example, one could claim that *two* single quotes `''` look so similar to *one* double quote `\"` that a reader may confuse an *empty* `str` object with a missing closing quote `\"`. With the double quotes `\"` convention we at least avoid such confusion (i.e., empty `str` objects are written as `\"\"`).\n", + "\n", + "This discussion is an excellent example of a [flame war ](https://en.wikipedia.org/wiki/Flaming_%28Internet%29#Flame_war) in the programming world: Everyone has an opinion and the discussion leads to *no* result." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Einstein said, \"If you can\\'t explain it, you don\\'t understand it.\"'" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Einstein said, \\\"If you can't explain it, you don't understand it.\\\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Einstein said, \"If you can\\'t explain it, you don\\'t understand it.\"'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "'Einstein said, \"If you can\\'t explain it, you don\\'t understand it.\"'" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "An *important* fact to know is that enclosing quotes of either kind are *not* part of the `str` object's *value*! They are merely *syntax* indicating the literal notation.\n", + "\n", + "So, printing out the sentence with the built-in [print() ](https://docs.python.org/3/library/functions.html#print) function does the same in both cases." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Einstein said, \"If you can't explain it, you don't understand it.\"\n" + ] + } + ], + "source": [ + "print(\"Einstein said, \\\"If you can't explain it, you don't understand it.\\\"\")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Einstein said, \"If you can't explain it, you don't understand it.\"\n" + ] + } + ], + "source": [ + "print('Einstein said, \"If you can\\'t explain it, you don\\'t understand it.\"')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As an alternative to the literal notation, we may use the built-in [str() ](https://docs.python.org/3/library/stdtypes.html#str) constructor to cast non-`str` objects as `str` ones. As [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb) reveals, basically any object in Python has a **text representation**. Because of that we may also pass `list` objects, the boolean `True` and `False`, or `None` to [str() ](https://docs.python.org/3/library/stdtypes.html#str)." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'42'" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "str(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'42.87'" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "str(42.87)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'[1, 2, 3]'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "str([1, 2, 3])" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'True'" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "str(True)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'False'" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "str(False)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'None'" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "str(None)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### User Input" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As shown in the \"*Guessing a Coin Toss*\" example in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb#Example:-Guessing-a-Coin-Toss), the built-in [input() ](https://docs.python.org/3/library/functions.html#input) function displays a prompt to the user and returns whatever is entered as a `str` object. [input() ](https://docs.python.org/3/library/functions.html#input) is in particular valuable when writing command-line tools." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Whatever you enter is put in a new string: 123\n" + ] + } + ], + "source": [ + "user_input = input(\"Whatever you enter is put in a new string: \")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "str" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(user_input)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'123'" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "user_input" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Reading Files" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A more common situation where we obtain `str` objects is when reading the contents of a file with the [open() ](https://docs.python.org/3/library/functions.html#open) built-in. In its simplest usage form, to open a [text file ](https://en.wikipedia.org/wiki/Text_file) file, we pass in its path (i.e., \"filename\") as a `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "file = open(\"lorem_ipsum.txt\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[open() ](https://docs.python.org/3/library/functions.html#open) returns a **[proxy ](https://en.wikipedia.org/wiki/Proxy_pattern)** object of type `TextIOWrapper` that allows us to interact with the file on disk. `mode='r'` shows that we opened the file in read-only mode and `encoding='UTF-8'` is explained in detail in the [The `bytes` Type ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/01_content.ipynb#The-bytes-Type) section at the end of this chapter." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "_io.TextIOWrapper" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(file)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "<_io.TextIOWrapper name='lorem_ipsum.txt' mode='r' encoding='UTF-8'>" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`TextIOWrapper` objects come with plenty of type-specific methods and attributes." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.readable()" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.writable()" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'lorem_ipsum.txt'" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.name" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'UTF-8'" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.encoding" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "So far, we have not yet read anything from the file (i.e., from disk)! That is intentional as, for example, the file could contain more data than could fit into our computer's memory. Therefore, we have to explicitly instruct the `file` object to read some of or all the data in the file.\n", + "\n", + "One way to do that, is to simply loop over the `file` object with the `for` statement as shown next: In each iteration, `line` is assigned the next line in the file. Because we may loop over `TextIOWrapper` objects, they are *iterables*." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lorem Ipsum is simply dummy text of the printing and typesetting industry.\n", + "\n", + "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s\n", + "\n", + "when an unknown printer took a galley of type and scrambled it to make a type\n", + "\n", + "specimen book. It has survived not only five centuries but also the leap into\n", + "\n", + "electronic typesetting, remaining essentially unchanged. It was popularised in\n", + "\n", + "the 1960s with the release of Letraset sheets.\n", + "\n" + ] + } + ], + "source": [ + "for line in file:\n", + " print(line)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Once we looped over the `file` object, it is **exhausted**: We can *not* loop over it a second time. So, the built-in [print() ](https://docs.python.org/3/library/functions.html#print) function is *never* called in the code cell below!" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "for line in file:\n", + " print(line)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "After the `for`-loop, the `line` variable is still set and references the *last* line in the file. We verify that it is indeed a `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'the 1960s with the release of Letraset sheets.\\n'" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "line" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "str" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(line)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "An *important* observation is that the `file` object is still associated with an *open* **[file descriptor ](https://en.wikipedia.org/wiki/File_descriptor)**. Without going into any technical details, we note that an operating system can only handle a *limited* number of \"open files\" at the same time, and, therefore, we should always *close* the file once we are done processing it.\n", + "\n", + "`TextIOWrapper` objects have a `closed` attribute on them that indicates if the associated file descriptor is still open or has been closed. We can \"manually\" close any `TextIOWrapper` object with the [close() ](https://docs.python.org/3/library/io.html#io.IOBase.close) method." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.closed" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "file.close()" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.closed" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The more Pythonic way is to use [open() ](https://docs.python.org/3/library/functions.html#open) within the compound `with` statement (cf., [reference ](https://docs.python.org/3/reference/compound_stmts.html#the-with-statement)): In the example below, the indented code block is said to be executed within the **context** of the `file` object that now plays the role of a **[context manager ](https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers)**. Many different kinds of context managers exist in Python with different applications and purposes. Context managers returned from [open() ](https://docs.python.org/3/library/functions.html#open) mainly ensure that file descriptors get automatically closed after the last line in the code block is executed." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lorem Ipsum is simply dummy text of the printing and typesetting industry.\n", + "\n", + "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s\n", + "\n", + "when an unknown printer took a galley of type and scrambled it to make a type\n", + "\n", + "specimen book. It has survived not only five centuries but also the leap into\n", + "\n", + "electronic typesetting, remaining essentially unchanged. It was popularised in\n", + "\n", + "the 1960s with the release of Letraset sheets.\n", + "\n" + ] + } + ], + "source": [ + "with open(\"lorem_ipsum.txt\") as file:\n", + " for line in file:\n", + " print(line)" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.closed" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Using syntax familiar from [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb#The-try-Statement) to explain what the `with open(...) as file:` does above, we provide an alternative formulation with a `try` statement below: The `finally`-branch is *always* executed, even if an exception is raised inside the `for`-loop. Therefore, `file` is sure to be closed too. However, this formulation is somewhat less expressive." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lorem Ipsum is simply dummy text of the printing and typesetting industry.\n", + "\n", + "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s\n", + "\n", + "when an unknown printer took a galley of type and scrambled it to make a type\n", + "\n", + "specimen book. It has survived not only five centuries but also the leap into\n", + "\n", + "electronic typesetting, remaining essentially unchanged. It was popularised in\n", + "\n", + "the 1960s with the release of Letraset sheets.\n", + "\n" + ] + } + ], + "source": [ + "try:\n", + " file = open(\"lorem_ipsum.txt\")\n", + " for line in file:\n", + " print(line)\n", + "finally:\n", + " file.close()" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.closed" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As an alternative to reading the contents of a file by looping over a `TextIOWrapper` object, we may also call one of the methods they come with.\n", + "\n", + "For example, the [read() ](https://docs.python.org/3/library/io.html#io.TextIOBase.read) method takes a single `size` argument of type `int` and returns a `str` object with the specified number of characters." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "file = open(\"lorem_ipsum.txt\")" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem Ipsum'" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.read(11)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When we call [read() ](https://docs.python.org/3/library/io.html#io.TextIOBase.read) again, the returned `str` object begins where the previous one left off. This is because `TextIOWrapper` objects like `file` simply store a position at which the associated file on disk is being read. In other words, `file` is like a **cursor** pointing into a file." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "' is simply '" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.read(11)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "On the contrary, the [readline() ](https://docs.python.org/3/library/io.html#io.TextIOBase.readline) method keeps reading until it hits a **newline character**. These are shown in `str` objects as `\"\\n\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'dummy text of the printing and typesetting industry.\\n'" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.readline()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When we call [readline() ](https://docs.python.org/3/library/io.html#io.TextIOBase.readline) again, we obtain the next line." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Lorem Ipsum has been the industry's standard dummy text ever since the 1500s\\n\"" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.readline()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Lastly, the [readlines() ](https://docs.python.org/3/library/io.html#io.IOBase.readlines) method returns a `list` object that holds *all* lines in the `file` from the current position to the end of the file. The latter position is often abbreviated as **EOF** in the documentation. Let's always remember that [readlines() ](https://docs.python.org/3/library/io.html#io.IOBase.readlines) has the potential to crash a computer with a `MemoryError`." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['when an unknown printer took a galley of type and scrambled it to make a type\\n',\n", + " 'specimen book. It has survived not only five centuries but also the leap into\\n',\n", + " 'electronic typesetting, remaining essentially unchanged. It was popularised in\\n',\n", + " 'the 1960s with the release of Letraset sheets.\\n']" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.readlines()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Calling [readlines() ](https://docs.python.org/3/library/io.html#io.IOBase.readlines) a second time, is as pointless as looping over `file` a second time." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "file.readlines()" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "file.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Because every `str` object created by reading the contents of a file in any of the ways shown in this section ends with a `\"\\n\"`, we see empty lines printed between each `line` in the `for`-loops above. To print the entire text without empty lines in between, we pass a `end=\"\"` argument to the [print() ](https://docs.python.org/3/library/functions.html#print) function." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lorem Ipsum is simply dummy text of the printing and typesetting industry.\n", + "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s\n", + "when an unknown printer took a galley of type and scrambled it to make a type\n", + "specimen book. It has survived not only five centuries but also the leap into\n", + "electronic typesetting, remaining essentially unchanged. It was popularised in\n", + "the 1960s with the release of Letraset sheets.\n" + ] + } + ], + "source": [ + "with open(\"lorem_ipsum.txt\") as file:\n", + " for line in file:\n", + " print(line, end=\"\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## A String of Characters" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A **sequence** is yet another *abstract* concept (cf., the \"*Containers vs. Iterables*\" section in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb#Containers-vs.-Iterables)).\n", + "\n", + "It unifies *four* [orthogonal ](https://en.wikipedia.org/wiki/Orthogonality) (i.e., \"independent\") concepts into one bigger idea: Any data type, such as `str`, is considered a sequence if it\n", + "\n", + "1. **contains**\n", + "2. a **finite** number of other objects that\n", + "3. can be **iterated** over\n", + "4. in a *predictable* **order**.\n", + "\n", + "[Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb#Collections-vs.-Sequences) formalizes these concepts in great detail. Here, we keep our focus on the `str` type that historically received its name as it models a **[string of characters ](https://en.wikipedia.org/wiki/String_%28computer_science%29)**. *String* is simply another term for *sequence* in the computer science literature.\n", + "\n", + "Another example of a sequence is the `list` type. Because of that, `str` objects may be treated like `list` objects in many situations.\n", + "\n", + "Below, the built-in [len() ](https://docs.python.org/3/library/functions.html#len) function tells us how many characters make up `text`. [len() ](https://docs.python.org/3/library/functions.html#len) would not work with an \"infinite\" object. As anything modeled in a program must fit into a computer's finite memory, there cannot exist truly infinite objects; however, [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb#Iterators-vs.-Iterables) introduces specialized iterable data types that can be used to model an *infinite* series of \"things\" and that, consequently, have no concept of \"length.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum dolor sit amet.'" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "27" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(text)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Being iterable, we may loop over `text` and do something with the individual characters, for example, print them out with extra space in between them. If it were not for the appropriately chosen name of the `text` variable, we could not tell what *concrete* type of object the `for` statement is looping over." + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "L o r e m i p s u m d o l o r s i t a m e t . " + ] + } + ], + "source": [ + "for character in text:\n", + " print(character, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With the [reversed() ](https://docs.python.org/3/library/functions.html#reversed) built-in, we may loop over `text` in reversed order. Reversing `text` only works as it has a forward order to begin with." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + ". t e m a t i s r o l o d m u s p i m e r o L " + ] + } + ], + "source": [ + "for character in reversed(text):\n", + " print(character, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Being a container, we may check if a given `str` object is contained in `text` with the `in` operator, which has *two* distinct usages: First, it checks if a *single* character is contained in a `str` object. Second, it may also check if a shorter `str` object, then called a **substring**, is contained in a longer one." + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"L\" in text" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"ipsum\" in text" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"veni, vidi, vici\" in text" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Indexing" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As `str` objects are *ordered* and *finite*, we may **index** into them to obtain individual characters with the **indexing operator** `[]`. This is analogous to how we obtained individual elements of a `list` object in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_content.ipynb#Who-am-I?-And-how-many?)." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'L'" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'o'" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[1]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The index must be of type `int`; othewise, we get a `TypeError`." + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "string indices must be integers", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: string indices must be integers" + ] + } + ], + "source": [ + "text[1.0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The last index is one less than the above \"length\" of the `str` object as we start counting at `0`." + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'.'" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[26] # == text[len(text) - 1]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "An `IndexError` is raised whenever the index is out of range." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "IndexError", + "evalue": "string index out of range", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m27\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;31m# == text[len(text)]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mIndexError\u001b[0m: string index out of range" + ] + } + ], + "source": [ + "text[27] # == text[len(text)]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We may use *negative* indexes to start counting from the end of the `str` object, as shown in the figure below. Note how this only works because sequences are *finite*." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "| Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12| 13| 14| 15| 16| 17| 18| 19| 20| 21| 22| 23| 24| 25| 26|\n", + "|:---------:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|\n", + "|**Reverse**|-27|-26|-25|-24|-23|-22|-21|-20|-19|-18|-17|-16|-15|-14|-13|-12|-11|-10|-9 |-8 |-7 |-6 |-5 |-4 |-3 |-2 |-1 |\n", + "| **Character** |`L`|`o`|`r`|`e`|`m`|` `|`i`|`p`|`s`|`u`|`m`|` `|`d`|`o`|`l`|`o`|`r`|` `|`s`|`i`|`t`|` `|`a`|`m`|`e`|`t`|`.`|" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'.'" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[-1]" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'L'" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[-27] # == text[-len(text)]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "One reason why programmers like to start counting at `0` is that a positive index and its *corresponding* negative index always add up to the length of the sequence. Here, `6` and `21` add to `27`." + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'i'" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[6]" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'i'" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[-21]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Slicing" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A **slice** is a substring of a `str` object.\n", + "\n", + "The **slicing operator** is a generalization of the indexing operator: We put one, two, or three integers within the brackets `[]`, separated by colons `:`. The three integers are then referred to as the *start*, *stop*, and *step* values.\n", + "\n", + "Let's start with two integers, *start* and *stop*. Whereas the character at the *start* position is included in the returned `str` object, the one at the *stop* position is not. If both *start* and *stop* are positive, the difference \"*stop* minus *start*\" tells us how many characters the resulting slice has. So, below, `5 - 0 == 5` implies that `\"Lorem\"` consists of `5` characters. So, colloquially speaking, `text[0:5]` means \"taking the first `5 - 0 == 5` characters of `text`.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem'" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[0:5]" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'dolor sit amet.'" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[12:len(text)]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If left out, *start* defaults to `0` and *stop* to the length of the `str` object (i.e., the end)." + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem'" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'dolor sit amet.'" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[12:]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Not including the character at the *stop* position makes working with individual slices easier as they add up to the original `str` object again (cf., the \"*String Operations*\" section below regarding the overloaded `+` operator)." + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum dolor sit amet.'" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[:5] + text[5:]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Slicing and indexing makes it easy to obtain shorter versions of the original `str` object. A common application would be to **parse** out meaningful substrings from raw text data." + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum sit amet.'" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[:11] + text[-10:]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "By combining a positive *start* with a negative *stop* index, we specify both ends of the slice *relative* to the ends of the entire `str` object. So, colloquially speaking, `[6:-10]` below means \"drop the first six and last ten characters.\" The length of the resulting slice can then *not* be calculated from the indexes and depends only on the length of the original `str` object!" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'ipsum dolor'" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[6:-10]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For convenience, the indexes do not need to lie within the range from `0` to `len(text)` when slicing. So, no `IndexError` is raised here." + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum dolor sit amet.'" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[-999:999]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "By leaving out both *start* and *stop*, we take a \"full\" slice that is essentially a *copy* of the original `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum dolor sit amet.'" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[:]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A *step* value of `i` can be used to obtain only every `i`th character." + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lrmismdlrstae.'" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[::2]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A negative *step* size of `-1` reverses the order of the characters." + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'.tema tis rolod muspi meroL'" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text[::-1]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Immutability" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Whereas elements of a `list` object *may* be *re-assigned*, as shortly hinted at in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_content.ipynb#Who-am-I?-And-how-many?), this is *not* allowed for the individual characters of `str` objects. Once created, they can *not* be changed. Formally, we say that `str` objects are **immutable**. In that regard, they are like the numeric types in [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb).\n", + "\n", + "On the contrary, objects that may be changed after creation, are called **mutable**. We already saw in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_content.ipynb#Who-am-I?-And-how-many?) how mutable objects are more difficult to reason about for a beginner, in particular, if more than one variable references it. Yet, mutability does have its place in a programmer's toolbox, and we revisit this idea in the next chapters.\n", + "\n", + "The `TypeError` indicates that `str` objects are *immutable*: Assignment to an index or a slice are *not* supported." + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'str' object does not support item assignment", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"X\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: 'str' object does not support item assignment" + ] + } + ], + "source": [ + "text[0] = \"X\"" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'str' object does not support item assignment", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"random\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: 'str' object does not support item assignment" + ] + } + ], + "source": [ + "text[:5] = \"random\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## String Methods" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Objects of type `str` come with many **methods** bound on them (cf., the [documentation ](https://docs.python.org/3/library/stdtypes.html#string-methods) for a full list). As seen before, they work like *normal* functions and are accessed via the **dot operator** `.`. Calling a method is also referred to as **method invocation**.\n", + "\n", + "The [find() ](https://docs.python.org/3/library/stdtypes.html#str.find) method returns the index of the first occurrence of a character or a substring. If no match is found, it returns `-1`. A mirrored version searching from the right called [rfind() ](https://docs.python.org/3/library/stdtypes.html#str.rfind) exists as well. The [index() ](https://docs.python.org/3/library/stdtypes.html#str.index) and [rindex() ](https://docs.python.org/3/library/stdtypes.html#str.rindex) methods work in the same way but raise a `ValueError` if no match is found. So, we can control if a search fails *silently* or *loudly*." + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum dolor sit amet.'" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "22" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.find(\"a\")" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.find(\"b\")" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.find(\"dolor\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[find() ](https://docs.python.org/3/library/stdtypes.html#str.find) takes optional *start* and *end* arguments that allow us to find occurrences other than the first one." + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.find(\"o\")" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "13" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.find(\"o\", 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.find(\"o\", 2, 12)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [count() ](https://docs.python.org/3/library/stdtypes.html#str.count) method does what we expect." + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum dolor sit amet.'" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.count(\"l\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As [count() ](https://docs.python.org/3/library/stdtypes.html#str.count) is *case-sensitive*, we must **chain** it with the [lower() ](https://docs.python.org/3/library/stdtypes.html#str.lower) method to get the count of all `\"L\"`s and `\"l\"`s." + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 83, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.lower().count(\"l\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Alternatively, we can use the [upper() ](https://docs.python.org/3/library/stdtypes.html#str.upper) method and search for `\"L\"`s." + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.upper().count(\"L\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Because `str` objects are *immutable*, [upper() ](https://docs.python.org/3/library/stdtypes.html#str.upper) and [lower() ](https://docs.python.org/3/library/stdtypes.html#str.lower) return *new* `str` objects, even if they do *not* change the value of the original `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "example = \"random\"" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140667840152112" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(example)" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "lower = example.lower()" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140667764453680" + ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(lower)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`example` and `lower` are *different* objects with the *same* value." + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 89, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "example is lower" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 90, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "example == lower" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Besides [upper() ](https://docs.python.org/3/library/stdtypes.html#str.upper) and [lower() ](https://docs.python.org/3/library/stdtypes.html#str.lower) there exist also [title() ](https://docs.python.org/3/library/stdtypes.html#str.title) and [swapcase() ](https://docs.python.org/3/library/stdtypes.html#str.swapcase) methods." + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'lorem ipsum dolor sit amet.'" + ] + }, + "execution_count": 91, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.lower()" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'LOREM IPSUM DOLOR SIT AMET.'" + ] + }, + "execution_count": 92, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.upper()" + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem Ipsum Dolor Sit Amet.'" + ] + }, + "execution_count": 93, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.title()" + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'lOREM IPSUM DOLOR SIT AMET.'" + ] + }, + "execution_count": 94, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.swapcase()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Another popular string method is [split() ](https://docs.python.org/3/library/stdtypes.html#str.split): It separates a longer `str` object into smaller ones collected in a `list` object. By default, groups of contiguous whitespace characters are used as the *separator*.\n", + "\n", + "As an example, we use [split() ](https://docs.python.org/3/library/stdtypes.html#str.split) to print out the individual words in `text` with more whitespace in between them." + ] + }, + { + "cell_type": "code", + "execution_count": 95, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Lorem', 'ipsum', 'dolor', 'sit', 'amet.']" + ] + }, + "execution_count": 95, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.split()" + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lorem ipsum dolor sit amet. " + ] + } + ], + "source": [ + "for word in text.split():\n", + " print(word, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The opposite of splitting is done with the [join() ](https://docs.python.org/3/library/stdtypes.html#str.join) method. It is typically invoked on a `str` object that represents a separator (e.g., `\" \"` or `\", \"`) and connects the elements provided by an *iterable* argument (e.g., `words` below) into one *new* `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "words = [\"This\", \"will\", \"become\", \"a\", \"sentence.\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "sentence = \" \".join(words)" + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'This will become a sentence.'" + ] + }, + "execution_count": 99, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sentence" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As the `str` object `\"abcde\"` below is an *iterable* itself, its characters (!) are joined together with a space `\" \"` in between." + ] + }, + { + "cell_type": "code", + "execution_count": 100, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'a b c d e'" + ] + }, + "execution_count": 100, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\" \".join(\"abcde\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [replace() ](https://docs.python.org/3/library/stdtypes.html#str.replace) method creates a *new* `str` object with parts of the original `str` object potentially replaced." + ] + }, + { + "cell_type": "code", + "execution_count": 101, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'This is a sentence.'" + ] + }, + "execution_count": 101, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sentence.replace(\"will become\", \"is\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Note how `sentence` itself remains unchanged. Bound to an immutable object, [replace() ](https://docs.python.org/3/library/stdtypes.html#str.replace) must create *new* objects." + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'This will become a sentence.'" + ] + }, + "execution_count": 102, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sentence" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As seen previously, the [strip() ](https://docs.python.org/3/library/stdtypes.html#str.strip) method is often helpful in cleaning text data from unreliable sources like user input from unnecessary leading and trailing whitespace. The [lstrip() ](https://docs.python.org/3/library/stdtypes.html#str.lstrip) and [rstrip() ](https://docs.python.org/3/library/stdtypes.html#str.rstrip) methods are specialized versions of it." + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'text with whitespace'" + ] + }, + "execution_count": 103, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\" text with whitespace \".strip()" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'text with whitespace '" + ] + }, + "execution_count": 104, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\" text with whitespace \".lstrip()" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "' text with whitespace'" + ] + }, + "execution_count": 105, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\" text with whitespace \".rstrip()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When justifying a `str` object for output, the [ljust() ](https://docs.python.org/3/library/stdtypes.html#str.ljust) and [rjust() ](https://docs.python.org/3/library/stdtypes.html#str.rjust) methods may be helpful." + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'This will become a sentence. '" + ] + }, + "execution_count": 106, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sentence.ljust(40)" + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "' This will become a sentence.'" + ] + }, + "execution_count": 107, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sentence.rjust(40)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Similarly, the [zfill() ](https://docs.python.org/3/library/stdtypes.html#str.zfill) method can be used to pad a `str` representation of a number with leading `0`s for justified output." + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'0000042.87'" + ] + }, + "execution_count": 108, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"42.87\".zfill(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'-000042.87'" + ] + }, + "execution_count": 109, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"-42.87\".zfill(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## String Operations" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As mentioned in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Operator-Overloading), the `+` and `*` operators are *overloaded* and used for **string concatenation**. They always create *new* `str` objects. That has nothing to do with the `str` type's immutability, but is the default behavior of operators." + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Hello Lore'" + ] + }, + "execution_count": 110, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Hello \" + text[:4]" + ] + }, + { + "cell_type": "code", + "execution_count": 111, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum ...'" + ] + }, + "execution_count": 111, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "5 * text[:12] + \"...\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### String Comparison" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The *relational* operators also work with `str` objects, another example of operator overloading. Comparison is done one character at a time in a pairwise fashion until the first pair differs or one operand ends. However, `str` objects are sorted in a \"weird\" way. For example, all upper case characters come before all lower case characters. The reason for that is given in the \"*Characters are Numbers with a Convention*\" sub-section in the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/02_content.ipynb#Characters-are-Numbers-with-a-Convention) of this chapter." + ] + }, + { + "cell_type": "code", + "execution_count": 112, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 112, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Apple\" < \"Banana\"" + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 113, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"apple\" < \"Banana\"" + ] + }, + { + "cell_type": "code", + "execution_count": 114, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 114, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"apple\" < \"Banana\".lower()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Below is an example with typical German last names that shows how characters other than the first decide the ordering." + ] + }, + { + "cell_type": "code", + "execution_count": 115, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 115, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Mai\" < \"Maier\" < \"Mayer\" < \"Meier\" < \"Meyer\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## String Interpolation" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Often, we want to use `str` objects as drafts in the source code that are filled in with concrete text only at runtime. This approach is called **string interpolation**. There are three ways to do that in Python." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### f-strings" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**[Formatted string literals ](https://docs.python.org/3/reference/lexical_analysis.html#formatted-string-literals)**, of **f-strings** for short, are the least recently added (cf., [PEP 498 ](https://www.python.org/dev/peps/pep-0498/) in 2016) and most readable way: We simply prepend a `str` in its literal notation with an `f`, and put variables, or more generally, expressions, within curly braces `{}`. These are then filled in when the string literal is evaluated." + ] + }, + { + "cell_type": "code", + "execution_count": 116, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "name = \"Alexander\"\n", + "time_of_day = \"morning\"" + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Hello Alexander! Good morning.'" + ] + }, + "execution_count": 117, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "f\"Hello {name}! Good {time_of_day}.\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Separated by a colon `:`, various formatting options are available. In the beginning, the ability to round numbers for output may be particularly useful: This can be achieved by adding `:.2f` to the variable name inside the curly braces, which casts the number as a `float` and rounds it to two digits. The `:.2f` is a so-called format specifier, and there exists a whole **[format specification mini-language ](https://docs.python.org/3/library/string.html#formatspec)** to govern how specifiers work." + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "pi = 3.141592653" + ] + }, + { + "cell_type": "code", + "execution_count": 119, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Pi is 3.14'" + ] + }, + "execution_count": 119, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "f\"Pi is {pi:.2f}\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### [format() ](https://docs.python.org/3/library/stdtypes.html#str.format) Method" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`str` objects also provide a [format() ](https://docs.python.org/3/library/stdtypes.html#str.format) method that accepts an arbitrary number of *positional* arguments that are inserted into the `str` object in the same order replacing empty curly brackets `{}`. String interpolation with the [format() ](https://docs.python.org/3/library/stdtypes.html#str.format) method is a more traditional and probably the most common way as of today. While f-strings are the recommended way going forward, usage of the [format() ](https://docs.python.org/3/library/stdtypes.html#str.format) method is likely not declining any time soon." + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Hello Alexander! Good morning.'" + ] + }, + "execution_count": 120, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Hello {}! Good {}.\".format(name, time_of_day)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We may use index numbers inside the curly braces if the order is different in the `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 121, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Good morning, Alexander'" + ] + }, + "execution_count": 121, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Good {1}, {0}\".format(name, time_of_day)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [format() ](https://docs.python.org/3/library/stdtypes.html#str.format) method may alternatively be used with *keyword* arguments as well. Then, we must put the keywords' names within the curly brackets." + ] + }, + { + "cell_type": "code", + "execution_count": 122, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Hello Alexander! Good morning.'" + ] + }, + "execution_count": 122, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Hello {name}! Good {time}.\".format(name=name, time=time_of_day)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Format specifiers work as in the f-string case." + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Pi is 3.14'" + ] + }, + "execution_count": 123, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Pi is {:.2f}\".format(pi)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### `%` Operator" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `%` operator that we saw in the context of modulo division in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#%28Arithmetic%29-Operators) is overloaded with string interpolation when its first operand is a `str` object. The second operand consists of all expressions to be filled in. Format specifiers work with a `%` instead of curly braces and according to a different set of rules referred to as **[printf-style string formatting ](https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting)**. So, `{:.2f}` becomes `%.2f`.\n", + "\n", + "This way of string interpolation is the oldest and originates from the [C language ](https://en.wikipedia.org/wiki/C_%28programming_language%29). It is still widely spread, but we should use one of the other two ways instead. We show it here mainly for completeness sake." + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Pi is 3.14'" + ] + }, + "execution_count": 124, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Pi is %.2f\" % pi" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To insert more than one expression, we must list them in order and between parenthesis `(` and `)`. As [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb#The-tuple-Type) reveals, this literal syntax creates an object of type `tuple`. Also, to format an expression as text, we use the format specifier `%s`." + ] + }, + { + "cell_type": "code", + "execution_count": 125, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Hello Alexander! Good morning.'" + ] + }, + "execution_count": 125, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Hello %s! Good %s.\" % (name, time_of_day)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/06_text/lorem_ipsum.txt b/06_text/lorem_ipsum.txt new file mode 100644 index 0000000..c0e21ab --- /dev/null +++ b/06_text/lorem_ipsum.txt @@ -0,0 +1,6 @@ +Lorem Ipsum is simply dummy text of the printing and typesetting industry. +Lorem Ipsum has been the industry's standard dummy text ever since the 1500s +when an unknown printer took a galley of type and scrambled it to make a type +specimen book. It has survived not only five centuries but also the leap into +electronic typesetting, remaining essentially unchanged. It was popularised in +the 1960s with the release of Letraset sheets. diff --git a/README.md b/README.md index 29198ed..d5d2970 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,15 @@ Alternatively, the content can be viewed in a web browser - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/05_review.ipynb) - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/06_resources.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/06_resources.ipynb) + - *Chapter 6*: Text & Bytes + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_numbers/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/00_content.ipynb) + (`str` Type; + Reading Files; + Sequences; + Indexing & Slicing; + String Methods & Operations; + String Interpolation) #### Videos From c6716db0b864779b603f64b125bba09509a8de45 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 11:34:08 +0200 Subject: [PATCH 073/142] Add initial version of chapter 06's exercises --- 06_text/01_exercises.ipynb | 995 +++++++++++++++++++++++++++++++++++++ README.md | 3 + 2 files changed, 998 insertions(+) create mode 100644 06_text/01_exercises.ipynb diff --git a/06_text/01_exercises.ipynb b/06_text/01_exercises.ipynb new file mode 100644 index 0000000..0781f2b --- /dev/null +++ b/06_text/01_exercises.ipynb @@ -0,0 +1,995 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/01_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 6: Text & Bytes (Coding Exercises)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/00_content.ipynb) of Chapter 6.\n", + "\n", + "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Detecting Palindromes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[Palindromes ](https://en.wikipedia.org/wiki/Palindrome) are sequences of characters that read the same backward as forward. Examples are first names like \"Hannah\" or \"Otto,\" words like \"radar\" or \"level,\" or sentences like \"Was it a car or a cat I saw?\"\n", + "\n", + "In this exercise, you implement various functions that check if the given arguments are palindromes or not. We start with an iterative implementation and end with a recursive one.\n", + "\n", + "Conceptually, the first function, `unpythonic_palindrome()`, is similar to the \"*Is the square of a number in `[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]` greater than `100`?*\" example in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb#Example:-Is-the-square-of-a-number-in-[7,-11,-8,-5,-3,-12,-2,-6,-9,-10,-1,-4]-greater-than-100?): It assumes that the `text` argument is a palindrome (i.e., it initializes `is_palindrom` to `True`) and then checks in a `for`-loop if a pair of corresponding characters, `forward` and `backward`, contradicts that.\n", + "\n", + "**Q1**: How many iterations are needed in the `for`-loop? Take into account that `text` may contain an even or odd number of characters! Inside `unpythonic_palindrome()` below, write an expression whose result is assigned to `chars_to_check`!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer > " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: `forward_index` is the index going from left to right. How can we calculate `backward_index`, the index going from right to left, for a given `forward_index`? Write an expression whose result is assigned to `backward_index`! Then, use the indexing operator `[]` to obtain the two characters, `forward` and `backward`, from `text`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: Finish `unpythonic_palindrome()` below! Add code that adjusts `text` such that the function is case insensitive if the `ignore_case` argument is `True`! Make sure that the function returns once the first pair of corresponding characters does not match!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def unpythonic_palindrome(text, *, ignore_case=True):\n", + " \"\"\"Check if a text is a palindrome or not.\n", + "\n", + " Args:\n", + " text (str): Text to be checked; must be an individual word\n", + " ignore_case (bool): If the check is case insensitive; defaults to True\n", + "\n", + " Returns:\n", + " is_palindrome (bool)\n", + " \"\"\"\n", + " # answer to Q3\n", + " is_palindrome = ...\n", + " if ignore_case:\n", + " ...\n", + " # answer to Q1\n", + " chars_to_check = ...\n", + "\n", + " for forward_index in range(chars_to_check):\n", + " # answer to Q2\n", + " backward_index = ...\n", + " forward = ...\n", + " backward = ...\n", + "\n", + " print(forward, \"and\", backward) # added for didactical purposes\n", + "\n", + " # answer to Q3\n", + " if ...:\n", + " is_palindrome = ...\n", + " ...\n", + "\n", + " return is_palindrome" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: Ensure that `unpythonic_palindrome()` works for the provided test cases (i.e., the `assert` statements do *not* raise an `AssertionError`)! Also, for each of the test cases, provide a brief explanation of what makes them *unique*!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert unpythonic_palindrome(\"noon\") is True" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your explanation 1 >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert unpythonic_palindrome(\"Hannah\") is True" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your explanation 2 >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert unpythonic_palindrome(\"Hannah\", ignore_case=False) is False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your explanation 3 >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert unpythonic_palindrome(\"radar\") is True" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your explanation 4 >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert unpythonic_palindrome(\"Hanna\") is False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your explanation 5 >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert unpythonic_palindrome(\"Warsaw\") is False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your explanation 6 >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`unpythonic_palindrome()` is considered *not* Pythonic as it uses index variables to implement the looping logic. Instead, we should simply loop over an *iterable* object to work with its elements one by one.\n", + "\n", + "**Q5**: Copy your solutions to the previous questions into `almost_pythonic_palindrome()` below!\n", + "\n", + "**Q6**: The [reversed() ](https://docs.python.org/3/library/functions.html#reversed) and [zip() ](https://docs.python.org/3/library/functions.html#zip) built-ins allow us to loop over the same `text` argument *in parallel* in both forward *and* backward order. Finish the `for` statement's header line to do just that!\n", + "\n", + "Hint: You may need to slice the `text` argument." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def almost_pythonic_palindrome(text, *, ignore_case=True):\n", + " \"\"\"Check if a text is a palindrome or not.\n", + "\n", + " Args:\n", + " text (str): Text to be checked; must be an individual word\n", + " ignore_case (bool): If the check is case insensitive; defaults to True\n", + "\n", + " Returns:\n", + " is_palindrome (bool)\n", + " \"\"\"\n", + " # answers from above\n", + " is_palindrome = ...\n", + " if ignore_case:\n", + " ...\n", + " chars_to_check = ...\n", + "\n", + " # answer to Q6\n", + " for ... in ...:\n", + "\n", + " print(forward, \"and\", backward) # added for didactical purposes\n", + "\n", + " # answers from above\n", + " if ...:\n", + " is_palindrome = ...\n", + " ...\n", + "\n", + " return is_palindrome" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: Verify that the test cases work as before!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert almost_pythonic_palindrome(\"noon\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert almost_pythonic_palindrome(\"Hannah\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert almost_pythonic_palindrome(\"Hannah\", ignore_case=False) is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert almost_pythonic_palindrome(\"radar\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert almost_pythonic_palindrome(\"Hanna\") is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert almost_pythonic_palindrome(\"Warsaw\") is False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: `almost_pythonic_palindrome()` above may be made more Pythonic by removing the variable `is_palindrome` with the *early exit* pattern. Make the corresponding changes!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def pythonic_palindrome(text, *, ignore_case=True):\n", + " \"\"\"Check if a text is a palindrome or not.\n", + "\n", + " Args:\n", + " text (str): Text to be checked; must be an individual word\n", + " ignore_case (bool): If the check is case insensitive; defaults to True\n", + "\n", + " Returns:\n", + " is_palindrome (bool)\n", + " \"\"\"\n", + " # answers from above\n", + " if ignore_case:\n", + " ...\n", + " chars_to_check = ...\n", + "\n", + " for ... in ...:\n", + "\n", + " print(forward, \"and\", backward) # added for didactical purposes\n", + "\n", + " if ...:\n", + " # answer to Q8\n", + " ...\n", + "\n", + " # answer to Q8\n", + " return ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: Verify that the test cases still work!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert pythonic_palindrome(\"noon\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "assert pythonic_palindrome(\"Hannah\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert pythonic_palindrome(\"Hannah\", ignore_case=False) is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert pythonic_palindrome(\"radar\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert pythonic_palindrome(\"Hanna\") is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert pythonic_palindrome(\"Warsaw\") is False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: `pythonic_palindrome()` is *not* able to check numeric palindromes. In addition to the string method that implements the case insensitivity and that essentially causes the `AttributeError`, what *abstract behaviors* are numeric data types, such as the `int` type in the example below, missing that would also cause runtime errors? " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pythonic_palindrome(12321)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: Copy your code from `pythonic_palindrome()` above into `palindrome_ducks()` below and make the latter conform to *duck typing*!\n", + "\n", + "Hints: You may want to use the [str() ](https://docs.python.org/3/library/functions.html#func-str) built-in. You only need to add *one* short line of code." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def palindrome_ducks(text, *, ignore_case=True):\n", + " \"\"\"Check if a text is a palindrome or not.\n", + "\n", + " Args:\n", + " text (str): Text to be checked; must be an individual word\n", + " ignore_case (bool): If the check is case insensitive; defaults to True\n", + "\n", + " Returns:\n", + " is_palindrome (bool)\n", + " \"\"\"\n", + " # answer to Q11\n", + " ...\n", + " # answers from above\n", + " if ignore_case:\n", + " text = ...\n", + " chars_to_check = ...\n", + "\n", + " for ... in ...:\n", + " if ...:\n", + " ...\n", + "\n", + " return ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q12**: Verify that the two new test cases work as well!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert palindrome_ducks(12321) is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert palindrome_ducks(12345) is False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`palindrome_ducks()` can *not* process palindromes that consist of more than one word." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "palindrome_ducks(\"Never odd or even.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "palindrome_ducks(\"Eva, can I stab bats in a cave?\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "palindrome_ducks(\"A man, a plan, a canal - Panama.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "palindrome_ducks(\"A Santa lived as a devil at NASA!\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "palindrome_ducks(\"\"\"\n", + " Dennis, Nell, Edna, Leon, Nedra, Anita, Rolf, Nora, Alice, Carol, Leo, Jane,\n", + " Reed, Dena, Dale, Basil, Rae, Penny, Lana, Dave, Denny, Lena, Ida, Bernadette,\n", + " Ben, Ray, Lila, Nina, Jo, Ira, Mara, Sara, Mario, Jan, Ina, Lily, Arne, Bette,\n", + " Dan, Reba, Diane, Lynn, Ed, Eva, Dana, Lynne, Pearl, Isabel, Ada, Ned, Dee,\n", + " Rena, Joel, Lora, Cecil, Aaron, Flora, Tina, Arden, Noel, and Ellen sinned.\n", + "\"\"\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q13**: Implement the final iterative version `is_a_palindrome()` below. Copy your solution from `palindrome_ducks()` above and add code that removes the \"special\" characters (and symbols) from the longer example palindromes above so that they are effectively ignored! Note that this processing should only be done if the `ignore_symbols` argument is set to `True`.\n", + "\n", + "Hints: Use the [replace() ](https://docs.python.org/3/library/stdtypes.html#str.replace) method on the `str` type to achieve that. You may want to do so within another `for`-loop." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def is_a_palindrome(text, *, ignore_case=True, ignore_symbols=True):\n", + " \"\"\"Check if a text is a palindrome or not.\n", + "\n", + " Args:\n", + " text (str): Text to be checked; may be multiple words\n", + " ignore_case (bool): If the check is case insensitive; defaults to True\n", + " ignore_symbols (bool): If special characters like \".\" or \"?\" and others\n", + " are ignored; defaults to True\n", + "\n", + " Returns:\n", + " is_palindrome (bool)\n", + " \"\"\"\n", + " # answers from above\n", + " ...\n", + " if ignore_case:\n", + " ...\n", + " # answer to Q13\n", + " if ignore_symbols:\n", + " for ... in ...:\n", + " ...\n", + " # answers from above\n", + " chars_to_check = ...\n", + "\n", + " for ... in ...:\n", + " if ...:\n", + " ...\n", + "\n", + " return ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q14**: Verify that all test cases below work!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"noon\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"Hannah\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"Hannah\", ignore_case=False) is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"radar\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"Hanna\") is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"Warsaw\") is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(12321) is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(12345) is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"Never odd or even.\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"Never odd or even.\", ignore_symbols=False) is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"Eva, can I stab bats in a cave?\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"A man, a plan, a canal - Panama.\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"A Santa lived as a devil at NASA!\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert is_a_palindrome(\"\"\"\n", + " Dennis, Nell, Edna, Leon, Nedra, Anita, Rolf, Nora, Alice, Carol, Leo, Jane,\n", + " Reed, Dena, Dale, Basil, Rae, Penny, Lana, Dave, Denny, Lena, Ida, Bernadette,\n", + " Ben, Ray, Lila, Nina, Jo, Ira, Mara, Sara, Mario, Jan, Ina, Lily, Arne, Bette,\n", + " Dan, Reba, Diane, Lynn, Ed, Eva, Dana, Lynne, Pearl, Isabel, Ada, Ned, Dee,\n", + " Rena, Joel, Lora, Cecil, Aaron, Flora, Tina, Arden, Noel, and Ellen sinned.\n", + "\"\"\") is True" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, let's look at a *recursive* formulation in `recursive_palindrome()` below.\n", + "\n", + "**Q15**: Copy the code from `is_a_palindrome()` that implements the duck typing, the case insensitivity, and the removal of special characters!\n", + "\n", + "The recursion becomes apparent if we remove the *first* and the *last* character from a given `text`: `text` can only be a palindrome if the two removed characters are the same *and* the remaining substring is a palindrome itself! So, the word `\"noon\"` has only *one* recursive call while `\"radar\"` has *two*.\n", + "\n", + "Further, `recursive_palindrome()` has *two* base cases of which only *one* is reached for a given `text`: First, if `recursive_palindrome()` is called with either an empty `\"\"` or a `text` argument with `len(text) == 1`, and, second, if the two removed characters are *not* the same.\n", + "\n", + "**Q16**: Implement the two base cases in `recursive_palindrome()`! Use the *early exit* pattern!\n", + "\n", + "**Q17**: Add the recursive call to `recursive_palindrome()` with a substring of `text`! Pass in the `ignore_case` and `ignore_symbols` arguments as `False`! This avoids unnecessary computations in the recursive calls. Why is that the case?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def recursive_palindrome(text, *, ignore_case=True, ignore_symbols=True):\n", + " \"\"\"Check if a text is a palindrome or not.\n", + "\n", + " Args:\n", + " text (str): Text to be checked; may be multiple words\n", + " ignore_case (bool): If the check is case insensitive; defaults to True\n", + " ignore_symbols (bool): If special characters like \".\" or \"?\" and others\n", + " are ignored; defaults to True\n", + "\n", + " Returns:\n", + " is_palindrome (bool)\n", + " \"\"\"\n", + " # answers from above\n", + " ...\n", + " if ignore_case:\n", + " ...\n", + " if ignore_symbols:\n", + " for ... in ...:\n", + " ...\n", + "\n", + " # answer to Q16\n", + " if ...:\n", + " ...\n", + " elif ...:\n", + " ...\n", + "\n", + " # answer to Q17\n", + " return ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q18**: Lastly, verify that `recursive_palindrome()` passes all the test cases below!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"noon\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"Hannah\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"Hannah\", ignore_case=False) is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"radar\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"Hanna\") is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"Warsaw\") is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(12321) is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(12345) is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"Never odd or even.\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"Never odd or even.\", ignore_symbols=False) is False" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"Eva, can I stab bats in a cave?\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"A man, a plan, a canal - Panama.\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"A Santa lived as a devil at NASA!\") is True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert recursive_palindrome(\"\"\"\n", + " Dennis, Nell, Edna, Leon, Nedra, Anita, Rolf, Nora, Alice, Carol, Leo, Jane,\n", + " Reed, Dena, Dale, Basil, Rae, Penny, Lana, Dave, Denny, Lena, Ida, Bernadette,\n", + " Ben, Ray, Lila, Nina, Jo, Ira, Mara, Sara, Mario, Jan, Ina, Lily, Arne, Bette,\n", + " Dan, Reba, Diane, Lynn, Ed, Eva, Dana, Lynne, Pearl, Isabel, Ada, Ned, Dee,\n", + " Rena, Joel, Lora, Cecil, Aaron, Flora, Tina, Arden, Noel, and Ellen sinned.\n", + "\"\"\") is True" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index d5d2970..430dbb9 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,9 @@ Alternatively, the content can be viewed in a web browser Indexing & Slicing; String Methods & Operations; String Interpolation) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/01_exercises.ipynb) + (Detecting Palindromes) #### Videos From 3c91b00749cfcfa6998d3f71479893573f4f8b03 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 11:40:27 +0200 Subject: [PATCH 074/142] Add initial version of chapter 06, part 2 --- 06_text/02_content.ipynb | 2100 ++++++++++++++++++++++++++++++++++++++ 06_text/full_house.bin | 1 + 06_text/umlauts.txt | 12 + README.md | 7 + 4 files changed, 2120 insertions(+) create mode 100644 06_text/02_content.ipynb create mode 100644 06_text/full_house.bin create mode 100644 06_text/umlauts.txt diff --git a/06_text/02_content.ipynb b/06_text/02_content.ipynb new file mode 100644 index 0000000..6ff2407 --- /dev/null +++ b/06_text/02_content.ipynb @@ -0,0 +1,2100 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/01_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 6: Text & Bytes (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In this second part of the chapter, we look in more detail at how `str` objects work in memory, in particular how the $0$s and $1$s in the memory translate into characters." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Special Characters" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As previously seen, some characters have a special meaning when following the **escape character** `\"\\\"`. Besides escaping the kind of quote used as the `str` object's delimiter, `'` or `\"`, most of these **escape sequences** (i.e., `\"\\\"` with the subsequent character), act as a **control character** that moves the \"cursor\" in the output *without* generating any pixel on the screen. Because of that, we only see the effect of such escape sequences when used with the [print() ](https://docs.python.org/3/library/functions.html#print) function. The [documentation ](https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals) lists all available escape sequences, of which we show the most important ones below.\n", + "\n", + "The most common escape sequence is `\"\\n\"` that \"prints\" a [newline character ](https://en.wikipedia.org/wiki/Newline) that is also called the line feed character or LF for short." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'This is a sentence\\nthat is printed\\non three lines.'" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"This is a sentence\\nthat is printed\\non three lines.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This is a sentence\n", + "that is printed\n", + "on three lines.\n" + ] + } + ], + "source": [ + "print(\"This is a sentence\\nthat is printed\\non three lines.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`\"\\b\"` is the [backspace character ](https://en.wikipedia.org/wiki/Backspace), or BS for short, that moves the cursor back by one character." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ABX\n" + ] + } + ], + "source": [ + "print(\"ABC\\bX\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ABXY\n" + ] + } + ], + "source": [ + "print(\"ABC\\bXY\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Similarly, `\"\\r\"` is the [carriage return character ](https://en.wikipedia.org/wiki/Carriage_return), or CR for short, that moves the cursor back to the beginning of the line." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "XBC\n" + ] + } + ], + "source": [ + "print(\"ABC\\rX\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "XYC\n" + ] + } + ], + "source": [ + "print(\"ABC\\rXY\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While Linux and modern MacOS systems use solely `\"\\n\"` to express a new line, Windows systems default to using `\"\\r\\n\"`. This may lead to \"weird\" bugs on software projects where people using both kind of operating systems collaborate." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This is a sentence\n", + "that is printed\n", + "on three lines.\n" + ] + } + ], + "source": [ + "print(\"This is a sentence\\r\\nthat is printed\\r\\non three lines.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`\"\\t\"` makes the cursor \"jump\" in equidistant tab stops. That may be useful for formatting a program with lengthy and tabular results." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Jump\tfrom\ttab\tstop\tto\ttab\tstop.\n", + "The\tsecond\tline\tdoes\tso\ttoo.\n" + ] + } + ], + "source": [ + "print(\"Jump\\tfrom\\ttab\\tstop\\tto\\ttab\\tstop.\\nThe\\tsecond\\tline\\tdoes\\tso\\ttoo.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Raw Strings" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Sometimes we do *not* want the backslash `\"\\\"` and its subsequent character be interpreted as an escape sequence. For example, let's print a typical installation path on a Windows systems. Obviously, the newline character `\"\\n\"` does *not* makes sense here." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C:\\Programs\n", + "ew_application\n" + ] + } + ], + "source": [ + "print(\"C:\\Programs\\new_application\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Some `str` objects even produce a `SyntaxError` because the `\"\\U\"` can *not* be interpreted as a Unicode code point (cf., next section)." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "(unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape (, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print(\"C:\\Users\\Administrator\\Desktop\\Project\")\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape\n" + ] + } + ], + "source": [ + "print(\"C:\\Users\\Administrator\\Desktop\\Project\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A simple solution would be to escape the escape character with a *second* backslash `\"\\\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C:\\Programs\\new_application\n" + ] + } + ], + "source": [ + "print(\"C:\\\\Programs\\\\new_application\")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C:\\Users\\Administrator\\Desktop\\Project\n" + ] + } + ], + "source": [ + "print(\"C:\\\\Users\\\\Administrator\\\\Desktop\\\\Project\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, this is tedious to remember and type. For such use cases, Python allows to prefix any string literal with a `r`. The literal is then interpreted in a \"raw\" way." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C:\\Programs\\new_application\n" + ] + } + ], + "source": [ + "print(r\"C:\\Programs\\new_application\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C:\\Users\\Administrator\\Desktop\\Project\n" + ] + } + ], + "source": [ + "print(r\"C:\\Users\\Administrator\\Desktop\\Project\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Characters are Numbers with a Convention" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "So far, we used the term **character** without any further consideration. In this section, we briefly look into what characters are and how they are modeled in software.\n", + "\n", + "[Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb) gives us an idea on how individual **bits** are used to express all types of numbers, from \"simple\" `int` objects to \"complex\" `float` ones. To model characters, another **layer of abstraction** is put on top of whole numbers. So, just as bits are used to express integers, they themselves are used to express characters." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### ASCII" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Many conventions have been developed as to what integer is associated with which character. The most basic one that was also adopted around the world is the the so-called [American Standard Code for Information Interchange ](https://en.wikipedia.org/wiki/ASCII), or **ASCII** for short. It uses 7 bits of information to map the unprintable control characters as well as the printable letters of the alphabet, numbers, and common symbols to the numbers `0` through `127`.\n", + "\n", + "A mapping from characters to numbers is referred to by the technical term **encoding**. We may use the built-in [ord() ](https://docs.python.org/3/library/functions.html#ord) function to **encode** any single character. The inverse to that is the built-in [chr() ](https://docs.python.org/3/library/functions.html#chr) function, which **decodes** a number into a character." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "65" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ord(\"A\")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'A'" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chr(65)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Of course, unprintable escape sequences like `\"\\n\"` count as only *one* character." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ord(\"\\n\")" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'\\n'" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chr(10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In ASCII, the numbers `0` through `31` (and `127`) are mapped to all kinds of unprintable control characters. The decimal digits are encoded with the numbers `48` through `57`, the upper case letters with `65` through `90`, and the lower case letters with `97` through `122`. While this seems random as first, there is of course a \"sophisticated\" system behind it. That can immediately be seen when looking at the encoded numbers in their *binary* representations.\n", + "\n", + "For example, the digit `5` is mapped to the number `53` in ASCII. The binary representation of `53` is `0b_11_0101` and the least significant four bits, `0101`, mean $5$. Similarly, the letter `\"E\"` is the fifth letter in the alphabet. It is encoded with the number `69` in ASCII, which is `0b_100_0101` in binary. And, the least significant bits, `0_0101`, mean $5$. Analogously, `\"e\"` is encoded with `101` in ASCII, which is `0b_110_0101` in binary. And, the least significant bits, `0_0101`, mean $5$ again. This encoding was chosen mainly because programmers \"in the old days\" needed to implement these encodings \"by hand.\" Python abstracts that logic away from its users.\n", + "\n", + "This encoding scheme is also the cause for the \"weird\" sorting in the \"*String Comparison*\" section in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/01_content.ipynb#String-Comparison) of this chapter, where `\"apple\"` comes *after* `\"Banana\"`. As `\"a\"` is encoded with `97` and `\"B\"` with `66`, `\"Banana\"` must of course be \"smaller\" than `\"apple\"` when comparison is done in a pairwise fashion of the individual characters." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "48 0b110000 -> 0\n", + "49 0b110001 -> 1\n", + "50 0b110010 -> 2\n", + "51 0b110011 -> 3\n", + "52 0b110100 -> 4\n", + "53 0b110101 -> 5\n", + "54 0b110110 -> 6\n", + "55 0b110111 -> 7\n", + "56 0b111000 -> 8\n", + "57 0b111001 -> 9\n" + ] + } + ], + "source": [ + "for number in range(48, 58):\n", + " print(number, bin(number), \"-> \", chr(number))" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "65 0b1000001 -> A\t66 0b1000010 -> B\t67 0b1000011 -> C\n", + "68 0b1000100 -> D\t69 0b1000101 -> E\t70 0b1000110 -> F\n", + "71 0b1000111 -> G\t72 0b1001000 -> H\t73 0b1001001 -> I\n", + "74 0b1001010 -> J\t75 0b1001011 -> K\t76 0b1001100 -> L\n", + "77 0b1001101 -> M\t78 0b1001110 -> N\t79 0b1001111 -> O\n", + "80 0b1010000 -> P\t81 0b1010001 -> Q\t82 0b1010010 -> R\n", + "83 0b1010011 -> S\t84 0b1010100 -> T\t85 0b1010101 -> U\n", + "86 0b1010110 -> V\t87 0b1010111 -> W\t88 0b1011000 -> X\n", + "89 0b1011001 -> Y\t90 0b1011010 -> Z\t" + ] + } + ], + "source": [ + "for i, number in enumerate(range(65, 91), start=1):\n", + " end = \"\\n\" if i % 3 == 0 else \"\\t\"\n", + " print(number, bin(number), \"-> \", chr(number), end=end)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 97 0b1100001 -> a\t 98 0b1100010 -> b\t 99 0b1100011 -> c\n", + "100 0b1100100 -> d\t101 0b1100101 -> e\t102 0b1100110 -> f\n", + "103 0b1100111 -> g\t104 0b1101000 -> h\t105 0b1101001 -> i\n", + "106 0b1101010 -> j\t107 0b1101011 -> k\t108 0b1101100 -> l\n", + "109 0b1101101 -> m\t110 0b1101110 -> n\t111 0b1101111 -> o\n", + "112 0b1110000 -> p\t113 0b1110001 -> q\t114 0b1110010 -> r\n", + "115 0b1110011 -> s\t116 0b1110100 -> t\t117 0b1110101 -> u\n", + "118 0b1110110 -> v\t119 0b1110111 -> w\t120 0b1111000 -> x\n", + "121 0b1111001 -> y\t122 0b1111010 -> z\t" + ] + } + ], + "source": [ + "for i, number in enumerate(range(97, 123), start=1):\n", + " end = \"\\n\" if i % 3 == 0 else \"\\t\"\n", + " print(str(number).rjust(3), bin(number), \"-> \", chr(number), end=end)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The remaining `symbols` encoded in ASCII are encoded with the numbers still unused, which is why they are scattered." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "symbols = (\n", + " list(range(32, 48))\n", + " + list(range(58, 65))\n", + " + list(range(91, 97))\n", + " + list(range(123, 127))\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 32 0b100000 -> \t 33 0b100001 -> !\t 34 0b100010 -> \"\n", + " 35 0b100011 -> #\t 36 0b100100 -> $\t 37 0b100101 -> %\n", + " 38 0b100110 -> &\t 39 0b100111 -> '\t 40 0b101000 -> (\n", + " 41 0b101001 -> )\t 42 0b101010 -> *\t 43 0b101011 -> +\n", + " 44 0b101100 -> ,\t 45 0b101101 -> -\t 46 0b101110 -> .\n", + " 47 0b101111 -> /\t 58 0b111010 -> :\t 59 0b111011 -> ;\n", + " 60 0b111100 -> <\t 61 0b111101 -> =\t 62 0b111110 -> >\n", + " 63 0b111111 -> ?\t 64 0b1000000 -> @\t 91 0b1011011 -> [\n", + " 92 0b1011100 -> \\\t 93 0b1011101 -> ]\t 94 0b1011110 -> ^\n", + " 95 0b1011111 -> _\t 96 0b1100000 -> `\t123 0b1111011 -> {\n", + "124 0b1111100 -> |\t125 0b1111101 -> }\t126 0b1111110 -> ~\n" + ] + } + ], + "source": [ + "for i, number in enumerate(symbols, start=1):\n", + " end = \"\\n\" if i % 3 == 0 else \"\\t\"\n", + " print(str(number).rjust(3), bin(number).rjust(10), \"-> \", chr(number), end=end)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As the ASCII character set does not work for many languages other than English, various encodings were developed. Popular examples are [ISO 8859-1 ](https://en.wikipedia.org/wiki/ISO/IEC_8859-1) for western European letters or [Windows 1250 ](https://en.wikipedia.org/wiki/Windows-1250) for Latin ones. Many of these encodings use 8-bit numbers (i.e., `0` through `255`) to map the multitude of non-English letters (e.g., the German [umlauts ](https://en.wikipedia.org/wiki/Umlaut_%28linguistics%29) `\"ä\"`, `\"ö\"`, `\"ü\"`, or `\"ß\"`)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Unicode" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, none of these specialized encodings can map *all* characters of *all* languages around the world from *all* times in human history. To achieve that, a truly global standard called **[Unicode ](https://en.wikipedia.org/wiki/Unicode)** was developed and its first version released in 1991. Since then, Unicode has been amended with many other \"characters.\" The most popular among them being [emojis ](https://en.wikipedia.org/wiki/Emoji) or the [Klingon ](https://en.wikipedia.org/wiki/Klingon_scripts) language (from the science fiction series [Star Trek ](https://en.wikipedia.org/wiki/Star_Trek)). In Unicode, every character is given an identity referred to as the **code point**. Code points are hexadecimal numbers from `0x0000` through `0x10ffff`, written as U+0000 and U+10FFFF outside of Python. Consequently, there exist at most $1,114,112$ code points, of which only about 10% are currently in use, allowing lots of room for new characters to be invented. The first `127` code points are identical to the ASCII encoding for reasons explained in the \"*The `bytes` Type*\" section further below. There exist plenty of lists of all Unicode characters on the web (e.g., [Wikipedia ](https://en.wikipedia.org/wiki/List_of_Unicode_characters)).\n", + "\n", + "All we need to know to print a character is its code point. Python uses the escape sequence `\"\\U\"` that is followed by eight hexadecimal digits. Underscore separators are unfortunately *not* allowed here.\n", + "\n", + "So, to print a smiley, we just need to look up the corresponding number (e.g., [here ](https://en.wikipedia.org/wiki/Emoji#Unicode_blocks))." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'😄'" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"\\U0001f604\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Every Unicode character also has a descriptive name that we can use with the escape sequence `\"\\N\"` and within curly braces `{}`." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'😂'" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"\\N{FACE WITH TEARS OF JOY}\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Whenever the code point can be expressed with just four hexadecimal digits, we may use the escape sequence `\"\\u\"` for brevity." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'A'" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"\\U00000041\" # hex(65) == 0x41" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'A'" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"\\u0041\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Analogously, if the code point can be expressed with two hexadecimal digits, we may use the escape sequence `\"\\x\"` for even conciser code." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'A'" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"\\x41\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As the `str` type is based on Unicode, a `str` object's behavior is more in line with how humans view text and not how it is expressed in source code.\n", + "\n", + "For example, while it is obvious that `len(\"A\")` evaluates to `1`, ..." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(\"A\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... what should `len(\"\\N{SNAKE}\")` evaluate to? As the idea of a snake is expressed as *one* \"character,\" [len() ](https://docs.python.org/3/library/functions.html#len) also returns `1` here." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'ðŸ'" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"\\N{SNAKE}\"" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(\"\\N{SNAKE}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Many of the built-in `str` methods also consider Unicode. For example, in contrast to [lower() ](https://docs.python.org/3/library/stdtypes.html#str.lower), the [casefold() ](https://docs.python.org/3/library/stdtypes.html#str.casefold) method knows that the German `\"ß\"` is commonly converted to `\"ss\"`. So, when searching for exact matches, normalizing text with [casefold() ](https://docs.python.org/3/library/stdtypes.html#str.casefold) may yield better results than with [lower() ](https://docs.python.org/3/library/stdtypes.html#str.lower)." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'straße'" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Straße\".lower()" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'strasse'" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"Straße\".casefold()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Many other methods like [isdecimal() ](https://docs.python.org/3/library/stdtypes.html#str.isdecimal), [isdigit() ](https://docs.python.org/3/library/stdtypes.html#str.isdigit), [isnumeric() ](https://docs.python.org/3/library/stdtypes.html#str.isnumeric), [isprintable() ](https://docs.python.org/3/library/stdtypes.html#str.isprintable), [isidentifier() ](https://docs.python.org/3/library/stdtypes.html#str.isidentifier), and many more may be worthwhile to know for the data science practitioner, especially when it comes to data cleaning." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Multi-line Strings" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Sometimes, it is convenient to split text across multiple lines in source code. For example, to make lines fit into the 79 characters requirement of [PEP 8 ](https://www.python.org/dev/peps/pep-0008/) or because the text consists of many lines and typing out `\"\\n\"` is tedious. However, using single double quotes `\"` around multiple lines results in a `SyntaxError`." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "EOL while scanning string literal (, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m \"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m EOL while scanning string literal\n" + ] + } + ], + "source": [ + "\"\n", + "Do not break the lines like this\n", + "\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Instead, we may enclose a string literal with either **triple double** quotes `\"\"\"` or **triple single** quotes `'''`. Then, newline characters in the source code are converted into `\"\\n\"` characters in the resulting `str` object. Docstrings are precisely that, and, by convention, always written within triple double quotes `\"\"\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "multi_line = \"\"\"\n", + "I am a multi-line string\n", + "consisting of four lines.\n", + "\"\"\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A caveat is that `\"\\n\"` characters are often inserted at the beginning or end of the text when we try to format the source code nicely." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'\\nI am a multi-line string\\nconsisting of four lines.\\n'" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "multi_line" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "I am a multi-line string\n", + "consisting of four lines.\n", + "\n" + ] + } + ], + "source": [ + "print(multi_line)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Using the [split() ](https://docs.python.org/3/library/stdtypes.html#str.split) method with the optional `sep` argument, we confirm that `multi_line` consists of *four* lines with the first and last line being empty." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 \n", + "2 I am a multi-line string\n", + "3 consisting of four lines.\n", + "4 \n" + ] + } + ], + "source": [ + "for i, line in enumerate(multi_line.split(\"\\n\"), start=1):\n", + " print(i, line)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To mitigate that, we often see the [strip() ](https://docs.python.org/3/library/stdtypes.html#bytes.strip) method in source code." + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "multi_line = \"\"\"\n", + "I am a multi-line string\n", + "consisting of two lines.\n", + "\"\"\".strip()" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 I am a multi-line string\n", + "2 consisting of two lines.\n" + ] + } + ], + "source": [ + "for i, line in enumerate(multi_line.split(\"\\n\"), start=1):\n", + " print(i, line)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The `bytes` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To end this chapter, we want to briefly look at the `bytes` data type, which conceptually is a sequence of bytes. That data format is probably one of the most generic ways of exchanging data between any two programs or computers (e.g., a web browser obtains its data from a web server in this format).\n", + "\n", + "Let's open a binary file in read-only mode (i.e., `mode=\"rb\"`) and read in all of its contents." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "with open(\"full_house.bin\", mode=\"rb\") as binary_file:\n", + " data = binary_file.read()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`data` is an object of type `bytes`." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139880714782512" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(data)" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "bytes" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(data)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "It's value is given out in the literal bytes notation with a `b` prefix (cf., the [reference ](https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals)). Every byte is expressed in hexadecimal representation with the escape sequence `\"\\x\"`. This representation is commonly chosen as we can *not* tell what kind of information is hidden in the `data` by just looking at the bytes. Instead, we must be told by some other source how to **decode** the raw bytes into information we can interpret." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "b'\\xf0\\x9f\\x82\\xa7\\xf0\\x9f\\x82\\xb7\\xf0\\x9f\\x83\\x97\\xf0\\x9f\\x83\\x8e\\xf0\\x9f\\x83\\x9e'" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`bytes` objects work like `str` objects in many ways. In particular, they are *sequences* as well: The number of bytes is *finite* and we may *iterate* over them in *order*." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "20" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(data)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Consisting of 8 bits, a single byte can always be interpreted as a whole number between `0` through `255`. That is exactly what we see when we loop over the `data` ..." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "240 159 130 167 240 159 130 183 240 159 131 151 240 159 131 142 240 159 131 158 " + ] + } + ], + "source": [ + "for byte in data:\n", + " print(byte, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... or index into them." + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "158" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data[-1]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Slicing returns another `bytes` object." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "b'\\xf0\\x82\\xf0\\x82\\xf0\\x83\\xf0\\x83\\xf0\\x83'" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "data[::2]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Character Encodings" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Luckily, `data` consists of bytes encoded with the [UTF-8 ](https://en.wikipedia.org/wiki/UTF-8) encoding. That is the most common way of mapping a Unicode character's code point to a sequence of bytes.\n", + "\n", + "To obtain a `str` object out of a given `bytes` object, we decode it with the `bytes` type's [decode() ](https://docs.python.org/3/library/stdtypes.html#bytes.decode) method." + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "cards = data.decode()" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "str" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(cards)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "So, `data` consisted of a [full house ](https://en.wikipedia.org/wiki/List_of_poker_hands#Full_house) hand in a poker game." + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'🂧🂷🃗🃎🃞'" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cards" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To go the opposite direction and encode a given `str` object, we use the `str` type's [encode() ](https://docs.python.org/3/library/stdtypes.html#str.encode) method." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "place = \"Café Kastanientörtchen\"" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "b'Caf\\xc3\\xa9 Kastanient\\xc3\\xb6rtchen'" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "place.encode()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "By default, [encode() ](https://docs.python.org/3/library/stdtypes.html#str.encode) and [decode() ](https://docs.python.org/3/library/stdtypes.html#bytes.decode) use an `encoding=\"utf-8\"` argument. We may use another encoding like, for example, `\"iso-8859-1\"`, which can deal with ASCII and western European letters." + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "b'Caf\\xe9 Kastanient\\xf6rtchen'" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "place.encode(\"iso-8859-1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, we must use the *same* encoding for the decoding step as for the encoding step. Otherwise, a `UnicodeDecodeError` is raised." + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "UnicodeDecodeError", + "evalue": "'utf-8' codec can't decode byte 0xe9 in position 3: invalid continuation byte", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mUnicodeDecodeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mplace\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"iso-8859-1\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mUnicodeDecodeError\u001b[0m: 'utf-8' codec can't decode byte 0xe9 in position 3: invalid continuation byte" + ] + } + ], + "source": [ + "place.encode(\"iso-8859-1\").decode()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Not all encodings map all Unicode code points. For example `\"iso-8859-1\"` does not know Czech letters. Below, [encode() ](https://docs.python.org/3/library/stdtypes.html#str.encode) raises a `UnicodeEncodeError` because of that." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "UnicodeEncodeError", + "evalue": "'latin-1' codec can't encode character '\\u0159' in position 12: ordinal not in range(256)", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mUnicodeEncodeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;34m\"Dobrý den, přátelé!\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"iso-8859-1\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mUnicodeEncodeError\u001b[0m: 'latin-1' codec can't encode character '\\u0159' in position 12: ordinal not in range(256)" + ] + } + ], + "source": [ + "\"Dobrý den, přátelé!\".encode(\"iso-8859-1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Reading Files (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [open() ](https://docs.python.org/3/library/functions.html#open) function takes an optional `encoding` argument as well." + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "UnicodeDecodeError", + "evalue": "'utf-8' codec can't decode byte 0xe4 in position 9: invalid continuation byte", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mUnicodeDecodeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"umlauts.txt\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mfile\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfile\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreadlines\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m~/.pyenv/versions/3.8.6/lib/python3.8/codecs.py\u001b[0m in \u001b[0;36mdecode\u001b[0;34m(self, input, final)\u001b[0m\n\u001b[1;32m 320\u001b[0m \u001b[0;31m# decode input (taking the buffer into account)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 321\u001b[0m \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbuffer\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0minput\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 322\u001b[0;31m \u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconsumed\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_buffer_decode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0merrors\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfinal\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 323\u001b[0m \u001b[0;31m# keep undecoded input until the next call\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 324\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbuffer\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mconsumed\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mUnicodeDecodeError\u001b[0m: 'utf-8' codec can't decode byte 0xe4 in position 9: invalid continuation byte" + ] + } + ], + "source": [ + "with open(\"umlauts.txt\") as file:\n", + " print(\"\".join(file.readlines()))" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lerchen-Lärchen-Ähnlichkeiten\n", + "fehlen. Dieses abzustreiten\n", + "mag im Klang der Worte liegen.\n", + "Merke, eine Lerch' kann fliegen,\n", + "Lärchen nicht, was kaum verwundert,\n", + "denn nicht eine unter hundert\n", + "ist geflügelt. Auch im Singen\n", + "sind die Bäume zu bezwingen.\n", + "Die Bätrachtung sollte reichen,\n", + "Rächtschreibfählern auszuweichen.\n", + "Leicht gälingt's, zu unterscheiden,\n", + "wär ist wär nun von dän beiden.\n" + ] + } + ], + "source": [ + "with open(\"umlauts.txt\", encoding=\"iso-8859-1\") as file:\n", + " print(\"\".join(file.readlines()))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Best Practice: Use UTF-8 explicitly" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A best practice is to *always* specify the `encoding`, especially on computers running on Windows (cf., the talk by Åukasz Langa in the [Further Resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/05_resources.ipynb#Unicode)) section at the end of this chapter.\n", + "\n", + "Below is the first example involving [open() ](https://docs.python.org/3/library/functions.html#open) one last time: It shows how *all* the contents of a text file should be read into one `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "with open(\"lorem_ipsum.txt\", encoding=\"utf-8\") as file:\n", + " content = \"\".join(file.readlines())" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Lorem Ipsum is simply dummy text of the printing and typesetting industry.\\nLorem Ipsum has been the industry's standard dummy text ever since the 1500s\\nwhen an unknown printer took a galley of type and scrambled it to make a type\\nspecimen book. It has survived not only five centuries but also the leap into\\nelectronic typesetting, remaining essentially unchanged. It was popularised in\\nthe 1960s with the release of Letraset sheets.\\n\"" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "content" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lorem Ipsum is simply dummy text of the printing and typesetting industry.\n", + "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s\n", + "when an unknown printer took a galley of type and scrambled it to make a type\n", + "specimen book. It has survived not only five centuries but also the leap into\n", + "electronic typesetting, remaining essentially unchanged. It was popularised in\n", + "the 1960s with the release of Letraset sheets.\n", + "\n" + ] + } + ], + "source": [ + "print(content)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/06_text/full_house.bin b/06_text/full_house.bin new file mode 100644 index 0000000..4ad5e35 --- /dev/null +++ b/06_text/full_house.bin @@ -0,0 +1 @@ +🂧🂷🃗🃎🃞 \ No newline at end of file diff --git a/06_text/umlauts.txt b/06_text/umlauts.txt new file mode 100644 index 0000000..9c0f911 --- /dev/null +++ b/06_text/umlauts.txt @@ -0,0 +1,12 @@ +Lerchen-Lärchen-Ähnlichkeiten +fehlen. Dieses abzustreiten +mag im Klang der Worte liegen. +Merke, eine Lerch' kann fliegen, +Lärchen nicht, was kaum verwundert, +denn nicht eine unter hundert +ist geflügelt. Auch im Singen +sind die Bäume zu bezwingen. +Die Bätrachtung sollte reichen, +Rächtschreibfählern auszuweichen. +Leicht gälingt's, zu unterscheiden, +wär ist wär nun von dän beiden. \ No newline at end of file diff --git a/README.md b/README.md index 430dbb9..fd12c45 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,13 @@ Alternatively, the content can be viewed in a web browser - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/01_exercises.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/01_exercises.ipynb) (Detecting Palindromes) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/02_content.ipynb) + (Special Characters; + ASCII & Unicode; + Multi-line Strings; + `bytes` Type; + Character Encodings) #### Videos From 163f2cd98d310b1d9a4c2397b2a85822b8fe9950 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 11:41:41 +0200 Subject: [PATCH 075/142] Add initial version of chapter 06's summary --- 06_text/03_summary.ipynb | 75 ++++++++++++++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 76 insertions(+) create mode 100644 06_text/03_summary.ipynb diff --git a/06_text/03_summary.ipynb b/06_text/03_summary.ipynb new file mode 100644 index 0000000..bcb6506 --- /dev/null +++ b/06_text/03_summary.ipynb @@ -0,0 +1,75 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 6: Text & Bytes (TL;DR)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Textual data is modeled with the **immutable** `str` type.\n", + "\n", + "The `str` type supports *four* orthogonal **abstract concepts** that together constitute the idea of a **sequence**: Every `str` object is an *iterable container* of a *finite* number of *ordered* characters.\n", + "\n", + "A single **character** in a `str` object follows the idea of a **Unicode** character. It is mapped to a *unique* **code point** that is encoded into **bytes** with a dedicated character encoding, for example, **UTF-8**." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index fd12c45..cccdf7b 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,7 @@ Alternatively, the content can be viewed in a web browser Multi-line Strings; `bytes` Type; Character Encodings) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/03_summary.ipynb) #### Videos From a328d6c9e098c29a0255f0ddb84e26d3883603fe Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 11:43:42 +0200 Subject: [PATCH 076/142] Add initial version of chapter 06's review --- 06_text/04_review.ipynb | 199 ++++++++++++++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 200 insertions(+) create mode 100644 06_text/04_review.ipynb diff --git a/06_text/04_review.ipynb b/06_text/04_review.ipynb new file mode 100644 index 0000000..9804bfc --- /dev/null +++ b/06_text/04_review.ipynb @@ -0,0 +1,199 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 6: Text & Bytes (Review Questions)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/00_content.ipynb) and [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/01_content.ipynb) part of Chapter 6.\n", + "\n", + "Be concise in your answers! Most questions can be answered in *one* sentence." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Essay Questions " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Answer the following questions *briefly*!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: In what sense is a **\"string\" of characters** a **sequence**? What is a **sequence** after all? A *concrete* **data type**, or something else?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: What is a direct consequence of the `str` type's property of being an **ordered** sequence? What operations could we *not* do with it if it were *unordered*?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: What does it mean for an object to be **immutable**? Discuss how we can verify the `str` type's immutability by comparing the two variables `example` and `full_slice` below. Are they pointing to the *same* object in memory?\n", + "```python\n", + "example = \"text\"\n", + "full_slice = example[:]\n", + "```\n", + "Hint: Find out what `[:]` does first!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: Describe in your own words what we mean by **string interpolation**!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## True / False Questions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Motivate your answer with *one short* sentence!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: **Triple-double** quotes `\"\"\"` and **triple-single** quotes `'''` create a *new* object of type `text` that models so-called **multi-line strings**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: A **substring** is a string that *subsumes* another string." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: Indexing into a `str` object with a *negative* index **fails silently**: It does *neither* raise an error *nor* do anything useful." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: We *cannot* assign a *different* character to an index or slice of a `str` object because it is **immutable**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index cccdf7b..bace325 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,7 @@ Alternatively, the content can be viewed in a web browser `bytes` Type; Character Encodings) - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/03_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/04_review.ipynb) #### Videos From 1ef4b283ac477fce88ae23ef6f1db53b032ffb1d Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 11:45:56 +0200 Subject: [PATCH 077/142] Add initial version of chapter 06's further resources --- 06_text/05_resources.ipynb | 359 +++++++++++++++++++++++++++++++++++++ README.md | 2 + 2 files changed, 361 insertions(+) create mode 100644 06_text/05_resources.ipynb diff --git a/06_text/05_resources.ipynb b/06_text/05_resources.ipynb new file mode 100644 index 0000000..a5517f1 --- /dev/null +++ b/06_text/05_resources.ipynb @@ -0,0 +1,359 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/05_resources.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 6: Text & Bytes (Further Resources)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Unicode" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We refer to the official [Unicode HOWTO ](https://docs.python.org/3/howto/unicode.html) in the Python documentation. Furthermore, the [unicodedata ](https://docs.python.org/3/library/unicodedata.html) module in the [standard library ](https://docs.python.org/3/library/index.html) provides a lot of utility functions around the Unicode standard.\n", + "\n", + "Next is a brief summary video by the YouTube channel [Computerphile](https://www.youtube.com/channel/UC9-y-6csu5WGm29I7JiwpnA) titled \"*Characters, Symbols and the Unicode Miracle*\"." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from IPython.display import YouTubeVideo\n", + "YouTubeVideo(\"MijmeoH9LT4\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In his [PyCon Australia 2018](https://2018.pycon-au.org/) talk titled \"*Unicode and Python: The absolute minimum you need to know*\" [Raphaël Merx](https://www.linkedin.com/in/raphaelmerx/) explains some caveats and best practices regarding Unicode." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"oXVmZGN6plY\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In a similar talk at [PyCon 2017](https://us.pycon.org/2017/) titled \"*Unicode what is the big deal*\" [Åukasz Langa](https://www.linkedin.com/in/llanga/) provides further lessons learned regarding Unicode." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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BERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQDKgNktpI66CeUmNklNVV8E0TJBI6NtJW1VLG+Ro75heyn14P5RxlTxWE7H2S4UEL6Dk1vFE6quMjZYquZkrIKyrqamJraTkW7DmMmYzTqx3nOt4pNM5TlJSVtln+LF3tVtnBBbZauleySZ9nrbtQskjl3c0NJBFLrfwBa3VUU2Wkg/Geg4kZ9qqGNtQXzZ5LJDBPu4ppcVE+ndU0YjYTNUnXH8UzLhvGZHELBpdhrrNRspJeQRim2cuVjgkZPUPM0tXHQxw1ErXU43EeKPLmDURr5ypU7IV1LSzUNBMzkYqIKmnjkqqmGch8xlr6KWtiYZmxPfmRs4y7Mz2kY4rpmwttI6qVbt21W/BPHbK37lk4klIfO6mbC2lqnVZqGMdI+E0bYuUCRsbHPILeYZ5uKpV+3dthp21b5ZzSuidNyiKhrp4WMY5zH76SGAtgka5jg5j8EaTkLG6bYuubDNrhoZXy3R1c2HltwjfFG+hhpRye6MZyiCpa5jwXhp1Nc4d7nArz7P3ottscxo6+Kj1VEsc9XUQGasEzn0YkkbSO5TBTx6cF4Bc9jHkAtTMh4me1q+HQyraK9GlbRubHrFVWU1L3+qMsbUE5fpIzqGPBKr3y+U9EI9+ZC6VzmxRQwT1M8ha3U/RBTsMjmtbxLgMDgo7bK2VdVBSGBtPyimrKWrdFLNIyF25JL42zMhLuc8HafqUdfbfeKvkz3R0se5lm3tHFdK6KKdj2RiGZ1ZBStl1xvEnxBaWnWDnIC0jFPadZTkr2+W4kKvbi2xtifv5JBPTcriFPS1dS91MCGumMdPEXNY0uAdkcMjK9uu21speMs7i3korXPhp6mpjipHB5ZUTSU8ZbDE4RyaS/GdDscxURsfshVUjYRM+Bxjtk1E7dulIMr6p8zXjeNzu9DhzknOefnWH7S26qtlBU2+PcVE9bs9S0D48VgkdVQUs9E0UBZTFlZryPiiWlmA53B3DeMIN2RxnVqRjdrp6mzKra6ginFO+Z+vXBG5zaeofBFLVaOTxT1LI9zTyv3kelshB+Nj8YzPLXG0OyNzqZn4fA+F09tnhdLW1sfJY6N9JJNStoYWGnlLpIJXiZxz8dgghoWxwtJxirWO9KU23nL6BERczsEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQBERAEREAREQHIXb62j8tS9Ub7SdvraPy1L1RvtLVyvtnWg1lGCAWuq6UEEZBBnjBBB4EYPMvUvC0kr5q5HgI47EN2z3zNg9v3aLy9J1VntJ2/dovL0nVWe0uqG7MW3H8n0XVYPYT7mLb5vouqwewqnS6Hw108j0PduK+M+vmcr9v3aLy9J1VntJ2/dovL0nVWe0uqPuYtvm+i6rB7CfcxbfN9F1WD2E0yh8NdPIz3bivjPr5nK/b92i8vSdVZ7Sdv3aLy9J1VntLqj7mLb5vouqwewn3MW3zfRdVg9hNMofDXTyHduK+M+vmcr9v3aLy9J1VntL3t9bR+WpOqN9pdP3PZq3CCYigogRFIQeSwcCGH+YuDYT3rfzR/YpmF7Gvf2ErFblBYnCZt6rd7+O77m1O31tH5al6o32k7fW0flqXqjfaWrkUvRaXCuRW94YjjfM2j2+to/LUvVG+0nb62j8tS9Ub7S1c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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"7m5JA3XaZ4k\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In a \"classic\" talk from PyCon 2012 titled \"*Pragmatic Unicode, or, How do I stop the pain?*\" [Ned Batchelder](https://nedbatchelder.com/) explains among others the concept of a \"Unicode Sandwich.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"sgHbC6udIqc\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Character Encodings" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Lastly, in his entertaining talk at [PyCon.DE 2019](https://de.pycon.org/) titled \"*Your Name is Invalid!*\" [Miroslav Å edivý](https://www.linkedin.com/in/%C5%A1ediv%C3%BD/) shows how hard it actually is to write software that can process any name a human can possibly have. Miroslav also gave a lightning talk where he shows how he uses only one keyboard for the 12 (!!!) languages he speaks." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAUDBAgICAgICAgICAgGBwgIBwcHBwgICAgICAgICAgICAgIChALCAgPCQgIDRUODhERExMTCAsWGBYSGBASExIBBQUFBwYHDwgIDh4VEhUfGh4bGhseHh0aHRsbGhseHR4bGR8eGRkeGh0dGBofHR0aFxoXGh0XGxgfHx0dFRcdFf/AABEIAWgB4AMBIgACEQEDEQH/xAAdAAEAAgMBAQEBAAAAAAAAAAAABwgEBQYDAQIJ/8QASxAAAQQBAgMDBwkFBwIDCQAAAQACAwQFERIGEyEHFBgiMUFRZqXlCBUyVFWSk9PUI0JhcYEWJDNSYpGhcvAJgrIXNDU2Q3S00eH/xAAbAQEAAgMBAQAAAAAAAAAAAAAAAgQBBQYDB//EADIRAQABAwMCBAQFAwUAAAAAAAABAgMREiFhBDEFE0FRInGRsQaB0eHwMqHxFCNCUsH/2gAMAwEAAhEDEQA/AKZIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIrS+CzN/aNP8ACP5ieCzN/aNP8I/mLOEdUKtIrS+CzN/aNP8ACP5ieCzN/aNP8I/mJg1Qq0itL4LM39o0/wAI/mJ4LM39o0/wj+YmDVCrSK0vgszf2jT/AAj+Yngszf2jT/CP5iYNUKtIrS+CzN/aNP8ACP5ieCzN/aNP8I/mJg1Qq0itL4LM39o0/wAI/mJ4LM39o0/wj+YmDVCrSK0vgszf2jT/AAj+Yngszf2jT/CP5iYNUKtIrS+CzN/aNP8ACP5ieCzN/aNP8I/mJg1Qq0itL4LM39o0/wAI/mJ4LM39o0/wj+YmDVCrSK0vgszf2jT/AAj+Yngszf2jT/CP5iYNUKtIrS+CzN/aNP8ACP5ieCzN/aNP8I/mJg1Qq0itL4LM39o0/wAI/mJ4LM39o0/wj+YmDVCrSK0vgszf2jT/AAj+Yngszf2jT/CP5iYNUKtIrS+CzN/aNP8ACP5ieCzN/aNP8I/mJg1Qq0itL4LM39o0/wAI/mJ4LM39o0/wj+YmDVCrSK0vgszf2jT/AAj+Yngszf2jT/CP5iYNUKtIrS+CzN/aNP8ACP5ieCzN/aNP8I/mJg1Qq0itL4LM39o0/wAI/mJ4LM39o0/wj+YmDVCrSK0vgszf2jT/AAj+Yh+RZm/tGn+GfzEwaoVaRXM8DHtT7g+JJ4GPan3B8SWElM0VzPAx7U+4PiSeBj2p9wfEkFM0VzPAx7U+4PiSeBj2p9wfEkFM0VzPAx7U+4PiSeBj2p9wfEkFM0VzPAx7U+4PiSeBj2p9wfEkFM0VzPAx7U+4PiSeBj2p9wfEkFM0VzPAx7U+4PiSeBj2p9wfEkFM0VzPAx7U+4PiSeBj2p9wfEkFM0VzPAx7U+4PiSeBj2p9wfEkFM0VzPAx7U+4PiSeBj2p9wfEkFM0VzPAx7U+4PiSeBj2p9wfEkFzEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBEXxrgeoII9YOoQfUREBERAREQEXxzgNNSBqdBqfOfUP4r6gIiICIiAiIgIiICIiAiIgItXkuI6FYWTPcrR/N8Elm2108e+vBEwSSSyx67mMawhxJHmI9YWPwPxbjs3TZfxVqO5Uke9jZo2vbo+M6PY5krWvY4dOjgDoQfMQg3iIvzNK1gLnua1o87nENaP5k9Ag/SL4xwIBBBBGoIOoIPmII84X5jla4atc1wB01aQRr6unpQftEWLVyVaV74orEMskP8AixxTMe+Prp5bGklvX1oMpERAREQEREBFznHHEbsa2vII2ysmlLJGlxa7QNLgWO6gHUekH+iyeHOJql8aQyaSAaugk8mVo9J266OHUdWkjqq0dXZ86bOr4vb9Fj/S3vKi9p+H3bpERWVcREQEREBERAREQEREBERAREQEREBQT8u//wCSr/8A95j/AP8ALjU7LhO3js8/tThJ8P3zuPeZq8veu7d628iVsu3k82PXXbpruGmvpQUq7DGQu4i4T/sb88CxyKP9snSDWi3Xl/OBHo5GzvWgl8ndyNnlKXq/yjMvjK/GVHPdz+e+GdjcRyYDCy2ZpxTZLyHPO+Jrpatjo7UxzO82nWzHAmC+a8VjMZzef8042lR5/L5XO7pWjr83l7ncvdy923c7TXTU+dVI7deDqPFHahQxdWPXkU6knE0rPoGOtune120BwlNR1Wvv3HQzRDQbDqGP2scT8R5yDgbA5W6MeeNpG2co6mw1YzWuWWV6dVzddZXCs/mGJ50dLYYD9BpElcB9gTuE89TtYPiR8NCblsv4fLvjkkvtc8xyBhg5Ubn7XN5Z5Zc17fO4OIUhdvPY1Q4sq1Y5ZpaFzFPc/G5Cq1pfXLw0OjdESBJCTHE7QOY4GJujm9QeO7L/AJNrcfl4c5nM7e4lyFANFB95sjWVyzdy3uM9ieSVzC5zmDc1rXO10JAICP7nbpxtlRxBmOH62KiwPCk0gfHdY59m1DDuc97vLBdJymGVzW8va14aC9w1OdxZ8ozNzs4Jnwtamx3GL3wWKN1pc024clFjnRMstcDFC6Xfo/QkNc06aghbrij5Kplt5F2J4lvYjFZ+US5PDxVzNFKeY6R0bXtsxtMWr3BrXsdtDiNXDouizPyc6jrHCD6N91OrwHNFLFWkqCzJfcy9BeldLYbNG2GWSWJ5c4RuGsxIaAA1BynCPahxM7NcQ8J8TR46SaDAXbkNnFse2Jv91ZK1jTIdZYHRT+dzQ4Oj0OuvSEuyDsYp5DgvI8VRZG/j8vhHZGatLBPHHW0oV2WA0hsYmZI4F7N7ZBoXg6HTQ2vyHYuZeKcjxL85hvzph5MWKHcdeTzKkVbn957yOZoY923lt+lpr01UW4n5H9uKAY+XjTIuxEkvNs4qtSkrV5natJdy3ZCSBsurGHc6J/0B06BBDna9xXY4gwXZ1byzLF2V9vN07gqt/vV6KvdxUP7ED6VuSEBmvpk1Pp0Vpvkk8O4qnRyM2LxGcwzbV2OKerxE0ssyGvCHsmhY4a8kiy5uvpLHepYfaj8nGDJVuHKuIyZwUXB3eXUiyj32R8tiWrPzy82YtswmrGQu8rc6Unpp17zsg4Ny+HZbbluIrHELrT4nQSWKYqmq2MSB7GgWJA8PLmn0abPT6Apn2v5zF8U8YWrVu9kY+HZzHhsTnjA4Y7E5JkUDuY3V4jnqvkjmlcC+J221zDoIwTeThvET0MJBRtXX5Gejje7y5CVhZJaMUJYJpGuked5aBqS9xJ1JJ1UB3PkiRyOmpN4jvw8Oy35MhFgoqzCYbToTCx4sySuY8taQ3cYtSwBvn8tTX2WcIXsRhY8ReyoyzqsTq9W8aRqSNq7AyGGZhsy850Y1aH6t1aGAjVpc4P5tcJNxBw0ggGXPGPztH8zjGhxh7rsra7uX+05+7venL8rXlejVW/xXazxFgeIOHcRxTJWbjs5gacklyWFsU9fI9ya2yyxYY4xySNvROa4NAGlqM+gayd8nDsn/ALH4uxje/wDzj3rJSXu8dz7pt5lerX5XL58u7Tu27duH09NOmpiL/wARFtSzSwGPZEZ83dyrm4tkenMED2NhssJIPkyTyUWhurdzmA6/syCHHcb9oub4p4E4uyl9kEOJbk8fWwsEcHLnLW5WtLKZpC4iTZE+tGHN6F3O/wAq0HFPZk3hXhPBcaYLMZGjk7UGJmtQvsRGGZ92u2Z0cDGRNL2CTUmKXmNcwOB8x1s1lexCKfgiHg6K22nsr0xNfZVM4dZhtRXbUwgMrCRLO2Q6F/kiQefTRcFgvklbn0487xTlM3jcXsFXEuZNWrxtjAa2FhkuTcmDY0MLYRGdvQOagxsp22cWZnP1MHwvUx1aaLCUMrkXZVrizdbo1Lz4y4P3RwNF2vDo1u8vLjqB1HH5HtU4q4i4c48r2mUKsnD0lWKzXawgw1JJMoy/AyZjnCaw11WBrX9Adrj6VL/an8nk5POx5/D521w9dfBHXuuowFxmjijZAx0L454jXdyGMjLfKaRHGdAWnc7L/k4V8PU4ooz5WfIVuMYWQPc6s2C1VZH30B5sOlkbZsf3wO3ljRui1LTu0ARHwF2p5zhTs2p5BzKdk5DIPpcPbhI8wxPmyE1ya+3VpkkEsE4YGu08uInUBwPQXO1nj/EZrhXEZ2HEsbxHkaUck9WPmPkr2btWCaBwDw2G1E2VwLmgtPNZpqWknqKfyXmHhy1w5dz1q5B31l3CzmqY/miVjbALWVzacyeJ/eZS9oMYcXuI0do4YeL+S7cGRwWUvcW3clZ4cu1LAbdpvlZJBTsw2IalcyXC6o39k8Fx5mpkB2jboQ5n/wARGKN9zgxk0U88MlrKNmr1BrZmjdLhQ+KuPTO5pLWj/MQuY+TtPUw3GGVkpjK4PFUcDYuy4DiAviyV8Q1ec8xVtux+wxPlBLi4NB01BdtsL8onsXl4smw1iHLnETcOy2poZW0O+OfLYdSfG9v95i5RjdTB/e13+jTrpOAfk6urZp2dz2ftcR3BUmqQixV7rGyKxWlqSte3vEpkZyJ5mhjdgBlc7qeqCF2/Km4ndEMy2Thx1Q3uV/ZhpsuyoqhxHOLw36O3yebu+kd3K29F2PFPbxxbPxRZwvDePq5COziqVzGV7EbYZYBcxdG86e1NJMxhaznyDa5zBq9g1Omjuhw3yZLlORlSrxjmavD8V11tmHqh9ew3eNHRDIRWAAD5yeVp1J27iXLuML2OCrxla4tbkNzbdCOk3Fmo7WJsdSpVDzddYJlP913dYx9PTU6akK/G7PX4648s2YYJbVbs8tT2a9mKOatJZiwmFkmimhBLJYDK1zS3XaWkjzFfvGds+Ww/B3DVzE0sVXkzGayNWajVotgrvbHM0METQ/8AZyuJ0L3F30v4BTVmewU2c9xNm/nXZ/a7h+3he6fN+7ufeqVSn3nnd5HeNvdd+zYzXfpuGmp56X5MJOEwWH+fAP7N5WzkRa+ade8mxK2UQ8nvv7HbtI3bna6+YIPPhLtU4tpcbUuGeJYcS6PO1JLVY4rm6VWcq3JFtlk8qUb6csTmvGupDg7QaO2fyreBKN99TL8Q5p9XhvAwvdPhYo3NkvXHiba6GcTDWy4GFjW8txAZLoQHuI63insj79xjieLPnDlfMlDufzb3Pfz/AP4h+071zxyv/f8Azct3+F5/K6c78oX5P8nF+QrXH52ejBRqMhhx4pd6gE4mmkktBr7LGNle18TD5GpFdupI0ACrgv5zE9mTI3SWalTiTibl1BJI5rjizSfLKIyPKiqzWYtSBoHiOU6bZdXd7W7PIuD+OsRw/jcjemx/F+Gmr5aGeSF0sjbEWQrmTZHE2MMY+NssZLSWlkgJcC4GZ5fk/PvYS7hs9xDfzQs2a1rH3ZohHNjJqzHsb3dkksrHMc2R7HN0bq17gNDo5vj2NfJyjweVjzWSzdzPX6UDq+OfajdEypE6N0PmksTPkc2J8jGjc1jRK7yddCAgHsb4VtUch2lYfBOsPs0MXdo45+9vfJGw3jFo18YYO8uia4AtDfKI006LleD4G4fIcFWsfh87icnDmIaWduZKOSGtfmsWYWGtUbJ1LTXdO17drNolYCHEhytxwh2CxU8rxTkLORNuDjVlyOalFUdUfVjuWHzER2xZeXyNa/aHhjOoDhp5loOCvk1S1cji7OS4lu5jHcNWpbeGxViryxBO+UTRvlsGw/m7ZGtcQGN3Fjfot1aQsOiIgIT/AMf9+hEQRLiPlFcJ2ZeUy/I3WSGFsslK0InzztDo4GlsZJm0Ohbp5LiGnRxAMsSPDQXOIAaNSSdAAPOST5gv5d9lvGkPCXE9iwa081OtasU5a+6uy2KzLbem+aF45obCA5reWXdW72a6i3L+1ePNmQS0bVSlFYlr1+XMXSP7u8xmzyXBsNiBzh5IGhGw+X12jE03qqZ8qnMxHbOEZu2bUxN6rTTM4zjOPySB2kZenfEdaCzFzK8hfvfubA8lpbsbPpsDuuup0b/qC1vZfVkhyrWSscx3dpiA4ecHZo5p8z2n1jUFcq+CARd5N2qKm7Z3jc7fv03cnum3n8/aCdu3TTru06rf9j3FMc+TbRrRyd3EE8pmtP1lc9uwaxQMcYqrDuOo1e46DVw8y5bp+i6zqus8+/b06Z39O3pifvGzp7/X9H0/R+RYua4qjb17+uY2/Kd01IuU4epy245ppLtxrvnDIxNbFLG1jWQX7METWtMZ6BkbR118y9auUfTkuw25nWIqNSG620Ym84QSusMe2dkDQ17mGs5wc1o1a7zatJPXTb7xDk6b8TETMYiXTIvwZG7d+4bQ3du1G3bpru182mnXVaO1ddZdjzXnmghvRyTb2RRiRzOUySPVtmJ2zo7XQgHqvC5Xop1Yz2/us0U66tOff+zfotNPVuwgyRWX2ywaur2o4GmQDztjlrxs5cnqLg4evTzjLjy0JrstbiIpWMc07XF3l6bW7GguL9TptA116KMXoziqMfNObU4zTOfkzkWBRy8Ez+W0vbJtLhHPBNXe5o0Bcxs7Gl7RqNSNdNQsFmeYLdiFwm2QthDNtO07y3GYSalkZ8jyGaO8x66E9VieotxidUb7EWLk5jHZvUWFfykMLgx/Mc9zdwZDXnnft1I3FsDHEDUHqfUV64+7FOzfE7c0OLXdHNc1w01Y9jgHMeNR0cAeoU4uUTVpid/ZCaKojVjZkIiKaIiIgIiICIiAiIgLEr4ytHK+eOvBHNLu5k8cMbZX7iHO3yNbudq5oJ1PUgLLRAREQEREBERAREQEREBYlnGVpZWTSV4JJodvKmkhjfLHtcXN2SObubo4kjQ9CVlog0nH+Zkx2JyeQhh7xLjMbctw1/K/bSVq8kzIjt8rRzmAdOvXoq2cOdoOeojg/MT8SMzrONLsMGQwEdOozukVppdJLT7sOc00z5EmvQuGjtNVa4jXofMfOFxfDHZRw3jLrsjj8NQqXHbttiCANMW8Fr+7t+hW1a5zTyw3o4jzHRBFXCXbRxBmKEl2HH4arXy2My1jBn56jN6CXH95aBdpyAPnB7tId1djg3RpcNN235i+MuKLfZpJlmWqnz3NXY6rdbNXiLoH2oI3ySmdsdeC9y3WGhg6aiLTVxAUtcN9mHD2Ntz36OIpVrdxr2TTRQgEslOsjGMOrImO9LWBoPpWThez7C06FnF1sbVix2Qklkt0Wx615nzMZHIXRuJA1bHGNBoBsGmmiCE+xztNzFQ5ypbx3E+dZjcjSjpNlgx02Yrw3KPenNyBgnbBoCGkaOc4c4A6aaDM7R+Js5LxDwjFVv5LC1eMqeYrT4qzUqCxQsU6jzFbfrv3T77UT9m7b/dmefcVNHA3BmLwdd1TE0oaNd8rpnxwB3lyuAaXve8lz3bWtHUnQNAHQL3zHDFC5bo3rNZktvDOmdj7Di8PrOstYycs2uA8prGA6g/RQR18k3iPI5PBTT5O5JeswZnIVe8ysjY50Vd7GRjbE0NHpP8AVRNge0/PZDiGxw6/JzY6vY48ztaPNPihJNHGcow4DHkxGNtp2/XfL1Aki2l5Ox0+YrsxoUshj7mO1owY1uSdJRgdNyrU+R5IdLNumLNreXI7QsJLnRkOYGua/KudmGBminhkxkDo7mWfmZhulDjlH/SvMkD98Ng9fKYW+coInt/KBvR5KQNxlQ4Srxc3hKWR99/zwbmpa65HVLNr6w0JDer3aeceUW/GdvmUilzLLuNo1HUMdmrmMqzSX2WbBxDZJABLJAKmRhfEwPLqspLA7Ut0BKlmXsv4edlG5p2JpuyjHtkF10WsnNZpsnLSdjrA0BEhG8EA66hY7eyLhoT27Iw1Ns2TisxW3tY5u9l1nLt7WB22F0rCWudGGlwJBPVBE+L7bOLLE1WszA4bn5nhiLiPGvOWsCJlTa10rbI5G507gRtY3aGmRgL3AOI87vyjMjYr0XYvF0RPJwpPxRkhk774YWVatiarNVpOYzWWwX15HNL9Bo5uvmdpOFXgXExSVpo6UTJMfi/miq9rpNYcboB3Rvl6cvQDz9eg6rT5Tsd4YtVqVSxhactfEtcyjG9ryYGPkMro2v3b3RGRznbHEt1J6II1ufKBvd4xc7cZWq4fJUcTaluZCW7puyfSSFt2tWfVqPieWtHejGJNzXAtDgu2+VXxJdxHCOWyGOsOq3KncORYY1jnM5uTpQSaNkaWnWKR7eo/eW+zXZZw7dsVrVnD0pJ6EUMNZ/J2NZDXLXV4XRRkRywsLW7WPDmt2jQBeWJ7NaIxVvD392TpZC/ZuSw2nSlgEt3vkMDd0rpOXG5sWmr+pYToAdoCAsL2y5zBuzrL8l7IywvwMGIxmcr1Yb8cuUEgktzvwsUjJKWoOjWGSQlsTQ1heSelf2+Z5laow8PxPydniVuDEDnX6Na6Ja7pYrFU5KvFPVHMa5jhNHqwDU9TtEqYvsd4XrVbdKDCUGVsoI23YTDvE4idviDnPJcNr/KboRo7Qjr1WViOy7h+pDTgq4qrBFi7wyNNkTXt5d4NDBac4O3SzbGtbueXdGgeYBBXTibjPKPyuar5Z07u58Rdn8LMfVylqvXx9rI4+y++2vLWc100HeATscS12xrvOGkddW7fcyJ4ZZcTjvmqTjl/CL52XbAumUv0jssgMRjaxsbXuOrzuOjQGDyzMeR7OcJYsWbU2OhksX7dG7amc6XdLaxrHsozO0fpuibI8DTQeV11X5PZtg+WIfm6HltzPz61m6XQZbQjv2u/XnaE/wAP4IOtREQEKLV8X2I4sdfll/woaFqSXXXTlsgkc/XTr9EHzIP5Y8UEZ3im86ExiPN5+3Ix4fHFEyCzdkk5rpX6RsjbE4vL3dAGlx9KsRxH2IZqKaHF2uK/nKtXFaaGq9sle5LBHI99aKrZnkcGtDwSIxMSNGljSQNI3+T92AZPibE5e/EY60bq4rYmS1Hqy5ajsxTzCN2u6FjRByjLoRrO4DXa/Tp+xbKzS4xlO5YL7WIlmpGrPIHWKsEUjuXA5jjvbG17pWtB6DTaOjQBb6O3Fy5plrvFL1Vmxrpj1j/Pz5ShkqU/zb3fkzd4+emM7vypDPv7lJ5PK03l/wDDTVSL2H9nl+jaGRuBkA5EkbKpO+c8zad0m07Yho3zak9eobotv8n23LNVsiaR8vdpmMgMh3ujYY9SxjndQzX0a6BSenV1zF2qGPDbdM9PRVH83cbwpjrEkM748hYgY7J5bSKKGk5rdMnbB0dNA5x1IJ6k+f1dF0OKxEVcS9ZJpLJBsT2HB8kxDdrQ7QBjWBuoDGNa0au0HU65teBkYLY2NY0ue8tY0NBfI4vkcQP3nPc5xPpLiUtCTY/lFgk2nlmRpcwO08kva0gluvqIVequapW7dmKKYzvMOLduERwRJ3GZtdh18o4h7XSmQnXXQQRyVN3n3tafSF0GRAFygANAG2gAB0AETdAB6AvuLxswsPt2nxPndEIImwMLY4YQ7e9oc8lz3vftLidB+zjAA0Jdm5DHQWNvOiZJyySzeNdpPQ6eroodTmunFPE/nmJ/8enSR5c5q5j8sTEfcymQirRmSV2noYwDWSR5+jHEwdXyE9AAtLVdPWr0aw5cc9uSQPe8F7IXObLZkY1oI5j/ADtA1A6E9dNDtqWHqwu3xV4mP005jWDfp6g/zgfwWRdqRTsMc0bZGEg7XtBGoOrSNfM4HqCOoVaq3cr+Kdp9PrGd+ce2y3Tcop+GN4/zjbjPvu5/JRyR2scJLLZS648sYYWMk0FWyHOaWH6GhAOoPVzeq2FIjv8AcHp7tSOn8N1sa/y1B/2WRVxFaIhzIWBzXB4edXP3Na5rTvcS46Ne8Dr03H1rU8UWqkdiu2euJXPABm128lj54oW73+bYXyF21xA0ifpqdAYUWK6Z1c57z7Y7/twlVeoqjTxjtHvnt+7LM889ixDHKyBlMxtP7ISSyOkjbLv8s7WRaO2joSS1/UaaLx4dJ73faZWzOaKoe9jAzy9kgIcASC8NDAT08wGnRYeRy+KsN54AsOiDA57WTRuZE543cyTaNrQ3fIGuPlBjiAfOtvg5ahMjKrGsdDtjlY2F0QAY+VgDdzQHsEjJxubqCWu6qUWa9cVT6TM9559O3r/MsTdo0TER3iPSOPXv6NoiIrSsIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIR/wAoiDkuz/s4w2AkvSYim2l87TMmtRRPkMO9m/aIYnOLYIxzH+QzRo3aAAABZ+P4MxFd1p0GMoROycr5bz2U4Q61JI4vkdO4N1lJeSfK16lb5EjbsTv3aXhjhmrjeeKjXRstSNkdEXl7GOa3b+z3eUGn1En+Gi3SIs1VTVOZRoopojTTGIERFhIREQEREBYdvFwTP5ksYkdtDSHkljmtLnMD49dkm1znEbgdCSRosxEGAMPW2lpiDgW7CZHOkc5ukrdHPeS5w2zzDqfNIVk16scZJY0NJa1pIHUtaXFo19QL3n/zFeyICIiAiIgIoe7/AMd/Uqv3qX6lO/8AHf1Kr96l+pU9HLz8ziUwooe7/wAd/Uqv3qX6lO/8d/Uqv3qX6lNHJ5nEphRQ93/jv6lV+9S/Up3/AI7+pVfvUv1KaOTzOJTCih7v/Hf1Kr96l+pTv/Hf1Kr96l+pTRyeZxKYUUPd/wCO/qVX71L9Snf+O/qVX71L9Smjk8ziUwooe7/x39Sq/epfqU7/AMd/Uqv3qX6lNHJ5nEphRQ93/jv6lV+9S/Up3/jv6lV+9S/Upo5PM4lMKKHu/wDHf1Kr96l+pTv/AB39Sq/epfqU0cnmcSmFFD3f+O/qVX71L9Snf+O/qVX71L9Smjk8ziUwooe7/wAd/Uqv3qX6lO/8d/Uqv3qX6lNHJ5nEphRQ93/jv6lV+9S/Up3/AI7+pVfvUv1KaOTzOJTCih7v/Hf1Kr96l+pTv/Hf1Kr96l+pTRyeZxKYUUPd/wCO/qVX71L9Snf+O/qVX71L9Smjk8ziUwooe7/x39Sq/epfqU7/AMd/Uqv3qX6lNHJ5nEphRQ93/jv6lV+9S/Up3/jv6lV+9S/Upo5PM4lMKKHu/wDHf1Kr96l+pTv/AB39Sq/epfqU0cnmcSmFFD3f+O/qVX71L9Snf+O/qVX71L9Smjk8ziUwooe7/wAd/Uqv3qX6lO/8d/Uqv3qX6lNHJ5nEphRQ93/jv6lV+9S/Ur47IceaHSlV106eVS/Upo5PM4lMSIig9BERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQERajijPw0IJJpA+V0cbpG14QHSyBo1O1pIA8x6kjzek9FCu5Tbp1VTiEqaKq500xu26Lk+EeLG5ahHdhHK1kfFYg3B7oZWn6JfoNdWljgdB0kavud1fVstJJ31p29SfTE8Khe8Soo/pjPqXaarczFUbw6tFB3B73NpwlrnNP7Q6tcQf8V/pC6ijxDbh80pkb/km8sH/zHyh/QrVW/wAT2tWLlEx8t/0VLXUxXTFUx3SSi0WC4kisEMeOVKegaTq15/0O9f8AA9f5reroOn6m11NGu1VmFiJid4ERF7siIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgxsneirRPmmcWRRAF7w1ztoJDdSGAnTUj0dF8iyELpZIGv3SQsY+UBri1jX6lm5+m0OIBO3XXTrpovmZriWvPEWczmwSM5eoG/cwgNBPQE6+crlX8Pzx46rEIjNIZ4J8nAZWB9nyP2sRke7Y8BwjGhOhbEBqqd+9et1/DTmMZ9fp9vT0nvstWbVqun4qsTnH7/f6x23dk2VpAIc0hx0aQRoT6h6/MhmZ/mb01B6j0DU/wCw6rh6mFtQywWGUw2IZCWz83wywt7u11Tu8bhq4Rbtxe5wadBuGmq+43huWaeN12sOU6TI2pmGRj2GaxLHHDG4Ndq8d3j182nmB69F4x1l6dvL3/PGNt845+ez1npLUbzcjH5ZzvtjPHy3ddPk4GTQ13SATWmvdCzR3liMav0cBtHT1nr10X6qZCGVrnseC2OZ8LnEFo5kbyx7RuA3aOBGo6HTpquc4gwk9i1NOxu11WpB83yFzdDZZNJM8aa6taQ2OM6gAh5WtrYGy0Uu8Um3I217BlrPlh2Q3LE7pnSyB7i142u26t3EddB5lirq+opuTGjb07+8RzzPbtjlmnprFVETr39Y29pnjiO/fPEO7M7B1Lm9NddXD0dD/sv0JGk6ajXTXTXrp69PUuDwnC8gbELVRsja+Nm2xSOika65ZsSTSMOriC4ARjcenUHXpqkfC08UdHkRCKzDjbTZ7QcwP7zJWbFDE94O57Wvc7QjVrRGNNOiR1nUTEVeV98+nHM/QnpLETMeb9sevPEfV3jJGnUAglp0cAQSD6j6isetkYZOYWPBEMzoJCQWgSs03MBcBuI1HUahc3wfhHwz841nVQyqINrn1t0zi5r3PkbWaQ/Qt6Pe8uO92oC1dvAWnV4t1QyTudfle0yVZI2y2pi9rJ4piGlhYGftIn726EAHVZq6u9FEVxb99t/eOOfb0YjpbM1zT5ntvt7Tzx7+rubl6OJ0TZHbTZl5UQ2uO6Ta5+nkg6Daxx1Og6L1E7P8zejd30h9H1/y/iucy+Psl9IxRaijUtO0bINveTWbBBG0yu3H6Umjj6upWuq8JBvcoxXawMxlmG7M3lh0k00MUQjkIO6QamUjzgbR5uinV1N+K5iKMxt78ccz9EaenszTEzXjv7c88R9XbcxvQajUjUDXzj1j+CxMjlIa8L55H/s42hzizyjoXBg0aOp8ogLjG4G9JSL5otbXLqVe7CZgcaVd0ZnibKHbWvmc17j1002j0L6OHpnSzOZRbVhsWMa3ksfDoytBIbFh5ax20HeGAtb6eo3dSvKrrb8x8Nud42784ztxHrnd609HZifiuRtPHHbfn2xs7Wtea/m6tfGIZjFulDWh5Aad0ZBOrNXaanTqCsWLO13PZGHOLpLU1Vg2O6y12udL10+iA13lebouUgwVnWu+xRbbY/vss1V8sGyK1ZsmRssge4skAhIbqNxHXQLY8KYaWHuAmhex1WG3K93Njcxs9qUaxuAcXveI9dHDpoT1JSjquorqiNGO3eJ944iO0z6+jFfTWKKZnXn5Y55me8R9XXIiLaNcIiICIhKDT8S5uOo1rdW8+fUQRk/S2/ScR6WjUfz1H9OBsTvkcXvcXPedXOPnP/6H8FhcV4nJX7clgwbQ/pVhkngjm5DCdmkL5A8OPVxBGurisHD3J+e2nNHJznPEbQWkSh58zZGnrppod3q6np1XHeJdTevXcVUzFPpt3/eXSdL0lFu3qpqiavXg4Hm+aMwap8nH8Q+RF/kgvNOsTfU0EuLQB5+ZGP3CpUkoSPY5u3Tc1zepA84IXrg8DFXDXOa2SYdTKRrsdoQRHr9EaEjXznU/yW4W36XwyfKiL0/T7NP1t+i/c1Ux+6LcTwdfr1oo3xse6NpDuVK09S5zum7TXzrGnhfG4te1zHDzte0tP+x9CltY1+jFOAJWNftOrSR1aR6j/wB6qj1X4Zt1RM2apiee38+rVx01NNMU0+jj+GMV/wDWfoDpq0u6CNn+c/6iPN/D+a6jH5Jj38vr0GjHO879PPr6itZmN7HcvTRnnH+v/U4+k6+j0LBa4ggg6EHUEegj0roPCvCaOksY9ZQm7onTDskXhRn5kbX+lw6j+I6H/le6szGNlqJzGRERYZEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAXKdrnEMmKwt67DoJ4o2R1y4ahs1iWOvG8t/eDXSh2n+ldWoy+UzDM/h6wIY+Y/vVMlm9rPJFlmpLndBp0P9F49RVNNqqae+JX/CrdF3rrVFz+maqc/LMZQHnMdjMhbw0eOtXbN7KPgiy9i6HmWO3NLAzmskkYC5zd0zjtLmgRs0Pn1nvsVzjM13u8+Npfjb9qnRmb1LqDxHLWEhP05WxP2bvOQ4nzuKq3Xxt8EObEyNzTq13egHNPrDo2kg/yU19jde/DhGxVw4Tz8T7ZxUnezWs7Gs/xZ+STAzfG3ytpA0b61z/S1zTcmqqnnEcdvzzu+ifiixbjw/aqapp2iZ7xmZzvtGMYiIxtELDoo8vWuIa8bqrTDZniwrrPeo4HF0lyOIwurs1Ajc90zmTNJA1DXN2ddVj2c/Zrwx9xlvzRvisvltZHF3rMxuRRVzBREDI4nRtkL5CXAFocxzQQeg3E9fTHemY+b5j5Uz2lJaKPanFeVbbY21RdHUM8pklhrTSviigxbrM0LgwEvf3kxNY9gIfpI0DcBrj/ADjffkpeQ/IHdlqojrvqyil81Pp1n2ZHySQhsT2vdORo8P3gN0PUJPX0YzET3weTLsONJDHTlnZGZX1m8xsYO0uGoDxrof3ST5v3VEVzPZWx0jZ3dh9DGhjtP4vlO7X/AKdF2vZ/bsy4ay66+w6x3d3NFoWQ4OMH+WxBGGndrqIy9vQeUudfG4BriCBICWE+ZwBLSR/UEf0XQeD36LtnXp79suc8at1xdimKpiMb4/V03Y4bDYrUVh5eecyZpdI6R37Rmx2rnfxib6fSV3y4bs0/xLHq5cf/AKnf/wBXcqv1u9+Z/nZf8MjHTUx8/uIiKqviIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgLne0rGm3ib0LRueYDJG30ufA5s7Gj+JdGB/VdEhUaqYqiYl62bs2blNynvExP0U8Ck35P+ebBbmpSO0bkGtdCSdBz4Q7yP5vjLvwmj0rm+0/hs4zISRtbpXsl01RwHTluPlRD+LHEt09Ww+lcbkcqykwWHPcwxuBiLDpIZGnczl+p4IB19GmvoXP01VdPczPo+u9RbteK9DMRPw1xtPt7fSe/wBFzEUX9knagL2OrTZgQ4+xYfy4JJJWshttOvJkaX6COR4B8k9Habm9HACT2nUajzHzLfW7lNyM0vk3VdJd6WuaLsY+0/KX1EXPce8VwYijNdmjlmZA6Njo67Q5wdK4MYXkkCNmpGrj6xpqSAZV100RmqXlZs13q4t24zM7RHMvXizKV4mtrzO2d+D4t/oiaWEc13+kPLB/U+orlrFWSGzXDqr7EVaJjA1sbnRzahz3uaQ0hw5kjiP+karjr2ZdkH97LxIJ2gxln0BH+61g9AGp6efXXXrqsnFWLbnMr15p2mVwYyOOaRrdT/Bp0A01JPqBWys06KcxO0r3Vfh+LlMTNWmqO/8AOEocJ44xc+V0bYjak1EDSDymN1LWHToHeUToPNqP5DerEw9JteCOFp15TdC4+d7j1e8/xLiT/VZapXKprqzLV0W6bcaaewiIoJCIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiINHb4vxkWTgw0tyFmUu13Wa1BziJpoG87dJGNNHACvOdNddInH0LVX+0jGMmnq1jYyVqhfp0chUxVaS3NRkvPeyKW01ugjrt5che/U7Qw+nQKKO3LgPOWuLIc/iqZmkwHDdWXGSGeKOOxk6+cMs+NdrIHgTYye4wkgN/ajygR04/Adj+erTWpJKMks2RzXA+Wu2efXInuQWLl/iGYay67YrFt7fMNwA2goLb6j1pvGmuo09eo0/3VXLvYpfdieJLMVKwcxk+IskRVdlHwC/w7Pl696bH1yybkUxZjga4uIa8kFriAdFif+yzKPpZUx4S7SxE/EWJv4nhRtrF2n14qlKeHIWJ6VqV9O7XmsSxvNMzRHWEOD27W7gsxY4kpx5GHFPlIv26k1yGDlSkOrQSMilk5oby26PkYNpcCd3QFbbePWP8AdVWd2a8TS4+lFBQ+brUHBWfxcQjueTBNYykM1GrvmuzvrvmpREbRM+OHmBocxrQ1uFw92KWL13Ixf2cl4fxeR7P5cTEy3er3mszLMlWtQzbY7D3MAmhbN+7uMJedrpCgtsXAecgaDXz+j1qOOzPtYp5zI3aEE2NeKbZJKz6mQmnkuwNsPi7zDFLUiYa7QI2ufG+QCVz2glrWvfofk98M5kuy2a4kqCtl8xHQoCq+aOZrKeNoxQl7XxOcI22Lb7MrmAnTVvn0UccO8AcWRR5HGY2jdw1F+Cy9eGvkMrj8jXp35Zf7lHgMpGBkI68sZeXCUNawyya6uDSgtYHD1j0/8edcjmO0OhBYt1I47t65jJcay7Ux1GexNA3KuIqznyQx8G1r3vcxztgYdRqQDXZvZTmnYnMw4/B2sXDZq4aGbCOydKqzMGnZjkyDY46ckgryvrtfEbD5wZmuGrAdSvar2Y5Uvzj6nD1jFU7+S4LmxuOmvVbD4q+LuSy39Nth7YGsDjJyw4gczQddWgLY7h6x/utVRzzJrtmkyC2O4sjMtuSrJFTdJIGuENexLoLUgY4FxiDmt10Lg7VqgiLsqyTuJzQmoxv4Si4kn4ujsSTRyNffnoNhGPFVx3RxtvSTy6aFhaSOmqwewbg6Y8SWKMzmz4fsvsZGtgXh75N1jNubZbHK9xIklqVHPhIOu10jNNNEFm0REBERAREQaHjnhiDK1XV5fJe07684GroZdCA4D95h8xb6R6iARVODs3ycmYlZm4OVXoO8lrSXV7bSdY21XkDmQHQF7iAf3SGkkNuUsHOYmC7C+CwzeyRrmktc5j27gWl0cjCHRv0PnaQVU6jpKb06vVvPCvG7vQ0zaneifrHMfoqTnHvzmXq4yudIXWG1YyzTTqf7zYA821kbHkf6Yun0lYnL2nV3sr1XOhhqQRwxxxnRrWMaAxob5tAwMC1HAHY/BhslJfhsvnjFZ8VSGeNokrvlID3mVmjZP2YLRo1uge7XXVbPJ4u0ZZHmCQ7nkjaA/pr5P0CfRouF/E1HW2emiLVNWqqrMzTnaI7bx2bzxXxPpetu0W7M/wC3RT67ZqnvtP8AM5YsuUsuGjp5NPUHlv8A6dF+8QYnmSrZaJKuQjdXsRv6tc2QFvX7xGv+rX0L8sxNk9BBL/Vhb/y7QLY0+FrD9OYWxN9Op3u/2adP+VyPh9jxi51VF+3RXVVTP/LOOYmZ2xMbTu1lyuxTRMZiPl+yDG0LOAzDsLKJZ69uUOxz2sc972zOPKexrBqTqCyQAdHMc4DTz2A4G4Y7o3nTAGzI3TToRC0/ug+l59J/oPSTv2Y+LfFK9jJJ68Too7L42GZrH7OaGyaatDzGwkDQHYPUstfbuli5ateXM7eke0e2VbxTxqrrqafhxOPin/tPvj0z6iIi9WjEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQaDtE4gdisXeyLIopn0K7pWxWLcVKF5BAAktTeRCzrqSdfNoASQFEd75QromV3DH1Hltd9i8z50nhkk2ZmfDPqYaCzQZLkcg2SEyOhe2IN5kTQ5+9rjPMsbXtcx7Q5rwWua4Atc0jQtcD0II9CxGYmqBC0Vq4FQ61miCMCA+uEbf2R/wCnRBBTe3iXHUso/ICtNPjquYu1DNaipuu904nymHhoRMEWhcyGtV8tu5xMzdRqdT6Tdt+Rpx3Raq4mWxFxLmsbCXZSanWrVMXFNaHfpTUkdFZfBE3l+SGyAukJja3QzhLh6jw0Oq13BnM2h1eIhvOOs23VvTeervX6dV+rOLqy7xJWgk5r2PkEkMb974xox79zfKc0DoT1CCHO0/tTuwDEd1nqYiPLcPz5rm5COnZkmliNDTGV3WMhVqcxkdx80jucdWQHZ61j43tuytgwCHDVZI5ruFxoms5KahM69msNWykEjqRpzd3qNfPtd+1keG6ENkIIU33aMM7Q2eGKZrXB7WzRska1zfouAeCA4etHUIC4vMMRc6RkpcYmFxljbtZITpqZGtAAd5wAgrnf+UJegtOsSVqDasOBFqzip8rHDObdXiLL4W981ymoX5Kd5oxcuJ3KGhbro52h23AXGluDJQUa+JZSqZTj3iWncyMMNCOtdFSPKlh2R2TbORJoV980sQDuU7yjq1TgcPU1a7utbdG4OY7u8WrHB75Q5p2+S4SSSO1Hpe4+clezaMAIIhiBZK+ZpEbAWyybg+UHTpI4Odq7zncfWgyEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERARUz8c/st7/8AhqeOf2W9/wDw1BcxFTPxz+y3v/4anjn9lvf/AMNQXMRUz8c/st7/APhqeOf2W9//AA1BcxFTPxz+y3v/AOGp45/Zb3/8NQXMRUz8c/st7/8AhqeOf2W9/wDw1BcxFTPxz+y3v/4anjn9lvf/AMNQXMRUz8c/st7/APhqeOf2W9//AA1BcxFTPxz+y3v/AOGp45/Zb3/8NQXMRUz8c/st7/8AhqeOf2W9/wDw1BcxFTPxz+y3v/4anjn9lvf/AMNQXMRUz8c/st7/APhqeOf2W9//AA1BcxFTPxz+y3v/AOGp45/Zb3/8NQXMRUz8c/st7/8AhqeOf2W9/wDw1BcxFTPxz+y3v/4anjn9lvf/AMNQXMRUz8c/st7/APhqeOf2W9//AA1BcxFTPxz+y3v/AOGp45/Zb3/8NQXMRUz8c/st7/8AhqeOf2W9/wDw1BcxFTPxz+y3v/4anjn9lvf/AMNQXMRUz8c/st7/APhqeOf2W9//AA1BcxFTPxz+y3v/AOGp45/Zb3/8NQXMRUz8c/st7/8AhqeOf2W9/wDw1BcxFTPxz+y3v/4anjn9lvf/AMNQXMRUz8c/st7/APhqeOf2W9//AA1BcxFTPxz+y3v/AOGp45/Zb3/8NQXMRUz8c/st7/8AhqeOf2W9/wDw1BcxFTPxz+y3v/4anjn9lvf/AMNQXMRUz8c/st7/APhqeOf2W9//AA1BcxFTPxz+y3v/AOGp45/Zb3/8NQXMRUz8c/st7/8AhqeOf2W9/wDw1BcxFTPxz+y3v/4anjn9lvf/AMNQUzREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERB//2Q==\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"pBuS7EUPnQA\", width=\"60%\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"-4QjII981sM\", width=\"60%\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/README.md b/README.md index bace325..82348a5 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,8 @@ Alternatively, the content can be viewed in a web browser Character Encodings) - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/03_summary.ipynb) - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/04_review.ipynb) + - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/05_resources.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/05_resources.ipynb) #### Videos From 84e05ab2988ddf8b861617faceadabfacefb5acf Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 12:00:32 +0200 Subject: [PATCH 078/142] Move detailed ToC in a file on its own --- CONTENTS.md | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 150 +---------------------------------------------- 2 files changed, 166 insertions(+), 147 deletions(-) create mode 100644 CONTENTS.md diff --git a/CONTENTS.md b/CONTENTS.md new file mode 100644 index 0000000..50bf5f4 --- /dev/null +++ b/CONTENTS.md @@ -0,0 +1,163 @@ +# Table of Contents + +The materials are designed to resemble an *interactive* book. + +It is recommended + to follow the [installation instructions](https://github.com/webartifex/intro-to-python#installation) + in the [README.md](README.md) file + and work through the content on one's own computer. + +If this is not possible, + the files can be viewed in a web browser + either statically (i.e., read-only) on [nbviewer ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/tree/develop/) + or interactively (i.e., code can be executed) on [Binder ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab). + +- *Chapter 0*: Introduction + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/00_intro/00_content.ipynb) + (Python's History & Background; + Open-source & Communities; + JupyterLab; + Programming vs. Computer Science; + Learning Tips) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/00_intro/01_exercises.ipynb) + (Mastering Markdown) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/02_review.ipynb) +- **Part A: Expressing Logic** + - *Chapter 1*: Elements of a Program + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/00_content.ipynb) + (A first Example: Averaging Even Numbers; + Operators; + Objects & Data Types; + Errors) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/01_exercises.ipynb) + (Printing Output) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/02_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/02_exercises.ipynb) + (Simple `for`-loops) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/03_content.ipynb) + (Memory in Detail; + Variables & References; + Mutability; + Expressions & Statements) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/04_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/04_exercises.ipynb) + (Python as a Calculator) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/05_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/06_review.ipynb) + - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/07_resources.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/07_resources.ipynb) + - *Chapter 2*: Functions & Modularization + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/00_content.ipynb) + (Built-in Functions & Constructors; + Function Definitions; + Function Calls & Scoping Rules; + Positional vs. Keyword Arguments; + Modularization) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/01_exercises.ipynb) + (Volume of a Sphere) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/02_content.ipynb) + (Standard Library: `math` & `random` Modules; + Third-party Packages: `numpy` Library; + Writing one's own Modules) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/03_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/04_review.ipynb) + - *Chapter 3*: Conditionals & Exceptions + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/00_content.ipynb) + (Boolean Expressions; + Relational Operators; + Logical Operators; + `if` statement; + Exception Handling) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/01_exercises.ipynb) + (Discounting Customer Orders) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/02_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/02_exercises.ipynb) + (Fizz Buzz) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/03_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/04_review.ipynb) + - *Chapter 4*: Recursion & Looping + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/00_content.ipynb) + (Recursion; + Examples: Factorial, Euclid's Algorithm, & Fibonacci; + Duck Typing; + Type Casting & Checking; + Input Validation) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/01_exercises.ipynb) + (Towers of Hanoi) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/02_content.ipynb) + (Looping with `while` & `for`; + Examples: Collatz Conjecture, Factorial, Euclid's Algorithm, & Fibonacci; + Containers vs. Iterables) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/03_content.ipynb) + (Customizing Loops with `break` and `continue`; + Indefinite Loops) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/04_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/04_exercises.ipynb) + (Throwing Dice) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/05_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/06_review.ipynb) +- **Part B: Managing Data and Memory** + - *Chapter 5*: Numbers & Bits + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/00_content.ipynb) + (`int` Type; + Binary & Hexadecimal Representations; + Bit Arithmetic; + Bitwise Operators) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/01_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/01_content.ipynb) + (`float` Type; + Floating-point Standard; + Special Values; + Imprecision; + Binary & Hexadecimal Representations) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/02_content.ipynb) + (`complex` Type; + Numerical Tower; + Duck vs. Goose Typing) + - [appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/03_appendix.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/03_appendix.ipynb) + (`Decimal` Type; + `Fraction` Type) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/04_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/05_review.ipynb) + - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/06_resources.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/06_resources.ipynb) + - *Chapter 6*: Text & Bytes + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_numbers/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/00_content.ipynb) + (`str` Type; + Reading Files; + Sequences; + Indexing & Slicing; + String Methods & Operations; + String Interpolation) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/01_exercises.ipynb) + (Detecting Palindromes) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/02_content.ipynb) + (Special Characters; + ASCII & Unicode; + Multi-line Strings; + `bytes` Type; + Character Encodings) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/03_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/04_review.ipynb) + - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/05_resources.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/05_resources.ipynb) \ No newline at end of file diff --git a/README.md b/README.md index 82348a5..db07011 100644 --- a/README.md +++ b/README.md @@ -6,163 +6,19 @@ This project is a *thorough* introductory course ### Table of Contents -The materials are designed to resemble an *interactive* book. -It is recommended - to follow the [installation instructions](https://github.com/webartifex/intro-to-python#installation) below - and work through the content on one's own computer. -Alternatively, the content can be viewed in a web browser - either statically (i.e., read-only) on [nbviewer ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/tree/develop/) - or interactively (i.e., code can be executed) on [Binder ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab). +The following is a high-level overview of the contents. +For a more *detailed version* with **clickable links** + see the [CONTENTS.md](CONTENTS.md) file. - *Chapter 0*: Introduction - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/00_intro/00_content.ipynb) - (Python's History & Background; - Open-source & Communities; - JupyterLab; - Programming vs. Computer Science; - Learning Tips) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/00_intro/01_exercises.ipynb) - (Mastering Markdown) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/02_review.ipynb) - **Part A: Expressing Logic** - *Chapter 1*: Elements of a Program - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/00_content.ipynb) - (A first Example: Averaging Even Numbers; - Operators; - Objects & Data Types; - Errors) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/01_exercises.ipynb) - (Printing Output) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/02_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/02_exercises.ipynb) - (Simple `for`-loops) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/03_content.ipynb) - (Memory in Detail; - Variables & References; - Mutability; - Expressions & Statements) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/04_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/04_exercises.ipynb) - (Python as a Calculator) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/05_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/06_review.ipynb) - - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/07_resources.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/07_resources.ipynb) - *Chapter 2*: Functions & Modularization - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/00_content.ipynb) - (Built-in Functions & Constructors; - Function Definitions; - Function Calls & Scoping Rules; - Positional vs. Keyword Arguments; - Modularization) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/01_exercises.ipynb) - (Volume of a Sphere) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/02_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/02_content.ipynb) - (Standard Library: `math` & `random` Modules; - Third-party Packages: `numpy` Library; - Writing one's own Modules) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/03_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/04_review.ipynb) - *Chapter 3*: Conditionals & Exceptions - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/00_content.ipynb) - (Boolean Expressions; - Relational Operators; - Logical Operators; - `if` statement; - Exception Handling) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/01_exercises.ipynb) - (Discounting Customer Orders) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/02_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/02_exercises.ipynb) - (Fizz Buzz) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/03_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/04_review.ipynb) - *Chapter 4*: Recursion & Looping - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/00_content.ipynb) - (Recursion; - Examples: Factorial, Euclid's Algorithm, & Fibonacci; - Duck Typing; - Type Casting & Checking; - Input Validation) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/01_exercises.ipynb) - (Towers of Hanoi) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/02_content.ipynb) - (Looping with `while` & `for`; - Examples: Collatz Conjecture, Factorial, Euclid's Algorithm, & Fibonacci; - Containers vs. Iterables) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/03_content.ipynb) - (Customizing Loops with `break` and `continue`; - Indefinite Loops) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/04_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/04_exercises.ipynb) - (Throwing Dice) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/05_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/06_review.ipynb) - **Part B: Managing Data and Memory** - *Chapter 5*: Numbers & Bits - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/00_content.ipynb) - (`int` Type; - Binary & Hexadecimal Representations; - Bit Arithmetic; - Bitwise Operators) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/01_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/01_content.ipynb) - (`float` Type; - Floating-point Standard; - Special Values; - Imprecision; - Binary & Hexadecimal Representations) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/02_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/02_content.ipynb) - (`complex` Type; - Numerical Tower; - Duck vs. Goose Typing) - - [appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/03_appendix.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/03_appendix.ipynb) - (`Decimal` Type; - `Fraction` Type) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/04_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/05_review.ipynb) - - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/06_resources.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/06_resources.ipynb) - *Chapter 6*: Text & Bytes - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_numbers/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/00_content.ipynb) - (`str` Type; - Reading Files; - Sequences; - Indexing & Slicing; - String Methods & Operations; - String Interpolation) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/01_exercises.ipynb) - (Detecting Palindromes) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/02_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/02_content.ipynb) - (Special Characters; - ASCII & Unicode; - Multi-line Strings; - `bytes` Type; - Character Encodings) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/03_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/04_review.ipynb) - - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/05_resources.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/05_resources.ipynb) #### Videos From f6704143953df44d506142b297cb57128837f72d Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 18:35:13 +0200 Subject: [PATCH 079/142] Add initial version of chapter 07, part 1 --- 00_intro/00_content.ipynb | 2 +- 07_sequences/00_content.ipynb | 958 ++++++++++++++++++++++++++++++++++ CONTENTS.md | 7 +- README.md | 1 + 4 files changed, 966 insertions(+), 2 deletions(-) create mode 100644 07_sequences/00_content.ipynb diff --git a/00_intro/00_content.ipynb b/00_intro/00_content.ipynb index de293c8..a5d9e15 100644 --- a/00_intro/00_content.ipynb +++ b/00_intro/00_content.ipynb @@ -770,7 +770,7 @@ "- How is data stored in memory?\n", " - *Chapter 5*: [Numbers & Bits ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb)\n", " - *Chapter 6*: [Text & Bytes ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/00_content.ipynb)\n", - " - *Chapter 7*: Sequential Data\n", + " - *Chapter 7*: [Sequential Data ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb)\n", " - *Chapter 8*: Map, Filter, & Reduce\n", " - *Chapter 9*: Mappings & Sets\n", " - *Chapter 10*: Arrays & Dataframes\n", diff --git a/07_sequences/00_content.ipynb b/07_sequences/00_content.ipynb new file mode 100644 index 0000000..43ec596 --- /dev/null +++ b/07_sequences/00_content.ipynb @@ -0,0 +1,958 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/00_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 7: Sequential Data" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We studied numbers (cf., [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb)) and textual data (cf., [Chapter 6 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/00_content.ipynb)) first mainly because objects of the presented data types are \"simple.\" That is so for two reasons: First, they are *immutable*, and, as we saw in the \"*Who am I? And how many?*\" section in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_content.ipynb#Who-am-I?-And-how-many?), mutable objects can quickly become hard to reason about. Second, they are \"flat\" in the sense that they are *not* composed of other objects.\n", + "\n", + "The `str` type is a bit of a corner case in this regard. While one could argue that a longer `str` object, for example, `\"text\"`, is composed of individual characters, this is *not* the case in memory as the literal `\"text\"` only creates *one* object (i.e., one \"bag\" of $0$s and $1$s modeling all characters).\n", + "\n", + "This chapter, [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb), [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb), and [Chapter 10 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/10_arrays/00_content.ipynb) introduce various \"complex\" data types. While some are mutable and others are not, they all share that they are primarily used to \"manage,\" or structure, the memory in a program (i.e., they provide references to other objects). Unsurprisingly, computer scientists refer to the ideas behind these data types as **[data structures ](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) and [vectors ](https://en.wikipedia.org/wiki/Vector_%28mathematics_and_physics%29). These formats share the property that they are composed of smaller units that come in a sequence of, for example, rows/columns/cells or elements/entries." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Collections vs. Sequences" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[Chapter 6 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/00_content.ipynb#A-\"String\"-of-Characters) already describes the **sequence** properties of `str` objects. In this section, we take a step back and study these properties one by one.\n", + "\n", + "The [collections.abc ](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/develop/05_numbers/02_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 according to their behavior in various contexts.\n", + "\n", + "As an illustration, consider `numbers` and `text` below, two objects of *different* types." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]\n", + "text = \"Lorem ipsum dolor sit amet.\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Among others, one commonality between the two is that we may loop over them with the `for` statement. So, in the context of iteration, both exhibit the *same* behavior." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "7 11 8 5 3 12 2 6 9 10 1 4 " + ] + } + ], + "source": [ + "for number in numbers:\n", + " print(number, end=\" \")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "L o r e m i p s u m d o l o r s i t a m e t . " + ] + } + ], + "source": [ + "for character in text:\n", + " print(character, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb#Containers-vs.-Iterables), we referred to such types as *iterables*. That is *not* a proper [English](https://dictionary.cambridge.org/spellcheck/english-german/?q=iterable) word, even if it may sound like one at first sight. Yet, it is an official term in the Python world formalized with the `Iterable` ABC in the [collections.abc ](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." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import collections.abc as abc" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "collections.abc.Iterable" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "abc.Iterable" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As seen in [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/02_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`." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(numbers, abc.Iterable)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(text, abc.Iterable)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Contrary to `list` or `str` objects, numeric objects are *not* iterable." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(999, abc.Iterable)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Instead of asking, we could try to loop over `999`, but this results in a `TypeError`." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'int' object is not iterable", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\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 chapter and [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb) and [Chapter 10 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/10_arrays/00_content.ipynb) 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 may be looped over.\n", + "- `Container`: An object \"contains\" references to other objects; a \"whole\" is composed of many \"parts.\"\n", + "- `Sized`: The number of references to other objects, the \"parts,\" is *finite*.\n", + "\n", + "The characteristical operation supported by `Container` types is the `in` operator for membership testing." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0 in numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"l\" in text" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Alternatively, we could also check if `numbers` and `text` are `Container` types with [isinstance() ](https://docs.python.org/3/library/functions.html#isinstance)." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(numbers, abc.Container)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(text, abc.Container)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Numeric objects do *not* \"contain\" references to other objects, and that is why they are considered \"flat\" data types. The `in` operator raises a `TypeError`. Conceptually speaking, Python views numeric types as \"wholes\" without any \"parts.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(999, abc.Container)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "argument of type 'int' is not iterable", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\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 `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*." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "27" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(text)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(numbers, abc.Sized)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(text, abc.Sized)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "On the contrary, even though `999` consists of three digits for humans, numeric objects in Python have no concept of a \"size\" or \"length,\" and the [len() ](https://docs.python.org/3/library/functions.html#len) function raises a `TypeError`." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(999, abc.Sized)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "object of type 'int' has no len()", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "\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", + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(numbers, abc.Collection)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(text, abc.Collection)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "They share one more common behavior: When looping over them, we can *predict* the *order* of the elements or characters. The ABC in the [collections.abc ](https://docs.python.org/3/library/collections.abc.html) module corresponding to this behavior is `Reversible`. While sounding unintuitive at first, it is evident that if something is reversible, it must have a forward order, to begin with.\n", + "\n", + "The [reversed() ](https://docs.python.org/3/library/functions.html#reversed) built-in allows us to loop over the elements or characters in reverse order." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4 1 10 9 6 2 12 3 5 8 11 7 " + ] + } + ], + "source": [ + "for number in reversed(numbers):\n", + " print(number, end=\" \")" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + ". t e m a t i s r o l o d m u s p i m e r o L " + ] + } + ], + "source": [ + "for character in reversed(text):\n", + " print(character, end=\" \")" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(numbers, abc.Reversible)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(text, abc.Reversible)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Collections that exhibit this fourth behavior are referred to as **sequences**, formalized with the `Sequence` ABC in the [collections.abc ](https://docs.python.org/3/library/collections.abc.html) module." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(numbers, abc.Sequence)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(text, abc.Sequence)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The data types introduced in this chapter are sequences. Nevertheless, we also look at some data types that are neither collections nor sequences but are still useful to model sequential data in practice in [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb).\n", + "\n", + "In Python-related documentations, the terms collection and sequence are heavily used, and the data science practitioner should always think of them in terms of the three or four behaviors they exhibit.\n", + "\n", + "Data types that are collections but not sequences are covered in [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index 50bf5f4..fd55699 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -160,4 +160,9 @@ If this is not possible, - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/03_summary.ipynb) - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/04_review.ipynb) - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/05_resources.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/05_resources.ipynb) \ No newline at end of file + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/05_resources.ipynb) + - *Chapter 7*: Sequential Data + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/00_content.ipynb) + (Collections vs. Sequences; + ABCs: `Container`, `Iterable`, `Sized`, & `Reversible`) \ No newline at end of file diff --git a/README.md b/README.md index db07011..e9b2add 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ For a more *detailed version* with **clickable links** - **Part B: Managing Data and Memory** - *Chapter 5*: Numbers & Bits - *Chapter 6*: Text & Bytes + - *Chapter 7*: Sequential Data #### Videos From 049aeb104d016ee66ddea850076d3db9aeb0158b Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 19:14:29 +0200 Subject: [PATCH 080/142] Prefix methods with a dot --- 01_elements/00_content.ipynb | 6 +++--- 04_iteration/03_content.ipynb | 2 +- 05_numbers/01_content.ipynb | 8 ++++---- 05_numbers/02_content.ipynb | 4 ++-- 06_text/00_content.ipynb | 34 +++++++++++++++++----------------- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/01_elements/00_content.ipynb b/01_elements/00_content.ipynb index 6003bd9..4d6c792 100644 --- a/01_elements/00_content.ipynb +++ b/01_elements/00_content.ipynb @@ -1340,9 +1340,9 @@ } }, "source": [ - "Different types imply different behaviors for the objects. The `b` object, for example, may be \"asked\" if it is a whole number with the [is_integer() ](https://docs.python.org/3/library/stdtypes.html#float.is_integer) \"functionality\" that comes with *every* `float` object.\n", + "Different types imply different behaviors for the objects. The `b` object, for example, may be \"asked\" if it is a whole number 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** (i.e., as opposed to functions) and we look at them in detail in [Chapter 10 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/10_classes/00_content.ipynb). For now, it suffices to know that we access them with the **dot operator** `.` on the object. Of course, `b` is a whole number, which the boolean object `True` tells us." + "Formally, we call such type-specific functionalities **methods** (i.e., as opposed to functions) and we look at them in detail in [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb). For now, it suffices to know that we access them with the **dot operator** `.` on the object. Of course, `b` is a whole number, which the boolean object `True` tells us." ] }, { @@ -1377,7 +1377,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 already know it is an `int`: 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." ] }, { diff --git a/04_iteration/03_content.ipynb b/04_iteration/03_content.ipynb index 8cfb19f..dafbe08 100644 --- a/04_iteration/03_content.ipynb +++ b/04_iteration/03_content.ipynb @@ -788,7 +788,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 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`." + "`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`." ] }, { diff --git a/05_numbers/01_content.ipynb b/05_numbers/01_content.ipynb index af18f07..00157ad 100644 --- a/05_numbers/01_content.ipynb +++ b/05_numbers/01_content.ipynb @@ -1827,7 +1827,7 @@ "\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 $1023$ and separated by a `\"p\"`." + "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\"`." ] }, { @@ -1875,7 +1875,7 @@ } }, "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." + "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." ] }, { @@ -2030,7 +2030,7 @@ } }, "source": [ - "As seen in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/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." + "As seen in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/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." ] }, { @@ -2091,7 +2091,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/index.html) 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." ] }, { diff --git a/05_numbers/02_content.ipynb b/05_numbers/02_content.ipynb index e430e90..ae1b01d 100644 --- a/05_numbers/02_content.ipynb +++ b/05_numbers/02_content.ipynb @@ -581,7 +581,7 @@ } }, "source": [ - "A `complex` number comes with two **attributes** `real` and `imag` that return the two parts as `float` objects on their own." + "A `complex` number comes with two **attributes** `.real` and `.imag` that return the two parts as `float` objects on their own." ] }, { @@ -640,7 +640,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." ] }, { diff --git a/06_text/00_content.ipynb b/06_text/00_content.ipynb index 7c9fff8..236942b 100644 --- a/06_text/00_content.ipynb +++ b/06_text/00_content.ipynb @@ -2370,7 +2370,7 @@ "source": [ "Objects of type `str` come with many **methods** bound on them (cf., the [documentation ](https://docs.python.org/3/library/stdtypes.html#string-methods) for a full list). As seen before, they work like *normal* functions and are accessed via the **dot operator** `.`. Calling a method is also referred to as **method invocation**.\n", "\n", - "The [find() ](https://docs.python.org/3/library/stdtypes.html#str.find) method returns the index of the first occurrence of a character or a substring. If no match is found, it returns `-1`. A mirrored version searching from the right called [rfind() ](https://docs.python.org/3/library/stdtypes.html#str.rfind) exists as well. The [index() ](https://docs.python.org/3/library/stdtypes.html#str.index) and [rindex() ](https://docs.python.org/3/library/stdtypes.html#str.rindex) methods work in the same way but raise a `ValueError` if no match is found. So, we can control if a search fails *silently* or *loudly*." + "The [.find() ](https://docs.python.org/3/library/stdtypes.html#str.find) method returns the index of the first occurrence of a character or a substring. If no match is found, it returns `-1`. A mirrored version searching from the right called [.rfind() ](https://docs.python.org/3/library/stdtypes.html#str.rfind) exists as well. The [.index() ](https://docs.python.org/3/library/stdtypes.html#str.index) and [.rindex() ](https://docs.python.org/3/library/stdtypes.html#str.rindex) methods work in the same way but raise a `ValueError` if no match is found. So, we can control if a search fails *silently* or *loudly*." ] }, { @@ -2477,7 +2477,7 @@ } }, "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." + "[.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." ] }, { @@ -2560,7 +2560,7 @@ } }, "source": [ - "The [count() ](https://docs.python.org/3/library/stdtypes.html#str.count) method does what we expect." + "The [.count() ](https://docs.python.org/3/library/stdtypes.html#str.count) method does what we expect." ] }, { @@ -2619,7 +2619,7 @@ } }, "source": [ - "As [count() ](https://docs.python.org/3/library/stdtypes.html#str.count) is *case-sensitive*, we must **chain** it with the [lower() ](https://docs.python.org/3/library/stdtypes.html#str.lower) method to get the count of all `\"L\"`s and `\"l\"`s." + "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." ] }, { @@ -2654,7 +2654,7 @@ } }, "source": [ - "Alternatively, we can use the [upper() ](https://docs.python.org/3/library/stdtypes.html#str.upper) method and search for `\"L\"`s." + "Alternatively, we can use the [.upper() ](https://docs.python.org/3/library/stdtypes.html#str.upper) method and search for `\"L\"`s." ] }, { @@ -2689,7 +2689,7 @@ } }, "source": [ - "Because `str` objects are *immutable*, [upper() ](https://docs.python.org/3/library/stdtypes.html#str.upper) and [lower() ](https://docs.python.org/3/library/stdtypes.html#str.lower) return *new* `str` objects, even if they do *not* change the value of the original `str` object." + "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." ] }, { @@ -2833,7 +2833,7 @@ } }, "source": [ - "Besides [upper() ](https://docs.python.org/3/library/stdtypes.html#str.upper) and [lower() ](https://docs.python.org/3/library/stdtypes.html#str.lower) there exist also [title() ](https://docs.python.org/3/library/stdtypes.html#str.title) and [swapcase() ](https://docs.python.org/3/library/stdtypes.html#str.swapcase) methods." + "Besides [.upper() ](https://docs.python.org/3/library/stdtypes.html#str.upper) and [.lower() ](https://docs.python.org/3/library/stdtypes.html#str.lower) there exist also [.title() ](https://docs.python.org/3/library/stdtypes.html#str.title) and [.swapcase() ](https://docs.python.org/3/library/stdtypes.html#str.swapcase) methods." ] }, { @@ -2940,9 +2940,9 @@ } }, "source": [ - "Another popular string method is [split() ](https://docs.python.org/3/library/stdtypes.html#str.split): It separates a longer `str` object into smaller ones collected in a `list` object. By default, groups of contiguous whitespace characters are used as the *separator*.\n", + "Another popular string method is [.split() ](https://docs.python.org/3/library/stdtypes.html#str.split): It separates a longer `str` object into smaller ones collected in a `list` object. By default, groups of contiguous whitespace characters are used as the *separator*.\n", "\n", - "As an example, we use [split() ](https://docs.python.org/3/library/stdtypes.html#str.split) to print out the individual words in `text` with more whitespace in between them." + "As an example, we use [.split() ](https://docs.python.org/3/library/stdtypes.html#str.split) to print out the individual words in `text` with more whitespace in between them." ] }, { @@ -2999,7 +2999,7 @@ } }, "source": [ - "The opposite of splitting is done with the [join() ](https://docs.python.org/3/library/stdtypes.html#str.join) method. It is typically invoked on a `str` object that represents a separator (e.g., `\" \"` or `\", \"`) and connects the elements provided by an *iterable* argument (e.g., `words` below) into one *new* `str` object." + "The opposite of splitting is done with the [.join() ](https://docs.python.org/3/library/stdtypes.html#str.join) method. It is typically invoked on a `str` object that represents a separator (e.g., `\" \"` or `\", \"`) and connects the elements provided by an *iterable* argument (e.g., `words` below) into one *new* `str` object." ] }, { @@ -3095,7 +3095,7 @@ } }, "source": [ - "The [replace() ](https://docs.python.org/3/library/stdtypes.html#str.replace) method creates a *new* `str` object with parts of the original `str` object potentially replaced." + "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." ] }, { @@ -3130,7 +3130,7 @@ } }, "source": [ - "Note how `sentence` itself remains unchanged. Bound to an immutable object, [replace() ](https://docs.python.org/3/library/stdtypes.html#str.replace) must create *new* objects." + "Note how `sentence` itself remains unchanged. Bound to an immutable object, [.replace() ](https://docs.python.org/3/library/stdtypes.html#str.replace) must create *new* objects." ] }, { @@ -3165,7 +3165,7 @@ } }, "source": [ - "As seen previously, the [strip() ](https://docs.python.org/3/library/stdtypes.html#str.strip) method is often helpful in cleaning text data from unreliable sources like user input from unnecessary leading and trailing whitespace. The [lstrip() ](https://docs.python.org/3/library/stdtypes.html#str.lstrip) and [rstrip() ](https://docs.python.org/3/library/stdtypes.html#str.rstrip) methods are specialized versions of it." + "As seen previously, the [.strip() ](https://docs.python.org/3/library/stdtypes.html#str.strip) method is often helpful in cleaning text data from unreliable sources like user input from unnecessary leading and trailing whitespace. The [.lstrip() ](https://docs.python.org/3/library/stdtypes.html#str.lstrip) and [.rstrip() ](https://docs.python.org/3/library/stdtypes.html#str.rstrip) methods are specialized versions of it." ] }, { @@ -3248,7 +3248,7 @@ } }, "source": [ - "When justifying a `str` object for output, the [ljust() ](https://docs.python.org/3/library/stdtypes.html#str.ljust) and [rjust() ](https://docs.python.org/3/library/stdtypes.html#str.rjust) methods may be helpful." + "When justifying a `str` object for output, the [.ljust() ](https://docs.python.org/3/library/stdtypes.html#str.ljust) and [.rjust() ](https://docs.python.org/3/library/stdtypes.html#str.rjust) methods may be helpful." ] }, { @@ -3307,7 +3307,7 @@ } }, "source": [ - "Similarly, the [zfill() ](https://docs.python.org/3/library/stdtypes.html#str.zfill) method can be used to pad a `str` representation of a number with leading `0`s for justified output." + "Similarly, the [.zfill() ](https://docs.python.org/3/library/stdtypes.html#str.zfill) method can be used to pad a `str` representation of a number with leading `0`s for justified output." ] }, { @@ -3706,7 +3706,7 @@ } }, "source": [ - "`str` objects also provide a [format() ](https://docs.python.org/3/library/stdtypes.html#str.format) method that accepts an arbitrary number of *positional* arguments that are inserted into the `str` object in the same order replacing empty curly brackets `{}`. String interpolation with the [format() ](https://docs.python.org/3/library/stdtypes.html#str.format) method is a more traditional and probably the most common way as of today. While f-strings are the recommended way going forward, usage of the [format() ](https://docs.python.org/3/library/stdtypes.html#str.format) method is likely not declining any time soon." + "`str` objects also provide a [.format() ](https://docs.python.org/3/library/stdtypes.html#str.format) method that accepts an arbitrary number of *positional* arguments that are inserted into the `str` object in the same order replacing empty curly brackets `{}`. String interpolation with the [.format() ](https://docs.python.org/3/library/stdtypes.html#str.format) method is a more traditional and probably the most common way as of today. While f-strings are the recommended way going forward, usage of the [.format() ](https://docs.python.org/3/library/stdtypes.html#str.format) method is likely not declining any time soon." ] }, { @@ -3776,7 +3776,7 @@ } }, "source": [ - "The [format() ](https://docs.python.org/3/library/stdtypes.html#str.format) method may alternatively be used with *keyword* arguments as well. Then, we must put the keywords' names within the curly brackets." + "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." ] }, { From d6bec8afa2bb493d53e185865485398ae065b067 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 19:15:05 +0200 Subject: [PATCH 081/142] Add initial version of chapter 07, part 2 --- 07_sequences/01_content.ipynb | 2995 +++++++++++++++++++++++++++++++++ CONTENTS.md | 9 +- 2 files changed, 3003 insertions(+), 1 deletion(-) create mode 100644 07_sequences/01_content.ipynb diff --git a/07_sequences/01_content.ipynb b/07_sequences/01_content.ipynb new file mode 100644 index 0000000..5123ac8 --- /dev/null +++ b/07_sequences/01_content.ipynb @@ -0,0 +1,2995 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/01_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 7: Sequential Data (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In this second part of the chapter, we look closely at the built-in `list` type, which is probably the most commonly used sequence data type in practice." + ] + }, + { + "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": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "empty = []" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": 3, + "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://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." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[[], 10, 20.0, 'Thirty', [40, 50]]" + ] + }, + "execution_count": 4, + "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": 5, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139688113982848" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(nested)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "list" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(nested)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Alternatively, we use the built-in [list() ](https://docs.python.org/3/library/functions.html#func-list) constructor to create a `list` object out of any (finite) *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/develop/04_iteration/02_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://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": 7, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(range(1, 13))" + ] + }, + { + "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": 8, + "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 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!" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['i', 't', 'e', 'r', 'a', 'b', 'l', 'e']" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(\"iterable\")" + ] + }, + { + "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, 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 loop over 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 `empty` and `simple` count as *one* element each. In other words, `nested` holds five references to other objects, two of which are `list` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "5" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(nested)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[] 139688113991744 \n", + "10 94291567220768 \n", + "20.0 139688114016656 \n", + "Thirty 139688114205808 \n", + "[40, 50] 139688113982208 \n" + ] + } + ], + "source": [ + "for element in nested:\n", + " print(str(element).ljust(10), str(id(element)).ljust(18), type(element))" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "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 **[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." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 13, + "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": 14, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "20 in nested" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 15, + "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*, 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* in this context." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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": 18, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[40, 50]" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested[-1]" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "50" + ] + }, + "execution_count": 19, + "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": 20, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[10, 20.0, 'Thirty']" + ] + }, + "execution_count": 20, + "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)` when omitted, in steps of `2`." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[[], 20.0, [40, 50]]" + ] + }, + "execution_count": 21, + "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*. 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." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "middle = slice(1, 4)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "slice" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(middle)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[10, 20.0, 'Thirty']" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested[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": 25, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "middle.start" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 26, + "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": 27, + "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": 28, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "nested_copy = nested[:]" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[[], 10, 20.0, 'Thirty', [40, 50]]" + ] + }, + "execution_count": 29, + "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 (cf., the \"*List Comparison*\" section below for more details).\n", + "\n", + "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": 30, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested == nested_copy" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 31, + "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 *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* 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": 32, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested[0] is nested_copy[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139688113991744" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(nested[0])" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139688113991744" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(nested_copy[0])" + ] + }, + { + "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": 35, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import copy" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "nested_deep_copy = copy.deepcopy(nested)" + ] + }, + { + "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": [ + "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": 38, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested[0] is nested_deep_copy[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139688113991744" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(nested[0])" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139688113233152" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(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 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": 41, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "nested[0] = 0" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[0, 10, 20.0, 'Thirty', [40, 50]]" + ] + }, + "execution_count": 42, + "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": 43, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "nested[:4] = [100, 100, 100]" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[100, 100, 100, [40, 50]]" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 45, + "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 main point behind mutable objects." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139688113982848" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(nested) # same memory location as before" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`nested_copy` is unchanged!" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[[], 10, 20.0, 'Thirty', [40, 50]]" + ] + }, + "execution_count": 47, + "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": 48, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "nested_copy[-1][:] = [1, 2, 3]" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[[], 10, 20.0, 'Thirty', [1, 2, 3]]" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested_copy" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "That has a surprising side effect on `nested`." + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[100, 100, 100, [1, 2, 3]]" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 51, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "del nested[-1]" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[100, 100, 100]" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `del` statement also works for slices. Here, we remove all references `nested` holds." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "del nested[:]" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "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." + ] + }, + { + "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/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/develop/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." + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "names = [\"Carl\", \"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 be the return value." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "names.append(\"Eckardt\")" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Carl', 'Peter', 'Eckardt']" + ] + }, + "execution_count": 57, + "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. Here, the iterable is a `list` object itself holding two `str` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "names.extend([\"Karl\", \"Oliver\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Carl', 'Peter', 'Eckardt', 'Karl', 'Oliver']" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 60, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "names.insert(1, \"Berthold\")" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Carl', 'Berthold', 'Peter', 'Eckardt', 'Karl', 'Oliver']" + ] + }, + "execution_count": 61, + "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": 62, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Berthold', 'Carl', 'Eckardt', 'Karl', 'Oliver', 'Peter']" + ] + }, + "execution_count": 62, + "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": 63, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Carl', 'Berthold', 'Peter', 'Eckardt', 'Karl', 'Oliver']" + ] + }, + "execution_count": 63, + "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": 64, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "names.sort()" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Berthold', 'Carl', 'Eckardt', 'Karl', 'Oliver', 'Peter']" + ] + }, + "execution_count": 65, + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "names.sort(reverse=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Peter', 'Oliver', 'Karl', 'Eckardt', 'Carl', 'Berthold']" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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/develop/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", + "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": 68, + "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. 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": 69, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Karl', 'Carl', 'Peter', 'Oliver', 'Eckardt', 'Berthold']" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "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": 70, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "names.reverse()" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Berthold', 'Eckardt', 'Oliver', 'Peter', 'Carl', 'Karl']" + ] + }, + "execution_count": 71, + "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. 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": 72, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "removed = names.pop()" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Karl'" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "removed" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Berthold', 'Eckardt', 'Oliver', 'Peter', 'Carl']" + ] + }, + "execution_count": 74, + "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": 75, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "removed = names.pop(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Eckardt'" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "removed" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Berthold', 'Oliver', 'Peter', 'Carl']" + ] + }, + "execution_count": 77, + "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": 78, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "names.remove(\"Peter\")" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Berthold', 'Oliver', 'Carl']" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Also, `.remove()` raises a `ValueError` if the value is not found." + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "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 element that compares equal to its argument. It fails *loudly* with a `ValueError` if no element compares equal." + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Berthold', 'Oliver', 'Carl']" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 82, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names.index(\"Oliver\")" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": 84, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names.count(\"Carl\")" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 85, + "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": 86, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "names_copy = names.copy()" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Berthold', 'Oliver', 'Carl']" + ] + }, + "execution_count": 87, + "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": 88, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "names_copy.clear()" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 89, + "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": 90, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Berthold', 'Oliver', 'Carl']" + ] + }, + "execution_count": 90, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Berthold', 'Oliver', 'Carl', 'Diedrich', 'Yves']" + ] + }, + "execution_count": 91, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names + [\"Diedrich\", \"Yves\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Berthold', 'Oliver', 'Carl', 'Berthold', 'Oliver', 'Carl']" + ] + }, + "execution_count": 92, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "2 * names" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Unpacking" + ] + }, + { + "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": 93, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Achim', 'Berthold', 'Oliver', 'Carl', 'Xavier']" + ] + }, + "execution_count": 93, + "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": 94, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Achim', 'Berthold', 'Oliver', 'Carl', 'Xavier']" + ] + }, + "execution_count": 94, + "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": 95, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['Berthold', 'Oliver', 'Carl']" + ] + }, + "execution_count": 95, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names" + ] + }, + { + "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": [ + "names == [\"Berthold\", \"Oliver\", \"Carl\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 97, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names != [\"Berthold\", \"Oliver\", \"Karl\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 98, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "names < [\"Berthold\", \"Oliver\", \"Karl\"]" + ] + }, + { + "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.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 99, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[\"Berthold\", \"Oliver\"] < names < [\"Berthold\", \"Oliver\", \"Carl\", \"Xavier\"]" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index fd55699..bc87a09 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -165,4 +165,11 @@ If this is not possible, - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/00_content.ipynb) (Collections vs. Sequences; - ABCs: `Container`, `Iterable`, `Sized`, & `Reversible`) \ No newline at end of file + ABCs: `Container`, `Iterable`, `Sized`, & `Reversible`) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/01_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/01_content.ipynb) + (`list` Type; + Indexing & Slicing; + Shallow vs. Deep Copies; + List Methods & Operations; + Unpacking) \ No newline at end of file From 4dfc956f0efe028d20ffbbf0dd0eae0c2f97e05f Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 19:20:28 +0200 Subject: [PATCH 082/142] Add initial version of chapter 07's exercises, part 1 --- 07_sequences/02_exercises.ipynb | 257 ++++++++++++++++++++++++++++++++ CONTENTS.md | 5 +- 2 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 07_sequences/02_exercises.ipynb diff --git a/07_sequences/02_exercises.ipynb b/07_sequences/02_exercises.ipynb new file mode 100644 index 0000000..0ec7907 --- /dev/null +++ b/07_sequences/02_exercises.ipynb @@ -0,0 +1,257 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/02_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 7: Sequential Data (Coding Exercises)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/01_content.ipynb) of Chapter 7.\n", + "\n", + "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Working with Lists" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: Write a function `nested_sum()` that takes a `list` object as its argument, which contains other `list` objects with numbers, and adds up the numbers! Use `nested_numbers` below to test your function!\n", + "\n", + "Hint: You need at least one `for`-loop." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "nested_numbers = [[1, 2, 3], [4], [5], [6, 7], [8], [9]]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def nested_sum(list_of_lists):\n", + " \"\"\"Add up numbers in nested lists.\n", + " \n", + " Args:\n", + " list_of_lists (list): A list containing the lists with the numbers\n", + " \n", + " Returns:\n", + " sum (int or float)\n", + " \"\"\"\n", + " ...\n", + " ...\n", + " ...\n", + "\n", + " return ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "nested_sum(nested_numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: Generalize `nested_sum()` into a function `mixed_sum()` that can process a \"mixed\" `list` object, which contains numbers and other `list` objects with numbers! Use `mixed_numbers` below for testing!\n", + "\n", + "Hints: Use the built-in [isinstance() ](https://docs.python.org/3/library/functions.html#isinstance) function to check how an element is to be processed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mixed_numbers = [[1, 2, 3], 4, 5, [6, 7], 8, [9]]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import collections.abc as abc" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def mixed_sum(list_of_lists_or_numbers):\n", + " \"\"\"Add up numbers in nested lists.\n", + " \n", + " Args:\n", + " list_of_lists_or_numbers (list): A list containing both numbers and\n", + " lists with numbers\n", + " \n", + " Returns:\n", + " sum (int or float)\n", + " \"\"\"\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + "\n", + " return ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mixed_sum(mixed_numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3.1**: Write a function `cum_sum()` that takes a `list` object with numbers as its argument and returns a *new* `list` object with the **cumulative sums** of these numbers! So, `sum_up` below, `[1, 2, 3, 4, 5]`, should return `[1, 3, 6, 10, 15]`.\n", + "\n", + "Hint: The idea behind is similar to the [cumulative distribution function ](https://en.wikipedia.org/wiki/Cumulative_distribution_function) from statistics." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sum_up = [1, 2, 3, 4, 5]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def cum_sum(numbers):\n", + " \"\"\"Create the cumulative sums for some numbers.\n", + "\n", + " Args:\n", + " numbers (list): A list with numbers for that the cumulative sums\n", + " are calculated\n", + " \n", + " Returns:\n", + " cum_sums (list): A list with all the cumulative sums\n", + " \"\"\"\n", + " ...\n", + " ...\n", + "\n", + " ...\n", + " ...\n", + " ...\n", + "\n", + " return ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cum_sum(sum_up)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3.2**: We should always make sure that our functions also work in corner cases. What happens if your implementation of `cum_sum()` is called with an empty list `[]`? Make sure it handles that case *without* crashing! What would be a good return value in this corner case?\n", + "\n", + "Hint: It is possible to write this without any extra input validation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "cum_sum([])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index bc87a09..73a5070 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -172,4 +172,7 @@ If this is not possible, Indexing & Slicing; Shallow vs. Deep Copies; List Methods & Operations; - Unpacking) \ No newline at end of file + Unpacking) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/02_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/02_exercises.ipynb) + (Working with Lists) \ No newline at end of file From 9a21de45390406a3ab385090451f2dee7776a5d2 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 19:45:22 +0200 Subject: [PATCH 083/142] Add initial version of chapter 07, part 3 --- 07_sequences/03_content.ipynb | 2494 +++++++++++++++++++++++++++++++++ CONTENTS.md | 11 +- 2 files changed, 2502 insertions(+), 3 deletions(-) create mode 100644 07_sequences/03_content.ipynb diff --git a/07_sequences/03_content.ipynb b/07_sequences/03_content.ipynb new file mode 100644 index 0000000..7b63f1e --- /dev/null +++ b/07_sequences/03_content.ipynb @@ -0,0 +1,2494 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/03_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 7: Sequential Data (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In this third part of the chapter, we first look at a major implication of the `list` type's mutability. Then, we see how its close relative, the `tuple` type, can mitigate this. Lastly, we see how Python's syntax assumes sequential data at various places: for example, when unpacking iterables during a `for`-loop or an assignment, or when working with `function` objects." + ] + }, + { + "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": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "letters = [\"a\", \"b\", \"c\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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, 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, referencing the *same* object. We can also visualize that with [PythonTutor ](http://pythontutor.com/visualize.html#code=letters%20%3D%20%5B%22a%22,%20%22b%22,%20%22c%22%5D%0A%0Adef%20add_xyz%28arg%29%3A%0A%20%20%20%20arg.extend%28%5B%22x%22,%20%22y%22,%20%22z%22%5D%29%0A%20%20%20%20return%20arg%0A%0Aletters_with_xyz%20%3D%20add_xyz%28letters%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false)." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "letters_with_xyz = add_xyz(letters)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['a', 'b', 'c', 'x', 'y', 'z']" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "letters_with_xyz" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['a', 'b', 'c', 'x', 'y', 'z']" + ] + }, + "execution_count": 5, + "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. [PythonTutor ](http://pythontutor.com/visualize.html#code=letters%20%3D%20%5B%22a%22,%20%22b%22,%20%22c%22%5D%0A%0Adef%20add_xyz%28arg%29%3A%0A%20%20%20%20new_arg%20%3D%20arg%5B%3A%5D%0A%20%20%20%20new_arg.extend%28%5B%22x%22,%20%22y%22,%20%22z%22%5D%29%0A%20%20%20%20return%20new_arg%0A%0Aletters_with_xyz%20%3D%20add_xyz%28letters%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows that as well. 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 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb)." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "letters = [\"a\", \"b\", \"c\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def add_xyz(arg):\n", + " \"\"\"Create a new list from an existing one.\"\"\"\n", + " new_arg = arg[:]\n", + " new_arg.extend([\"x\", \"y\", \"z\"])\n", + " return new_arg" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "letters_with_xyz = add_xyz(letters)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "scrolled": true, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['a', 'b', 'c', 'x', 'y', 'z']" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "letters_with_xyz" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['a', 'b', 'c']" + ] + }, + "execution_count": 10, + "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`. [PythonTutor ](http://pythontutor.com/visualize.html#code=letters%20%3D%20%5B%22a%22,%20%22b%22,%20%22c%22%5D%0A%0Adef%20add_xyz%28arg%29%3A%0A%20%20%20%20arg.extend%28%5B%22x%22,%20%22y%22,%20%22z%22%5D%29%0A%20%20%20%20return%0A%0Aadd_xyz%28letters%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how there is only *one* reference to `letters` after the function call." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "letters = [\"a\", \"b\", \"c\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": 13, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "add_xyz(letters)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['a', 'b', 'c', 'x', 'y', 'z']" + ] + }, + "execution_count": 14, + "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": 15, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "add_xyz(letters)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['a', 'b', 'c', 'x', 'y', 'z', 'x', 'y', 'z']" + ] + }, + "execution_count": 16, + "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": 17, + "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": 18, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4)" + ] + }, + "execution_count": 18, + "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": 19, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "numbers = (7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4)" + ] + }, + "execution_count": 20, + "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": 21, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140248673535456" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "tuple" + ] + }, + "execution_count": 22, + "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": 23, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "empty_tuple = ()" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "empty_tuple" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "tuple" + ] + }, + "execution_count": 25, + "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": 26, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "one_tuple = (1,) # we could ommit the parentheses but not the comma" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1,)" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "one_tuple" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "tuple" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(one_tuple)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "no_tuple = (1)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "no_tuple" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "int" + ] + }, + "execution_count": 31, + "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": 32, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1,)" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tuple([1])" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "('i', 't', 'e', 'r', 'a', 'b', 'l', 'e')" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tuple(\"iterable\")" + ] + }, + { + "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. So, `numbers` holds a *finite* number of elements ..." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12" + ] + }, + "execution_count": 34, + "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": 35, + "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": 36, + "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": "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": 37, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0 in numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 in numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 39, + "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": 40, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers[-1]" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(2, 6, 9, 10, 1, 4)" + ] + }, + "execution_count": 42, + "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": 43, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": [ + "The `+` and `*` operators work with `tuple` objects as well: They always create *new* `tuple` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4, 99)" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers + (99,) " + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "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": 45, + "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 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 `.sort()`, `.copy()`, and `.clear()` methods." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers.count(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers.index(1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The relational operators work in the *same* way as for `list` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4)" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers == (7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4)" + ] + }, + { + "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": [ + "numbers != (99, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4)" + ] + }, + { + "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": [ + "numbers < (99, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4)" + ] + }, + { + "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 references to 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 as a whole." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "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": 53, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, [2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 12)" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "not_immutable" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "not_immutable[1][:] = [99, 99, 99]" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, [99, 99, 99], 12)" + ] + }, + "execution_count": 55, + "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 in the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/01_content.ipynb#List-Operations) of this chapter, 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 literal `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": 56, + "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": 57, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "n1" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "11" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "n2" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "8" + ] + }, + "execution_count": 59, + "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": 60, + "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": 61, + "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": 62, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "first, *middle, last = numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "first" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[11, 8, 5, 3, 12, 2, 6, 9, 10, 1]" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "middle # always a list!" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 65, + "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 that we unpack by listing several loop variables.\n", + "\n", + "So, the `name, position` below 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": 66, + "metadata": {}, + "outputs": [], + "source": [ + "names = [\"Berthold\", \"Oliver\", \"Carl\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "positions = (\"goalkeeper\", \"defender\", \"midfielder\", \"striker\", \"coach\")" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Berthold is a goalkeeper\n", + "Oliver is a defender\n", + "Carl is a midfielder\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": 69, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ('Berthold', 'goalkeeper')\n", + " ('Oliver', 'defender')\n", + " ('Carl', 'midfielder')\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 `number` inside the `for`-loop. In each iteration, a `tuple` object consisting of `number` and another `tuple` object is created. The inner one then holds the `name` and `position`." + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Berthold (jersey #1) is a goalkeeper\n", + "Oliver (jersey #2) is a defender\n", + "Carl (jersey #3) is a midfielder\n" + ] + } + ], + "source": [ + "for number, (name, position) in enumerate(zip(names, positions), start=1):\n", + " print(f\"{name} (jersey #{number}) 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": 71, + "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": 72, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "temp = a\n", + "a = b\n", + "b = temp\n", + "\n", + "del temp" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With unpacking, the solution is more elegant. *All* expressions on the right-hand side are evaluated *before* any assignment takes place." + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "a, b = 0, 1" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "a, b = b, a" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 0)" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a, b" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "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/develop/04_iteration/02_content.ipynb#\"Hard-at-first-Glance\"-Example:-Fibonacci-Numbers-%28revisited%29) in a concise way." + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": { + "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", + " 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": 79, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "144" + ] + }, + "execution_count": 79, + "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": 80, + "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": 81, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 81, + "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": 82, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "100" + ] + }, + "execution_count": 82, + "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": 83, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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": 84, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "one_hundred = [2, 5, 10]" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[2, 5, 10]" + ] + }, + "execution_count": 85, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "product(one_hundred) # a semantic error!" + ] + }, + { + "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": 86, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "100" + ] + }, + "execution_count": 86, + "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": 87, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "100" + ] + }, + "execution_count": 87, + "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": [ + "In the \"*Packing & Unpacking with Functions*\" [exercise ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/04_exercises.ipynb), 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. 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": 88, + "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": 89, + "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": 90, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2 5 10\n" + ] + } + ], + "source": [ + "print(*one_hundred) # replaces the for-loop" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index 73a5070..5abc9fb 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -171,8 +171,13 @@ If this is not possible, (`list` Type; Indexing & Slicing; Shallow vs. Deep Copies; - List Methods & Operations; - Unpacking) + List Methods & Operations) - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/02_exercises.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/02_exercises.ipynb) - (Working with Lists) \ No newline at end of file + (Working with Lists) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/03_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/03_content.ipynb) + (Modifiers vs. Pure Functions; + `tuple` Type; + Packing & Unpacking; + `*args` in Function Definitions) \ No newline at end of file From 98cb3bef933bc0b1a28ed9a1b32ef995482e2f42 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 19:49:34 +0200 Subject: [PATCH 084/142] Add initial version of chapter 07's exercises, part 2 --- 07_sequences/04_exercises.ipynb | 663 ++++++++++++++++++++++++++++++++ CONTENTS.md | 5 +- 2 files changed, 667 insertions(+), 1 deletion(-) create mode 100644 07_sequences/04_exercises.ipynb diff --git a/07_sequences/04_exercises.ipynb b/07_sequences/04_exercises.ipynb new file mode 100644 index 0000000..1d46524 --- /dev/null +++ b/07_sequences/04_exercises.ipynb @@ -0,0 +1,663 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/04_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 7: Sequential Data (Coding Exercises)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read the [third part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/03_content.ipynb) of Chapter 7.\n", + "\n", + "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Packing & Unpacking with Functions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the \"*Function Definitions & Calls*\" section in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/03_content.ipynb#Function-Definitions-&-Calls), we define the following function `product()`. In this exercise, you will improve it by making it more \"user-friendly.\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def product(*args):\n", + " \"\"\"Multiply all arguments.\"\"\"\n", + " result = args[0]\n", + "\n", + " for arg in args[1:]:\n", + " result *= arg\n", + "\n", + " return result" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `*` in the function's header line *packs* all *positional* arguments passed to `product()` into one *iterable* called `args`.\n", + "\n", + "**Q1**: What is the data type of `args` within the function's body?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Because of the packing, we may call `product()` with an abitrary number of *positional* arguments: The product of just `42` remains `42`, while `2`, `5`, and `10` multiplied together result in `100`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(42)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(2, 5, 10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, \"abitrary\" does not mean that we can pass *no* argument. If we do so, we get an `IndexError`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: What line in the body of `product()` causes this exception? What is the exact problem?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb#Function-Definitions-&-Calls), we also pass a `list` object, like `one_hundred`, to `product()`, and *no* exception is raised." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "one_hundred = [2, 5, 10]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(one_hundred)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: What is wrong with that? What *kind* of error (cf., [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Formal-vs.-Natural-Languages)) is that conceptually? Describe precisely what happens to the passed in `one_hundred` in every line within `product()`!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Of course, one solution is to *unpack* `one_hundred` with the `*` symbol. We look at another solution further below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(*one_hundred)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's continue with the issue when calling `product()` *without* any argument.\n", + "\n", + "This revised version of `product()` avoids the `IndexError` from before." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def product(*args):\n", + " \"\"\"Multiply all arguments.\"\"\"\n", + " result = None\n", + "\n", + " for arg in args:\n", + " result *= arg\n", + "\n", + " return result" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: Describe why no error occurs by going over every line in `product()`!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Unfortunately, the new version cannot process any arguments we pass in any more." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(42)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(2, 5, 10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: What line causes troubles now? What is the exact problem?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: Replace the `None` in `product()` above with something reasonable that does *not* cause exceptions! Ensure that `product(42)` and `product(2, 5, 10)` return a correct result.\n", + "\n", + "Hints: It is ok if `product()` returns a result *different* from the `None` above. Look at the documentation of the built-in [sum() ](https://docs.python.org/3/library/functions.html#sum) function for some inspiration." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def product(*args):\n", + " \"\"\"Multiply all arguments.\"\"\"\n", + " result = ...\n", + "\n", + " for arg in args:\n", + " result *= arg\n", + "\n", + " return result" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(42)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(2, 5, 10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, calling `product()` without any arguments returns what we would best describe as a *default* or *start* value. To be \"philosophical,\" what is the product of *no* numbers? We know that the product of *one* number is just the number itself, but what could be a reasonable result when multiplying *no* numbers? The answer is what you use as the initial value of `result` above, and there is only *one* way to make `product(42)` and `product(2, 5, 10)` work." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: Rewrite `product()` so that it takes a *keyword-only* argument `start`, defaulting to the above *default* or *start* value, and use `start` internally instead of `result`!\n", + "\n", + "Hint: Remember that a *keyword-only* argument is any parameter specified in a function's header line after the first and only `*` (cf., [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb#Keyword-only-Arguments))." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def product(*args, ...):\n", + " \"\"\"Multiply all arguments.\"\"\"\n", + " ...\n", + " ...\n", + "\n", + " return ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, we can call `product()` with a truly arbitrary number of *positional* arguments." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(42)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(2, 5, 10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Without any *positional* arguments but only the *keyword* argument `start`, for example, `start=0`, we can adjust the answer to the \"philosophical\" problem of multiplying *no* numbers. Because of the *keyword-only* syntax, there is *no* way to pass in a `start` number *without* naming it." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(start=0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We could use `start` to inject a multiplier, for example, to double the outcomes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(42, start=2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(2, 5, 10, start=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There is still one issue left: Because of the function's name, a user of `product()` may assume that it is ok to pass a *collection* of numbers, like `one_hundred`, which are then multiplied." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(one_hundred)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: What is a **collection**? How is that different from a **sequence**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: Rewrite the latest version of `product()` to check if the *only* positional argument is a *collection* type! If so, its elements are multiplied together. Otherwise, the logic remains the same.\n", + "\n", + "Hints: Use the built-in [len() ](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", + " ...\n", + " ...\n", + "\n", + " ...\n", + " ...\n", + "\n", + " return ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "All *five* code cells below now return correct results. We may unpack `one_hundred` or not." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(42)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(2, 5, 10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(one_hundred)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(*one_hundred)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Side Note**: Above, we make `product()` work with a single *collection* type argument instead of a *sequence* type to keep it more generic: For example, we can pass in a `set` object, like `{2, 5, 10}` below, and `product()` continues to work correctly. The `set` type is introducted in [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb#The-set-Type), and one essential difference to the `list` type is that objects of type `set` have *no* order regarding their elements. So, even though `[2, 5, 10]` and `{2, 5, 10}` look almost the same, the order implied in the literal notation gets lost in memory!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product([2, 5, 10]) # the argument is a collection that is also a sequence" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product({2, 5, 10}) # the argument is a collection that is NOT a sequence" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "isinstance({2, 5, 10}, abc.Sequence) # sets are NO sequences" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's continue to improve `product()` and make it more Pythonic. It is always a good idea to mimic the behavior of built-ins when writing our own functions. And, [sum() ](https://docs.python.org/3/library/functions.html#sum), for example, raises a `TypeError` if called *without* any arguments. It does *not* return the \"philosophical\" answer to adding *no* numbers, which would be `0`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sum()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: Adapt the latest version of `product()` to also raise a `TypeError` if called *without* any *positional* arguments!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def product(*args, ...):\n", + " \"\"\"Multiply all arguments.\"\"\"\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + "\n", + " ...\n", + " ...\n", + "\n", + " return ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index 5abc9fb..e8568e6 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -180,4 +180,7 @@ If this is not possible, (Modifiers vs. Pure Functions; `tuple` Type; Packing & Unpacking; - `*args` in Function Definitions) \ No newline at end of file + `*args` in Function Definitions) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/04_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/04_exercises.ipynb) + (Packing & Unpacking with Functions) \ No newline at end of file From e34d8be113ae9c979163c8c78cf71cfdfa130ce4 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 19:57:18 +0200 Subject: [PATCH 085/142] Add initial version of chapter 07's appendix --- 07_sequences/05_appendix.ipynb | 605 +++++++++++++++++++++++++++++++++ CONTENTS.md | 7 +- 2 files changed, 610 insertions(+), 2 deletions(-) create mode 100644 07_sequences/05_appendix.ipynb diff --git a/07_sequences/05_appendix.ipynb b/07_sequences/05_appendix.ipynb new file mode 100644 index 0000000..3c8c454 --- /dev/null +++ b/07_sequences/05_appendix.ipynb @@ -0,0 +1,605 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/05_appendix.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 7: Sequential Data (Appendix)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In the [third part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/03_content.ipynb#Tuples-are-like-\"Immutable-Lists\") of the chapter, we proposed the idea that `tuple` objects are like \"immutable lists.\" Often, however, we use `tuple` objects to represent a **record** of related **fields**. Then, each element has a *semantic* meaning (i.e., a descriptive name).\n", + "\n", + "As an example, think of a spreadsheet with information on students in a course. Each row represents a record and holds all the data associated with an individual student. The columns (e.g., matriculation number, first name, last name) are the fields that may come as *different* data types (e.g., `int` for the matriculation number, `str` for the names).\n", + "\n", + "A simple way of modeling a single student is as a `tuple` object, for example, `(123456, \"John\", \"Doe\")`. A disadvantage of this approach is that we must remember the order and meaning of the elements/fields in the `tuple` object.\n", + "\n", + "An example from a different domain is the representation of $(x, y)$-points in the $x$-$y$-plane. Again, we could use a `tuple` object like `current_position` below to model the point $(4, 2)$." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "current_position = (4, 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We implicitly assume that the first element represents the $x$ and the second the $y$ coordinate. While that follows intuitively from convention in math, we should at least add comments somewhere in the code to document this assumption." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The `namedtuple` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A better way is to create a *custom* data type. While that is covered in depth in [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb), 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": 2, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "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": 3, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "Point = namedtuple(\"Point\", [\"x\", \"y\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `Point` object is a so-called **class**. That is what it means if an object is of type `type`. It can be used as a **factory** to create *new* `tuple`-like objects of type `Point`. In a way, [namedtuple() ](https://docs.python.org/3/library/collections.html#collections.namedtuple) gives us a way to create our own custom **constructors**." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "94457911453856" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(Point)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "type" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(Point)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The value of `Point` is just itself in a *literal notation*." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "__main__.Point" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Point" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We write `Point(4, 2)` to create a *new* object of type `Point`." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "current_position = Point(4, 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Now, `current_position` has a somewhat nicer representation. In particular, the coordinates are named `x` and `y`." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Point(x=4, y=2)" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "current_position" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "It is *not* a `tuple` any more but an object of type `Point`." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140376178109184" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(current_position)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "__main__.Point" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(current_position)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We use the dot operator `.` to access the defined attributes." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "current_position.x" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "current_position.y" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As before, we get an `AttributeError` if we try to access an undefined attribute." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'Point' object has no attribute 'z'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\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 with the familiar four properties." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "current_position[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "current_position[1]" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4\n", + "2\n" + ] + } + ], + "source": [ + "for number in current_position:\n", + " print(number)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2\n", + "4\n" + ] + } + ], + "source": [ + "for number in reversed(current_position):\n", + " print(number)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(current_position)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index e8568e6..bb9df6c 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -139,7 +139,7 @@ If this is not possible, - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/06_resources.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/06_resources.ipynb) - *Chapter 6*: Text & Bytes - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_numbers/00_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/00_content.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/00_content.ipynb) (`str` Type; Reading Files; @@ -183,4 +183,7 @@ If this is not possible, `*args` in Function Definitions) - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/04_exercises.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/04_exercises.ipynb) - (Packing & Unpacking with Functions) \ No newline at end of file + (Packing & Unpacking with Functions) + - [appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/05_appendix.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/05_appendix.ipynb) + (`namedtuple` Type) \ No newline at end of file From eda9266a79774541da834260ef35a1b7d0474b9a Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 20:01:04 +0200 Subject: [PATCH 086/142] Add initial version of chapter 07's summary --- 07_sequences/06_summary.ipynb | 85 +++++++++++++++++++++++++++++++++++ CONTENTS.md | 3 +- 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 07_sequences/06_summary.ipynb diff --git a/07_sequences/06_summary.ipynb b/07_sequences/06_summary.ipynb new file mode 100644 index 0000000..8115139 --- /dev/null +++ b/07_sequences/06_summary.ipynb @@ -0,0 +1,85 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 7: Sequential Data (TL;DR)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Sequences** are an *abstract* concept that summarizes *four* behaviors an object may or may not exhibit. Sequences are\n", + "- **finite** and\n", + "- **ordered**\n", + "- **containers** that we may\n", + "- **loop over**.\n", + "\n", + "Examples are the `list`, `tuple`, but also the `str` types.\n", + "\n", + "Objects that exhibit all behaviors *except* being ordered are referred to as **collections**.\n", + "\n", + "The objects inside a sequence are called its **elements** and may be labeled with a unique **index**, an `int` object in the range $0 \\leq \\text{index} < \\lvert \\text{sequence} \\rvert$.\n", + "\n", + "`list` objects are **mutable**. That means we can change the references to the other objects it contains, and, in particular, re-assign them.\n", + "\n", + "On the contrary, `tuple` objects are like **immutable** lists: We can use them in place of any `list` object as long as we do *not* need to mutate it. Often, `tuple` objects are also used to model **records** of related **fields**." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index bb9df6c..ac2e795 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -186,4 +186,5 @@ If this is not possible, (Packing & Unpacking with Functions) - [appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/05_appendix.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/05_appendix.ipynb) - (`namedtuple` Type) \ No newline at end of file + (`namedtuple` Type) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/06_summary.ipynb) \ No newline at end of file From 2796007e39e35eaf2acb63ae07895658502cb8f6 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 20:03:02 +0200 Subject: [PATCH 087/142] Add initial version of chapter 07's review --- 07_sequences/07_review.ipynb | 252 +++++++++++++++++++++++++++++++++++ CONTENTS.md | 3 +- 2 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 07_sequences/07_review.ipynb diff --git a/07_sequences/07_review.ipynb b/07_sequences/07_review.ipynb new file mode 100644 index 0000000..de10156 --- /dev/null +++ b/07_sequences/07_review.ipynb @@ -0,0 +1,252 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 7: Sequential Data (Review Questions)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb), [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/01_content.ipynb), and the [third ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/03_content.ipynb) part of Chapter 7. Some questions regard the [Appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/05_appendix.ipynb); that is indicated with a **\\***.\n", + "\n", + "Be concise in your answers! Most questions can be answered in *one* sentence." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Essay Questions " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Answer the following questions *briefly*!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: We have seen **containers** and **iterables** before in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb#Containers-vs.-Iterables). How do they relate to **sequences**? " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: What are **abstract base classes**? How can we make use of the ones from the [collections.abc ](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": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: How are the *abstract behaviors* of **reversibility** and **finiteness** essential for *indexing* and *slicing* sequences?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: Explain the difference between **mutable** and **immutable** objects in Python with the examples of the `list` and `tuple` types!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: What is the difference between a **shallow** and a **deep** copy of an object? How can one of them become a \"problem?\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: Many **list methods** change `list` objects \"**in place**.\" What do we mean by that?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7.1**: `tuple` objects have *two* primary usages. First, they can be used in place of `list` objects where **mutability** is *not* required. Second, we use them to model data **records**.\n", + "\n", + "Describe why `tuple` objects are a suitable replacement for `list` objects in general!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7.2\\***: What do we mean by a **record**? How are `tuple` objects suitable to model records? How can we integrate a **semantic meaning** when working with records into our code?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: How is (iterable) **packing** and **unpacking** useful in the context of **function definitions** and **calls**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## True / False Questions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Motivate your answer with *one short* sentence!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: `sequence` objects are *not* part of core Python but may be imported from the [standard library ](https://docs.python.org/3/library/index.html)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: The built-in [.sort() ](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": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: Passing **mutable** objects as arguments to functions is not problematic because functions operate in a **local** scope without affecting the **global** scope." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index ac2e795..bbc1dee 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -187,4 +187,5 @@ If this is not possible, - [appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/05_appendix.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/05_appendix.ipynb) (`namedtuple` Type) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/06_summary.ipynb) \ No newline at end of file + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/06_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/07_review.ipynb) From 9f5500606131564c57dc0e500171b0f4473bab07 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 20:08:27 +0200 Subject: [PATCH 088/142] Streamline slides --- 07_sequences/01_content.ipynb | 6 +++++- 07_sequences/05_appendix.ipynb | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/07_sequences/01_content.ipynb b/07_sequences/01_content.ipynb index 5123ac8..1dd590a 100644 --- a/07_sequences/01_content.ipynb +++ b/07_sequences/01_content.ipynb @@ -2714,7 +2714,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "source": [ "### Unpacking" ] diff --git a/07_sequences/05_appendix.ipynb b/07_sequences/05_appendix.ipynb index 3c8c454..09ebd0c 100644 --- a/07_sequences/05_appendix.ipynb +++ b/07_sequences/05_appendix.ipynb @@ -65,7 +65,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "source": [ "## The `namedtuple` Type" ] From 9257890614b470abfbb640859d379e882b805f58 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 19 Oct 2020 20:12:06 +0200 Subject: [PATCH 089/142] Pin the dependencies - upgrade transient dependencies: + lxml + nbclient + urllib3 --- poetry.lock | 85 ++++++++++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/poetry.lock b/poetry.lock index 9f17fc7..8e454f7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -475,7 +475,7 @@ test = ["pytest", "requests"] [[package]] name = "lxml" -version = "4.5.2" +version = "4.6.1" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." category = "dev" optional = false @@ -505,8 +505,8 @@ python-versions = "*" [[package]] name = "nbclient" -version = "0.5.0" -description = "A client library for executing notebooks. Formally nbconvert's ExecutePreprocessor." +version = "0.5.1" +description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." category = "main" optional = false python-versions = ">=3.6" @@ -920,7 +920,7 @@ test = ["pytest"] [[package]] name = "urllib3" -version = "1.25.10" +version = "1.25.11" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false @@ -928,7 +928,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] brotli = ["brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)", "ipaddress"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] [[package]] @@ -1169,37 +1169,42 @@ jupyterlab-server = [ {file = "jupyterlab_server-1.2.0.tar.gz", hash = "sha256:5431d9dde96659364b7cc877693d5d21e7b80cea7ae3959ecc2b87518e5f5d8c"}, ] lxml = [ - {file = "lxml-4.5.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:74f48ec98430e06c1fa8949b49ebdd8d27ceb9df8d3d1c92e1fdc2773f003f20"}, - {file = "lxml-4.5.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e70d4e467e243455492f5de463b72151cc400710ac03a0678206a5f27e79ddef"}, - {file = "lxml-4.5.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:7ad7906e098ccd30d8f7068030a0b16668ab8aa5cda6fcd5146d8d20cbaa71b5"}, - {file = "lxml-4.5.2-cp27-cp27m-win32.whl", hash = "sha256:92282c83547a9add85ad658143c76a64a8d339028926d7dc1998ca029c88ea6a"}, - {file = "lxml-4.5.2-cp27-cp27m-win_amd64.whl", hash = "sha256:05a444b207901a68a6526948c7cc8f9fe6d6f24c70781488e32fd74ff5996e3f"}, - {file = "lxml-4.5.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:94150231f1e90c9595ccc80d7d2006c61f90a5995db82bccbca7944fd457f0f6"}, - {file = "lxml-4.5.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bea760a63ce9bba566c23f726d72b3c0250e2fa2569909e2d83cda1534c79443"}, - {file = "lxml-4.5.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c3f511a3c58676147c277eff0224c061dd5a6a8e1373572ac817ac6324f1b1e0"}, - {file = "lxml-4.5.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:59daa84aef650b11bccd18f99f64bfe44b9f14a08a28259959d33676554065a1"}, - {file = "lxml-4.5.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:c9d317efde4bafbc1561509bfa8a23c5cab66c44d49ab5b63ff690f5159b2304"}, - {file = "lxml-4.5.2-cp35-cp35m-win32.whl", hash = "sha256:9dc9006dcc47e00a8a6a029eb035c8f696ad38e40a27d073a003d7d1443f5d88"}, - {file = "lxml-4.5.2-cp35-cp35m-win_amd64.whl", hash = "sha256:08fc93257dcfe9542c0a6883a25ba4971d78297f63d7a5a26ffa34861ca78730"}, - {file = "lxml-4.5.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:121b665b04083a1e85ff1f5243d4a93aa1aaba281bc12ea334d5a187278ceaf1"}, - {file = "lxml-4.5.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5591c4164755778e29e69b86e425880f852464a21c7bb53c7ea453bbe2633bbe"}, - {file = "lxml-4.5.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:cc411ad324a4486b142c41d9b2b6a722c534096963688d879ea6fa8a35028258"}, - {file = "lxml-4.5.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:1fa21263c3aba2b76fd7c45713d4428dbcc7644d73dcf0650e9d344e433741b3"}, - {file = "lxml-4.5.2-cp36-cp36m-win32.whl", hash = "sha256:786aad2aa20de3dbff21aab86b2fb6a7be68064cbbc0219bde414d3a30aa47ae"}, - {file = "lxml-4.5.2-cp36-cp36m-win_amd64.whl", hash = "sha256:e1cacf4796b20865789083252186ce9dc6cc59eca0c2e79cca332bdff24ac481"}, - {file = "lxml-4.5.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:80a38b188d20c0524fe8959c8ce770a8fdf0e617c6912d23fc97c68301bb9aba"}, - {file = "lxml-4.5.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ecc930ae559ea8a43377e8b60ca6f8d61ac532fc57efb915d899de4a67928efd"}, - {file = "lxml-4.5.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a76979f728dd845655026ab991df25d26379a1a8fc1e9e68e25c7eda43004bed"}, - {file = "lxml-4.5.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cfd7c5dd3c35c19cec59c63df9571c67c6d6e5c92e0fe63517920e97f61106d1"}, - {file = "lxml-4.5.2-cp37-cp37m-win32.whl", hash = "sha256:5a9c8d11aa2c8f8b6043d845927a51eb9102eb558e3f936df494e96393f5fd3e"}, - {file = "lxml-4.5.2-cp37-cp37m-win_amd64.whl", hash = "sha256:4b4a111bcf4b9c948e020fd207f915c24a6de3f1adc7682a2d92660eb4e84f1a"}, - {file = "lxml-4.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5dd20538a60c4cc9a077d3b715bb42307239fcd25ef1ca7286775f95e9e9a46d"}, - {file = "lxml-4.5.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:2b30aa2bcff8e958cd85d907d5109820b01ac511eae5b460803430a7404e34d7"}, - {file = "lxml-4.5.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:aa8eba3db3d8761db161003e2d0586608092e217151d7458206e243be5a43843"}, - {file = "lxml-4.5.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f0ec6b9b3832e0bd1d57af41f9238ea7709bbd7271f639024f2fc9d3bb01293"}, - {file = "lxml-4.5.2-cp38-cp38-win32.whl", hash = "sha256:107781b213cf7201ec3806555657ccda67b1fccc4261fb889ef7fc56976db81f"}, - {file = "lxml-4.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:f161af26f596131b63b236372e4ce40f3167c1b5b5d459b29d2514bd8c9dc9ee"}, - {file = "lxml-4.5.2.tar.gz", hash = "sha256:cdc13a1682b2a6241080745b1953719e7fe0850b40a5c71ca574f090a1391df6"}, + {file = "lxml-4.6.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:4b7572145054330c8e324a72d808c8c8fbe12be33368db28c39a255ad5f7fb51"}, + {file = "lxml-4.6.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:302160eb6e9764168e01d8c9ec6becddeb87776e81d3fcb0d97954dd51d48e0a"}, + {file = "lxml-4.6.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:d4ad7fd3269281cb471ad6c7bafca372e69789540d16e3755dd717e9e5c9d82f"}, + {file = "lxml-4.6.1-cp27-cp27m-win32.whl", hash = "sha256:189ad47203e846a7a4951c17694d845b6ade7917c47c64b29b86526eefc3adf5"}, + {file = "lxml-4.6.1-cp27-cp27m-win_amd64.whl", hash = "sha256:56eff8c6fb7bc4bcca395fdff494c52712b7a57486e4fbde34c31bb9da4c6cc4"}, + {file = "lxml-4.6.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:23c83112b4dada0b75789d73f949dbb4e8f29a0a3511647024a398ebd023347b"}, + {file = "lxml-4.6.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:0e89f5d422988c65e6936e4ec0fe54d6f73f3128c80eb7ecc3b87f595523607b"}, + {file = "lxml-4.6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2358809cc64394617f2719147a58ae26dac9e21bae772b45cfb80baa26bfca5d"}, + {file = "lxml-4.6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:be1ebf9cc25ab5399501c9046a7dcdaa9e911802ed0e12b7d620cd4bbf0518b3"}, + {file = "lxml-4.6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:4fff34721b628cce9eb4538cf9a73d02e0f3da4f35a515773cce6f5fe413b360"}, + {file = "lxml-4.6.1-cp35-cp35m-win32.whl", hash = "sha256:475325e037fdf068e0c2140b818518cf6bc4aa72435c407a798b2db9f8e90810"}, + {file = "lxml-4.6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:f98b6f256be6cec8dd308a8563976ddaff0bdc18b730720f6f4bee927ffe926f"}, + {file = "lxml-4.6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:be7c65e34d1b50ab7093b90427cbc488260e4b3a38ef2435d65b62e9fa3d798a"}, + {file = "lxml-4.6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:d18331ea905a41ae71596502bd4c9a2998902328bbabd29e3d0f5f8569fabad1"}, + {file = "lxml-4.6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3d9b2b72eb0dbbdb0e276403873ecfae870599c83ba22cadff2db58541e72856"}, + {file = "lxml-4.6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d20d32cbb31d731def4b1502294ca2ee99f9249b63bc80e03e67e8f8e126dea8"}, + {file = "lxml-4.6.1-cp36-cp36m-win32.whl", hash = "sha256:d182eada8ea0de61a45a526aa0ae4bcd222f9673424e65315c35820291ff299c"}, + {file = "lxml-4.6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:c0dac835c1a22621ffa5e5f999d57359c790c52bbd1c687fe514ae6924f65ef5"}, + {file = "lxml-4.6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d84d741c6e35c9f3e7406cb7c4c2e08474c2a6441d59322a00dcae65aac6315d"}, + {file = "lxml-4.6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:8862d1c2c020cb7a03b421a9a7b4fe046a208db30994fc8ff68c627a7915987f"}, + {file = "lxml-4.6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3a7a380bfecc551cfd67d6e8ad9faa91289173bdf12e9cfafbd2bdec0d7b1ec1"}, + {file = "lxml-4.6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:2d6571c48328be4304aee031d2d5046cbc8aed5740c654575613c5a4f5a11311"}, + {file = "lxml-4.6.1-cp37-cp37m-win32.whl", hash = "sha256:803a80d72d1f693aa448566be46ffd70882d1ad8fc689a2e22afe63035eb998a"}, + {file = "lxml-4.6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:24e811118aab6abe3ce23ff0d7d38932329c513f9cef849d3ee88b0f848f2aa9"}, + {file = "lxml-4.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2e311a10f3e85250910a615fe194839a04a0f6bc4e8e5bb5cac221344e3a7891"}, + {file = "lxml-4.6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a71400b90b3599eb7bf241f947932e18a066907bf84617d80817998cee81e4bf"}, + {file = "lxml-4.6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:211b3bcf5da70c2d4b84d09232534ad1d78320762e2c59dedc73bf01cb1fc45b"}, + {file = "lxml-4.6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e65c221b2115a91035b55a593b6eb94aa1206fa3ab374f47c6dc10d364583ff9"}, + {file = "lxml-4.6.1-cp38-cp38-win32.whl", hash = "sha256:d6f8c23f65a4bfe4300b85f1f40f6c32569822d08901db3b6454ab785d9117cc"}, + {file = "lxml-4.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:573b2f5496c7e9f4985de70b9bbb4719ffd293d5565513e04ac20e42e6e5583f"}, + {file = "lxml-4.6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:1d87936cb5801c557f3e981c9c193861264c01209cb3ad0964a16310ca1b3301"}, + {file = "lxml-4.6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:2d5896ddf5389560257bbe89317ca7bcb4e54a02b53a3e572e1ce4226512b51b"}, + {file = "lxml-4.6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9b06690224258db5cd39a84e993882a6874676f5de582da57f3df3a82ead9174"}, + {file = "lxml-4.6.1-cp39-cp39-win32.whl", hash = "sha256:bb252f802f91f59767dcc559744e91efa9df532240a502befd874b54571417bd"}, + {file = "lxml-4.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ecaef52fd9b9535ae5f01a1dd2651f6608e4ec9dc136fc4dfe7ebe3c3ddb230"}, + {file = "lxml-4.6.1.tar.gz", hash = "sha256:c152b2e93b639d1f36ec5a8ca24cde4a8eefb2b6b83668fcd8e83a67badcb367"}, ] markupsafe = [ {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, @@ -1241,8 +1246,8 @@ mistune = [ {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, ] nbclient = [ - {file = "nbclient-0.5.0-py3-none-any.whl", hash = "sha256:8a6e27ff581cee50895f44c41936ce02369674e85e2ad58643d8d4a6c36771b0"}, - {file = "nbclient-0.5.0.tar.gz", hash = "sha256:8ad52d27ba144fca1402db014857e53c5a864a2f407be66ca9d74c3a56d6591d"}, + {file = "nbclient-0.5.1-py3-none-any.whl", hash = "sha256:4d6b116187c795c99b9dba13d46e764d596574b14c296d60670c8dfe454db364"}, + {file = "nbclient-0.5.1.tar.gz", hash = "sha256:01e2d726d16eaf2cde6db74a87e2451453547e8832d142f73f72fddcd4fe0250"}, ] nbconvert = [ {file = "nbconvert-6.0.7-py3-none-any.whl", hash = "sha256:39e9f977920b203baea0be67eea59f7b37a761caa542abe80f5897ce3cf6311d"}, @@ -1467,8 +1472,8 @@ traitlets = [ {file = "traitlets-5.0.5.tar.gz", hash = "sha256:178f4ce988f69189f7e523337a3e11d91c786ded9360174a3d9ca83e79bc5396"}, ] urllib3 = [ - {file = "urllib3-1.25.10-py2.py3-none-any.whl", hash = "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"}, - {file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"}, + {file = "urllib3-1.25.11-py2.py3-none-any.whl", hash = "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e"}, + {file = "urllib3-1.25.11.tar.gz", hash = "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2"}, ] virtualenv = [ {file = "virtualenv-20.0.35-py2.py3-none-any.whl", hash = "sha256:0ebc633426d7468664067309842c81edab11ae97fcaf27e8ad7f5748c89b431b"}, From 5d8af25aa63cefe2357c9cc417d6c4123f0765e3 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 21 Oct 2020 16:58:20 +0200 Subject: [PATCH 090/142] Add initial version of chapter 08, part 1 --- 00_intro/00_content.ipynb | 2 +- 08_mfr/00_content.ipynb | 1377 +++++++++++++++++++++++++++++++++++++ CONTENTS.md | 7 + README.md | 1 + 4 files changed, 1386 insertions(+), 1 deletion(-) create mode 100644 08_mfr/00_content.ipynb diff --git a/00_intro/00_content.ipynb b/00_intro/00_content.ipynb index a5d9e15..b1c99a6 100644 --- a/00_intro/00_content.ipynb +++ b/00_intro/00_content.ipynb @@ -771,7 +771,7 @@ " - *Chapter 5*: [Numbers & Bits ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb)\n", " - *Chapter 6*: [Text & Bytes ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/00_content.ipynb)\n", " - *Chapter 7*: [Sequential Data ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb)\n", - " - *Chapter 8*: Map, Filter, & Reduce\n", + " - *Chapter 8*: [Map, Filter, & Reduce ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb)\n", " - *Chapter 9*: Mappings & Sets\n", " - *Chapter 10*: Arrays & Dataframes\n", "- How can we create custom data types?\n", diff --git a/08_mfr/00_content.ipynb b/08_mfr/00_content.ipynb new file mode 100644 index 0000000..4cf1aa7 --- /dev/null +++ b/08_mfr/00_content.ipynb @@ -0,0 +1,1377 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/00_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 8: Map, Filter, & Reduce" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In this chapter, we continue the study of sequential data by looking at memory efficient ways to process the elements in a sequence. That is an important topic for the data science practitioner who must be able to work with data that does *not* fit into a single computer's memory.\n", + "\n", + "As shown in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb#Containers-vs.-Iterables), both the `list` objects `[0, 1, 2, 3, 4]` and `[1, 3, 5, 7, 9]` on the one side and the `range` objects `range(5)` and `range(1, 10, 2)` on the other side allow us to loop over the same numbers. However, the latter two only create *one* `int` object in every iteration while the former two create *all* `int` objects before the loop even starts. In this aspect, we consider `range` objects to be \"rules\" in memory that know how to calculate the numbers *without* calculating them.\n", + "\n", + "In [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/01_content.ipynb#The-list-Type), we see how the built-in [list() ](https://docs.python.org/3/library/functions.html#func-list) constructor **materializes** the `range(1, 13)` object into the `list` object `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]`. In other words, we make `range(1, 13)` calculate *all* numbers at once and store them in a `list` object for further processing.\n", + "\n", + "In many cases, however, it is not necessary to do that, and, in this chapter, we look at other types of \"rules\" in memory and how we can compose different \"rules\" together to implement bigger computations.\n", + "\n", + "Next, we take a step back and continue with a simple example involving the familiar `numbers` list. Then, we iteratively exchange `list` objects with \"rule\"-like objects *without* changing the overall computation at all. As computations involving sequential data are commonly classified into three categories **map**, **filter**, or **reduce**, we do so too for our `numbers` example." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "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 the squares. In essence, we apply the transformation $y := x^2 + 1$ as expressed with the `transform()` function below." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "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 an initially empty `transformed_numbers` list." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "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": 4, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[50, 122, 65, 26, 10, 145, 5, 37, 82, 101, 2, 17]" + ] + }, + "execution_count": 4, + "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 exactly *one* positional argument and an `iterable` that provides the objects to be mapped.\n", + "\n", + "We call [map() ](https://docs.python.org/3/library/functions.html#map) with a reference to 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": 5, + "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": 6, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "transformer" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "map" + ] + }, + "execution_count": 7, + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "50" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(transformer)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "122" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(transformer)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "65" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(transformer)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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.\n", + "\n", + "If we are sure that a `map` object generates a *finite* number of elements, we may materialize them into a `list` object with the built-in [list() ](https://docs.python.org/3/library/functions.html#func-list) constructor. Below, we \"pull out\" the remaining `int` objects from `transformer`, which itself is derived from a *finite* `list` object." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[26, 10, 145, 5, 37, 82, 101, 2, 17]" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(transformer)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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": 12, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "transformed_numbers = list(map(transform, numbers))" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[50, 122, 65, 26, 10, 145, 5, 37, 82, 101, 2, 17]" + ] + }, + "execution_count": 13, + "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 (i.e., `True`) or not (i.e., `False`).\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": 14, + "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 `element % 2 == 0` is already a boolean expression, we could shorten `is_even()` like so." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "def is_even(element):\n", + " \"\"\"Filter out odd numbers.\"\"\"\n", + " return element % 2 == 0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As before, we first use a `for`-loop that appends the elements to be kept iteratively to an initially empty `even_numbers` list." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "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": 17, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[50, 122, 26, 10, 82, 2]" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "even_numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Analogously to the `map` object above, we use the [filter() ](https://docs.python.org/3/library/functions.html#filter) built-in to create an object of type `filter` and assign it to `evens`." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "evens = filter(is_even, transformed_numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "evens" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "filter" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(evens)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`evens` works like `transformer` above: With the built-in [next() ](https://docs.python.org/3/library/functions.html#next) function we 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": 21, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[50, 122, 65, 26, 10, 145, 5, 37, 82, 101, 2, 17]" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "transformed_numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "50" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(evens)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "122" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(evens)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "26" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(evens)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As above, we could create a materialized `list` object with the [list() ](https://docs.python.org/3/library/functions.html#func-list) constructor." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[50, 122, 26, 10, 82, 2]" + ] + }, + "execution_count": 25, + "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 `map` and `filter` objects derived from the original `numbers` list. As the entire cell is *one* big expression consisting of nested function calls, we read it from the inside out." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[50, 122, 26, 10, 82, 2]" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(\n", + " filter(\n", + " is_even,\n", + " map(transform, numbers),\n", + " )\n", + ")" + ] + }, + { + "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 in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_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 loop \"at the C level\" and are a lot faster because of that. Because of that, experienced Pythonistas tend to *not* use explicit `for`-loops so often." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[65, 145, 5, 37, 101, 17]" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(\n", + " map(\n", + " transform,\n", + " filter(is_even, numbers),\n", + " )\n", + ")" + ] + }, + { + "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": 28, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "370" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(\n", + " map(\n", + " transform,\n", + " filter(is_even, numbers),\n", + " )\n", + ")" + ] + }, + { + "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": 29, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "5" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "min(map(transform, filter(is_even, numbers)))" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "145" + ] + }, + "execution_count": 30, + "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 `sum_alt()`. Its implementation only adds two numbers." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def sum_alt(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": 32, + "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. The latter is captured separately as the initial `result` with the [next() ](https://docs.python.org/3/library/functions.html#next) function. We know from above 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). The first `210` is the sum of the first two elements generated by `evens_transformed`, `65` and `145`.\n", + "\n", + "So, we also learn that `map` objects, and analogously `filter` objects, are *iterable* as we may loop over them." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "210 215 252 353 370 " + ] + } + ], + "source": [ + "result = next(evens_transformed)\n", + "\n", + "for number in evens_transformed:\n", + " result = sum_alt(result, number)\n", + " print(result, end=\" \") # line added for didactical purposes" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The final `result` is the same `370` as above." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "370" + ] + }, + "execution_count": 34, + "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 (and speed) replacing the `for`-loop. It takes two arguments, `function` and `iterable`, 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 immediately, and the code cell evaluates to `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, one by one." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "from functools import reduce" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "370" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reduce(\n", + " sum_alt,\n", + " map(\n", + " transform,\n", + " filter(is_even, numbers),\n", + " )\n", + ")" + ] + }, + { + "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 `sum_alt()` 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 *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/develop/02_functions/00_content.ipynb#Anonymous-Functions), we use `lambda` expressions to create `function` objects *without* a name referencing them.\n", + "\n", + "So, the above `sum_alt()` function could be rewritten as a `lambda` expression like so ..." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(sum_so_far, next_number)>" + ] + }, + "execution_count": 37, + "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": 38, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(x, y)>" + ] + }, + "execution_count": 38, + "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": 39, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "370" + ] + }, + "execution_count": 39, + "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 materialized `list` object in memory at all!" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "370" + ] + }, + "execution_count": 40, + "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 could write the entire computation as *one* expression." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "370" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(\n", + " map(\n", + " lambda x: (x ** 2) + 1,\n", + " filter(\n", + " lambda x: x % 2 == 0,\n", + " range(1, 13),\n", + " )\n", + " )\n", + ")" + ] + }, + { + "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 and **3** `list` objects\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 and **no** `list` object\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 and **no** `list` object\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.\n", + "\n", + "The map-filter-reduce 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. The workings in the memory as shown in this section provide an idea why." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index bbc1dee..476808d 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -189,3 +189,10 @@ If this is not possible, (`namedtuple` Type) - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/06_summary.ipynb) - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/07_review.ipynb) + - *Chapter 8*: Map, Filter, & Reduce + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/00_content.ipynb) + (Mapping; + Filtering; + Reducing; + `lambda` Expression) \ No newline at end of file diff --git a/README.md b/README.md index e9b2add..23b5521 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ For a more *detailed version* with **clickable links** - *Chapter 5*: Numbers & Bits - *Chapter 6*: Text & Bytes - *Chapter 7*: Sequential Data + - *Chapter 8*: Map, Filter, & Reduce #### Videos From e63f5ae25a058635873544e2a0c1ace956dccc44 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 21 Oct 2020 17:47:42 +0200 Subject: [PATCH 091/142] Add initial version of chapter 08, part 2 --- 08_mfr/01_content.ipynb | 2380 +++++++++++++++++++++++++++++++++++++++ CONTENTS.md | 8 +- 2 files changed, 2387 insertions(+), 1 deletion(-) create mode 100644 08_mfr/01_content.ipynb diff --git a/08_mfr/01_content.ipynb b/08_mfr/01_content.ipynb new file mode 100644 index 0000000..2cf8720 --- /dev/null +++ b/08_mfr/01_content.ipynb @@ -0,0 +1,2380 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/01_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 8: Map, Filter, & Reduce (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "After introducing the Map-Filter-Reduce paradigm in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb) of this chapter, we first see how `list` comprehensions can replace the [map() ](https://docs.python.org/3/library/functions.html#map) and [filter() ](https://docs.python.org/3/library/functions.html#filter) built-ins in many cases. Then, we learn how `generator` expressions are like `list` comprehensions *without* using the memory. We end this part with a short discussion of 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." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## `list` Comprehensions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Consider again the original \"*A simple Filter*\" example from [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb#Example:-A-simple-Filter), re-written such that both the mapping and the filtering are done in *one* `for`-loop instead of the *two* above." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": 3, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "370" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(evens_transformed)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**`list` comprehensions** 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)). Practically, this means that we place the `for` and `if` inside brackets `[` and `]`.\n", + "\n", + "So, the example can be written in a single *expression* like below replacing the compound `for` *statement* above." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[65, 145, 5, 37, 101, 17]" + ] + }, + "execution_count": 4, + "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 always be used in a place where otherwise a `list` object would work.\n", + "\n", + "For example, let's rewrite the \"*A simple Filter*\" example from [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb#Example:-A-simple-Filter) in just one line. As a caveat, the code below *materializes* all elements in memory *before* summing them up, and may, therefore, cause a `MemoryError` when executed with a bigger `numbers` list. We see with [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) how a `list` object exists in memory at step 17 and then \"gets lost\" right after. As the next section shows, this downside may be mitigated." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "370" + ] + }, + "execution_count": 5, + "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 three other `list` objects with a series of numbers in them. The first and last numbers in each inner `list` object are offset by `1`." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "nested_numbers = [\n", + " [1, 2, 3, 4, 5, 6, 7],\n", + " [2, 3, 4, 5, 6, 7, 8],\n", + " [3, 4, 5, 6, 7, 8, 9],\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "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": 7, + "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 remove the inner layer of `list` objects and **flatten** (i.e., \"un-nest\") the data.\n", + "\n", + "Without list comprehensions, we achieve that with two nested `for`-loops." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "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": 9, + "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": 9, + "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 as above." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "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": 10, + "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 way we want.\n", + "\n", + "For example, to 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* with *all* its elements *before* the sum is calculated!" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "105" + ] + }, + "execution_count": 11, + "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": 12, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "105" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum([sum(inner_numbers) for inner_numbers in nested_numbers])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Example: Working with 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 $n$-tuple in the [Cartesian product ](https://en.wikipedia.org/wiki/Cartesian_product) created from elements of $n$ iterables. In the generic illustration below, a transformation $f(x, y)$ is applied to each $2$-tuple in the Cartesian product $X \\times Y$ where $x$ is an element in $X = \\{x_1, x_2, x_3\\}$ and $y$ is an element in $Y = \\{y_1, y_2, y_3\\}$." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "|$Y$ \\ $X$| $x_1$ | $x_2$ | $x_3$ |\n", + "|---------|--------------|--------------|--------------|\n", + "| $y_1$ |f($x_1$,$y_1$)|f($x_2$,$y_1$)|f($x_3$,$y_1$)|\n", + "| $y_2$ |f($x_1$,$y_2$)|f($x_2$,$y_2$)|f($x_3$,$y_2$)|\n", + "| $y_3$ |f($x_1$,$y_3$)|f($x_2$,$y_3$)|f($x_3$,$y_3$)|" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For example, let's add each quotient obtained by taking the numerator $x$ from `[10, 20, 30]` and the denominator $y$ from `[40, 50, 60]` to `1`, and then find the overall product. This transformation can be described mathematically as the function $z = f(x, y)$ below." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "$$z = f(x, y) = \\prod{ (1 + \\frac{x}{y} )}$$" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Further, the table below visualizes the calculations: The result is the product of *nine* entries." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "|`y` \\ `x`|**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 |" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To express that in Python, we start by creating two `list` objects, `first` and `second`." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "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 must loop over *all* possible $2$-tuples where one element is drawn from `first` and the other from `second`. We achieve that with two nested `for`-loops, in which we calculate each `quotient` and append it to an initially empty `cartesian_product` list." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1.25, 1.2, 1.1666666666666667, 1.5, 1.4, 1.3333333333333333, 1.75, 1.6, 1.5]" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cartesian_product = []\n", + "\n", + "for numerator in first:\n", + " for denominator in second:\n", + " quotient = 1 + (numerator / denominator)\n", + " cartesian_product.append(quotient)\n", + "\n", + "cartesian_product" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Next, we convert the two explicit `for`-loops into one list comprehensions and use `x` and `y` as shorter variable names." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1.25, 1.2, 1.1666666666666667, 1.5, 1.4, 1.3333333333333333, 1.75, 1.6, 1.5]" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[1 + (x / y) 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": 16, + "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": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[1 + (x / y) 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 list comprehension into the `product()` function from the \"*Function Definitions & Calls*\" sub-section in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/03_content.ipynb#Function-Definitions-&-Calls)." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "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": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "20.58" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "product(*[1 + (x / y) 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": 19, + "metadata": {}, + "outputs": [], + "source": [ + "from functools import reduce" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "20.58" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reduce(lambda x, y: x * y, [1 + (x / y) 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": [ + "Because of the high memory consumption, Pythonistas avoid materialized `list` objects, and, thus, also `list` comprehensions, whenever possible. Instead, they prefer to work with **[`generator` expressions ](https://docs.python.org/3/reference/expressions.html#generator-expressions)**. Syntactically, they work like list comprehensions except that parentheses, `(` and `)`, replace brackets, `[` and `]`.\n", + "\n", + "Let's go back to the original \"*A simple Filter*\" example from [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb#Example:-A-simple-Filter) one more time, apply the transformation $y := x^2 + 1$ to all even `numbers`, and sum them up." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "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 a list comprehension above ..." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[65, 145, 5, 37, 101, 17]" + ] + }, + "execution_count": 22, + "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": 23, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + " at 0x7fcf703d84a0>" + ] + }, + "execution_count": 23, + "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 `generator` expression evaluates to yet another \"rule\"-like object 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 true for `generator` expressions: *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", + "To materialize the elements specified by a `generator` expression, we use the [list() ](https://docs.python.org/3/library/functions.html#func-list) constructor as seen above." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[65, 145, 5, 37, 101, 17]" + ] + }, + "execution_count": 24, + "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 in a function call, we may merge the double parentheses, `((` and `))`, into just `(` and `)`." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[65, 145, 5, 37, 101, 17]" + ] + }, + "execution_count": 25, + "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) as in the original \"*A simple Filter*\" example. [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 do so (cf., step 17)." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "370" + ] + }, + "execution_count": 26, + "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 to which the `generator` expression below evaluates to to a variable `gen` and inspect it." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "gen = ((n ** 2) + 1 for n in numbers if n % 2 == 0)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + " at 0x7fcf703d8c10>" + ] + }, + "execution_count": 28, + "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": 29, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "generator" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(gen)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`generator` objects work just like the `map` and `filter` objects in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb) of this chapter. So, with the [next() ](https://docs.python.org/3/library/functions.html#next) function, we can generate elements one by one." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "65" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(gen)" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "145" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(gen)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "5" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(gen)" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "37" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(gen)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "101" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next(gen)" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "17" + ] + }, + "execution_count": 35, + "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, and we say that the `generator` object is **exhausted**. To loop over its elements again, we must create a *new* one." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "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": 37, + "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*. We look into this in detail further below in the \"*The `for` Statement (revisited)*\" section." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "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 (continued)" + ] + }, + { + "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 above, we should replace `list` objects or `list` comprehensions with `generator` expressions wherever possible. The result is the *same*, but no intermediate `list` objects are materialized! That makes our code scale to large amounts of data.\n", + "\n", + "Let's adapt the example `nested_numbers` from above." + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "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": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested_numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Compared to the implementation with the list comprehension above, we simply remove the brackets, `[` and `]`: The argument to [sum() ](https://docs.python.org/3/library/functions.html#sum) becomes a generator expression." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "105" + ] + }, + "execution_count": 40, + "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": 41, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "105" + ] + }, + "execution_count": 41, + "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 (i.e., the inner `list` objects are series of consecutive `int` objects), we can even provide an effectively **memoryless** implementation 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` objects materialize at any point in time." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "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": 43, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + " at 0x7fcf70347970>" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nested_numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "105" + ] + }, + "execution_count": 44, + "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": 45, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(sum(inner_numbers) for inner_numbers in nested_numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Example: Working with Cartesian Products (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's also rewrite the \"*Working with 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": 46, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "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 alternative solutions may be more appealing to many readers. In general, many practitioners seem to dislike `lambda` expressions.\n", + "\n", + "In the first solution, we *unpack* the elements produced by `(1 + (x / y) for x in first for y in second)` into the `product()` function from the \"*Function Definitions & Calls*\" sub-section in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/03_content.ipynb#Function-Definitions-&-Calls). However, inside `product()`, the elements are *packed* into `args`, a *materialized* `tuple` object! So, all the memory efficiency gained by using a generator expression is lost! [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*%281%20%2B%20%28x%20/%20y%29%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": 47, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "20.58" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "product(*(1 + (x / y) for x in first for y in second)) # not memory efficient!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "On the contrary, the second 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* the 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%281%20%2B%20%28x%20/%20y%29%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": 48, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "20.58" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reduce(lambda x, y: x * y, (1 + (x / y) 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` objects *may* be a *bad* idea." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Example: Averaging all even Numbers in a List (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 all even Numbers in a List*\" example from [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List) such that it efficiently handles a large sequence of numbers. We continue from its latest implementation, the `average_evens()` function in the \"*Keyword-only Arguments*\" section in [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb#Keyword-only-Arguments).\n", + "\n", + "We assume that `average_evens()` is called with a *finite* and *iterable* object that generates a **stream** of numeric objects that can be cast as `int` objects. After all, the idea of even and odd numbers makes sense only in the context of whole numbers.\n", + "\n", + "The `generator` expression `(round(n) for n in numbers)` implements the type casting, and, when it is evaluated during a function call, *nothing* happens except that a `generator` object is assigned to `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 with the `generator` object to which the inner `generator` expression `((n, 1) for n in integers if n % 2 == 0)` evaluates to. That `generator` object takes the `integers` generator as its source and produces `tuple` objects consisting of the next *even* number in line and `1`. Two such `tuple` objects are then iteratively passed to the `function` object to which the `lambda` expression evaluates to. `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 calling [reduce() ](https://docs.python.org/3/library/functools.html#functools.reduce) is again a `tuple` object, namely the final `total` and `count`. Lastly, we calculate the simple average and scale it.\n", + "\n", + "In summary, this implementation of `average_evens()` does *not* keep materialized `list` objects internally like its predecessors in [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb) but processes the elements of the `numbers` argument on a one-by-one basis." + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def average_evens(numbers, *, scalar=1):\n", + " \"\"\"Calculate the average of all even numbers.\n", + "\n", + " Args:\n", + " numbers (iterable): a finite stream of the numbers to be averaged;\n", + " if non-whole numbers are provided, they are rounded\n", + " scalar (float, optional): multiplies the average; defaults to 1\n", + "\n", + " Returns:\n", + " float: (scaled) average\n", + " \"\"\"\n", + " integers = (round(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 scalar * total / count" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7.0" + ] + }, + "execution_count": 50, + "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": [ + "We may provide an optional `scalar` argument as before." + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "14.0" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "average_evens([7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4], scalar=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "An argument with `float` objects works just as well." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "7.0" + ] + }, + "execution_count": 52, + "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 large 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` object in memory. The result approaching `50` confirms that [randint() ](https://docs.python.org/3/library/random.html#random.randint) must be based on a uniform distribution." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import random" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(42)" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "49.994081434519636" + ] + }, + "execution_count": 55, + "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": 56, + "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, scalar)\u001b[0m\n\u001b[1;32m 11\u001b[0m \"\"\"\n\u001b[1;32m 12\u001b[0m \u001b[0mintegers\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mround\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mn\u001b[0m \u001b[0;32min\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;32m---> 13\u001b[0;31m total, count = reduce(\n\u001b[0m\u001b[1;32m 14\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[1;32m 15\u001b[0m \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[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": "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 built-in [tuple() ](https://docs.python.org/3/library/functions.html#func-tuple) constructor 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": 57, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(65, 145, 5, 37, 101, 17)" + ] + }, + "execution_count": 57, + "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": 58, + "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": 59, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "all(x ** 2 < 100 for x in numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 60, + "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 explained in [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb#Short-Circuiting), and we mimic that by testing for the *opposite* condition in the `if` statement and stopping 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` argument, 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": 61, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 61, + "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 in a `for`-loop's body inside a function, 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": 62, + "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": 63, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "all_alt(x ** 2 < 100 for x in numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 64, + "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": 65, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "any(x ** 2 > 100 for x in numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "any(x ** 2 > 150 for x in numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The code cell below shows how [any() ](https://docs.python.org/3/library/functions.html#any) works internally: It also follows the *short-circuiting* strategy. Here, we do *not* need to check for the opposite condition." + ] + }, + { + "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": [ + "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 and also uses the early exit pattern." + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "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": 69, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "any_alt(x ** 2 > 100 for x in numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "any_alt(x ** 2 > 150 for x in numbers)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index 476808d..ac3e1db 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -195,4 +195,10 @@ If this is not possible, (Mapping; Filtering; Reducing; - `lambda` Expression) \ No newline at end of file + `lambda` Expression) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/01_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/01_content.ipynb) + (`list` Comprehension; + `generator` Expression; + Streams of Data; + Boolean Reducers) \ No newline at end of file From f40c74f89e869826ea8131980a179a1bb89cd5e6 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 21 Oct 2020 18:46:56 +0200 Subject: [PATCH 092/142] Explain what files are used and where they are --- CONTENTS.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CONTENTS.md b/CONTENTS.md index ac3e1db..6291fb2 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -2,6 +2,21 @@ The materials are designed to resemble an *interactive* book. +The files come + primarily in the [Jupyter Notebook](https://jupyter-notebook.readthedocs.io/en/stable/) + format (i.e., \*.ipynb) + but also as [modules and packages ](https://docs.python.org/3/tutorial/modules.html) + (i.e., \*.py). +Together with some other static files (e.g., images), + they are stored in one folder per chapter in this repository. +They are to be opened + from within the [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) application, + even though other ways are certainly possible as well. +Both the files and the folders + are appropriately named with prefixes + indicating the order in which they should be read + and starting with "00_". + It is recommended to follow the [installation instructions](https://github.com/webartifex/intro-to-python#installation) in the [README.md](README.md) file From f4f2cf476e74334e04dcaef54b4079f03952a4e1 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 21 Oct 2020 18:48:59 +0200 Subject: [PATCH 093/142] Add initial version of chapter 08's exercises, part 1 --- 08_mfr/02_exercises.ipynb | 760 ++++++++++++++++++++++++++++++++++++++ 08_mfr/stream.py | 51 +++ CONTENTS.md | 5 +- 3 files changed, 815 insertions(+), 1 deletion(-) create mode 100644 08_mfr/02_exercises.ipynb create mode 100644 08_mfr/stream.py diff --git a/08_mfr/02_exercises.ipynb b/08_mfr/02_exercises.ipynb new file mode 100644 index 0000000..0911aaf --- /dev/null +++ b/08_mfr/02_exercises.ipynb @@ -0,0 +1,760 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/02_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 8: Map, Filter, & Reduce (Coding Exercises)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb) and [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/01_content.ipynb) part of Chapter 8.\n", + "\n", + "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Removing Outliers in Streaming Data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's say we are given a `list` object with random integers like `sample` below, and we want to calculate some basic statistics on them." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sample = [\n", + " 45, 46, 40, 49, 36, 53, 49, 42, 25, 40, 39, 36, 38, 40, 40, 52, 36, 52, 40, 41,\n", + " 35, 29, 48, 43, 42, 30, 29, 33, 55, 33, 38, 50, 39, 56, 52, 28, 37, 56, 45, 37,\n", + " 41, 41, 37, 30, 51, 32, 23, 40, 53, 40, 45, 39, 99, 42, 34, 42, 34, 39, 39, 53,\n", + " 43, 37, 46, 36, 45, 42, 32, 38, 57, 34, 36, 44, 47, 51, 46, 39, 28, 40, 35, 46,\n", + " 41, 51, 41, 23, 46, 40, 40, 51, 50, 32, 47, 36, 38, 29, 32, 53, 34, 43, 39, 41,\n", + " 40, 34, 44, 40, 41, 43, 47, 57, 50, 42, 38, 25, 45, 41, 58, 37, 45, 55, 44, 53,\n", + " 82, 31, 45, 33, 32, 39, 46, 48, 42, 47, 40, 45, 51, 35, 31, 46, 40, 44, 61, 57,\n", + " 40, 36, 35, 55, 40, 56, 36, 35, 86, 36, 51, 40, 54, 50, 49, 36, 41, 37, 48, 41,\n", + " 42, 44, 40, 43, 51, 47, 46, 50, 40, 23, 40, 39, 28, 38, 42, 46, 46, 42, 46, 31,\n", + " 32, 40, 48, 27, 40, 40, 30, 32, 25, 31, 30, 43, 44, 29, 45, 41, 63, 32, 33, 58,\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "len(sample)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: `list` objects are **sequences**. What *four* behaviors do they always come with?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: Write a function `mean()` that calculates the simple arithmetic mean of a given `sequence` with numbers!\n", + "\n", + "Hints: You can solve this task with [built-in functions ](https://docs.python.org/3/library/functions.html) only. A `for`-loop is *not* needed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def mean(sequence):\n", + " ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sample_mean = mean(sample)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sample_mean" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: Write a function `std()` that calculates the [standard deviation ](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": [ + "**Q4**: 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", + "**Q5.1**: Rewrite `std()` to take an optional keyword-only argument `seq_mean`, defaulting to `None`. If provided, `seq_mean` is used instead of the result of calling `mean()`. Otherwise, the latter is called.\n", + "\n", + "Hint: You must check if `seq_mean` is still the default value." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def std(sequence, *, seq_mean=None):\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`std()` continues to work as before." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sample_std = std(sample)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sample_std" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5.2**: Now, rewrite `standardize()` to pass on the return value of `mean()` to `std()`! In summary, `standardize()` calculates the z-scores for the numbers in the `sequence` with as few computational steps as possible." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def standardize(sequence, *, digits=3):\n", + " ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "z_scores = standardize(sample)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mean(z_scores), std(z_scores)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: With both `sample` and `z_scores` being materialized `list` objects, we can loop over pairs consisting of a number from `sample` and its corresponding z-score. Write a `for`-loop that prints out all the \"outliers,\" as which we define numbers with an absolute z-score above `1.96`. There are *four* of them in the `sample`.\n", + "\n", + "Hint: Use the [abs() ](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/develop/08_mfr/stream.py) file in the repository)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stream import data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`data` is of type `generator` and has *no* length." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "type(data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "len(data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So, the only thing we can do with it is to pass it to the built-in [next() ](https://docs.python.org/3/library/functions.html#next) function and go over the numbers it streams one by one." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "next(data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: What happens if you call `mean()` with `data` as the argument? What is the problem?\n", + "\n", + "Hints: If you try it out, you may have to press the \"Stop\" button in the toolbar at the top. Your computer should *not* crash, but you will *have to* restart this Jupyter notebook with \"Kernel\" > \"Restart\" and import `data` again." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mean(data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: Write a function `take_sample()` that takes an `iterable` as its argument, like `data`, and creates a *materialized* `list` object out of its first `n` elements, defaulting to `1_000`!\n", + "\n", + "Hints: [next() ](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(iterable, *, n=1_000):\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We take a `new_sample` from the stream of `data`, and its statistics are similar to the initial `sample`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "new_sample = take_sample(data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "len(new_sample)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mean(new_sample)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "std(new_sample)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: Convert `standardize()` into a *new* function `standardized()` that implements the *same* logic but works on a possibly *infinite* stream of data, provided as an `iterable`, instead of a *finite* `sequence`.\n", + "\n", + "To calculate a z-score, we need the stream's overall mean and standard deviation, and that is *impossible* to calculate if we do not know how long the stream is, and, in particular, if it is *infinite*. So, `standardized()` first takes a sample from the `iterable` internally, and uses the sample's mean and standard deviation to calculate the z-scores.\n", + "\n", + "Hint: `standardized()` *must* return a `generator` object. So, use a `generator` expression as the return value; unless you know about the `yield` statement already (cf., [reference ](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": [ + "**Q10.1**: `standardized()` allows us to go over an *infinite* stream of z-scores. What we want to do instead is to loop over the stream's raw numbers and skip the outliers. In the remainder of this exercise, you look at the parts that make up the `skip_outliers()` function below to achieve precisely that.\n", + "\n", + "The first steps in `skip_outliers()` are the same as in `standardized()`: We take a `sample` from the stream of `data` and calculate its statistics." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sample = ...\n", + "seq_mean = ...\n", + "seq_std = ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10.2**: Just as in `standardized()`, write a `generator` expression that produces z-scores one by one! However, instead of just generating a z-score, the resulting `generator` object should produce `tuple` objects consisting of a \"raw\" number from `data` and its z-score.\n", + "\n", + "Hint: Look at the revisited \"*Averaging Even Numbers*\" example in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/01_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List-%28revisited%29) for some inspiration, which also contains a `generator` expression producing `tuple` objects." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "standardizer = (... for ... in data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`standardizer` should produce `tuple` objects." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "next(standardizer)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10.3**: Write another `generator` expression that loops over `standardizer`. It contains an `if`-clause that keeps only numbers with an absolute z-score below the `threshold_z`. If you fancy, use `tuple` unpacking." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "threshold_z = 1.96" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "no_outliers = (... for ... in standardizer if ...)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`no_outliers` should produce `int` objects." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "next(no_outliers)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10.4**: Lastly, put everything together in the `skip_outliers()` function! Make sure you refer to `iterable` inside the function and not the global `data`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def skip_outliers(iterable, *, threshold_z=1.96):\n", + " sample = ...\n", + " seq_mean = ...\n", + " seq_std = ...\n", + " standardizer = ...\n", + " no_outliers = ...\n", + " return no_outliers" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, we can create a `generator` object and loop over the `data` in the stream with outliers skipped. Instead of the default `1.96`, we use a `threshold_z` of only `0.05`: That filters out all numbers except `42`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "skipper = skip_outliers(data, threshold_z=0.05)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "skipper" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "type(skipper)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "next(skipper)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: You implemented the functions `mean()`, `std()`, `standardize()`, `standardized()`, and `skip_outliers()`. Which of them are **eager**, and which are **lazy**? How do these two concepts relate to **finite** and **infinite** data?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/08_mfr/stream.py b/08_mfr/stream.py new file mode 100644 index 0000000..6aa6cc7 --- /dev/null +++ b/08_mfr/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() diff --git a/CONTENTS.md b/CONTENTS.md index 6291fb2..c377dbd 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -216,4 +216,7 @@ If this is not possible, (`list` Comprehension; `generator` Expression; Streams of Data; - Boolean Reducers) \ No newline at end of file + Boolean Reducers) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/02_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/02_exercises.ipynb) + (Removing Outliers in Streaming Data) \ No newline at end of file From 7ca4754140741034c538728eb36e72caa8dbf91e Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 21 Oct 2020 19:03:56 +0200 Subject: [PATCH 094/142] Add initial version of chapter 08's exercises, part 2 --- 08_mfr/03_exercises.ipynb | 455 ++++++++++++++++++++++++++++++++++++++ CONTENTS.md | 5 +- 2 files changed, 459 insertions(+), 1 deletion(-) create mode 100644 08_mfr/03_exercises.ipynb diff --git a/08_mfr/03_exercises.ipynb b/08_mfr/03_exercises.ipynb new file mode 100644 index 0000000..b93761a --- /dev/null +++ b/08_mfr/03_exercises.ipynb @@ -0,0 +1,455 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/03_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 8: Map, Filter, & Reduce (Coding Exercises)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb) and [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/01_content.ipynb) part of Chapter 8.\n", + "\n", + "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Packing & Unpacking with Functions (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: Copy your solution to **Q10** from the \"*Packing & Unpacking with Functions*\" exercise in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/04_exercises.ipynb) into the code cell below!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import collections.abc as abc" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def product(*args, ...):\n", + " \"\"\"Multiply all arguments.\"\"\"\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + "\n", + " ...\n", + " ...\n", + "\n", + " return ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: Verify that all test cases below work (i.e., the `assert` statements must *not* raise an `AssertionError`)!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert product(42) == 42" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert product(2, 5, 10) == 100" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert product(2, 5, 10, start=2) == 200" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "one_hundred = [2, 5, 10]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert product(one_hundred) == 100" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert product(*one_hundred) == 100" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: Verify that `product()` raises a `TypeError` when called without any arguments!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This implementation of `product()` is convenient to use, in particular, because we can pass it any *collection* object with or without *unpacking* it.\n", + "\n", + "However, `product()` suffers from one last flaw: We cannot pass it a **stream** of data, as modeled, for example, with a `generator` object that produces elements on a one-by-one basis.\n", + "\n", + "**Q4**: Click through the following code cells and observe what they do!\n", + "\n", + "The [*stream.py* ](https://github.com/webartifex/intro-to-python/blob/develop/08_mfr/stream.py) module in the book's repository provides a `make_finite_stream()` function. It is a *factory* function creating objects of type `generator` that we use to model *streaming* data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from stream import make_finite_stream" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "data = make_finite_stream()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "type(data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`generator` objects are good for only *one* thing: Giving us the \"next\" element in a series of possibly *infinitely* many objects. While the `data` object is finite (i.e., execute the next code cell until you see a `StopIteration` exception), ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "next(data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "... it has *no* concept of a \"length:\" The built-in [len() ](https://docs.python.org/3/library/functions.html#len) function raises a `TypeError`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "len(data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can use the built-in [list() ](https://docs.python.org/3/library/functions.html#func-list) constructor to *materialize* all elements. However, in a real-world scenario, these may *not* fit into our machine's memory! If you get an empty `list` object below, you have to create a *new* `data` object above again." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list(data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To be more realistic, `make_finite_stream()` creates `generator` objects producing a varying number of elements." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list(make_finite_stream())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list(make_finite_stream())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "list(make_finite_stream())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's see what happens if we pass a `generator` object, as created by `make_finite_stream()`, instead of a materialized *collection*, like `one_hundred`, to `product()`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(make_finite_stream())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: What line causes the `TypeError`? What line is really the problem in `product()`? Hint: These may be different lines. Describe what happens on each line in the function's body until the exception is raised!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: Adapt `product()` one last time to make it work with `generator` objects, or more generallz *iterators*, as well!\n", + "\n", + "Hints: This task is as easy as replacing `Collection` with something else. Which of the three behaviors of *collections* do `generator` objects also exhibit? You may want to look at the documentations on the built-in [max() ](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", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + "\n", + " ...\n", + " ...\n", + "\n", + " return ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The final version of `product()` behaves like built-ins in edge cases (i.e., `sum()` also raises a `TypeError` when called without arguments), ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "... works with the arguments passed either separately as *positional* arguments, *packed* together into a single *collection* argument, or *unpacked*, ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(42)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(2, 5, 10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product([2, 5, 10])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(*[2, 5, 10])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "... and can handle *streaming* data with *indefinite* \"length.\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "product(make_finite_stream())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In real-world projects, the data science practitioner must decide if it is worthwhile to make a function usable in various different forms as we do in this exercise. This may be over-engineered.\n", + "\n", + "Yet, two lessons are important to take away:\n", + "- It is a good idea to *mimic* the behavior of *built-ins* when accepting arguments, and\n", + "- make functions capable of working with *streaming* data." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index c377dbd..971c890 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -219,4 +219,7 @@ If this is not possible, Boolean Reducers) - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/02_exercises.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/02_exercises.ipynb) - (Removing Outliers in Streaming Data) \ No newline at end of file + (Removing Outliers in Streaming Data) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/03_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/03_exercises.ipynb) + (Packing & Unpacking with Functions, continued) \ No newline at end of file From dc03b7ad64e8437bef1006b4979f9073cebfed2c Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 21 Oct 2020 19:17:43 +0200 Subject: [PATCH 095/142] Add initial version of chapter 08, part 3 --- 08_mfr/04_content.ipynb | 1672 +++++++++++++++++++++++++++++++++++++++ CONTENTS.md | 6 +- 2 files changed, 1677 insertions(+), 1 deletion(-) create mode 100644 08_mfr/04_content.ipynb diff --git a/08_mfr/04_content.ipynb b/08_mfr/04_content.ipynb new file mode 100644 index 0000000..ff55376 --- /dev/null +++ b/08_mfr/04_content.ipynb @@ -0,0 +1,1672 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/04_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 8: Map, Filter, & Reduce" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Similarly to how we classify different *concrete* data types like `list` or `str` by how they behave *abstractly* in a given context in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb#Collections-vs.-Sequences), we also do so for the data types we have introduced in this chapter." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Iterators vs. Iterables" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Here, the `map`, `filter`, and `generator` types all behave like \"rules\" in memory that govern how objects are produced \"on the fly.\" Their main commonality is their support for 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 in Python.\n", + "\n", + "So, one example of an iterator is `evens_transformed` below, an object of type `generator`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers = [7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "evens_transformed = ((x ** 2) + 1 for x in numbers if x % 2 == 0)" + ] + }, + { + "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": 3, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import collections.abc as abc" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 4, + "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 is *not* true! To be precise, iterators are *specializations* of iterables. That is what the \"Inherits from\" column means in the [collections.abc ](https://docs.python.org/3/library/collections.abc.html) module's documentation." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(evens_transformed, abc.Iterable)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Furthermore, we sharpen our definition of an *iterable* from [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb#Collections-vs.-Sequences): Just as we define an *iterator* to be any object that supports the [next() ](https://docs.python.org/3/library/functions.html#next) function, we define an *iterable* to be any 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. Then, the *iterator* object manages the iteration over the *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. \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": 6, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "iterator1 = iter(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": 8, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "list_iterator" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(iterator1)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "*Iterators* are useful for only *one* thing: 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": 9, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(7, 11, 8)" + ] + }, + "execution_count": 9, + "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 loop over the same *iterable* several times in parallel." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(5, 7)" + ] + }, + "execution_count": 10, + "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": 11, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "numbers[1], numbers[4] = 99, 99" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(99, 99)" + ] + }, + "execution_count": 12, + "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. After that, 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 work by simply keeping track of the *last* index obtained from the underlying *iterable*." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "numbers[:] = list(range(1, 13))" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(6, 3)" + ] + }, + "execution_count": 15, + "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 may 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": 16, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "zipper = zip(iterator1, iterator2)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "zipper" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "zip" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(zipper)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 19, + "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) in a `for`-loop. 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": 20, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "zipper_iterator = iter(zipper)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "zipper_iterator" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`zipper_iterator` references the *same* object as `zipper`! That is true 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": 22, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 22, + "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 only *six* more `tuple` objects derived from the now ordered `numbers` because the `iterator1` object hidden inside `zipper` already returned its first *six* elements. So, the respective first elements of the `tuple` objects printed range from `7` to `12`. Similarly, as `iterator2` already returned its first *three* elements from `numbers`, we see the respective second elements in the range from `4` to `9`." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "7 and 4 8 and 5 9 and 6 10 and 7 11 and 8 12 and 9 " + ] + } + ], + "source": [ + "for x, y in zipper:\n", + " print(x, \"and\", 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": 24, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "for x, y in zipper:\n", + " raise RuntimeError(\"We won't see this error\")" + ] + }, + { + "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": 25, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "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": 26, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "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. So, a beginner should regularly review this section until it becomes second nature." + ] + }, + { + "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/develop/04_iteration/02_content.ipynb#The-for-Statement), we argue that the `for` statement is syntactic sugar, replacing the `while` statement in many common 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* being looped over." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "iterable = [0, 1, 2, 3, 4]" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": 29, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 1 2 3 4 " + ] + } + ], + "source": [ + "index = 0\n", + "\n", + "while index < len(iterable):\n", + " element = iterable[index]\n", + " print(element, end=\" \")\n", + " index += 1\n", + "\n", + "del index" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "What actually 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 as the argument. The returned `iterator` 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 stops the `while`-loop with the `break` statement." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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=\" \")\n", + "\n", + "del iterator" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## sorted() vs. reversed()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Now that we know the concept of an *iterator*, let's compare some of the built-ins introduced in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb) in detail and make sure we understand what is going on in memory. This section also serves as a summary of all the concepts in this chapter.\n", + "\n", + "We use two simple examples, `numbers` and `memoryless`: `numbers` creates *thirteen* objects in memory and `memoryless` only *one* (cf., [PythonTutor ](http://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": 31, + "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": 32, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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* `iterable` 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 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://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": 33, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sorted(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" + ] + }, + "execution_count": 34, + "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://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": 35, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sorted(numbers, reverse=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]" + ] + }, + "execution_count": 36, + "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": 37, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "range(1, 13)" + ] + }, + "execution_count": 38, + "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` 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://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": 39, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "reversed(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 40, + "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) constructors. That creates *new* `list` and `tuple` objects (cf., [PythonTutor ](http://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": 41, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[4, 1, 10, 9, 6, 2, 12, 3, 5, 8, 11, 7]" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(reversed(numbers))" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)" + ] + }, + "execution_count": 42, + "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 *iterable*; 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": 43, + "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": 44, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": 45, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "range(1, 13)" + ] + }, + "execution_count": 46, + "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": 47, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sorted(numbers, reverse=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[4, 1, 10, 9, 6, 2, 12, 3, 5, 8, 11, 7]" + ] + }, + "execution_count": 49, + "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 in JupyterLab." + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `reverse()` method on the `list` type is *eager*, as opposed to the *lazy* [reversed() ](https://docs.python.org/3/library/functions.html#reversed) built-in. That means the mutations caused by the `reverse()` method are written into memory right away." + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "numbers.reverse()" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[4, 1, 10, 9, 6, 2, 12, 3, 5, 8, 11, 7]" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "*Sorting* `numbers` in place occurs eagerly." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "numbers.sort(reverse=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "numbers.sort()" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index 971c890..1309419 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -222,4 +222,8 @@ If this is not possible, (Removing Outliers in Streaming Data) - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/03_exercises.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/03_exercises.ipynb) - (Packing & Unpacking with Functions, continued) \ No newline at end of file + (Packing & Unpacking with Functions, continued) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/04_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/04_content.ipynb) + (Iterators vs. Iterables; + Example: `sorted()` vs. `reversed()`) \ No newline at end of file From 4d5b23ff03234d181688afe66c11b1dbb82c5871 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 21 Oct 2020 19:19:20 +0200 Subject: [PATCH 096/142] Add initial version of chapter 08's summary --- 08_mfr/05_summary.ipynb | 75 +++++++++++++++++++++++++++++++++++++++++ CONTENTS.md | 3 +- 2 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 08_mfr/05_summary.ipynb diff --git a/08_mfr/05_summary.ipynb b/08_mfr/05_summary.ipynb new file mode 100644 index 0000000..361fbd0 --- /dev/null +++ b/08_mfr/05_summary.ipynb @@ -0,0 +1,75 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 8: Map, Filter, & Reduce (TL;DR)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The operations we do with sequential data commonly follow the **map-filter-reduce paradigm**: We apply the same transformation to all elements, filter some of them out, and calculate summary statistics from the remaining ones.\n", + "\n", + "An essential idea in this chapter is that, in many situations, we need *not* have all the data **materialized** in memory. Instead, **iterators** allow us to process sequential data on a one-by-one basis.\n", + "\n", + "Examples for iterators are the `map`, `filter`, and `generator` types." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index 1309419..332a42f 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -226,4 +226,5 @@ If this is not possible, - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/04_content.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/04_content.ipynb) (Iterators vs. Iterables; - Example: `sorted()` vs. `reversed()`) \ No newline at end of file + Example: `sorted()` vs. `reversed()`) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/05_summary.ipynb) \ No newline at end of file From b1a55631147e8f4f6aa055265e4ba88d4bb40a51 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 21 Oct 2020 19:22:08 +0200 Subject: [PATCH 097/142] Add initial version of chapter 08's review --- 08_mfr/06_review.ipynb | 214 +++++++++++++++++++++++++++++++++++++++++ CONTENTS.md | 3 +- 2 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 08_mfr/06_review.ipynb diff --git a/08_mfr/06_review.ipynb b/08_mfr/06_review.ipynb new file mode 100644 index 0000000..2f3d29f --- /dev/null +++ b/08_mfr/06_review.ipynb @@ -0,0 +1,214 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 8: Map, Filter, & Reduce (Review Questions)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb), [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/01_content.ipynb), and [third ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/04_content.ipynb) part in Chapter 8.\n", + "\n", + "Be concise in your answers! Most questions can be answered in *one* sentence." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Essay Questions " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: With the [map() ](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": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: Looking at the `lambda` expression inside [reduce() ](https://docs.python.org/3/library/functools.html#functools.reduce) below, what [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": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: What is the primary use case of **`list` comprehensions**? Why do we describe them as **eager**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: **`generator` expressions** may replace `list` objects and list comprehensions in many scenarios. When evaluated, they create a **lazy** `generator` object that does *not* **materialize** its elements right away. What do we mean by that? What does it mean for a `generator` object to be **exhausted**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: What does it mean for the **boolean reducers**, the built-in [all() ](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": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: What is an **iterator**? How does it relate to an **iterable**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## True / False Questions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Motivate your answer with *one short* sentence!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: `lambda` expressions are useful in the context of the **map-filter-reduce** paradigm, where we often do *not* re-use a `function` object more than once." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: Using **`generator` expressions** in place of **`list` comprehensions** wherever possible is a good practice as it makes our programs use memory more efficiently." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: Just as **`list` comprehensions** create `list` objects, **`tuple` comprehensions** create `tuple` objects." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index 332a42f..bfa7f76 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -227,4 +227,5 @@ If this is not possible, [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/04_content.ipynb) (Iterators vs. Iterables; Example: `sorted()` vs. `reversed()`) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/05_summary.ipynb) \ No newline at end of file + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/05_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/06_review.ipynb) From 814069e7fbfc31eaca0985f2e584a53e6642f3bb Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 21 Oct 2020 19:24:51 +0200 Subject: [PATCH 098/142] Streamline slides --- 08_mfr/00_content.ipynb | 6 +++++- 08_mfr/01_content.ipynb | 8 ++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/08_mfr/00_content.ipynb b/08_mfr/00_content.ipynb index 4cf1aa7..1884515 100644 --- a/08_mfr/00_content.ipynb +++ b/08_mfr/00_content.ipynb @@ -329,7 +329,11 @@ { "cell_type": "code", "execution_count": 11, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { diff --git a/08_mfr/01_content.ipynb b/08_mfr/01_content.ipynb index 2cf8720..688d953 100644 --- a/08_mfr/01_content.ipynb +++ b/08_mfr/01_content.ipynb @@ -700,7 +700,11 @@ { "cell_type": "code", "execution_count": 19, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [], "source": [ "from functools import reduce" @@ -711,7 +715,7 @@ "execution_count": 20, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "-" } }, "outputs": [ From a52178732a67f51f84c260f603426ffd2528d732 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 22 Oct 2020 18:19:34 +0200 Subject: [PATCH 099/142] Add initial version of chapter 09, part 1 --- 00_intro/00_content.ipynb | 2 +- 09_mappings/00_content.ipynb | 3773 ++++++++++++++++++++++++++++++++++ CONTENTS.md | 8 + README.md | 1 + 4 files changed, 3783 insertions(+), 1 deletion(-) create mode 100644 09_mappings/00_content.ipynb diff --git a/00_intro/00_content.ipynb b/00_intro/00_content.ipynb index b1c99a6..bc62059 100644 --- a/00_intro/00_content.ipynb +++ b/00_intro/00_content.ipynb @@ -772,7 +772,7 @@ " - *Chapter 6*: [Text & Bytes ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/00_content.ipynb)\n", " - *Chapter 7*: [Sequential Data ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb)\n", " - *Chapter 8*: [Map, Filter, & Reduce ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb)\n", - " - *Chapter 9*: Mappings & Sets\n", + " - *Chapter 9*: [Mappings & Sets ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb)\n", " - *Chapter 10*: Arrays & Dataframes\n", "- How can we create custom data types?\n", " - *Chapter 11*: Classes & Instances" diff --git a/09_mappings/00_content.ipynb b/09_mappings/00_content.ipynb new file mode 100644 index 0000000..b20e7ec --- /dev/null +++ b/09_mappings/00_content.ipynb @@ -0,0 +1,3773 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/00_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 9: Mappings & Sets" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb) focuses on one special kind of *collection* types, namely *sequences*, this chapter introduces two more kinds: **Mappings** and **sets**. Both are presented in this chapter as they share the *same* underlying implementation.\n", + "\n", + "The `dict` type (cf, [documentation ](https://docs.python.org/3/library/stdtypes.html#dict)) introduced in the next section is an essential part in a data scientist's toolbox for two reasons: First, Python employs `dict` objects basically everywhere internally. Second, after the many concepts involving *sequential* data, *mappings* provide a different perspective on data and enhance our general problem solving skills." + ] + }, + { + "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 *value* every object has: In the \"bag\" analogy from [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Value-/-%28Semantic%29-\"Meaning\"), we describe an object's value to be the semantic meaning of the $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 *objects* on their own with distinct *values*.\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. Also, the commas `,` after the respective *last* items, `2: \"two\"` and `\"two\": 2`, are *not* a mistake although they *may* be left out. Besides easier reading, such a style has technical advantages that we do not go into detail about here (cf., [source ](https://www.python.org/dev/peps/pep-0008/#when-to-use-trailing-commas))." + ] + }, + { + "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": "fragment" + } + }, + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139936685526208" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(to_words)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "dict" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(to_words)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{0: 'zero', 1: 'one', 2: 'two'}" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "to_words" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139936686018688" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(from_words)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "dict" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(from_words)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'zero': 0, 'one': 1, 'two': 2}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from_words" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The built-in [dict() ](https://docs.python.org/3/library/functions.html#func-dict) constructor 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 obtain a *shallow* copy of a `dict` object or convert any other mapping object into a `dict` one." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'zero': 0, 'one': 1, 'two': 2}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dict(from_words)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Second, we may pass it a *finite* `iterable` providing *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": 10, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'zero': 0, 'one': 1, 'two': 2}" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dict([(\"zero\", 0), (\"one\", 1), (\"two\", 2)])" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'zero': 0, 'one': 1, 'two': 2}" + ] + }, + "execution_count": 11, + "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": 12, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'zero': 0, 'one': 1, 'two': 2}" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dict(zero=0, one=1, two=2)" + ] + }, + { + "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 entities \"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. In the example, [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.\n", + "\n", + "`people` makes many implicit assumptions about the structure of the data. For example, there are a [one-to-many ](https://en.wikipedia.org/wiki/One-to-many_%28data_model%29) relationship between a person 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." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "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": 14, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "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": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "people" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... but 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": 15, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "from pprint import pprint" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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/develop/00_intro/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 build some intuition, we describe the underlying implementation details on a conceptual level.\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": 17, + "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 {\n\u001b[0m\u001b[1;32m 2\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;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[1;32m 3\u001b[0m }\n", + "\u001b[0;31mTypeError\u001b[0m: unhashable type: 'list'" + ] + } + ], + "source": [ + "{\n", + " [\"zero\", \"one\"]: [0, 1],\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 keeps the position of the *first* mention of the `\"zero\"` key while only the *last* mention of the corresponding values, `999`, survives." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'zero': 999, 'one': 1, 'two': 2}" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{\n", + " \"zero\": 0,\n", + " \"one\": 1,\n", + " \"two\": 2,\n", + " \"zero\": 999, # to illustrate a point\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The reason for that is that the `dict` type is implemented with so-called [hash tables ](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 than needed to store the references to all the key and value objects. This bag is a **contiguous array** similar to the `list` type's implementation. Whereas in the `list` case the array is divided into equally sized *slots* capable of holding *one* reference, a `dict` object's array is divided into equally sized **buckets** with enough space to store *two* references each: One for an item's key and one for the mapped value. The buckets are labeled with *index* numbers. Because Python knows how wide each bucket, it can jump directly into *any* bucket by calculating its *offset* from the start.\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 index.\n", + "\n", + "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 and \"random\" `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 inside the object.\n", + "\n", + "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! Without this formal requirement an object may end up in *different* buckets depending on its current value. As the name of the `dict` type (i.e., \"dictionary\") suggests, a primary purpose of it is to insert objects and look them up later on. Without a *unique* bucket, this is of course not doable. 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\"`, an immutable `str` object. Hash values have *no* semantic meaning. Also, every time 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": 19, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-85344695604937002" + ] + }, + "execution_count": 19, + "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": 20, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hash(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hash(0.0)" + ] + }, + { + "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 semantic value. The converse statement does *not* hold: Two objects *may* (accidentally) have the *same* hash value and *not* compare equal. However, that rarely happens." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 == 1.0" + ] + }, + { + "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": [ + "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": 24, + "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[0mhash\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[0;31mTypeError\u001b[0m: unhashable type: 'list'" + ] + } + ], + "source": [ + "hash([\"zero\", \"one\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we need keys composed of several objects, we can use `tuple` objects instead." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-1616807732336770172" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hash((\"zero\", \"one\"))" + ] + }, + { + "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": 26, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{('zero', 'one'): [0, 1]}" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{\n", + " (\"zero\", \"one\"): [0, 1],\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "After obtaining the key object's hash value, Python must still convert that into a bucket index. We do not cover this step in technical detail but provide a conceptual description of it.\n", + "\n", + "The `buckets()` function below shows how we can obtain indexes from the binary representation of a hash value by simply extracting its least significant `bits` and interpreting them as index numbers. Alternatively, the hash value may also be divided with the `%` operator by the number of available buckets. We show this idea in the `buckets_alt()` function that takes the number of buckets, `n_buckets`, as its second argument." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def buckets(mapping, *, bits):\n", + " \"\"\"Calculate the bucket indices for a mapping's keys.\"\"\"\n", + " for key in mapping: # cf., next 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[-8:], address, bucket, sep=\"\\t\")" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "def buckets_alt(mapping, *, n_buckets):\n", + " \"\"\"Calculate the bucket indices for a mapping's keys.\"\"\"\n", + " for key in mapping: # cf., next section for details on looping\n", + " hash_value = hash(key)\n", + " bucket = hash_value % n_buckets\n", + " print(key, hash_value, bucket, sep=\"\\t\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With an infinite number of possible keys being mapped to a limited number of buckets, there is a realistic chance that two or more keys end up in the *same* bucket. That is called a **hash collision**. In such cases, Python uses a perturbation rule to rearrange the bits, and if the corresponding next bucket is empty, places an item there. Then, the nice offsetting logic from above breaks down and Python needs more time on average to place items into a hash table or look them up. The remedy is to use a bigger hash table as then the chance of collisions decreases. Python does all that for us in the background, and the main cost we pay for that is a *high* memory usage of `dict` objects in general.\n", + "\n", + "Because keys with the *same* semantic value have the *same* hash value, they end up in the *same* bucket. That is why the item that gets inserted last *overwrites* all previously inserted items whose keys compare equal, as we saw with the two `\"zero\"` keys above.\n", + "\n", + "Thus, to come up with indexes for 4 buckets, we need to extract 2 bits from the hash value (i.e., $2^2 = 4$)." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zero\t-85344695604937002\t0b...00101010\t10\t2\n", + "one\t6414592332130781825\t0b...10000001\t01\t1\n", + "two\t4316247523642253857\t0b...00100001\t01\t1\n" + ] + } + ], + "source": [ + "buckets(from_words, bits=2)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zero\t-85344695604937002\t2\n", + "one\t6414592332130781825\t1\n", + "two\t4316247523642253857\t1\n" + ] + } + ], + "source": [ + "buckets_alt(from_words, n_buckets=4)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Similarly, 3 bits provide indexes for 8 buckets (i.e., $2^3 = 8$) ..." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zero\t-85344695604937002\t0b...00101010\t010\t2\n", + "one\t6414592332130781825\t0b...10000001\t001\t1\n", + "two\t4316247523642253857\t0b...00100001\t001\t1\n" + ] + } + ], + "source": [ + "buckets(from_words, bits=3)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zero\t-85344695604937002\t6\n", + "one\t6414592332130781825\t1\n", + "two\t4316247523642253857\t1\n" + ] + } + ], + "source": [ + "buckets_alt(from_words, n_buckets=8)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... while 4 bits do so for 16 buckets (i.e., $2^4 = 16$)." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zero\t-85344695604937002\t0b...00101010\t1010\t10\n", + "one\t6414592332130781825\t0b...10000001\t0001\t1\n", + "two\t4316247523642253857\t0b...00100001\t0001\t1\n" + ] + } + ], + "source": [ + "buckets(from_words, bits=4)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zero\t-85344695604937002\t6\n", + "one\t6414592332130781825\t1\n", + "two\t4316247523642253857\t1\n" + ] + } + ], + "source": [ + "buckets_alt(from_words, n_buckets=16)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Python allocates the memory for a `dict` object's hash table according to some internal heuristics: 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", + "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 of Key-Value Pairs" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb#Collections-vs.-Sequences), we see how a *sequence* is a special kind of a *collection*, and that collections can be described as\n", + "- *iterable*\n", + "- *containers*\n", + "- with a *finite* number of elements.\n", + "\n", + "The `dict` type is another *collection* type and has these three properties as well.\n", + "\n", + "For example, 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. In the terminology of 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), both are `Sized` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(to_words)" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(from_words)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Also, `dict` objects may be looped over, for example, with the `for` statement. So, in the terminology of the [collections.abc ](https://docs.python.org/3/library/collections.abc.html) module, they are `Iterable` objects.\n", + "\n", + "Regarding the *iteration order* things are not that easy, and programmers seem to often be confused about this (e.g., this [discussion](https://stackoverflow.com/questions/58413076/why-are-python-dictionaries-not-reversible-for-python3-7)). The confusion usually comes from one of two reasons:\n", + "1. The internal implementation of the `dict` type has been changed over the last couple of minor release versions, and the communication thereof in the official release notes was done only in a later version. In a nutshell, before Python 3.6, the core developers did not care about the iteration order at all as the goal was to optimize `dict` objects for computational speed, primarily regarding key look-up (cf., the \"Indexing -> Key Look-up\" section below). That meant that looping over the *same* `dict` object several times during its lifetime could have resulted in *different* iteration orders. In Python 3.6, it was discovered that it is possible to make `dict` objects remember the order in that items have been inserted *without* giving up any computational speed or memory (cf., [Raymond Hettinger](https://github.com/rhettinger)'s talk in the [Further Resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/08_resources.ipynb#History-of-the-dict-Type) section at the end of the chapter. However, that change was kept an *implementation detail* and *not* made official in the release notes. That was then done in Python 3.7's release notes (cf., [Python 3.7 release notes ](https://www.python.org/downloads/release/python-370/)).\n", + "2. To make order an official part of a data type, it must adhere to the `Reversible` ABC in the [collections.abc ](https://docs.python.org/3/library/collections.abc.html) module and support the [reversed() ](https://docs.python.org/3/library/functions.html#reversed) built-in. Even though the items' order inside a `dict` is remembered for Python 3.6 and 3.7, `dict` objects are *not* `Reversible`. That was then changed in Python 3.8, but again *not* officially communicated (cf., [Python 3.8 release notes](https://www.python.org/downloads/release/python-380/)).\n", + "\n", + "In summary, we can say that depending on the exact Python version a `dict` object *may* remember the **insertion order** of its items.\n", + "\n", + "However, that order is only apparent to us (i.e., we could look it up) if we put the data stored in a `dict` object into the *source code* itself. Then, we say that we \"hard code\" the data in our program. That is often *not* useful as we want our software load the data to be processed, for example, from a file or a database.\n", + "\n", + "Therefore, we suggest and adopt the following *best practices* in this book:\n", + "- We *assume* that the items in a `dict` object are *not* in a **predictable order** and *never* make the correctness of the logic in our code dependent on it.\n", + "- Whenever we want or need to model data in `dict`-like objects with an *explicit* order, we use the [OrderedDict ](https://docs.python.org/3/library/collections.html#collections.OrderedDict) type in the [collections ](https://docs.python.org/3/library/collections.html) module in the [standard library ](https://docs.python.org/3/library/index.html).\n", + "\n", + "If you installed Python, as recommended, via the Anaconda Distribution, 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*." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.8.6\n" + ] + } + ], + "source": [ + "!python --version # the order in the for-loops is predictable only for Python 3.7 or higher" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "By convention, iteration goes over the *keys* in the `dict` object only. The \"*Dictionary Methods*\" section below shows how to loop over the *items* or the *values* instead." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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": 39, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zero\n", + "one\n", + "two\n" + ] + } + ], + "source": [ + "for word in from_words:\n", + " print(word)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For Python 3.8, `dict` objects are `Reversible` as well. So, passing a `dict` object to the [reversed() ](https://docs.python.org/3/library/functions.html#reversed) built-in works. However, for ealier Python versions, the two next cells raise a `TypeError`." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2\n", + "1\n", + "0\n" + ] + } + ], + "source": [ + "for number in reversed(to_words):\n", + " print(number)" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "two\n", + "one\n", + "zero\n" + ] + } + ], + "source": [ + "for word in reversed(from_words):\n", + " print(word)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Of course, we may always use the built-in [sorted() ](https://docs.python.org/3/library/functions.html#sorted) function to loop over, for example, `from_words` in a *predictable* order. However, that creates a temporary `list` object in memory and an order that has *nothing* to do with how the items are ordered inside the `dict` object." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "one\n", + "two\n", + "zero\n" + ] + } + ], + "source": [ + "for word in sorted(from_words):\n", + " print(word)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To show the `Container` behavior of *collection* types, we use the boolean `in` operator to check if a given object evaluates equal to a *key* in `to_words` or `from_words`." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1.0 in to_words # 1.0 is not a key but compares equal to a key" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "-1 in to_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": [ + "\"one\" in from_words" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"ten\" in from_words" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Membership Testing: `list` vs. `dict`" + ] + }, + { + "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: 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. Then, the Python interpreter jumps right there in only *one* step. Because that is true no matter how many items are in the hash table, we call that a **constant time** operation.\n", + "\n", + "Conceptually, the overall behavior of the `in` operator is like comparing the searched object against *all* key objects with the `==` operator *without* doing it.\n", + "\n", + "To show the speed, we run an experiment. 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": 47, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import random" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(87)" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "needle = 42" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "haystack = [random.randint(99, 9999) for _ in range(10_000_000)]\n", + "haystack.append(needle)" + ] + }, + { + "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": 51, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "random.shuffle(haystack)" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[8126, 7370, 3735, 213, 7922, 1434, 8557, 9609, 9704, 9564]" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "haystack[:10]" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[7237, 886, 5945, 4014, 4998, 2055, 3531, 6919, 7875, 1944]" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "haystack[-10:]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As modern computers are generally fast, we search the `haystack` a total of `10` times." + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4.44 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 the `haystack` into the keys of a `magic_haystack`, a `dict` object. We use `None` as a dummy value for all items." + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "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": [ + "To show the *massive* effect of the hash table implementation, we search the `magic_haystack` not `10` but `10_000_000` times. The code cell still runs in only a fraction of the time its counterpart does above." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "560 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 compares equal to the 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`." + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "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": 58, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'zero'" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "to_words[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'zero'" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "to_words_list[0]" + ] + }, + { + "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": 60, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 60, + "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": 61, + "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.\n", + "\n", + "To access deeper 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": 62, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'name': 'Gilbert Strang', 'emails': ['gilbert@mit.edu']},\n", + " {'name': 'Leonhard Euler', 'emails': []}]" + ] + }, + "execution_count": 62, + "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": 63, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'name': 'Gilbert Strang', 'emails': ['gilbert@mit.edu']}" + ] + }, + "execution_count": 63, + "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": 64, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Gilbert Strang'" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "people[\"mathematicians\"][0][\"name\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... or his `\"emails\"`." + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['gilbert@mit.edu']" + ] + }, + "execution_count": 65, + "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": [ + "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* the references to the mapped value objects." + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{0: 'zero', 1: 'one', 2: 'two'}" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "to_words" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "to_words[0] = \"null\"\n", + "to_words[1] = \"eins\"\n", + "to_words[2] = \"zwei\"" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{0: 'null', 1: 'eins', 2: 'zwei'}" + ] + }, + "execution_count": 68, + "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": 69, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "to_words[3] = \"drei\"\n", + "to_words[4] = \"vier\"" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{0: 'null', 1: 'eins', 2: 'zwei', 3: 'drei', 4: 'vier'}" + ] + }, + "execution_count": 70, + "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": 71, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139936685526208" + ] + }, + "execution_count": 71, + "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": 72, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "del to_words[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1: 'eins', 2: 'zwei', 3: 'drei', 4: 'vier'}" + ] + }, + "execution_count": 73, + "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": 74, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "people[\"physicists\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": 76, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'name': 'Guido', 'emails': ['guido@python.org', 'guido@dropbox.com']}" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "people[\"programmers\"][0]" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": 78, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": 79, + "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": "slide" + } + }, + "source": [ + "## `dict` Methods" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`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 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": 80, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import collections.abc as abc" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(from_words, abc.Mapping)" + ] + }, + { + "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(from_words, abc.MutableMapping)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While iteration over a mapping type already 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": 83, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zero\n", + "one\n", + "two\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": 84, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_keys(['zero', 'one', 'two'])" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from_words.keys()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Views 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 as the newly created `list` object has a \"*predictable*\" order (i.e., indexes `0`, `1`, ...) created from an *unpredictable* one." + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['zero', 'one', 'two']" + ] + }, + "execution_count": 85, + "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* (i.e., type `dict_values`) on the value objects inside `from_words` without copying them." + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "1\n", + "2\n" + ] + } + ], + "source": [ + "for number in from_words.values():\n", + " print(number)" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_values([0, 1, 2])" + ] + }, + "execution_count": 87, + "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 (i.e., type `dict_items`) 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": 88, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "zero -> 0\n", + "one -> 1\n", + "two -> 2\n" + ] + } + ], + "source": [ + "for word, number in from_words.items():\n", + " print(f\"{word} -> {number}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_items([('zero', 0), ('one', 1), ('two', 2)])" + ] + }, + "execution_count": 89, + "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* with a `KeyError` 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": 90, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1: 'eins', 2: 'zwei', 3: 'drei', 4: 'vier'}" + ] + }, + "execution_count": 90, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "to_words" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "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": 92, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'n/a'" + ] + }, + "execution_count": 92, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "to_words.get(0, \"n/a\")" + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'eins'" + ] + }, + "execution_count": 93, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "to_words.get(1, \"n/a\")" + ] + }, + { + "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) constructor allows, as well." + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "to_spanish = {\n", + " 0: \"cero\",\n", + " 1: \"uno\",\n", + " 2: \"dos\",\n", + " 3: \"tres\",\n", + " 4: \"cuatro\",\n", + " 5: \"cinco\", \n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 95, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "to_words.update(to_spanish)" + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1: 'uno', 2: 'dos', 3: 'tres', 4: 'cuatro', 0: 'cero', 5: 'cinco'}" + ] + }, + "execution_count": 96, + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'zero': 0, 'one': 1, 'two': 2}" + ] + }, + "execution_count": 97, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from_words" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "number = from_words.pop(\"zero\")" + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 99, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "number" + ] + }, + { + "cell_type": "code", + "execution_count": 100, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'one': 1, 'two': 2}" + ] + }, + "execution_count": 100, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from_words" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With an optional `default` argument, the loud `KeyError` 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": "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": 102, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 102, + "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": 103, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "word, number = from_words.popitem()" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "('two', 2)" + ] + }, + "execution_count": 104, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "word, number" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'one': 1}" + ] + }, + "execution_count": 105, + "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": 106, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "to_words.clear()" + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{}" + ] + }, + "execution_count": 107, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "to_words" + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "from_words.clear()" + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{}" + ] + }, + "execution_count": 109, + "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 also *mutates* the `dict` object.\n", + "\n", + "Consider the `people` example again and note how the `dict` object modeling `\"Albert Einstein\"` has *no* `\"emails\"` key in it." + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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\"`. We cannot be sure if a `dict` object modeling a person has already a `\"emails\"` key or not. To play it safe, 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. Then, 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 first." + ] + }, + { + "cell_type": "code", + "execution_count": 111, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "people[\"mathematicians\"][1].setdefault(\"emails\", []).append(\"leonhard@math.org\")" + ] + }, + { + "cell_type": "code", + "execution_count": 112, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "people[\"physicists\"][0].setdefault(\"emails\", []).append(\"albert@physics.org\")" + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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": [ + "`dict` objects also 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": 114, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "guido = people[\"programmers\"][0].copy()" + ] + }, + { + "cell_type": "code", + "execution_count": 115, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'name': 'Guido van Rossum', 'emails': ['guido@python.org']}" + ] + }, + "execution_count": 115, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "guido" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we mutate `guido` and, 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": 116, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "guido[\"emails\"].clear()" + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'name': 'Guido van Rossum', 'emails': []}" + ] + }, + "execution_count": 117, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "guido" + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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": [ + "## `dict` Comprehensions" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Analogous to `list` comprehensions in [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/01_content.ipynb#list-Comprehensions), `dict` comprehensions are a concise literal notation to derive new `dict` objects out of existing ones.\n", + "\n", + "For example, let's derive `from_words` out of `to_words` below by swapping the keys and values." + ] + }, + { + "cell_type": "code", + "execution_count": 119, + "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": 120, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'zero': 0, 'one': 1, 'two': 2}" + ] + }, + "execution_count": 120, + "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 curly braces `{}` replace the brackets `[]` and a colon `:` is added to separate the keys from the values." + ] + }, + { + "cell_type": "code", + "execution_count": 121, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'zero': 0, 'one': 1, 'two': 2}" + ] + }, + "execution_count": 121, + "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": 122, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'ONE': 1, 'TWO': 2}" + ] + }, + "execution_count": 122, + "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`)." + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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": 123, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{\n", + " (x, y): x * y\n", + " for x in range(1, 11) for y in range(1, 11)\n", + " if abs(x * y - 50) <= 5\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.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index bfa7f76..9e3fbb6 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -229,3 +229,11 @@ If this is not possible, Example: `sorted()` vs. `reversed()`) - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/05_summary.ipynb) - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/06_review.ipynb) + - *Chapter 9*: Mappings & Sets + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/00_content.ipynb) + (`dict` Type; + Nested Data; + Hash Tables; + `dict` Methods & Behavior; + `dict` Comprehension) diff --git a/README.md b/README.md index 23b5521..dd051e6 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ For a more *detailed version* with **clickable links** - *Chapter 6*: Text & Bytes - *Chapter 7*: Sequential Data - *Chapter 8*: Map, Filter, & Reduce + - *Chapter 9*: Mappings & Sets #### Videos From f02a5f0e01410e8d3d7b024199cddf0ca9441568 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 22 Oct 2020 18:26:17 +0200 Subject: [PATCH 100/142] Add initial version of chapter 09's exercises, part 1 --- 09_mappings/01_exercises.ipynb | 428 +++++++++++++++++++++++++++++++++ CONTENTS.md | 3 + 2 files changed, 431 insertions(+) create mode 100644 09_mappings/01_exercises.ipynb diff --git a/09_mappings/01_exercises.ipynb b/09_mappings/01_exercises.ipynb new file mode 100644 index 0000000..72b503d --- /dev/null +++ b/09_mappings/01_exercises.ipynb @@ -0,0 +1,428 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/01_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 9: Mappings & Sets (Coding Exercises)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb) of Chapter 9.\n", + "\n", + "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Working with Nested Data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's write some code to analyze the historic soccer game [Brazil vs. Germany ](https://en.wikipedia.org/wiki/Brazil_v_Germany_%282014_FIFA_World_Cup%29) during the 2014 World Cup.\n", + "\n", + "Below, `players` consists of two nested `dict` objects, one for each team, that hold `tuple` objects (i.e., records) with information on the players. Besides the jersey number, name, and position, each `tuple` objects contains a `list` object with the times when the player scored." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "players = {\n", + " \"Brazil\": [\n", + " (12, \"Júlio César\", \"Goalkeeper\", []),\n", + " (4, \"David Luiz\", \"Defender\", []),\n", + " (6, \"Marcelo\", \"Defender\", []),\n", + " (13, \"Dante\", \"Defender\", []),\n", + " (23, \"Maicon\", \"Defender\", []),\n", + " (5, \"Fernandinho\", \"Midfielder\", []),\n", + " (7, \"Hulk\", \"Midfielder\", []),\n", + " (8, \"Paulinho\", \"Midfielder\", []),\n", + " (11, \"Oscar\", \"Midfielder\", [90]),\n", + " (16, \"Ramires\", \"Midfielder\", []),\n", + " (17, \"Luiz Gustavo\", \"Midfielder\", []),\n", + " (19, \"Willian\", \"Midfielder\", []),\n", + " (9, \"Fred\", \"Striker\", []),\n", + " ],\n", + " \"Germany\": [\n", + " (1, \"Manuel Neuer\", \"Goalkeeper\", []),\n", + " (4, \"Benedikt Höwedes\", \"Defender\", []),\n", + " (5, \"Mats Hummels\", \"Defender\", []),\n", + " (16, \"Philipp Lahm\", \"Defender\", []),\n", + " (17, \"Per Mertesacker\", \"Defender\", []),\n", + " (20, \"Jérôme Boateng\", \"Defender\", []),\n", + " (6, \"Sami Khedira\", \"Midfielder\", [29]),\n", + " (7, \"Bastian Schweinsteiger\", \"Midfielder\", []),\n", + " (8, \"Mesut Özil\", \"Midfielder\", []),\n", + " (13, \"Thomas Müller\", \"Midfielder\", [11]),\n", + " (14, \"Julian Draxler\", \"Midfielder\", []),\n", + " (18, \"Toni Kroos\", \"Midfielder\", [24, 26]),\n", + " (9, \"André Schürrle\", \"Striker\", [69, 79]),\n", + " (11, \"Miroslav Klose\", \"Striker\", [23]),\n", + " ],\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: Write a dictionary comprehension to derive a new `dict` object, called `brazilian_players`, that maps a Brazilian player's name to his position!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "brazilian_players = {...: ...}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "brazilian_players" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: Generalize the code fragment into a `get_players()` function: Passed a `team` name, it returns a `dict` object like `brazilian_players`. Verify that the function works for the German team as well!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def get_players(team):\n", + " \"\"\"Creates a dictionary mapping the players' names to their position.\"\"\"\n", + " return {...: ...}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "get_players(\"Germany\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Often, we are given a `dict` object like the one returned from `get_players()`: Its main characteristic is that it maps a large set of unique keys (i.e., the players' names) onto a smaller set of non-unique values (i.e., the positions).\n", + "\n", + "**Q3**: Create a generic `invert()` function that swaps the keys and values of a `mapping` argument passed to it and returns them in a *new* `dict` object! Ensure that *no* key gets lost! Verify your implementation with the `brazilian_players` dictionary!\n", + "\n", + "Hints: Think of this as a grouping operation. The *new* values are `list` or `tuple` objects that hold the original keys. You may want to use either the [defaultdict ](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 on the ordinary `dict` type." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def invert(mapping):\n", + " \"\"\"Invert the keys and values of a mapping argument.\"\"\"\n", + " ...\n", + " ...\n", + " ...\n", + " return ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "invert(brazilian_players)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: Write a `score_at_minute()` function: It takes two arguments, `team` and `minute`, and returns the number of goals the `team` has scored up until this time in the game.\n", + "\n", + "Hints: The function may reference the global `players` for simplicity. Earn bonus points if you can write this in a one-line expression using some *reduction* function and a `generator` expression." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def score_at_minute(team, minute):\n", + " \"\"\"Determine the number of goals scored by a team until a given minute.\"\"\"\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " return ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The score at half time was:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "score_at_minute(\"Brazil\", 45)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "score_at_minute(\"Germany\", 45)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The final score was:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "score_at_minute(\"Brazil\", 90)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "score_at_minute(\"Germany\", 90)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: Write a `goals_by_player()` function that takes an argument like the global `players`, and returns a `dict` object mapping the players to the number of goals they scored!\n", + "\n", + "Hints: Do *not* \"hard code\" the names of the teams! Earn bonus points if you can solve it in a one-line `dict` comprehension." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def goals_by_player(players):\n", + " \"\"\"Create a dictionary mapping the players' names to the number of goals.\"\"\"\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " return ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "goals_by_player(players)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: Write a `dict` comprehension to filter out the players who did *not* score from the preceding result.\n", + "\n", + "Hints: Reference the `goals_by_player()` function from before." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "{...: ...}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: Write a `all_goals()` function that takes one argument like the global `players` and returns a `list` object containing $2$-element `tuple` objects where the first element is the minute a player scored and the second his name! The list should be sorted by the time.\n", + "\n", + "Hints: You may want to use either the built-in [sorted() ](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", + " \"\"\"Create a time table of the individual goals.\"\"\"\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " return ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "all_goals(players)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: Lastly, write a `summary()` function that takes one argument like the global `players` and prints out a concise report of the goals, the score at the half, and the final result.\n", + "\n", + "Hints: Use the `all_goals()` and `score_at_minute()` functions from before.\n", + "\n", + "The output should look similar to this:\n", + "```\n", + "12' Gerd Müller scores\n", + "...\n", + "HALFTIME: TeamA 1 TeamB 2\n", + "77' Ronaldo scores\n", + "...\n", + "FINAL: TeamA 1 TeamB 3\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def summary(players):\n", + " \"\"\"Create a written summary of the game.\"\"\"\n", + " # Create two lists with the goals of either half.\n", + " ...\n", + " ...\n", + " ...\n", + "\n", + " # Print the goals of the first half.\n", + " ...\n", + " ...\n", + "\n", + " # Print the half time score.\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + "\n", + " # Print the goals of the second half.\n", + " ...\n", + " ...\n", + "\n", + " # Print the final score.\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "summary(players)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index 9e3fbb6..20adfea 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -237,3 +237,6 @@ If this is not possible, Hash Tables; `dict` Methods & Behavior; `dict` Comprehension) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/01_exercises.ipynb) + (Working with Nested Data) From 0fe9cca303634a63a6e8309e9587cc1189ccd4a9 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 22 Oct 2020 18:31:16 +0200 Subject: [PATCH 101/142] Add initial version of chapter 09, part 2 --- 04_iteration/00_content.ipynb | 2 +- 09_mappings/02_content.ipynb | 1201 +++++++++++++++++++ 09_mappings/static/fibonacci_call_graph.png | Bin 0 -> 32706 bytes CONTENTS.md | 4 + 4 files changed, 1206 insertions(+), 1 deletion(-) create mode 100644 09_mappings/02_content.ipynb create mode 100644 09_mappings/static/fibonacci_call_graph.png diff --git a/04_iteration/00_content.ipynb b/04_iteration/00_content.ipynb index f7d1e41..0171c6c 100644 --- a/04_iteration/00_content.ipynb +++ b/04_iteration/00_content.ipynb @@ -874,7 +874,7 @@ "\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 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb#Memoization), after introducing the `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 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/02_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." ] diff --git a/09_mappings/02_content.ipynb b/09_mappings/02_content.ipynb new file mode 100644 index 0000000..ae2c380 --- /dev/null +++ b/09_mappings/02_content.ipynb @@ -0,0 +1,1201 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/02_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 9: Mappings & Sets (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "After introducing the `dict` type in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb) of this chapter, we first look at an extension of the packing and unpacking syntax that involves `dict` objects. Then, we see how mappings can help us write computationally more efficient implementations to recursive solutions of problems as introduced in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb#Recursion). In a way, this second part of the chapter \"finishes\" Chapter 4." + ] + }, + { + "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/develop/07_sequences/03_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." + ] + }, + { + "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": "fragment" + } + }, + "outputs": [], + "source": [ + "more_words = {\n", + " 2: \"TWO\", # 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` 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." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{0: 'zero', 1: 'one', 2: 'TWO', 3: 'three', 4: 'four'}" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{**to_words, **more_words}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "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": 4, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def print_args1(*args, **kwargs):\n", + " \"\"\"Print out all arguments passed in.\"\"\"\n", + " for index, arg in enumerate(args):\n", + " print(\"position\", index, arg)\n", + "\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": 5, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "print_args1()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": 7, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "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": 8, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": [ + "We may even unpack `dict` and `list` objects." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "flags = {\"flag\": True, \"another_flag\": False}" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "keyword flag True\n", + "keyword another_flag False\n" + ] + } + ], + "source": [ + "print_args1(**flags)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "position 0 42\n", + "position 1 87\n", + "keyword flag True\n", + "keyword another_flag False\n" + ] + } + ], + "source": [ + "print_args1(*[42, 87], **flags)" + ] + }, + { + "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 `positional` parameter, and one keyword argument, captured in `keyword`. Further, an optional keyword argument `default` may be passed in. Any other positional or keyword arguments are packed into either `args` or `kwargs`." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "def print_args2(positional, *args, keyword, default=True, **kwargs):\n", + " \"\"\"Print out all arguments passed in.\"\"\"\n", + " print(\"required positional\", positional)\n", + "\n", + " for index, arg in enumerate(args):\n", + " print(\"optional positional\", index, arg)\n", + "\n", + " print(\"required keyword\", keyword)\n", + " print(\"default keyword\", default)\n", + "\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": 13, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "print_args2() missing 1 required positional argument: 'positional'", + "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: 'positional'" + ] + } + ], + "source": [ + "print_args2()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "print_args2() missing 1 required keyword-only argument: 'keyword'", + "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: 'keyword'" + ] + } + ], + "source": [ + "print_args2(\"p\")" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "required positional p\n", + "required keyword k\n", + "default keyword True\n" + ] + } + ], + "source": [ + "print_args2(\"p\", keyword=\"k\")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "required positional p\n", + "required keyword k\n", + "default keyword False\n" + ] + } + ], + "source": [ + "print_args2(\"p\", keyword=\"k\", default=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "required positional p\n", + "optional positional 0 x\n", + "optional positional 1 y\n", + "required keyword k\n", + "default keyword True\n", + "optional keyword flag True\n" + ] + } + ], + "source": [ + "print_args2(\"p\", \"x\", \"y\", keyword=\"k\", flag=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "required positional p\n", + "optional positional 0 x\n", + "optional positional 1 y\n", + "required keyword k\n", + "default keyword False\n", + "optional keyword flag True\n" + ] + } + ], + "source": [ + "print_args2(\"p\", \"x\", \"y\", keyword=\"k\", default=False, flag=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As above, we may unpack `list` or `dict` objects in a function call." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "positionals = [\"x\", \"y\", \"z\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "required positional p\n", + "optional positional 0 x\n", + "optional positional 1 y\n", + "optional positional 2 z\n", + "required keyword k\n", + "default keyword False\n", + "optional keyword flag True\n", + "optional keyword another_flag False\n" + ] + } + ], + "source": [ + "print_args2(\"p\", *positionals, keyword=\"k\", default=False, **flags)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Memoization" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### \"Easy at first Glance\" Example: [Fibonacci Numbers ](https://en.wikipedia.org/wiki/Fibonacci_number) (repeated)" + ] + }, + { + "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/develop/04_iteration/00_content.ipynb#\"Easy-at-first-Glance\"-Example:-Fibonacci-Numbers) takes long to compute for large Fibonacci numbers. For easier comparison, we show the old `fibonacci()` version here again." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "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": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "144" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fibonacci(12)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Efficiency of Algorithms" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Timing the code cells below with the `%%timeit` magic shows how doubling the input (i.e., `12` becomes `24`) more than doubles how long it takes `fibonacci()` to calculate the solution. This is actually an understatement as we see the time go up by roughly a factor of $1000$ (i.e., from nano-seconds to milli-seconds). That is an example of **exponential growth**." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "40.1 µs ± 1.26 µ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": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "12.1 ms ± 149 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + ] + } + ], + "source": [ + "%%timeit -n 100\n", + "fibonacci(24)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The computation graph below visualizes what the problem is and also suggests a solution: In the recursive implementation, the same function calls are made over and over again. For example, in the visualization the call `fibonacci(3)`, shown as $F(3)$, is made *twice* when the actual goal is to calculate `fibonacci(5)`, shown as $F(5)$. This problem \"grows\" if the initial argument (i.e., `5` in the example) is chosen to be larger as we see with the many `fibonacci(2)`, `fibonacci(1)` and `fibonacci(0)` calls.\n", + "\n", + "Instead of calculating the return value of the `fibonacci()` function for the *same* argument over and over again, it makes sense to **cache** (i.e., \"store\") the result and reuse it. This concept is called **[memoization ](https://en.wikipedia.org/wiki/Memoization)**." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "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": [ + "Below is a revision of the recursive `fibonacci()` implementation that uses a **globally** defined `dict` object, called `memo`, to store intermediate results and look them up.\n", + "\n", + "To be precise, the the revised `fibonacci()` first checks if the `i`th Fibonacci number has already been calculated before. If yes, it is in the `memo`. That number is then returned immediately *without* any more calculations. As `dict` objects are *optimized* for constant-time key look-ups, this takes essentially \"no\" time! With a `list` object, for example, the `in` operator would trigger a linear search, which takes longer the more elements are in the list. If the `i`th Fibonacci number has not been calculated before, there is no corresponding item in the `memo` and a recursive function call must be made. The result obtained by recursion is then inserted into the `memo`." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "memo = {\n", + " 0: 0,\n", + " 1: 1,\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "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", + " 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": "markdown", + "metadata": {}, + "source": [ + "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)`, the left-most leaf node $F(1)$, 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. Effectively, this mirrors the *iterative* implementation in that the order of all computational steps are *identical* (cf., the \"*Hard at first Glance*\" example in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb#\"Hard-at-first-Glance\"-Example:-Fibonacci-Numbers--(revisited))).\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": 27, + "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": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fibonacci(12, debug=True)" + ] + }, + { + "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. Because of that, this `fibonacci()` is *not* a *pure* function." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "144" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fibonacci(12, debug=True) # no more recursive calls needed" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "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": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "memo" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "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": 30, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The slowest run took 252.65 times longer than the fastest. This could mean that an intermediate result is being cached.\n", + "6.68 µs ± 15.8 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n 1\n", + "fibonacci(99)" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The slowest run took 3603.20 times longer than the fastest. This could mean that an intermediate result is being cached.\n", + "85.1 µs ± 208 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n 1\n", + "fibonacci(999)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The iterative implementation still has an advantage as the `RecursionError` shows for larger `i`." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "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[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)\\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~/repos/intro-to-python/.venv/lib/python3.8/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 2379\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 2380\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-> 2381\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 2382\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 2383\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~/repos/intro-to-python/.venv/lib/python3.8/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~/repos/intro-to-python/.venv/lib/python3.8/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 1171\u001b[0m \u001b[0;32mbreak\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1172\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1173\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 1174\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 1175\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/3.8.6/lib/python3.8/timeit.py\u001b[0m in \u001b[0;36mrepeat\u001b[0;34m(self, repeat, number)\u001b[0m\n\u001b[1;32m 203\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 204\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--> 205\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 206\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 207\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~/repos/intro-to-python/.venv/lib/python3.8/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 15\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 16\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 17\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 18\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 19\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 15\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 16\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 17\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 18\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 19\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" + ] + } + ], + "source": [ + "%%timeit -n 1\n", + "fibonacci(9999)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "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 as the programmer.\n", + "\n", + "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. As we are good citizens, we reset everything to the defaults after our hack is completed." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import sys" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "old_recursion_limit = sys.getrecursionlimit()" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3000" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "old_recursion_limit" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "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": 37, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The slowest run took 50532.39 times longer than the fastest. This could mean that an intermediate result is being cached.\n", + "1.21 ms ± 2.97 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + ] + } + ], + "source": [ + "%%timeit -n 1\n", + "fibonacci(9999)" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "sys.setrecursionlimit(old_recursion_limit)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/09_mappings/static/fibonacci_call_graph.png b/09_mappings/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 diff --git a/CONTENTS.md b/CONTENTS.md index 20adfea..4ca214f 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -240,3 +240,7 @@ If this is not possible, - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/01_exercises.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/01_exercises.ipynb) (Working with Nested Data) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/02_content.ipynb) + (`**kwargs` in Function Definitions; + Memoization) From 4f6c14822c72729cf1b989fe67b54cf9e929e8b8 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 22 Oct 2020 18:33:59 +0200 Subject: [PATCH 102/142] Add initial version of chapter 09's exercises, part 2 --- 09_mappings/03_exercises.ipynb | 248 +++++++++++++++++++++++++++++++++ CONTENTS.md | 3 + 2 files changed, 251 insertions(+) create mode 100644 09_mappings/03_exercises.ipynb diff --git a/09_mappings/03_exercises.ipynb b/09_mappings/03_exercises.ipynb new file mode 100644 index 0000000..ea10e8f --- /dev/null +++ b/09_mappings/03_exercises.ipynb @@ -0,0 +1,248 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/03_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 9: Mappings & Sets (Coding Exercises)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/02_content.ipynb) of Chapter 9.\n", + "\n", + "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Memoization without Side Effects" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "It is considered *bad practice* to make a function and thereby its correctness dependent on a program's *global state*: For example, in the \"*Easy at second Glance: Fibonacci Numbers*\" section in [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/02_content.ipynb#\"Easy-at-second-Glance\"-Example:-Fibonacci-Numbers--%28revisited%29), we use a global `memo` to store the Fibonacci numbers that have already been calculated.\n", + "\n", + "That `memo` dictionary could be \"manipulated.\" More often than not, such things happen by accident: Imagine we wrote two independent recursive functions that both rely on memoization to solve different problems, and, unintentionally, we made both work with the *same* global `memo`. As a result, we would observe \"random\" bugs depending on the order in which we executed these functions. Such bugs are hard to track down in practice.\n", + "\n", + "A common remedy is to avoid global state and pass intermediate results \"down\" the recursion tree in a \"hidden\" argument. By convention, we prefix parameter names with a single leading underscore `_`, such as with `_memo` below, to indicate that the caller of our `fibonacci()` function *must not* use it. Also, we make `_memo` a *keyword-only* argument to force ourselves to always explicitly name it in a function call. Because it is an **implementation detail**, the `_memo` parameter is *not* mentioned in the docstring.\n", + "\n", + "Your task is to complete this version of `fibonacci()` so that the function works *without* any **side effects** in the global scope." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### \"Easy at third Glance\" Example: [Fibonacci Numbers ](https://en.wikipedia.org/wiki/Fibonacci_number)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "def fibonacci(i, *, debug=False, _memo=None):\n", + " \"\"\"Calculate the ith Fibonacci number.\n", + "\n", + " Args:\n", + " i (int): index of the Fibonacci number to calculate\n", + " debug (bool): show non-cached calls; defaults to False\n", + "\n", + " Returns:\n", + " ith_fibonacci (int)\n", + " \"\"\"\n", + " # answer to Q1\n", + " if ...:\n", + " ... = {\n", + " 0: 0,\n", + " 1: 1,\n", + " }\n", + "\n", + " # answer to Q2\n", + " if ...:\n", + " return ...\n", + "\n", + " if debug: # added for didactical purposes\n", + " print(f\"fibonacci({i}) is calculated\")\n", + "\n", + " # answer to Q3\n", + " recurse = (\n", + " fibonacci(...)\n", + " + fibonacci(...)\n", + " )\n", + " # answer to Q4\n", + " ... = ...\n", + " return ..." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Q1**: When `fibonacci()` is initially called, `_memo` is set to `None`. So, there is *no* `dict` object yet. Implement the *two* base cases in the first `if` statement!\n", + "\n", + "Hints: All you need to do is create a *new* `dict` object with the results for `i=0` and `i=1`. This object is then passed on in the recursive function calls. Use the `is` operator in the `if` statement." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: When `fibonacci()` is called for non-base cases (i.e., `i > 1`), it first checks if the result is already in the `_memo`. Implement that step in the second `if` statement!\n", + "\n", + "Hint: Use the early exit pattern." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: If `fibonacci()` is called for an `i` argument whose result is not yet in the `_memo`, it must calculate it with the usual recursive function calls. Fill in the arguments to the two recursive `fibonacci()` calls!\n", + "\n", + "Hint: You must pass on the hidden `_memo`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: Lastly, after the two recursive calls have returned, `fibonacci()` must store the `recurse` result for the given `i` in the `_memo` *before* returning it. Implement that logic!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: What happens to the hidden `_memo` after the initial call to `fibonacci()` returned? How many hidden `_memo` objects exist in memory during the entire computation?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Because `fibonacci()` is now independent of the *global state*, the same eleven recursive function calls are made each time! So, this `fibonacci()` is a **pure** function, meaning it has *no* side effects.\n", + "\n", + "**Q6**: Execute the following code cell a couple of times to observe that!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "fibonacci(12, debug=True) # = 13th Fibonacci number -> 11 recursive calls necessary" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The runtime of `fibonacci()` is now stable: There is no message that \"an intermediate result is being cached\" as in [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/02_content.ipynb#\"Easy-at-second-Glance\"-Example:-Fibonacci-Numbers--%28revisited%29).\n", + "\n", + "**Q7**: Execute the following code cells a couple of times to observe that!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%timeit -n 1\n", + "fibonacci(99)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%timeit -n 1\n", + "fibonacci(999)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index 4ca214f..8941fed 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -244,3 +244,6 @@ If this is not possible, [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/02_content.ipynb) (`**kwargs` in Function Definitions; Memoization) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/03_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/03_exercises.ipynb) + (Memoization without Side Effects) From f7a8d4ed855eaa44d857aa9399851da17c5e6e50 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 22 Oct 2020 18:37:48 +0200 Subject: [PATCH 103/142] Add initial version of chapter 09, part 3 --- 09_mappings/04_content.ipynb | 1601 ++++++++++++++++++++++++++++++++++ CONTENTS.md | 6 + 2 files changed, 1607 insertions(+) create mode 100644 09_mappings/04_content.ipynb diff --git a/09_mappings/04_content.ipynb b/09_mappings/04_content.ipynb new file mode 100644 index 0000000..97c3875 --- /dev/null +++ b/09_mappings/04_content.ipynb @@ -0,0 +1,1601 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/04_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 9: Mappings & Sets (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In this part of the chapter, we look at the `set` type, a concrete example of the more abstract concept of *sets* as a specialization of collections." + ] + }, + { + "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 members. The redundant `7`s and `4`s below are discarded." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers = {7, 7, 7, 7, 7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4, 4, 4, 4, 4}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`set` objects are objects on their own." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139739115782880" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "set" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}" + ] + }, + "execution_count": 4, + "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 built-in [set() ](https://docs.python.org/3/library/functions.html#func-set) constructor as empty curly brackets, `{}`, already create an empty `dict` object." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "empty_dict = {}" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "dict" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(empty_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "empty_set = set()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "set()" + ] + }, + "execution_count": 8, + "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) constructor 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": 9, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a', 'b', 'c', 'd', 'r'}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "set(\"abracadabra\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Sets are like Mappings 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) object and can only be contained in a set once due to the buckets logic." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "12" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(numbers)" + ] + }, + { + "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": 12, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 2 3 4 5 6 7 8 9 10 11 12 " + ] + } + ], + "source": [ + "for number in numbers: # beware the non-order!\n", + " print(number, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We confirm that `set` objects are not `Reversible`." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "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": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The boolean `in` operator checks if a given and immutable object compares 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 without doing it." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "0 in numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 in numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "2.0 in numbers # 2.0 is not a member itself but compares equal to a member" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "There is are `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 (cf., the \"*Set Operations*\" section below). Further, the `MutableSet` ABC standardizes all the methods that mutate a `set` object in place (cf., the \"*Mutability & Set Methods*\" section below)." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "import collections.abc as abc" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(numbers, abc.Set)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(numbers, abc.MutableSet)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## No Indexing, Key Look-up, or Slicing" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As `set` objects come without a *predictable* order, indexing and slicing are not supported and result 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": 20, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "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": 22, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "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 mainly 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": 23, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers.add(999)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 999}" + ] + }, + "execution_count": 24, + "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": 25, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "numbers.update(range(5))" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 999}" + ] + }, + "execution_count": 26, + "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": 27, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "numbers.remove(999)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "KeyError", + "evalue": "999", + "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;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;31mKeyError\u001b[0m: 999" + ] + } + ], + "source": [ + "numbers.remove(999)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "numbers.discard(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "numbers.discard(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}" + ] + }, + "execution_count": 31, + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers.pop()" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}" + ] + }, + "execution_count": 33, + "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": 34, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "numbers.clear()" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "set()" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "139739115782880" + ] + }, + "execution_count": 36, + "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 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": 37, + "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": 38, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{0}" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "zero" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{2, 4, 6, 8, 10, 12}" + ] + }, + "execution_count": 40, + "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": 41, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "zero | numbers # zero.union(numbers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Of course, the operators may be *chained*." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "zero | numbers | evens # zero.union(numbers).union(evens)" + ] + }, + { + "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": 43, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "set()" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "zero & numbers # zero.intersection(numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{2, 4, 6, 8, 10, 12}" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers & evens # numbers.intersection(evens)" + ] + }, + { + "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": 45, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1, 3, 5, 7, 9, 11}" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers - evens # numbers.difference(evens)" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "set()" + ] + }, + "execution_count": 46, + "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": 47, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1, 3, 5, 7, 9, 11}" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numbers ^ evens # numbers.symmetric_difference(evens)" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1, 3, 5, 7, 9, 11}" + ] + }, + "execution_count": 48, + "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 that works exactly like the one for `dict` comprehensions described in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb#dict-Comprehensions) of this chapter 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": 49, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144}" + ] + }, + "execution_count": 49, + "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": 50, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{4, 16, 36, 64, 100, 144}" + ] + }, + "execution_count": 50, + "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": 51, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{81, 84, 88, 90, 96, 99, 100, 108, 110, 120, 121, 132, 144}" + ] + }, + "execution_count": 51, + "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 built-in [frozenset() ](https://docs.python.org/3/library/functions.html#func-frozenset) constructor. Even though `frozenset` objects are hashable, their elements are *not* ordered." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "frozenset({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12})" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "frozenset([7, 7, 7, 7, 7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4, 4, 4, 4, 4])" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index 8941fed..e2decdb 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -247,3 +247,9 @@ If this is not possible, - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/03_exercises.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/03_exercises.ipynb) (Memoization without Side Effects) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/04_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/04_content.ipynb) + (`set` Type; + `set` Methods & Operations; + `set` Comprehension; + `frozenset` Type) From 79c0e79d6357f4e29ea3fc7f558d9fe689a8f8a3 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 22 Oct 2020 18:40:10 +0200 Subject: [PATCH 104/142] Add initial version of chapter 09's appendix --- 09_mappings/05_appendix.ipynb | 970 ++++++++++++++++++++++++++++++++++ CONTENTS.md | 5 + 2 files changed, 975 insertions(+) create mode 100644 09_mappings/05_appendix.ipynb diff --git a/09_mappings/05_appendix.ipynb b/09_mappings/05_appendix.ipynb new file mode 100644 index 0000000..fbe47e3 --- /dev/null +++ b/09_mappings/05_appendix.ipynb @@ -0,0 +1,970 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/05_appendix.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 9: Mappings & Sets (Appendix)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [collections ](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 use cases." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## The `defaultdict` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [defaultdict ](https://docs.python.org/3/library/collections.html#collections.defaultdict) type allows us to define a factory function that creates default values whenever we look up a key that does not yet exist. Ordinary `dict` objects would throw a `KeyError` exception in such situations.\n", + "\n", + "Let's say we have a `list` with *records* of goals scored during a soccer game. The records consist of the fields \"Country,\" \"Player,\" and the \"Time\" when a goal was scored. Our task is to group the goals by player and/or country." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "goals = [\n", + " (\"Germany\", \"Müller\", 11), (\"Germany\", \"Klose\", 23),\n", + " (\"Germany\", \"Kroos\", 24), (\"Germany\", \"Kroos\", 26),\n", + " (\"Germany\", \"Khedira\", 29), (\"Germany\", \"Schürrle\", 69),\n", + " (\"Germany\", \"Schürrle\", 79), (\"Brazil\", \"Oscar\", 90),\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Using a normal `dict` object, we have to tediously check if a player has already scored a goal before. If not, we must create a *new* `list` object with the first time the player scored. Otherwise, we append the goal to an already existing `list` object." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'Müller': [11],\n", + " 'Klose': [23],\n", + " 'Kroos': [24, 26],\n", + " 'Khedira': [29],\n", + " 'Schürrle': [69, 79],\n", + " 'Oscar': [90]}" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "goals_by_player = {}\n", + "\n", + "for _, player, minute in goals:\n", + " if player not in goals_by_player:\n", + " goals_by_player[player] = [minute]\n", + " else:\n", + " goals_by_player[player].append(minute)\n", + "\n", + "goals_by_player" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Instead, with a `defaultdict` object, we can portray the code fragment's intent in a concise form. We pass a reference to the [list() ](https://docs.python.org/3/library/functions.html#func-list) built-in to `defaultdict`." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "from collections import defaultdict" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "defaultdict(list,\n", + " {'Müller': [11],\n", + " 'Klose': [23],\n", + " 'Kroos': [24, 26],\n", + " 'Khedira': [29],\n", + " 'Schürrle': [69, 79],\n", + " 'Oscar': [90]})" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "goals_by_player = defaultdict(list)\n", + "\n", + "for _, player, minute in goals:\n", + " goals_by_player[player].append(minute)\n", + "\n", + "goals_by_player" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "collections.defaultdict" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(goals_by_player)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A reference to the factory function is stored in the `default_factory` attribute." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "list" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "goals_by_player.default_factory" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we want this code to produce a normal `dict` object, we pass `goals_by_player` to the [dict() ](https://docs.python.org/3/library/functions.html#func-dict) constructor." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'Müller': [11],\n", + " 'Klose': [23],\n", + " 'Kroos': [24, 26],\n", + " 'Khedira': [29],\n", + " 'Schürrle': [69, 79],\n", + " 'Oscar': [90]}" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dict(goals_by_player)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Being creative, we use a factory function, created with a `lambda` expression, that returns another [defaultdict ](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": 8, + "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": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "goals_by_country_and_player = defaultdict(lambda: defaultdict(list))\n", + "\n", + "for country, player, minute in goals:\n", + " goals_by_country_and_player[country][player].append(minute)\n", + "\n", + "goals_by_country_and_player" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Conversion into a normal and nested `dict` object is now a bit tricky but can be achieved in one line with a comprehension." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'Germany': {'Müller': [11],\n", + " 'Klose': [23],\n", + " 'Kroos': [24, 26],\n", + " 'Khedira': [29],\n", + " 'Schürrle': [69, 79]},\n", + " 'Brazil': {'Oscar': [90]}}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{country: dict(by_player) for country, by_player in goals_by_country_and_player.items()}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## The `Counter` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A common task is to count the number of occurrences of elements in an iterable.\n", + "\n", + "The [Counter ](https://docs.python.org/3/library/collections.html#collections.Counter) type provides an easy-to-use interface that can be called with any iterable and returns a `dict`-like object of type `Counter` that maps each unique elements to the number of times it occurs.\n", + "\n", + "To continue the previous example, let's create an overview that shows how many goals a player scorred. We use a generator expression as the argument to `Counter`." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[('Germany', 'Müller', 11),\n", + " ('Germany', 'Klose', 23),\n", + " ('Germany', 'Kroos', 24),\n", + " ('Germany', 'Kroos', 26),\n", + " ('Germany', 'Khedira', 29),\n", + " ('Germany', 'Schürrle', 69),\n", + " ('Germany', 'Schürrle', 79),\n", + " ('Brazil', 'Oscar', 90)]" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "goals" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "from collections import Counter" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "scorers = Counter(x[1] for x in goals)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Counter({'Müller': 1,\n", + " 'Klose': 1,\n", + " 'Kroos': 2,\n", + " 'Khedira': 1,\n", + " 'Schürrle': 2,\n", + " 'Oscar': 1})" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scorers" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "collections.Counter" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(scorers)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Now we can look up individual players. `scores` behaves like a normal dictionary with regard to key look-ups." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scorers[\"Müller\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "By default, it returns `0` if a key is not found. So, we do not have to handle a `KeyError`." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scorers[\"Lahm\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`Counter` objects have a [.most_common() ](https://docs.python.org/3/library/collections.html#collections.Counter.most_common) method that returns a `list` object containing $2$-element `tuple` objects, where the first element is the element from the original iterable and the second the number of occurrences. The `list` object is sorted in descending order of occurrences." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[('Kroos', 2), ('Schürrle', 2)]" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scorers.most_common(2)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We can increase the count of individual entries with the [.update() ](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": 18, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "scorers.update([\"Lahm\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Counter({'Müller': 1,\n", + " 'Klose': 1,\n", + " 'Kroos': 2,\n", + " 'Khedira': 1,\n", + " 'Schürrle': 2,\n", + " 'Oscar': 1,\n", + " 'Lahm': 1})" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scorers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we use a `str` object as the argument instead, each individual character is treated as an element to be updated. That is most likely not what we want." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "scorers.update(\"Lahm\")" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Counter({'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": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scorers" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## The `ChainMap` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Consider `to_words`, `more_words`, and `even_more_words` below. Instead of merging the items of the three `dict` objects together into a *new* one, we want to create an object that behaves as if it contained all the unified items in it without materializing them in memory a second time." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "to_words = {\n", + " 0: \"zero\",\n", + " 1: \"one\",\n", + " 2: \"two\",\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "more_words = {\n", + " 2: \"TWO\", # to illustrate a point\n", + " 3: \"three\",\n", + " 4: \"four\",\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "even_more_words = {\n", + " 4: \"FOUR\", # to illustrate a point\n", + " 5: \"five\",\n", + " 6: \"six\",\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The [ChainMap ](https://docs.python.org/3/library/collections.html#collections.ChainMap) type allows us to do precisely that." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "from collections import ChainMap" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We simply pass all mappings as positional arguments to `ChainMap` and obtain a **proxy** object that occupies almost no memory but gives us access to the union of all the items." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "chain = ChainMap(to_words, more_words, even_more_words)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's loop over the items in `chain` and see what is \"in\" it. The order is obviously *unpredictable* but all seven items we expected are there. Keys of later mappings do *not* overwrite earlier keys." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4 four\n", + "5 five\n", + "6 six\n", + "2 two\n", + "3 three\n", + "0 zero\n", + "1 one\n" + ] + } + ], + "source": [ + "for number, word in chain.items():\n", + " print(number, word)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When looking up a non-existent key, `ChainMap` objects raise a `KeyError` just like normal `dict` objects would." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "KeyError", + "evalue": "10", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "\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/3.8.6/lib/python3.8/collections/__init__.py\u001b[0m in \u001b[0;36m__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 896\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 897\u001b[0m \u001b[0;32mpass\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 898\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 899\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 900\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/3.8.6/lib/python3.8/collections/__init__.py\u001b[0m in \u001b[0;36m__missing__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 888\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 889\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--> 890\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 891\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 892\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]" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index e2decdb..76c1a8b 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -253,3 +253,8 @@ If this is not possible, `set` Methods & Operations; `set` Comprehension; `frozenset` Type) + - [appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/05_appendix.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/05_appendix.ipynb) + (`defaultdict` Type; + `Counter` Type; + `ChainMap` Type) From 8566a99c6af6ab1bd72842dd8d33eeb9c20b6af8 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 22 Oct 2020 18:41:12 +0200 Subject: [PATCH 105/142] Add initial version of chapter 09's summary --- 09_mappings/06_summary.ipynb | 77 ++++++++++++++++++++++++++++++++++++ CONTENTS.md | 1 + 2 files changed, 78 insertions(+) create mode 100644 09_mappings/06_summary.ipynb diff --git a/09_mappings/06_summary.ipynb b/09_mappings/06_summary.ipynb new file mode 100644 index 0000000..0b9c814 --- /dev/null +++ b/09_mappings/06_summary.ipynb @@ -0,0 +1,77 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 9: Mappings & Sets (TL;DR)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`dict` objects are **mutable** one-to-one **mappings** from a set of **key** objects to a set of **value** objects. The association between a key-value pair is also called **item**.\n", + "\n", + "The items contained in a `dict` object have **no order** that is *predictable*.\n", + "\n", + "The underlying **data structure** of the `dict` type are **hash tables**. They make key look-ups *extremely* fast by converting the items' keys into *deterministic* hash values specifiying *precisely* one of a fixed number of equally \"wide\" buckets in which an item's references are stored. A limitation is that objects used as keys must be *immutable* (for technical reasons) and *unique* (for practical reasons).\n", + "\n", + "A `set` object is a **mutable** and **unordered collection** of **immutable** objects. The `set` type mimics sets we know from math." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index 76c1a8b..28dcbc1 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -258,3 +258,4 @@ If this is not possible, (`defaultdict` Type; `Counter` Type; `ChainMap` Type) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/06_summary.ipynb) From e0aa914dc6512a4d3d09cdc6eadc5ed3722fdf36 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 22 Oct 2020 18:47:40 +0200 Subject: [PATCH 106/142] Add initial version of chapter 09's review --- 09_mappings/07_review.ipynb | 247 ++++++++++++++++++++++++++++++++++++ CONTENTS.md | 1 + 2 files changed, 248 insertions(+) create mode 100644 09_mappings/07_review.ipynb diff --git a/09_mappings/07_review.ipynb b/09_mappings/07_review.ipynb new file mode 100644 index 0000000..7e7722d --- /dev/null +++ b/09_mappings/07_review.ipynb @@ -0,0 +1,247 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 9: Mappings & Sets (Review Questions)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb), [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/02_content.ipynb), and [third ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/04_content.ipynb) part of Chapter 9.\n", + "\n", + "Be concise in your answers! Most questions can be answered in *one* sentence." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Essay Questions " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: `dict` objects are well-suited **to model** discrete mathematical **functions** and to approximate continuous ones. What property of dictionaries is the basis for that claim, and how does it relate to functions in the mathematical sense?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: Explain why **hash tables** are a **trade-off** between **computational speed** and **memory** usage!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3:** The `dict` type is an **iterable** that **contains** a **finite** number of key-value pairs. Despite that, why is it *not* considered a **sequence**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: Whereas *key* **look-ups** in a `dict` object run in so-called **[constant time ](https://en.wikipedia.org/wiki/Time_complexity#Constant_time)** (i.e., *extremely* fast), that does not hold for *reverse* look-ups. Why is that?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: Why is it conceptually correct that the Python core developers do not implement **slicing** with the `[]` operator for `dict` objects?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: **Memoization** is an essential concept to know to solve problems in the real world. Together with the idea of **recursion**, it enables us to solve problems in a \"backwards\" fashion *effectively*.\n", + "\n", + "Compare the **recursive** formulation of `fibonacci()` in [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/02_content.ipynb#\"Easy-at-second-Glance\"-Example:-Fibonacci-Numbers--(revisited)), the \"*Easy at second Glance*\" example, with the **iterative** version in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb#\"Hard-at-first-Glance\"-Example:-Fibonacci-Numbers-%28revisited%29), the \"*Hard at first Glance*\" example!\n", + "\n", + "How are they similar and how do they differ? Also consider how the flow of execution behaves when the functions are being executed." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: How are the `set` and the `dict` types related? How could we use the latter to mimic the former?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## True / False Questions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Motivate your answer with *one short* sentence!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: We may *not* put `dict` objects inside other `dict` objects because they are **mutable**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: **Mutable** objects (e.g., `list`) may generally *not* be used as keys in a `dict` object. However, if we collect, for example, `list` objects in a `tuple` object, the composite object becomes **hashable**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: **Mutability** of a `dict` object works until the underlying hash table becomes too crowded. Then, we cannot insert any items any more making the `dict` object effectively **immutable**. Luckily, that almost never happens in practice." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: A `dict` object's [.update() ](https://docs.python.org/3/library/stdtypes.html#dict.update) method only inserts key-value pairs whose key is *not* yet in the `dict` object. So, it does *not* overwrite anything." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q12**: The `set` type is both a mapping and a sequence." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index 28dcbc1..b7d6e4f 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -259,3 +259,4 @@ If this is not possible, `Counter` Type; `ChainMap` Type) - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/06_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/07_review.ipynb) From 7b339acd5421228d96cc1f3180df5a464112da0e Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 22 Oct 2020 18:49:35 +0200 Subject: [PATCH 107/142] Add initial version of chapter 09's further resources --- 09_mappings/08_resources.ipynb | 228 +++++++++++++++++++++++++++++++++ CONTENTS.md | 2 + 2 files changed, 230 insertions(+) create mode 100644 09_mappings/08_resources.ipynb diff --git a/09_mappings/08_resources.ipynb b/09_mappings/08_resources.ipynb new file mode 100644 index 0000000..9b23588 --- /dev/null +++ b/09_mappings/08_resources.ipynb @@ -0,0 +1,228 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/08_resources.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 9: Mappings & Sets (Further Resources)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Here, we list some conference talks that go into great detail regarding the workings of the `dict` type." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## How `dict` objects work" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[Brandon Rhodes](https://github.com/brandon-rhodes) explains in great detail in his PyCon 2010 & 2017 talks how dictionaries work \"under the hood.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "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\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from IPython.display import YouTubeVideo\n", + "YouTubeVideo(\"C4Kc8xzcA68\", width=\"60%\")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAUDBAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChwLCAgQCQgIDSANDh0dHx8fCAsgICAeIBweHx4BBQUFCAcIDwkJDxUQEBASFRIVFRYTGBUYFxUVEhIXFRUVFRIWFRIVFRUVEhISFRIVEhUVEhISEhISEhISEhUVEv/AABEIAWgB4AMBIgACEQEDEQH/xAAdAAEAAQUBAQEAAAAAAAAAAAAABgMEBQcIAQIJ/8QAWRAAAQMDAQMEDAcKCggHAQAAAQACAwQFERIGEyEUIjFBBxcYMlFTVWGUldLUFSMzVHGB0xY0QkNScnN0kaEIJDVigpKxsrO0JTZEk6O14fBjdYOiwcLRhP/EABsBAQACAwEBAAAAAAAAAAAAAAAEBQECAwYH/8QAPREAAgECAQkFBgQFBQEBAAAAAAECAxEEBRIUITFBUpGhExVR0eEiMlNhcYEkorHBBhYzQvA0coKS8dIj/9oADAMBAAIRAxEAPwDeaIi1KkIiIAiIgCIiAIiIAiKjcJ91DNKBqMUUkmCcZLGlwGeroWs5qEXJ7jpSpSqzUI7XqKyKKzbVvaHncN5glPfnju4qWQdXXygj+iFUftQ8Oc3ct4PezOs/g10dJno8D9X1KJ3hR8ehb/y9jeHqiTIrSy1hqIGTFoYXmQaQcgaJHx9P9DP1q7UqnUU450dhU16E6M3TntQREW5yCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIArO+/elV+rT/4TleKzvv3pVfq0/8AhOUfF/0pfQn5L/1VP/cjX1X3s35tT/lbaq0vyr/00v8AzmBUavvZvzan/K21VpflX/ppf+cwLy59T8CZbI/eUP0z/wCYlWVWK2R+8ofpn/zEqyq9Lgf6Mf8AN58xy1/rKn1CIillWEREARWlfcoIHRNlkDDM7QwHrPhP5LeI4nwhXawpLYbypySUmtT2BERZNAiIgCIiAIsXd9oqCjeI6qrp4Hlm80yyNaRHqLRI7PeR6gRqdwyCqlivdHXx72iqYqqLJAkheHsdglpLHjhIzUHN1NyMseOkFBYyCKO7QbWU0FMJaeWCeaeKV9HHvDu59zNDTzO3jAeZHJUR6scenCq7JbSQVwqImzRS1VFK+GrbDFVRRte2eogD4+VRgyRF9NM3UzU3MMoDjjKGbPaZ1FbG4U4nFKZ4RVOiMzaYyx8odC12kzCHVrMQccasYVyhgIiIAiIgCIiAIiIAiIgCIiAIiIAiLLbN0YkeXuGWx4wD0Fx6PpAxn9iAoUlpmkGQ0NB4gvOnP1YzhVpLDOBw3bvM1xz/AO5oClCLFzNiCyxuY4tcC1w6QeBC+FLrzQCdnDAkb3hPDh1tJ8Cw77BMBkGNx/JBIP1ZbhZMGJRfUjC0lrgQQcEHpBXygCIiAIiIAiIgCIiAIioXCsjgjMkhw1vg4lx6mtHW4o3YJNuyK6tKq5QR5DpGl4B+La4OkOB0BgOcqDXvaKac41bqInAjacF35zhxcfN0LEDBHhBUWeJ3R2llRwD1SqXzb7vM2LS7R0cmfjdGDpIkBaQesEdRWQpqqKX5ORkn5j2ux9IB4LVYAHQMcOnLi4/SXHKRTAOGl2HA8C04IPmI6CudPEyS9uxIxGApzk3QzrLx1m2lZ3370qv1af8AwnKM2DahzSI6k62dAl/Cb+fjvm+fp+lSW9nNJVEcQaWYgg8CDE7BC3xE1KjK3gcMBSlTxdNPiRr+r72b82p/yttVaX5V/wCml/5zAqNX3s35tT/lbaq0vyr/ANNL/wA5gXmj6f4Ey2R+8ofpn/zEqyqxGyjw2hhJOADPxPR98y4V5NcqdmNc0bc9GpwGV6LB1Ixoxu0v/T5vlbD1KmMqOEW9e5X3F2is4rpTvOls0bjx4BwJ4DJwB5sr2e5U7MB8rGEjIDnaTjJHQfOCpXbQ8VzK7Q697ZkuTLtWd5uLKWF0zw5wBADWjLnOd3rR4CfCUiulM84bNG49OA4HgFjdtJGuonlpBw9g4dS51a6UG4tXSJOCwE54iEKsWk5JPVbea82gqZ6iYzTtwXjmNzlrIwSGtb5un96m2xd8e7RR1IdvmgiOTvt41rdWl5HQ8NB4npwOtRiledB4n5AfhuH4Q4/L8D5+H0rI7Ifygzr5p68/7M7wvKpcNXmqqd9rsz6BlbA0Z4OUc23Zxbj8rI2CiIvRHy0IiIC0vNeykpqiqkDjHTQTVDwwZeWQxukcGjrdhpURud7uVCGT1c9K9wglrqmgio5hE2hpzHyxtJcdZ3tXBHK1/PA3mh2GsBy2cPaCCHAEEEEEZBB4EEHgRjqUIvmyMTJaIRMqJKR8vI6yN09TVOioZG644IGzSEU9C6qipmSBgyWaQSGNIQ2jYg+3F+uzzPAZpRUx187KeO3zy22TUJ5bfTwz1ETyHxRm6bP14M2Q5tRPqbgYWY2Eu4irY6hsVQ6jlkrYf4vR1M797cpKK6AzQwxl9KIrj8P07mvA0lp1YU/rdnreamS4TxMM25Mcj5ZZOT6MNaXvp3v5PvdDGM3pbqwxozjgsHe+yfaabLWyvqnA9FKwOZk8flnkRnj1tJXSnSlP3Vc1qYmnBe07Gsbf2PL5JbKGJ8AhqqATVtIWTtw19wjoJK2jnEpGJnSRXNp/BBrIyDwWxux1bK2mrrhJVUkkDJwIIH7yCVsjae6XyvNQ7cyHdRPju0DWtdzsslyABk4p3ZYmkBdS2eolYM88yPIAHSTuoCBw86N7KVa1ofLZJxEeOsPnDdPhBdTaT+1dtDq/Lmjg8oQa322bGY3byhrqi4XONlFNiUmCCqfFKyDc1VFa7dGGVLBzwIqnaCU6Dlu6GcEtzS2Orp6eB08QmNNQ0sIpaemqtxQG9XmVtwZZ3Uxdqkpmx3W1UzHBvNEU3QThSe09l22SnTMyopj1lzGzReAgmIl/7Wq6sOxtlnonR00rqiJzqcCqp6hsNbGykaGUkBraINnLYo9TQZSXYe7JK51KE4e8jrSxVOorJlGxbfgGjgqzHVOqdTvhKijFPb2wyQ1dZRvkiqqgzRvdR0csrmt1aAGF2kOCnFFVRzxslie2SORjJGPaeDmSMbIw+bLHtdx/KC1Ft32Maw0tfHQyRysqI6uGnp44zTSQQ1TYYmU5e2TTK0CG3xGU40w2vRgl7itnbJ2VtBSR04dvJOMlRMRgz1Eh1TS4J5rS7gGdDWtjaOAC4nWajtRlURENAiIgCIiAJnq/76//AMRY6kq3Oq6iI40sZHjw9GTn+v8AuC1lK1vmdadJzUmv7VfqjIoiLY5I8Bz0ef8AdwXqx2zlW6aASOABL5M46OLi7/7LIrWMs5XOlak6c3B7mZrZy3skDpJBqAdpa09GQASSOvpCz8MLGcGNa0E5IaAOPnx5lE6G5SwtLWacF2o5GeJAHh/mhV/h2f8Amf1T/wDq2OZKUUW+Haj+Z/V/6p8O1H8z+r/1WLGbkpRRf4en/wDD/qn/APV58O1H8z+r/wBUsLlfayMB8bgOLmuB8+kjB+nnfuCwiuq+ukm069PNzjSMdOM9fmVqsmAiIgCIiAIiIAiIgC19tZcjPOWg/FREsYOokcHP+sj9gCnNzm3cEzx0sikcPpDCW/vwtY6gCNLHPGBvAQRjwuZIHafqcomLq5iSLXJWEdeTs0rLefAOOpp4Y5wcRx/McHD6ivqR5cS5xy48SQMAn6OpfK9jLc87Vjj3uM9BxgE4PFRM2KedvLNVqkoqjf2bni+mPdjSAwB2MgNOp2CS3JLtPAk9A615q4cWafyX4c3W3HAljjkFXMWljdTiB4Sf3D6Vr7M1dnRuthZOnB3b8NZ8Mpienh9HFZqhuj20ktNwka9kkbX6uMYkaW4IHSATnj4VjopAeLT0eDqPT9SpspxqLy5znHpyIwCOrVoYC8jwuJ6Ek5PUtj2nKjGEbyqNxlHXHVvFX3kp8LKg/wBaGjjaPpJgf+5VpvlX/ppf+cwKk6dgdpLhqGOGejOMZ8HSP2hVMcc9Q3eTniXPuFLI4nP9I/UVCrUFFXiegyflKc2oVlZvY9lyS28f6JHAH5fgXFrT/GZelw70edYS9UofG92lmYyx+dZPDSA7IxzeH7dIWbt/8kt70fL8XN1NxymXvmjvm+ZYutqd017gGO5zGkBungY884k87gP3hc6vux+n7sk4F/8A61GviftEirC0EEbvIII5x6vqXjGtAaOZhrWtHOPQ1uB1eAK9ZQulL2slMZY1zycB+QzgW4dIB19IPUvl1C+JrHPmMm81YGGt06HFp72Q5zw6cdC4XPQZ0b23mS2fpQGb3SzL5GBvPcOa0jJB63avwf5qytzH+jZOAHx/Q1xeO+z0njnzfSrO21GW7shjd0+JgJGvVxPgdwfkd951Qvty0winG7LXOdI4hpaCQ8tAx1Y0nJ6+K7UpJXv4WKnEU5Vakbbpp/ZFhSg6DwPyA/L/ACh/N6foWS2R/lBn5vXn5sfCFYmop25AFPjQGjVnLuIOl3M7zp/YEguTaeojmi3fBo1AZwTgsLRze90cMrNKSjNN7mjviqcqtGpCO2UZJfdGzkUNqdsXn5KFgb1F5c4n6mkYSl2xkB+NhY5vXuy5pHn5xIK9Iq8GfKJYOrFtNa0TJFDrx2Q6OmIaYap5IyC1kQYfCA4ydIP/AMLCVPZYb+KoXHzyVAb/AO1sR/tVhSwNaolKK1Mpq+UsPRk4zlZrdZmzFFtvNtqa1N0kb6qe3MdM04OCcB8rvxcefrPUOlQup7KlYQd3TU0fncZJCPP3wGVDLZc5Yqs1vNqKtxL95UME5Eh/GNb0B4AwD1dWFOoZJne9TlcrsRl2la1O/wBbbCXW6wXO/wA7jdqiSlhjEcgpI26CBJxZoifzYnaeOqTU7ipfsDszQU76rRTRudFUujZLK0SyhjcYw94yPqUAi2ovJlkmj3gklDQ8x0jTkMGG4BjIHDwL2lu1/YXmMVoMrzI/TRZ1PPSfkeH1LWpgcTNq84xSvqTtq3biZSyxgKUZZtOcpNR9ppN3Vs7fqXh8jZWzp/0RJ+jqv7Hqq8/6H/8A5R/8LV0FTf2RmFkdwERDgWCjk0kP74fI545X0anaDdbjd3HdadGjkcmNP5PyOcKDHIdRRt2kfdttLSp/FtCVRyVKdnVVTYtnhtNg7X22nmtsDpYIpHBtKA9zGl4BY0HD8am8PAottTsC63zQ1FlqJqeaWTdtidJwyeIa2U8Sw472TI86wtRX350YheyuMTdOGuo3ADR3vHc54YXtXtFe3GN0u/JieJGF9G0YeOvhEM/QVKpZOxVL3KkXqSs3q1bdxBr5cwFe3aUZq0pttJJ60s3Xfc0S/Y3sgudN8H3ePkla1wYJHN3ccruhoeDwieepw5p6scAthrnnau91NwawVrIi6M8yXcCGYNPSzUOlh/JKzWz/AGRa2lgZARFUtjGlr5te90/gtc9rsOwOGSOoKRXyXKSzoWvvV/0KuhlunFuM723O2v7m7EWrqfssP/GUDT52VJb+50RysrQdk+kkc1hpatrncAGCKT/7g4UKeT68Fdx1fVFjTyrhpvNjLX9GTxFgn7V0g8afoYP2cXdP0K9tt5p6g6Y5Of8AkPGlx+gHg76lX58b2uWjpSSvYyCIqb6iNpw6RjT4C9oP7CVs2aJXKijlqmzcJudkPEjQep2lzNIB6DwB/YVXu1c6RpjjIYMkOdnJcBwwCOgLBvp9GHGRrOcA0l2nnk80AnryoM8XRv72z5M9DhMk4lQfs+8rbV5k4VC4SaYpXA4IjeQfA7SdP15wsdT3UtYBKAXDhqBDdXnII6Vj73VOlGS4MhbxAyeJ8JI752eGAtnjaVtvRkelkXFKprjs+a8y72Md8VI38mTIb0EAtbxx4CQf3rOqG0DXsLZYpR5jxIcPAR1jh0ebwqT0tc1zHPdiMMAL3Fw0gYJJJPQBjrWaOJpP2YszlHJuIi3VlHV9voXaKhQ1cc7BJC8SMd0Ob5ukEdIPmKrOIHE8AOJJ4ADpyfMpaaauinlCUXmtWfgeorehrYpwXRPDw06TjIIPVwPHBHEHr6lcImnrQnCUHaSswiKk+pjBwZIwfAXtB/YSjZqlcqojTnBHEHrHFEAREQBERAEREAREQFhtD96VH6J/90rWq2VtD96VH6J/90rWqiYjaWWC91nxJJp/7AA85LjgD6VUc0tOHAtI6QcZH0ox7m8WktPRlpIP7upeftP0nJ/aVE9rO+Rat0eyVr599fgesHEfSFfPbnqHA5GRkZ848Csou+H0j+1X62krqzI8akoSUo6mj5YD1kHADRgYAa3vWgE5wB4V94/6FAcH/vqVCGJwc5xc0B3SyNjw08QQTqkIB4dDQFz92yitRJWbXzqlWdpbtW0+9ByS3S3OcnTl2HABwBzjBAHSCvmpHNPmwqqp1Pen/vrW0YJO63nGpialRKMndR2FOkrpYgWskcGu6W5GPpAcMBwODlZWvk1Ma4F5aZIi3UwOGCw96GjLvPnrysCsq0fxeMkYBlZxdK5rTjWOBHyY6uHSo2MjqTLrIdV57hu2/oXEp5junvWfiHeNlXkB4Hp+Uk/EOH4qT9n0fWvmVw0O4s71n+1SeMk830fR09a8hcMHizv5P9rkP4t/XjiPP9SrLHqirQk65e/HPg72PTww7OoOHeecKxvhOpmdZOl3fgB3yj8cAMY8H1K9oOLpcYdh8J5kznYwHcS48f6CrD6/rJPXnr444lTsJG7ueey5iFCGZvf7EccSRjOPPhrsfU4Ku+F0mCXyjhjnGMn/ANgxhXd4hGA8DBzpPn4E5+nh+9U4e9H0KXOjGTuynw+Va9GGbB89di2fTkDp1YHE9f0lUlkVj5BxI8BK6pWIMpubcntZ901shrHtppshsuprHg4dFK5jmxyDqdhxHNPAqPbLNgZyuhroIeWUtU0757e+ha4snbx4FgIY4HrEvmUqsP31T/po/wC8FH+zzbuT1sNZHzRWU745COt8IbG/P0wyRj/01d5OnOrTlQzmr7PkygyjGlh8RDFSgpZu1W2ovex9bY7tdam4mFkdBSvDaaBjGsjc8fI6mNGCQz40565GdSlG3u3zLNOyI2+ephbSPr6yenfAwUVGyojp3TGKQh0+HyNOlnHCyvY1tPI7XSRYw98Ynlz0mSf4wg/mgtZ/QCie3uzXwrtBDRyVU1PSvsM/LIoWxaq2n+E6XNK+SRpMMbiBlzMHhjK3xVZznZPUtSI2Cw8Ixu1t1skT9uoRcPgzcymqNzit7W6mAOjltrrmK4Z6acRxytx05YVibb2VIJIq2oloK+npaagrLnTTuED219HQzcnndC1snxMu90gRy4yHA9CqOs8H3Zw1mkb4bOyNz1cLjGwO/OEcj258DlBLTtLBUbK3Gx07Z5q6ksN3fWMZE7TSyx1M8ApJ885tW4ue4MxxETyo1yaoRe7wJu3spRR05lrKCpopYrtQ2qqp5Zad5p3XCKOeCpMsLyySHczMcQOI5yluy96bXwyTMjdGI6yuoy1xBJdQ1c1I5/DqcYS7H85aQvNLSX+K5Mik39Dctp7LTxVMRcGOcywwQvdG8cSWTt4+din/APBxfUu2fhdWZ5W6tuzqnIAPKDc6ozZA4A7zV0JczOEUrmUpNvWOu0lrkoquBmmt5PWybvc1T7c2nfWNjiDt61jRUMw8jDuOFhrJ2WDUxVExstyjbFb4LpTsa6nqJquirJtxRPiip3kse97ZSQ/gwROJOFb020FHWbR3je1cDJbTb5bdR0j5A2UiSJlbdK3Q4cGZbTxZ8FNIetRXsDSNlFS9k2/dBsna6OrIbI1lNWQOuLeQlsg5k0dO2DU0dZeetLmezjbWvAnDuyY9zrYWWipfT3Ntl0VLpo2MjkvTZHsia1zM1Bijikc4s6AATjKdmjZsS0vL6doZUUfOfoAG8p85fqGMOLDz+PVvFiNjP4z9w1J0torAy8Sjq1tttLbaTJ6sural3/orbMkbXAtcA5rgWuaehzXDBB8IIXWhWdOSkiLisPCpFxsaUud4tz6N1bySmEslG2JkTY2sYyvMmiR4azqa0Pk+jSqeytAYYGukzvZAHOyAHNaeLGebhgnzkrCWvZ1xvTLQ4l0Udc8OB/ChiBkc4+d1PGP6ynl1++J/00v+I5dMoSlRp9mpN57ctu7chk5U8TWVXMUXTgobF7y2stlc0tLI7Dm83ByHEkcR1jHHOetWwUiAxwHQOA82FRHoJMuq68TmCOPOH4Imkb+FjgMdbcjifpWDkcAC4kNA4kuIAHnJPQskQsPUOa7LCCQctJ6uLM48PQUnNsxh6cc61tW8vpJTTl2l7Zg1odqZwZIC0OyOJ08CsaKCo1PlqITKyQzGMPkcY2El2ox6uHN8H81fNOf4voxgtiweGARpIyBnzFfU0EIZqEoc7Dzo5PKCNOdOXObpwfMq7ELYewyK9U1uztX0Lc0FRAcTteNYaRrcS7GHacB34OGn9iq0kT3P1Nmjj3XxgbJjBIDW6gC7nu5/R5iqTg3U7HTHMYjwAzzC7Ix1eZHPa1ryS/Iy4tbgAtbE53BxaQDloWbvsfudFTXeH/C/3MvaXPjlw6aORkzN9pjAOkkNA6Hcx3hCr3Oed7hFHC2WBhbPK1x+UDA7g7mkNjGc/sWNs7ml7SC/JYXFrsYAcGFvENGTxP7Ff3KAPjJP4HPHDJOOBA4cOBz/AEVrCn7DktpzxGLzcZGjJJp25lhVcpjdvWRclhkDWlsUuGvdHpY5/MAy7Uf3o2urHgxMe6WM4dIx7tQLGHW4DUDpGGnJHVlW7CZKeGXUdJe4NjI4jiHai4DByQR/RKq24OMhDXacxyZ4E5wO94flZ0/0lvCclRes1xFCm8dC6Wxv7q9v0MlbayqinZK2BgbpEcrdZOpjw1zTqLea7DC4eHLlnp75ITzA1rerIyfrPQo3ZKgzMe8nAD93ox3xbkasjhgAYwfywri4gGJ+rowM4yOGRno4rvhqko0ypyzRhVxcYWs9Sb+p9XHaeeRjtA+IjcGTTx5BLnA6Q0Z4s5rskdKsmkEZHEHiCOggq0rTSMjaYmTODWOdKx7nMa52nLhHj8HIHT4AqtEG6TobpaS1wbknGqNjiMnzkrWliJ1Je0d8pZKo4eip01bd9fmZS1XF9O4FpJZnns6nDrx4HedTmN4cA5vFrgHA+EEZB/Ytcqe2X73g/RM/sCs8PJ7DyWLglZl2iIpRCCIiAxP3UWzylb/TqX7RPuotnlK3+nUv2iwXczP8tN9Xn3tO5mf5ab6vPvasexw3G+RC/HfD6rzM791Fs8pW/wBOpftE+6i2eUrf6dS/aLBdzM/y031efe07mZ/lpvq8+9p2OG43yH474fVeZmpto7U9pa6425zXAhzTW0pBB6j8YrLlth+dWr0yl+0Vl3M7/LTfV597TuZn+Wm+rj72sPD4V/39DKlj1sp9V5l7y2w/OrV6ZS/aJy2w/OrV6ZS/aKy7mZ/lpvq8+9p3Mz/LTfV597TRsLx9DOflDg/N6l7y6w/OrV6ZS/aL6+ErF88tfptN9qrDuZn+Wm+rz72nczP8tN9Xn3tNGwvH0Gfj+D8y8y/+ErF88tfptN9qnwlYvnlr9NpvtVYdzM/y031efe07mZ/lpvq8+9rGjYTj6DPx/B+b1Mh8JWP55a/TKb7VeG42L53a/Tab7VWHczP8tN9Xn3te9zM/y031efe00bCcXT0GflDg/MvMvOXWH51avTKX7RVHXSyFoYay16QcgcspgAfNiTzlY7uZ3+Wm+rz72nczP8tN9Xn3tHhcI9sunobwr5Ri7xjb/l6l+blY/nls9Op/tfOf2oLjY+gVls6/9up+vp/Gqw7mZ/lpvq8+9p3Mz/LTfV597WuhYLi6eh003Kvg/wDv6mSiu1kZnTW2wauk8tpsnHR0yr34Xsvz22em0v2ixnczv8tN9Xn3te9zM/y031efe1lYXBrZLp6HOdfKU/ejf6y9TISXOxuGHVlrI6cGspftF8i4WL55a/Tab7VWHczP8tN9Xn3tO5mf5ab6vPvazo2E4unoaZ+P4PzLzMh8JWP55a/TKb7VfBrrD86tXplL9orLuZn+Wm+rz72nczP8tN9Xn3tNGwnH0GflDg/MvMv4rjYmuDm1dra5pBaRWUuQR0EfGJd7jYqxrW1VXaqhrCXMEtXSPDSRgkZk4HCsO5mf5ab6vPva97mZ/lpvq8+9reNHDR1qbX2NZadL3qd/uvMzY2ntYwBcrcAMADltLwH+8T7p7Xn+Urd6bS/s+UWD7mZ/lpvq8+9p3Mz/AC031efe1jscNxvkYtjfhrmvMzn3TWvp+Erdnozy2lzjwfKdCDaa1jJFytwJ4kitpeJ6AT8ZxKwfczP8tN9Xn3tO5mf5ab6vPvadjhuN8jP474fVeZm27TWsDAuNtAHQBW0oA+gbxejae19Vyt3X0VtL18T+M8JWD7mZ/lpvq8+9p3Mz/LTfV597TscNxvkPx3w+q8zMu2htBJJr7YS4EOJq6MlwPAhxL+Ix1FfY2mtfH/SVu4nJxW0vEnpJ+M4lYPuZn+Wm+rz72nczP8tN9Xn3tOxw3G+Q/HfD6rzM2NprWMYuNuHDH37S8AOgfKdC+vuotnlK3+nUv2iwXczP8tN9Xn3tO5mf5ab6vPvadjhuN8h+O+H1XmXzLhYRUOrBVWkVThh1QKqk3pGkM4v3me9AH1L2S4WJxLjV2olxJJNZS5JJySfjPCrHuZn+Wm+rz72nczP8tN9Xn3tZlRw0ts2/sI6dH3advuvMvOW2H51avTKX7RVvhey/PbZ6bTfaLGdzM/y031efe07mZ/lpvq8+9rTRsJx9DbPyhwfmXmZP4Xsvz22em032ip/CFiznlVqz4eV0mf27xWHczP8ALTfV597XvczP8tN9Xn3tNGwnF0GflDg/N6l5y6w4I5VaQD0gVdKM/TiRfPKrB86tfptN9qrTuZ3+Wm+rz72nczv8tN9Xn3tYeEwb2y6eh1hispw92LX/AC9S7bVbPjjyq1cTk/xym4noycy8TjrXj6nZ85zU2rj0/wAcpuPV43wK17mZ/lpvq8+9p3Mz/LTfV597TRMHszunoZ0vKl86zv453qXkNZYGHLKq1tIGMitpujwfKqpLc7G4YdWWsjwctpsf4qx/czP8tN9Xn3tO5nf5ab6uPvaaJg9md09DWWIyk5Zzi7+Od6l02p2fGAKm1ADoHLKbA+gb1fRrLBnPKrV9VZTfaqz7md/lpvq8+9p3Mz/LTfV597TRMHszunoZeKym3nZrv453qXsFdYWd5V2pufBW0w6eJ/G+ZVJbpZHAtdWWsg9I5ZTfaLHdzM/y031efe173Mz/AC031efe00XB8XT0NZV8pSlnON3453qXBn2e6OU2rwfflN0f71VYq+wtGG1dqA/XKX6PGeZWPczP8tN9Xn3tO5mf5ab6uPvaLCYNbJdPQ3nisqTVpRbX+71Mh8J2P55a/TKX7RXcW0lqaA1txtwaAAAK2lAAHV8osJ3M7/LTfV597XvczP8ALTfV597Wyw+FWyfQ4OWPe2n1XmZz7qLZ5St/p1L9on3UWzylb/TqX7RYLuZn+Wm+rz72nczP8tN9Xn3tZ7HDcb5GPx3w+q8zO/dRbPKVv9OpftE+6i2eUrf6dS/aLBdzM/y031efe07mZ/lpvq8+9p2OG43yH474fVeZ0WiIoB6MIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIi1//AAg9vptmbBVXiCniqZKeSlYIZnOYxwnqI4XZcziCA8n6kBsBFp/Z7sp3WlvFvsu01qpqCS8Mldaq631hq6Oomga18tLK2VgkglDXswTkEyMHnUwq+yhs9DchaJbxQMuTpGxCkdO0P3zyAyBx7xs5LmgRk5OocEBMEUQ2s7J2z1pqmUVyvFDRVUga4QzztY5rXd66XqgYfypMBVdteyNYrK6Fl1ulHRPqBqhZNKNb2Z07wMblwizkazw4HigJUiit/wCyLYqCKknrLtQwQ18MtRRTPqGbqqghjZLJJBI06ZWhkkZ5vTrbjOVYXfsv7MUjqVtVe7fA6tggqadskwaXU9SwSQTPBHxMb2ODg6THSgJyi1dtF2brRRbSUWzkksRlq4g6Sp5QwMpp5WNkpKaSPTl0kzZIS3B/GtUgqOyls7Hcvgh95t7LjvBDyV1QwPE7nBrYC7vBUFxDd2TnJAwgJiiilx7I9ip6/wCC5rpSR3HlFLSCidJ/GeUVrQ6ljEQGol7XNOejnNzjKoWzsp7O1NwNqgvNBLcA90XJmTtL3SszriY7vJJRpdzGknmlATJEUbO3dn5PcKr4QpuT2qd9LcZtfMo6iJzWSRTnHNeHOaMedASRFCtoOyxs3b+FbeaGmduIKprJZsSPgqRqgkjjxqlDm8cNzwBWVtu2tpqZKKGnr6aaS4076qhbHIHcqp487yWEjg8NwcjqxxQEgRRil7INklgoqqK50klPcasUFDMyUOZVVhe6MU8JHfSa2OH1Kzt3ZU2cqJZIYbzQSSQwVNTO0TtAgp6NwZUzVDjzYI2OPS/HQfAUBM0UP2V7J+z90FUbfd6Kr5FE+epEUvPigj7+cscNRhHDnjhxCpbP9lnZq4VcVDRXu31NXOzXDBFO1z5Bgu0s6jJpaTo6cDOEBNUUDq+zHstDUcklv1tjqRUyUjoX1DWvjqInaHxzZ4Q4fzdT8DIPFZPbfsiWOyOhju10pKGSfjFHPIBI9udO83becItQI1nhwPFASlFFtqeyJYrXT0tVX3WipqeuDXUczp2OZVMc1jhJAYyd7FpkjO8bwAe3J4rXvYj7NtPW0V0r7zWW+mpYdqamxWyohDxDUx6IX0XPD3CSR7XvdvG4bhpPADKA3WihHZ120m2d2fuF5ggjqZaJtOWwyucyN++q6endqcziMNmJ4eALC9mXsmVNi2eo7zDSwzzVM1uidDK57Y2itbl5Dm87LT4UBtFFEdseyZYLPUR0tzu1FRVMoDmQzzBsgY4kNkkaPkoyQ7nvwOaVk/uttnKjRcup+VCh+E9zvBq+D9WjlgPQ6n1cNQQGbRRyk26s83wburjTSfDG++C9EmrlvJwDPycDv9AIz4FV212xtdkgbU3Wup6GB7xGx879O8kIJ0RtHOe7AJw0dRQGeRai7EvZaZc3bUVNZV25lps1y3FHXRPbHA6hdHrZNNUOlLJCctGpuOkcFONhtvrNfGyutNypa/cFombBJmSLVqDDJE7ntadLsOIwdJwgJKi1htx2arTaNoLbYKl8e9rhLv6gzNY2gcIhJSsnjLcuM7nNY3B6XBRzsd9nygdNeqfaG5Wu3TUe0lytVBGX7hz6OkMbYpp9ch085zwZTpbzT4EBvJFFtt+yJY7I+GO63SkoZJ+MUc8gEj2507zdt5wi1ZGs8OB4q8t+2FrqKqOigr6aaqmomXGGGOVrzNQSECOshc3my07i4Ye0kIDOosLs5tXbrjSOr6GrhqqNhlBqYSXxZhGZdLgOdjzeBW1Dt1Z52250NxpZG3d0zbYWyAitdT534g/LLcHKAkaKK1XZGsUVLV1sl1o46Shq5KCqndKBHDWxY10hJ76oGRzG5PFU6PsmbPzW2W8RXeifbYHtjnrGzDdQSPdGxkcw76KQulj5rgD8Y3woCXItN7R9mOOS87MUdkq6GvoLnc7nbblMxr5jHLRUtJUtZTzNeGBwFSCThw57VnNreyRLLSuOytNDtDWtuLLbLu5tNDQSnVvZq2oYOMceG5bHx+MaehAbIRay7CXZErbxUXu23Sjpqa5WGqgpqp9DM+egqOUMkex0D5Gh7XjdPBY7oy3ryBs1AEREAREQBERAEREAREQBERAEREAREQBaX/hqW6oqtjbjBSwTVMzp7eWw08T5pXBtbA5xbHGC4gAE/Ut0IgOcbnU1W2e0Gyxo7XdKK1bO1RuldcLnRS0G9qI2xclpaNs3OmOuE6sdUg6McdQ0+xVa2C6bO3mTaGKoqb6+o3Nu2Zprg24ulqI3w3SC8uaHtaBznZeMAPHhC7sRAcrXARWa6bb0192euV8dfqinmt3JbfNUsudKYzHHRtrIGnkj4n46SCNOR1ZqiFmzu0dfXXfZ+vnt9x2atdFa4qallvTaHklHFBVWJ8zQTrdIzv38Hack8SupEQHG+wXY7roT2LaO62uR8cE+1NRV0tRTGeGijqjHVUTKwOaWQvLtLw2TiHcOkFV+zia4122Fubb6y3xzW+mhtkFl2Zp6kX+JlLkyXC6Glc9sUJaGgRlmkNwOc3j2CiA5VtNPNb7v2M6+ooK3kzNlYLXPLFQzTGmr30UMDI6prGaoCJHtGX4xh3gOITX2OpZsncdjJbBcajampvhe2qFvkfT1Tn10c4u3woW7psPJWuiL3HrOeGcdwIgNBdjTZU/d3tlWVtvE746bZ0UNZUU2Q+WO2RCodR1MrMB29hjyWHgWNz0LW2wdLU0NfYrXa7dcaqjhvuufZ/aLZ5sj9nYzUSPnu1JtCyMRktxra4OdnLOk9PYyICPbE7X0l3FwNHvcW251dpqd7Hu/43RFgn3XH4yLLxh3mK5T2olq6C3dkewyWm8S113vlbcKA01tqaimno6qWKVk/KY27trBHHk5P4QAyeC612R2ao7VTmloYjFE6eoqX6pJJpJJ6qZ888ss0zjJK90jzxcTwDR0ALMIDm/sdbOvftZLLVUD3Q/cFa6dsk9K4xb7FO2WAPkZp3unILOnpWsaSwXqg2G2Ou1Bbqx95tFbf6JlMYJmzwwXw3OndM+DTr0tk5O4cPxnUCSu3kQHJnYz7GFfatsbZYt1M+w2QVG0VPVSNmkimq6u1UVukh3rhu4pGXCOecMHU9/AZXvY+2eqaLYbaaqg2dgr7w+63Ex01fbGzS1NM6rpTrMMzNdVEyLXM2PiCYRgFdZIgOMtneUz391Zm71VN9w1zonVtdYWWWnZMyGaTkVNHT0rIzTxgYGrPEEBxACx2weLtZux5abZaa5lyt98prnV15tskNLDQwVFTNUVAry3dSskJjdzTxNPg87AXad5oGVVNUUshcI6mCWnkLCA8MmjdG4tJGA7S49KxnY+2VprHbKS1UbpnU1FGY4nTua+Ytc98nPcxgaTqeegDqQHG/whBLaeyBZmWeuuFzum11zjt76e2yVELpeVwaGmta3RC+EtdJpcRgTNI6Ss9tbstdrRtFNUXOouUVLXbO2u3xXChsUO0TC6loKamr7fKyaJxpTJUxSy80c7ecfN0/2PdhaOx/CXI31D/hW6VV2qeUPY/TU1ejeti0RjTDzBhpyenipSgOQtn9nTs5cdjLnV0N7r7JBYa2hj5Ta3zXC2V1TWVlYw1lBTl5p5DBVR04054ReZSD+D5XstFpvM1wsN0MVbt3UCjoG2l0lRTCpZSmmqX0rhpggiLCN43OC0BuThdOogNTfwvqKap2LvcNPDNUTSMogyGCJ80ryLlRk6Y4wXOw0E8OoFac7OvYTttDszb6y12+4vuRqbTvGNqrpWua17dVQTSSSua0Ajpxw8y68RAcrbTD4Gv23vwtZLjchtFR0nwNPTW2W4RVbG0U1O6hE0TDyZ4kfC3S7HyAP5GY9tfsBtFbrJsa2CCWS61Vrr9lLkRG+R1HS3p+ulEzoe8ZTRvlbqPAFg4rspEBy/2Etgqyh2yfbpaWVtm2Rgur7HVSsk0v8AuglgnbGyV40zvZBLVRlzT0jjxUn/AIRVO+l2k2OvlRQVVwtFtkukVYKWlfWuo5qunjZS1D6aMF7mbxrTqA4bgdekHfKIDiW4bL3C4Wzauoo7PcI6X7s6C9SWmWgkpqm4WiNkzpYY6R7RrcTJHLuxx+LPDK2n2Oc3jb2S/wBst1bQWin2aZbamert8luFXXPqzM2KOOVodMWxCIFwHDkzR0Fueh0QGhOzwx1JtlsTdpKKqnoaf4Ygqp6WjlqxFLU0rIKYTCFhLMySNILvyHkdBWtrrspUO2Q7JTXW2Z9XUbXVstIORSOqJ4fhCgdFLTjd7yWLBmIc3hxf512IiA4823slyotpJ7hWS3SmoLrs9baWmraPZ+C/Bgio4Iqu1zxzxOdSvfMySTgBnecfNZdk/Zus2Z2Z2UvloFwNVRU1zs723CmFPcW0u0DKx9G2qpmPdokpqifDY8ni+L6F2goXtv2N6G83C2V9dPXvZa5WVEFuZVOjtktVDJvIKqppQ346djs4OegkEEcEBU7DOx7bFYLXacN1UtIxtRji19VLmareP5rqiSU/WFyhQ9j6905uboaKpazseVT6jZqIicC4ie/OulS5jgwuqQbbButDc5Mrc9PDt9EByA3YyvoLLsHd6ygqq6npLrW3vaGigpXzVAmu72z0tZJRY1vNO3DSMZGVhuyHYq25W/sj3i32uvgtt4l2ait1M+gmp6itloaqk5XVRUWje7vVvX6yOO/f1h2O2EQHL38InsdT1U+w9tscbrOHvuUctZQUTmx0O9oaGF0swpg0RvdGwx5cQeHTwUq7BdxmpNkLjaZrfV2i5bO0tdRTiioZJH1MsUU5jutrbIAy4SyuY5/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": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "YouTubeVideo(\"66P5FMkWoVU\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## History of the `dict` Type" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `dict` type's order has been worked on with 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\n", + "\n", + "[Raymond Hettinger](https://github.com/rhettinger), a Python core developer and one of the greatest Python teachers in the world, summaries the history of the `dict` type in his PyCon 2017 talk." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "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": 3, + "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.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index b7d6e4f..02fe78a 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -260,3 +260,5 @@ If this is not possible, `ChainMap` Type) - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/06_summary.ipynb) - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/07_review.ipynb) + - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/08_resources.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/08_resources.ipynb) From f49e417988f8e7b53be11d6f979ca5564894f449 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Thu, 22 Oct 2020 18:55:22 +0200 Subject: [PATCH 108/142] Streamline slides --- 09_mappings/02_content.ipynb | 54 ++++++++++++++++++++++++++++++------ 09_mappings/04_content.ipynb | 6 +++- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/09_mappings/02_content.ipynb b/09_mappings/02_content.ipynb index ae2c380..78ac6ff 100644 --- a/09_mappings/02_content.ipynb +++ b/09_mappings/02_content.ipynb @@ -614,7 +614,11 @@ { "cell_type": "code", "execution_count": 21, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [], "source": [ "def fibonacci(i):\n", @@ -636,7 +640,11 @@ { "cell_type": "code", "execution_count": 22, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -655,14 +663,22 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "source": [ "#### Efficiency of Algorithms" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "Timing the code cells below with the `%%timeit` magic shows how doubling the input (i.e., `12` becomes `24`) more than doubles how long it takes `fibonacci()` to calculate the solution. This is actually an understatement as we see the time go up by roughly a factor of $1000$ (i.e., from nano-seconds to milli-seconds). That is an example of **exponential growth**." ] @@ -670,7 +686,11 @@ { "cell_type": "code", "execution_count": 23, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [ { "name": "stdout", @@ -688,7 +708,11 @@ { "cell_type": "code", "execution_count": 24, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "name": "stdout", @@ -729,7 +753,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "source": [ "### \"Easy at second Glance\" Example: [Fibonacci Numbers ](https://en.wikipedia.org/wiki/Fibonacci_number) (revisited)" ] @@ -797,7 +825,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "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)`, the left-most leaf node $F(1)$, 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. Effectively, this mirrors the *iterative* implementation in that the order of all computational steps are *identical* (cf., the \"*Hard at first Glance*\" example in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb#\"Hard-at-first-Glance\"-Example:-Fibonacci-Numbers--(revisited))).\n", "\n", @@ -1000,7 +1032,11 @@ { "cell_type": "code", "execution_count": 32, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "outputs": [ { "ename": "RecursionError", diff --git a/09_mappings/04_content.ipynb b/09_mappings/04_content.ipynb index 97c3875..06e0f7f 100644 --- a/09_mappings/04_content.ipynb +++ b/09_mappings/04_content.ipynb @@ -526,7 +526,11 @@ { "cell_type": "code", "execution_count": 17, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "outputs": [], "source": [ "import collections.abc as abc" From d874c83eb58f4e2b4c4fa3681121ae57ea8c1110 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 26 Oct 2020 16:26:32 +0100 Subject: [PATCH 109/142] Add initial version of chapter 11, part 1 --- 00_intro/00_content.ipynb | 2 +- 11_classes/00_content.ipynb | 2171 +++++++++++++++++++++++++++++++++++ CONTENTS.md | 8 + README.md | 1 + 4 files changed, 2181 insertions(+), 1 deletion(-) create mode 100644 11_classes/00_content.ipynb diff --git a/00_intro/00_content.ipynb b/00_intro/00_content.ipynb index bc62059..2bc24f2 100644 --- a/00_intro/00_content.ipynb +++ b/00_intro/00_content.ipynb @@ -775,7 +775,7 @@ " - *Chapter 9*: [Mappings & Sets ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb)\n", " - *Chapter 10*: Arrays & Dataframes\n", "- How can we create custom data types?\n", - " - *Chapter 11*: Classes & Instances" + " - *Chapter 11*: [Classes & Instances ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb)" ] }, { diff --git a/11_classes/00_content.ipynb b/11_classes/00_content.ipynb new file mode 100644 index 0000000..692af9c --- /dev/null +++ b/11_classes/00_content.ipynb @@ -0,0 +1,2171 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/00_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 11: Classes & Instances" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In contrast to all the built-in data types introduced in the previous chapters, **classes** allow us to create **user-defined data types**. They enable us to model **data** and its **associated behavior** in an *abstract* way. *Concrete* **instances** of these custom data types then **encapsulate** the **state** in a running program. Often, classes are **blueprints** modeling \"real world things.\"\n", + "\n", + "Classes and instances follow the **[object-oriented programming ](https://en.wikipedia.org/wiki/Object-oriented_programming)** (OOP) paradigm where a *large program* is broken down into many *small components* (i.e., the objects) that *reuse* code. This way, a program that is too big for a programmer to fully comprehend as a whole becomes maintainable via its easier to understand individual pieces.\n", + "\n", + "Often, we see the terminology \"classes & objects\" used instead of \"classes & instances\" in Python related texts. In this book, we are more precise as *both* classes and instances are objects as specified already in the \"*Objects vs. Types vs. Values*\" section in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Objects-vs.-Types-vs.-Values)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Example: Vectors & Matrices" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Neither core Python nor the standard library offer an implementation of common [linear algebra ](https://en.wikipedia.org/wiki/Linear_algebra) functionalities. While we introduce the popular third-party library [numpy](http://www.numpy.org/) in [Chapter 10 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/10_arrarys/00_content.ipynb) as the de-facto standard for that and recommend to use it in real-life projects, we show how one could use Python's object-oriented language features to implement common matrix and vector operations throughout this chapter. Once we have achieved that, we compare our own library with [numpy](http://www.numpy.org/).\n", + "\n", + "Without classes, we could model a vector, for example, with a `tuple` or a `list` object, depending on if we want it to be mutable or not.\n", + "\n", + "Let's take the following vector $\\vec{x}$ as an example and model it as a `tuple`:" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "$\\vec{x} = \\begin{pmatrix} 1 \\\\ 2 \\\\ 3 \\end{pmatrix}$" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "x = (1, 2, 3)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 2, 3)" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We can extend this approach and model a matrix as either a `tuple` holding other `tuple`s or as a `list` holding other `list`s or as a mixture of both. Then, we *must* decide if the inner objects represent rows or columns. A common convention is to go with the former.\n", + "\n", + "For example, let's model the matrix $\\bf{A}$ below as a `list` of row `list`s:" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "$\\bf{A} = \\begin{bmatrix} 1 & 2 & 3 \\\\ 4 & 5 & 6 \\\\ 7 & 8 & 9 \\end{bmatrix}$" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[[1, 2, 3], [4, 5, 6], [7, 8, 9]]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "A" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While this way of representing vectors and matrices in memory keeps things simple, we cannot work with them easily as Python does not know about the **semantics** (i.e., \"rules\") of vectors and matrices modeled as `tuple`s and `list`s of `list`s.\n", + "\n", + "For example, we should be able to multiply $\\bf{A}$ with $\\vec{x}$ if their dimensions match. However, Python does not know how to do this and raises a `TypeError`." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "$\\bf{A} * \\vec{x} = \\begin{bmatrix} 1 & 2 & 3 \\\\ 4 & 5 & 6 \\\\ 7 & 8 & 9 \\end{bmatrix} * \\begin{pmatrix} 1 \\\\ 2 \\\\ 3 \\end{pmatrix} = \\begin{pmatrix} 14 \\\\ 32 \\\\ 50 \\end{pmatrix}$" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "can't multiply sequence by non-int of type 'tuple'", + "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[0mA\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: can't multiply sequence by non-int of type 'tuple'" + ] + } + ], + "source": [ + "A * x" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Throughout this chapter, we \"teach\" Python the rules of [linear algebra ](https://en.wikipedia.org/wiki/Linear_algebra)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Class Definition" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The compound `class` statement creates a new variable that references a **class object** in memory.\n", + "\n", + "Following the *header* line, the indented *body* syntactically consists of function definitions (i.e., `.dummy_method()`) and variable assignments (i.e., `.dummy_variable`). Any code put here is executed just as if it were outside the `class` statement. However, the class object acts as a **namespace**, meaning that all the names do *not* exist in the global scope but may only be accessed with the dot operator `.` on the class object. In this context, the names are called **class attributes**.\n", + "\n", + "Within classes, functions are referred to as **methods** that are **bound** to *future* **instance objects**. This binding process means that Python *implicitly* inserts a reference to a *concrete* instance object as the first argument to any **method invocation** (i.e., \"function call\"). By convention, we call this parameter `self` as it references the instance object on which the method is invoked. Then, as the method is executed, we can set and access attributes via the dot operator `.` on `self`. That is how we manage the *state* of a *concrete* instance within a *generically* written class. At the same time, the code within a method is reused whenever we invoke a method on *any* instance.\n", + "\n", + "As indicated by [PEP 257 ](https://www.python.org/dev/peps/pep-0257/) and also section 3.8.4 of the [Google Python Style Guide ](https://github.com/google/styleguide/blob/gh-pages/pyguide.md#384-classes), we use docstrings to document relevant parts of the new data type. With respect to naming, classes are named according to the [CamelCase ](https://en.wikipedia.org/wiki/Camel_case) convention while instances are treated like normal variables and named in [snake\\_case ](https://en.wikipedia.org/wiki/Snake_case)." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "class Vector:\n", + " \"\"\"A standard one-dimensional vector from linear algebra.\"\"\"\n", + "\n", + " dummy_variable = \"I am a vector\"\n", + "\n", + " def dummy_method(self):\n", + " \"\"\"A dummy method for illustration purposes.\"\"\"\n", + " return self.dummy_variable" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`Vector` is an object on its own with an identity, a type, and a value." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "94113690586816" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(Vector)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Its type is `type` indicating that it represents a user-defined data type and it evaluates to its fully qualified name (i.e., `__main__` as it is defined in this Jupyter notebook).\n", + "\n", + "We have seen the type `type` before in the \"*Constructors*\" section in [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb#Constructors) and also in the \"*The `namedtuple` Type*\" section in [Chapter 7's Appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/05_appendix.ipynb#The-namedtuple-Type). In the latter case, we could also use a `Point` class but the [namedtuple() ](https://docs.python.org/3/library/collections.html#collections.namedtuple) function from the [collections ](https://docs.python.org/3/library/collections.html) module in the [standard library ](https://docs.python.org/3/library/index.html) is a convenient shortcut to create custom data types that can be derived out of a plain `tuple`.\n", + "\n", + "In all examples, if an object's type is `type`, we can simply view it as a blueprint for a \"family\" of objects." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "type" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(Vector)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "__main__.Vector" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Vector" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The docstrings are transformed into convenient help texts." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\u001b[0;31mInit signature:\u001b[0m \u001b[0mVector\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;31mDocstring:\u001b[0m A standard one-dimensional vector from linear algebra.\n", + "\u001b[0;31mType:\u001b[0m type\n", + "\u001b[0;31mSubclasses:\u001b[0m \n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "Vector?" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on class Vector in module __main__:\n", + "\n", + "class Vector(builtins.object)\n", + " | A standard one-dimensional vector from linear algebra.\n", + " | \n", + " | Methods defined here:\n", + " | \n", + " | dummy_method(self)\n", + " | A dummy method for illustration purposes.\n", + " | \n", + " | ----------------------------------------------------------------------\n", + " | Data descriptors defined here:\n", + " | \n", + " | __dict__\n", + " | dictionary for instance variables (if defined)\n", + " | \n", + " | __weakref__\n", + " | list of weak references to the object (if defined)\n", + " | \n", + " | ----------------------------------------------------------------------\n", + " | Data and other attributes defined here:\n", + " | \n", + " | dummy_variable = 'I am a vector'\n", + "\n" + ] + } + ], + "source": [ + "help(Vector)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We can use the built-in [vars() ](https://docs.python.org/3/library/functions.html#vars) function as an alternative to [dir() ](https://docs.python.org/3/library/functions.html#dir) to obtain a *brief* summary of the attributes on `Vector`. Whereas [vars() ](https://docs.python.org/3/library/functions.html#vars) returns a read-only `dict`-like overview on mostly the *explicitly* defined attributes, [dir() ](https://docs.python.org/3/library/functions.html#dir) also shows all *implicitly* added attributes in a `list`." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "mappingproxy({'__module__': '__main__',\n", + " '__doc__': 'A standard one-dimensional vector from linear algebra.',\n", + " 'dummy_variable': 'I am a vector',\n", + " 'dummy_method': ,\n", + " '__dict__': ,\n", + " '__weakref__': })" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "vars(Vector)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['__class__',\n", + " '__delattr__',\n", + " '__dict__',\n", + " '__dir__',\n", + " '__doc__',\n", + " '__eq__',\n", + " '__format__',\n", + " '__ge__',\n", + " '__getattribute__',\n", + " '__gt__',\n", + " '__hash__',\n", + " '__init__',\n", + " '__init_subclass__',\n", + " '__le__',\n", + " '__lt__',\n", + " '__module__',\n", + " '__ne__',\n", + " '__new__',\n", + " '__reduce__',\n", + " '__reduce_ex__',\n", + " '__repr__',\n", + " '__setattr__',\n", + " '__sizeof__',\n", + " '__str__',\n", + " '__subclasshook__',\n", + " '__weakref__',\n", + " 'dummy_method',\n", + " 'dummy_variable']" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dir(Vector)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With the dot operator `.` we access the class attributes." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'I am a vector'" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Vector.dummy_variable" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Vector.dummy_method" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "However, invoking the `.dummy_method()` raises a `TypeError`. That makes sense as the method expects a *concrete* instance passed in as the `self` argument. However, we have not yet created one." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "dummy_method() missing 1 required positional argument: 'self'", + "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[0mVector\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdummy_method\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: dummy_method() missing 1 required positional argument: 'self'" + ] + } + ], + "source": [ + "Vector.dummy_method()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "## Instantiation" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To create a *new* instance, we need to **instantiate** one.\n", + "\n", + "In the `class` statement, we see a `.__init__()` method that contains all the validation logic that we require a `Vector` instance to adhere to. In a way, this method serves as a constructor-like function.\n", + "\n", + "`.__init__()` is an example of a so-called **special method** that we use to make new data types integrate with Python's language features. Their naming follows the dunder convention. In this chapter, we introduce some of the more common special methods, and we refer to the [language reference ](https://docs.python.org/3/reference/datamodel.html) for an exhaustive list of all special methods. Special methods not *explicitly* defined in a class are *implicitly* added with a default implementation.\n", + "\n", + "The `.__init__()` method (cf., [reference ](https://docs.python.org/3/reference/datamodel.html#object.__init__)) is responsible for **initializing** a *new* instance object immediately after its creation. That usually means setting up some **instance attributes**. In the example, a new `Vector` instance is created from some sequence object (e.g., a `tuple` like `x`) passed in as the `data` argument. The elements provided by the `data` argument are first cast as `float` objects and then stored in a `list` object named `._entries` on the *new* instance object. Together, the `float`s represent the state encapsulated within an instance.\n", + "\n", + "A best practice is to *separate* the way we use a data type (i.e., its \"behavior\") from how we implement it. By convention, attributes that should not be accessed from \"outside\" of an instance start with one leading underscore `_`. In the example, the instance attribute `._entries` is such an **implementation detail**: We could have decided to store a `Vector`'s entries in a `tuple` instead of a `list`. However, this decision should *not* affect how a `Vector` instance is to be used. Moreover, if we changed how the `._entries` are modeled later on, this must *not* break any existing code using `Vector`s. This idea is also known as **[information hiding ](https://en.wikipedia.org/wiki/Information_hiding)** in software engineering." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "class Vector:\n", + " \"\"\"A standard one-dimensional vector from linear algebra.\n", + "\n", + " All entries are converted to floats.\n", + " \"\"\"\n", + "\n", + " def __init__(self, data):\n", + " \"\"\"Create a new vector.\n", + "\n", + " Args:\n", + " data (sequence): the vector's entries\n", + "\n", + " Raises:\n", + " ValueError: if no entries are provided\n", + " \"\"\"\n", + " self._entries = list(float(x) for x in data)\n", + " if len(self._entries) == 0:\n", + " raise ValueError(\"a vector must have at least one entry\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To create a `Vector` instance, we call the `Vector` class with the `()` operator. This call is forwarded to the `.__init__()` method behind the scenes. That is what we mean by saying \"make new data types integrate with Python's language features\" above: We use `Vector` just as any other built-in constructor." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "v = Vector([1, 2, 3])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`v` is an object as well." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140306181183328" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(v)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Unsurprisingly, the type of `v` is `Vector`. That is the main point of this chapter." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "__main__.Vector" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(v)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Every instance comes with a special `.__class__` attribute that also references the corresponding class object." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "__main__.Vector" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v.__class__" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`v`'s semantic \"value\" is not so clear yet. We fix this in the next section." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "<__main__.Vector at 0x7f9b9416d760>" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Although the `.__init__()` method defines *two* parameters, we must call it with only *one* `data` argument. As noted above, Python implicitly inserts a reference to the newly created instance object (i.e., `v`) as the first argument as `self`.\n", + "\n", + "Calling a class object with a wrong number of arguments leads to generic `TypeError`s ..." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "__init__() missing 1 required positional argument: 'data'", + "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[0mVector\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: __init__() missing 1 required positional argument: 'data'" + ] + } + ], + "source": [ + "Vector()" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "__init__() takes 2 positional arguments but 4 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[0mVector\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;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: __init__() takes 2 positional arguments but 4 were given" + ] + } + ], + "source": [ + "Vector(1, 2, 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... while creating a `Vector` instance from an empty sequence raises a custom `ValueError`." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "a vector must have at least one entry", + "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[0mVector\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, data)\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfloat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mdata\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[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\u001b[0m\u001b[0;34m)\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---> 18\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"a vector must have at least one entry\"\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: a vector must have at least one entry" + ] + } + ], + "source": [ + "Vector([])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Even though we can access the `._entries` attribute on the `v` object (i.e., \"from outside\"), we are *not* supposed to do that because of the underscore `_` convention. In other words, we should access `._entries` only from within a method via `self`." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1.0, 2.0, 3.0]" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v._entries # by convention not allowed" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Text Representations" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For all the built-in data types, an object's value is represented in a *literal notation*, implying that we can simply copy and paste the value into another code cell to create a *new* object with the *same* value.\n", + "\n", + "The exact representation of the value does *not* have to be identical to the one used to create the object. For example, we can create a `tuple` object without using parentheses and Python still outputs its value with `(` and `)`. That was an *arbitrary* design decision by the core development team." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "x = 1, 2, 3" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 2, 3)" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 2, 3)" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(1, 2, 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To control how objects of a user-defined data type are represented as text, we implement the `.__repr__()` (cf., [reference ](https://docs.python.org/3/reference/datamodel.html#object.__repr__)) and `.__str__()` (cf., [reference ](https://docs.python.org/3/reference/datamodel.html#object.__str__)) methods. Both take only a `self` argument and must return a `str` object." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "class Vector:\n", + "\n", + " def __init__(self, data):\n", + " self._entries = list(float(x) for x in data)\n", + " # ...\n", + "\n", + " def __repr__(self):\n", + " args = \", \".join(f\"{x:.3f}\" for x in self._entries)\n", + " return f\"Vector(({args}))\"\n", + "\n", + " def __str__(self):\n", + " first, last = self._entries[0], self._entries[-1]\n", + " n_entries = len(self._entries)\n", + " return f\"Vector({first:.1f}, ..., {last:.1f})[{n_entries:d}]\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Now, when `v` is evaluated in a code cell, we see the return value of the `.__repr__()` method.\n", + "\n", + "According to the specification, `.__repr__()` should return a `str` object that, when used as a literal, creates a *new* instance with the *same* state (i.e., their `._entries` attributes compare equal) as the original one. In other words, it should return a **text representation** of the object optimized for direct consumption by the Python interpreter. That is often useful when debugging or logging large applications.\n", + "\n", + "Our implementation of `.__repr__()` in the `Vector` class uses to a `tuple` notation for the `data` argument. So, even if we create `v` from a `list` object like `[1, 2, 3]` and even though the `_entries` are stored as a `list` object internally, a `Vector` instance's text representation \"defaults\" to `((` and `))` in the output. This decision is arbitrary and we could have used a `list` notation for the `data` argument as well." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "v = Vector([1, 2, 3])" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((1.000, 2.000, 3.000))" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If we copy and paste the value of the `v` object into another code cell, we create a *new* `Vector` instance with the *same* state as `v`." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((1.000, 2.000, 3.000))" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Vector((1.000, 2.000, 3.000))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Alternatively, the built-in [repr()]( https://docs.python.org/3/library/functions.html#repr) function returns an object's value as a `str` object (i.e., with the quotes `'`)." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Vector((1.000, 2.000, 3.000))'" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "repr(v)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "On the contrary, the `.__str__()` method should return a *human-readable* text representation of the object, and we use the built-in [str() ](https://docs.python.org/3/library/functions.html#func-str) and [print() ](https://docs.python.org/3/library/functions.html#print) functions to obtain this representation explicitly.\n", + "\n", + "For our `Vector` class, this representation only shows a `Vector`'s first and last entries followed by the total number of entries in brackets. So, even for a `Vector` containing millions of entries, we could easily make sense of the representation.\n", + "\n", + "While [str() ](https://docs.python.org/3/library/functions.html#func-str) returns the text representation as a `str` object, ..." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'Vector(1.0, ..., 3.0)[3]'" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "str(v)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... [print() ](https://docs.python.org/3/library/functions.html#print) does not show the enclosing quotes." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Vector(1.0, ..., 3.0)[3]\n" + ] + } + ], + "source": [ + "print(v)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From a theoretical point of view, the text representation provided by `.__repr__()` contains all the information (i.e., the $0$s and $1$s in memory) that is needed to model something in a computer. In a way, it is a natural extension from the binary (cf., [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb#Binary-Representations)), hexadecimal (cf., [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb#Hexadecimal-Representations)), and `bytes` (cf., [Chapter 6 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/02_content.ipynb#The-bytes-Type)) representations of information. After all, just like Unicode characters are encoded in `bytes`, the more \"complex\" objects in this chapter are encoded in Unicode characters via their text representations." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The `Matrix` Class" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Below is a first implementation of the `Matrix` class that stores the entries internally as a `list` of `list`s.\n", + "\n", + "The `.__init__()` method ensures that all the rows come with the same number of columns. Again, we do not allow `Matrix` instances without any entries." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "class Matrix:\n", + "\n", + " def __init__(self, data):\n", + " self._entries = list(list(float(x) for x in r) for r in data)\n", + " for row in self._entries[1:]:\n", + " if len(row) != len(self._entries[0]):\n", + " raise ValueError(\"rows must have the same number of entries\")\n", + " if len(self._entries) == 0:\n", + " raise ValueError(\"a matrix must have at least one entry\")\n", + "\n", + " def __repr__(self):\n", + " args = \", \".join(\"(\" + \", \".join(f\"{c:.3f}\" for c in r) + \",)\" for r in self._entries)\n", + " return f\"Matrix(({args}))\"\n", + "\n", + " def __str__(self):\n", + " first, last = self._entries[0][0], self._entries[-1][-1]\n", + " m, n = len(self._entries), len(self._entries[0])\n", + " return f\"Matrix(({first:.1f}, ...), ..., (..., {last:.1f}))[{m:d}x{n:d}]\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`Matrix` is an object as well." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "94113690738160" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(Matrix)" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "type" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(Matrix)" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "__main__.Matrix" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Matrix" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's create a new `Matrix` instance from a `list` of `tuple`s." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "m = Matrix([(1, 2, 3), (4, 5, 6), (7, 8, 9)])" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "140306180401856" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "id(m)" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "__main__.Matrix" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(m)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The text representations work as above." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Matrix((1.0, ...), ..., (..., 9.0))[3x3]\n" + ] + } + ], + "source": [ + "print(m)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Passing an invalid `data` argument when instantiating a `Matrix` results in the documented exceptions." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "a matrix must have at least one entry", + "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[0mMatrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, data)\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"rows must have the same number of entries\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\u001b[0m\u001b[0;34m)\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----> 9\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"a matrix must have at least one entry\"\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 10\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__repr__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\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;31mValueError\u001b[0m: a matrix must have at least one entry" + ] + } + ], + "source": [ + "Matrix(())" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "rows must have the same number of entries", + "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[0mMatrix\u001b[0m\u001b[0;34m(\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;36m3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m4\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[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, data)\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mrow\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\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[1;32m 6\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrow\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\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[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"rows must have the same number of entries\"\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 8\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\u001b[0m\u001b[0;34m)\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 9\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"a matrix must have at least one entry\"\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: rows must have the same number of entries" + ] + } + ], + "source": [ + "Matrix([(1, 2, 3), (4, 5)])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Instance Methods vs. Class Methods" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The methods we have seen so far are all **instance methods**. The characteristic idea behind an instance method is that the behavior it provides either depends on the state of a concrete instance or mutates it. In other words, an instance method *always* works with attributes on the `self` argument. If a method does *not* need access to `self` to do its job, it is conceptually *not* an instance method and we should probably convert it into another kind of method as shown below.\n", + "\n", + "An example of an instance method from linear algebra is the `.transpose()` method below that switches the rows and columns of an *existing* `Matrix` instance and returns a *new* `Matrix` instance based off that. It is implemented by passing the *iterator* created with the [zip() ](https://docs.python.org/3/library/functions.html#zip) built-in as the `data` argument to the `Matrix` constructor: The expression `zip(*self._entries)` may be a bit hard to understand because of the involved unpacking but simply flips a `Vector`'s rows and columns. The built-in [list() ](https://docs.python.org/3/library/functions.html#func-list) constructor within the `.__init__()` method then materializes the iterator into the `._entries`. Without a concrete `Matrix`'s rows and columns, `.transpose()` does not make sense, conceptually speaking." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "class Matrix:\n", + "\n", + " def __init__(self, data):\n", + " self._entries = list(list(float(x) for x in r) for r in data)\n", + " # ...\n", + "\n", + " def __repr__(self):\n", + " args = \", \".join(\"(\" + \", \".join(f\"{c:.3f}\" for c in r) + \",)\" for r in self._entries)\n", + " return f\"Matrix(({args}))\"\n", + "\n", + " def transpose(self):\n", + " return Matrix(zip(*self._entries))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `.transpose()` method returns a *new* `Matrix` instance where the rows and columns are flipped." + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "m = Matrix([(1, 2, 3), (4, 5, 6), (7, 8, 9)])" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((1.000, 4.000, 7.000,), (2.000, 5.000, 8.000,), (3.000, 6.000, 9.000,)))" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m.transpose()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Two invocations of `.transpose()` may be chained, which negates its overall effect but still creates a *new* instance object (i.e., `m is n` is `False`)." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "n = m.transpose().transpose()" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "n" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m is n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Unintuitively, the comparison operator `==` returns a wrong result as `m` and `n` have `_entries` attributes that compare equal. We fix this in the \"*Operator Overloading*\" section later in this chapter." + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m == n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Sometimes, it is useful to attach functionality to a class object that does *not* depend on the state of a concrete instance but on the class as a whole. Such methods are called **class methods** and can be created with the [classmethod() ](https://docs.python.org/3/library/functions.html#classmethod) built-in combined with the `@` **decorator** syntax. Then, Python adapts the binding process described above such that it implicitly inserts a reference to the class object itself instead of the instance when the method is invoked. By convention, we name this parameter `cls`.\n", + "\n", + "Class methods are often used to provide an alternative way to create instances, usually from a different kind of arguments. As an example, `.from_columns()` expects a sequence of columns instead of rows as its `data` argument. It forwards the invocation to the `.__init__()` method (i.e., what `cls(data)` does; `cls` references the *same* class object as `Matrix`), then calls the `.transpose()` method on the newly created instance, and lastly returns the instance created by `.transpose()`. Again, we are intelligently *reusing* a lot of code." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "class Matrix:\n", + "\n", + " def __init__(self, data):\n", + " self._entries = list(list(float(x) for x in r) for r in data)\n", + " # ...\n", + "\n", + " def __repr__(self):\n", + " args = \", \".join(\"(\" + \", \".join(f\"{c:.3f}\" for c in r) + \",)\" for r in self._entries)\n", + " return f\"Matrix(({args}))\"\n", + "\n", + " def transpose(self):\n", + " return Matrix(zip(*self._entries))\n", + "\n", + " @classmethod\n", + " def from_columns(cls, data):\n", + " return cls(data).transpose()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We use the alternative `.from_columns()` constructor to create a `Matrix` equivalent to `m` above from a `list` of columns instead of rows." + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "m = Matrix.from_columns([(1, 4, 7), (2, 5, 8), (3, 6, 9)])" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "There is also a [staticmethod() ](https://docs.python.org/3/library/functions.html#staticmethod) built-in to be used with the `@` syntax to define methods that are *independent* from both the class and instance objects but nevertheless related semantically to a class. In this case, the binding process is disabled an no argument is implicitly inserted upon a method's invocation. Such **static methods** are not really needed most of the time and we omit them here fore brevity." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Computed Properties" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "After creation, a `Matrix` instance exhibits certain properties that depend only on the concrete `data` encapsulated in it. For example, every `Matrix` instance implicitly has *two* dimensions: These are commonly denoted as $m$ and $n$ in math and represent the number of rows and columns.\n", + "\n", + "We would like our `Matrix` instances to have two attributes, `.n_rows` and `.n_cols`, that provide the correct dimensions as `int` objects. To achieve that, we implement two instance methods, `.n_rows()` and `.n_cols()`, and make them **derived attributes** by decorating them with the [property() ](https://docs.python.org/3/library/functions.html#property) built-in. They work like methods except that they do not need to be invoked with the call operator `()` but can be accessed as if they were instance variables.\n", + "\n", + "To reuse their code, we integrate the new properties already within the `.__init__()` method." + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "class Matrix:\n", + "\n", + " def __init__(self, data):\n", + " self._entries = list(list(float(x) for x in r) for r in data)\n", + " for row in self._entries[1:]:\n", + " if len(row) != self.n_cols:\n", + " raise ValueError(\"rows must have the same number of entries\")\n", + " if self.n_rows == 0:\n", + " raise ValueError(\"a matrix must have at least one entry\")\n", + "\n", + " def __repr__(self):\n", + " args = \", \".join(\"(\" + \", \".join(f\"{c:.3f}\" for c in r) + \",)\" for r in self._entries)\n", + " return f\"Matrix(({args}))\"\n", + "\n", + " @property\n", + " def n_rows(self):\n", + " return len(self._entries)\n", + "\n", + " @property\n", + " def n_cols(self):\n", + " return len(self._entries[0])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The revised `m` models a $2 \\times 3$ matrix." + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "m = Matrix([(1, 2, 3), (4, 5, 6)])" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(2, 3)" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m.n_rows, m.n_cols" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In its basic form, properties are *read-only* attributes. This makes sense for `Matrix` instances where we can *not* \"set\" how many rows and columns there are while keeping the `_entries` unchanged." + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "can't set attribute", + "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[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_rows\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m: can't set attribute" + ] + } + ], + "source": [ + "m.n_rows = 3" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index 02fe78a..a0f49df 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -262,3 +262,11 @@ If this is not possible, - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/07_review.ipynb) - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/08_resources.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/08_resources.ipynb) + - *Chapter 11*: Classes & Instances + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/00_content.ipynb) + (`class` Statement; + Instantiation; + Text Representations; + Instance Methods vs. Class Methods; + Computed Properties) diff --git a/README.md b/README.md index dd051e6..49ae3d0 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ For a more *detailed version* with **clickable links** - *Chapter 7*: Sequential Data - *Chapter 8*: Map, Filter, & Reduce - *Chapter 9*: Mappings & Sets + - *Chapter 11*: Classes & Instances #### Videos From fcf3410dddac205c029a7d5ae81ae1171b1abaf7 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 26 Oct 2020 22:31:56 +0100 Subject: [PATCH 110/142] Add initial version of chapter 11's exercises --- 11_classes/01_exercises.ipynb | 1502 +++++++++++++++++++++++++++++++++ CONTENTS.md | 3 + 2 files changed, 1505 insertions(+) create mode 100644 11_classes/01_exercises.ipynb diff --git a/11_classes/01_exercises.ipynb b/11_classes/01_exercises.ipynb new file mode 100644 index 0000000..915dbc9 --- /dev/null +++ b/11_classes/01_exercises.ipynb @@ -0,0 +1,1502 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/01_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 11: Classes & Instances (Coding Exercises)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb) of Chapter 11.\n", + "\n", + "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Berlin Tourist Guide: A Traveling Salesman Problem" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook is a hands-on and tutorial-like application to show how to load data from web services like [Google Maps](https://developers.google.com/maps) and use them to solve a logistics problem, namely a **[Traveling Salesman Problem ](https://en.wikipedia.org/wiki/Traveling_salesman_problem)**.\n", + "\n", + "Imagine that a tourist lands at Berlin's [Tegel Airport ](https://en.wikipedia.org/wiki/Berlin_Tegel_Airport) in the morning and has his \"connecting\" flight from [Schönefeld Airport ](https://en.wikipedia.org/wiki/Berlin_Sch%C3%B6nefeld_Airport) in the evening. By the time, the flights were scheduled, the airline thought that there would be only one airport in Berlin.\n", + "\n", + "Having never been in Berlin before, the tourist wants to come up with a plan of sights that he can visit with a rental car on his way from Tegel to Schönefeld.\n", + "\n", + "With a bit of research, he creates a `list` of `sights` like below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "arrival = \"Berlin Tegel Airport (TXL), Berlin\"\n", + "\n", + "sights = [\n", + " \"Alexanderplatz, Berlin\",\n", + " \"Brandenburger Tor, Pariser Platz, Berlin\",\n", + " \"Checkpoint Charlie, Friedrichstraße, Berlin\",\n", + " \"Kottbusser Tor, Berlin\",\n", + " \"Mauerpark, Berlin\",\n", + " \"Siegessäule, Berlin\",\n", + " \"Reichstag, Platz der Republik, Berlin\",\n", + " \"Soho House Berlin, Torstraße, Berlin\",\n", + " \"Tempelhofer Feld, Berlin\",\n", + "]\n", + "\n", + "departure = \"Berlin Schönefeld Airport (SXF), Berlin\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With just the street addresses, however, he cannot calculate a route. He needs `latitude`-`longitude` coordinates instead. While he could just open a site like [Google Maps](https://www.google.com/maps) in a web browser, he wonders if he can download the data with a bit of Python code using a [web API ](https://en.wikipedia.org/wiki/Web_API) offered by [Google](https://www.google.com).\n", + "\n", + "So, in this notebook, we solve the entire problem with code." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Geocoding" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In order to obtain coordinates for the given street addresses above, a process called **geocoding**, we use the [Google Maps Geocoding API](https://developers.google.com/maps/documentation/geocoding/start).\n", + "\n", + "**Q1**: Familiarize yourself with this [documentation](https://developers.google.com/maps/documentation/geocoding/start), register a developer account, create a project, and [create an API key](https://console.cloud.google.com/apis/credentials) that is necessary for everything to work! Then, [enable the Geocoding API](https://console.developers.google.com/apis/library/geocoding-backend.googleapis.com) and link a [billing account](https://console.developers.google.com/billing)!\n", + "\n", + "Info: The first 200 Dollars per month are not charged (cf., [pricing page](https://cloud.google.com/maps-platform/pricing/)), so no costs will incur for this tutorial. You must sign up because Google simply wants to know the people using its services.\n", + "\n", + "**Q2**: Assign the API key as a `str` object to the `key` variable!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "key = \" < your API key goes here > \"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To use external web services, our application needs to make HTTP requests just like web browsers do when surfing the web.\n", + "\n", + "We do not have to implement this on our own. Instead, we use the official Python Client for the Google Maps Services provided by Google in one of its corporate [GitHub repositories ](https://github.com/googlemaps).\n", + "\n", + "**Q3**: Familiarize yourself with the [googlemaps ](https://github.com/googlemaps/google-maps-services-python) package! Then, install it with the `pip` command line tool!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install googlemaps" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: Finish the following code cells and instantiate a `Client` object named `api`! Use the `key` from above. `api` provides us with a lot of methods to talk to the API." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import googlemaps" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "api = googlemaps.Client(...)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "api" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "type(api)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: Execute the next code cell to list the methods and attributes on the `api` object!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "[x for x in dir(api) if not x.startswith(\"_\")]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To obtain all kinds of information associated with a street address, we call the `geocode()` method with the address as the sole argument.\n", + "\n", + "For example, let's search for Brandenburg Gate. Its street address is `\"Brandenburger Tor, Pariser Platz, Berlin\"`.\n", + "\n", + "**Q6**: Execute the next code cell!\n", + "\n", + "Hint: If you get an error message, follow the instructions in it to debug it.\n", + "\n", + "If everything works, we receive a `list` with a single `dict` in it. That means the [Google Maps Geocoding API](https://developers.google.com/maps/documentation/geocoding/start) only knows about one place at the address. Unfortunately, the `dict` is pretty dense and hard to read." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "api.geocode(\"Brandenburger Tor, Pariser Platz, Berlin\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: Capture the first and only search result in the `brandenburg_gate` variable and \"pretty print\" it with the help of the [pprint() ](https://docs.python.org/3/library/pprint.html#pprint.pprint) function in the [pprint ](https://docs.python.org/3/library/pprint.html) module in the [standard library ](https://docs.python.org/3/library/index.html)!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "response = api.geocode(\"Brandenburger Tor, Pariser Platz, Berlin\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "brandenburg_gate = ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `dict` has several keys that are of use for us: `\"formatted_address\"` is a cleanly formatted version of the address. `\"geometry\"` is a nested `dict` with several `lat`-`lng` coordinates representing the place where `\"location\"` is the one we need for our calculations. Lastly, `\"place_id\"` is a unique identifier that allows us to obtain further information about the address from other Google APIs." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from pprint import pprint" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pprint(brandenburg_gate)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The `Place` Class" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To keep our code readable and maintainable, we create a `Place` class to manage the API results in a clean way.\n", + "\n", + "The `.__init__()` method takes a `street_address` (e.g., an element of `sights`) and a `client` argument (e.g., an object like `api`) and stores them on `self`. The place's `.name` is parsed out of the `street_address` as well: It is the part before the first comma. Also, the instance attributes `.latitude`, `.longitude`, and `.place_id` are initialized to `None`.\n", + "\n", + "**Q8**: Finish the `.__init__()` method according to the description!\n", + "\n", + "The `.sync_from_google()` method uses the internally kept `client` and synchronizes the place's state with the [Google Maps Geocoding API](https://developers.google.com/maps/documentation/geocoding/start). In particular, it updates the `.address` with the `formatted_address` and stores the values for `.latitude`, `.longitude`, and `.place_id`. It enables method chaining.\n", + "\n", + "**Q9**: Implement the `.sync_from_google()` method according to the description!\n", + "\n", + "**Q10**: Add a read-only `.location` property on the `Place` class that returns the `.latitude` and `.longitude` as a `tuple`!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Place:\n", + " \"\"\"A place connected to the Google Maps Geocoding API.\"\"\"\n", + "\n", + " # answer to Q8\n", + " def __init__(self, street_address, *, client):\n", + " \"\"\"Create a new place.\n", + "\n", + " Args:\n", + " street_address (str): street address of the place\n", + " client (googlemaps.Client): access to the Google Maps Geocoding API\n", + " \"\"\"\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + " ...\n", + "\n", + " def __repr__(self):\n", + " cls, name = self.__class__.__name__, self.name\n", + " synced = \" [SYNCED]\" if self.place_id else \"\"\n", + " return f\"<{cls}: {name}{synced}>\"\n", + "\n", + " # answer to Q9\n", + " def sync_from_google(self):\n", + " \"\"\"Download the place's coordinates and other info.\"\"\"\n", + " response = ...\n", + " first_hit = ...\n", + " ... = first_hit[...]\n", + " ... = first_hit[...][...][...]\n", + " ... = first_hit[...][...][...]\n", + " ... = first_hit[...]\n", + " return ...\n", + "\n", + " # answer to Q10\n", + " ...\n", + " ...\n", + " ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: Verify that the instantiating a `Place` object works!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "brandenburg_gate = Place(\"Brandenburger Tor, Pariser Platz, Berlin\", client=api)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "brandenburg_gate" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q12**: What do the angle brackets `<` and `>` mean in the text representation?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, we can obtain the geo-data from the [Google Maps Geocoding API](https://developers.google.com/maps/documentation/geocoding/start) in a clean way. As we enabled method chaining for `.sync_from_google()`, we get back the instance after calling the method.\n", + "\n", + "**Q13**: Verify that the `.sync_from_google()` method works!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "brandenburg_gate.sync_from_google()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "brandenburg_gate.address" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "brandenburg_gate.place_id" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "brandenburg_gate.location" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The `Place` Class (continued): Batch Synchronization" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q14**: Add an alternative constructor method named `.from_addresses()` that takes an `addresses`, a `client`, and a `sync` argument! `addresses` is a finite iterable of `str` objects (e.g., like `sights`). The method returns a `list` of `Place`s, one for each `str` in `addresses`. All `Place`s are initialized with the same `client`. `sync` is a flag and defaults to `False`. If it is set to `True`, the alternative constructor invokes the `.sync_from_google()` method on the `Place`s before returning them." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "code_folding": [] + }, + "outputs": [], + "source": [ + "class Place:\n", + " \"\"\"A place connected to the Google Maps Geocoding API.\"\"\"\n", + "\n", + " # answers from above\n", + "\n", + " # answer to Q14\n", + " ...\n", + " def from_addresses(cls, addresses, *, client, sync=False):\n", + " \"\"\"Create new places in a batch.\n", + "\n", + " Args:\n", + " addresses (iterable of str's): the street addresses of the places\n", + " client (googlemaps.Client): access to the Google Maps Geocoding API\n", + " Returns:\n", + " list of Places\n", + " \"\"\"\n", + " places = ...\n", + " for address in addresses:\n", + " place = ...\n", + " if sync:\n", + " ...\n", + " ...\n", + " return places" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q15**: Verify that the alternative constructor works with and without the `sync` flag!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Place.from_addresses(sights, client=api)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Place.from_addresses(sights, client=api, sync=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Visualization" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For geo-data it always makes sense to plot them on a map. We use the third-party library [folium ](https://github.com/python-visualization/folium) to achieve that.\n", + "\n", + "**Q16**: Familiarize yourself with [folium ](https://github.com/python-visualization/folium) and install it with the `pip` command line tool!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install folium" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q17**: Execute the code cells below to create an empty map of Berlin!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import folium" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "berlin = folium.Map(location=(52.513186, 13.3944349), zoom_start=14)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "type(berlin)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`folium.Map` instances are shown as interactive maps in Jupyter notebooks whenever they are the last expression in a code cell." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "berlin" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In order to put something on the map, [folium ](https://github.com/python-visualization/folium) works with so-called `Marker` objects.\n", + "\n", + "**Q18**: Review its docstring and then create a marker `m` with the location data of Brandenburg Gate! Use the `brandenburg_gate` object from above!\n", + "\n", + "Hint: You may want to use HTML tags for the `popup` argument to format the text output on the map in a nicer way. So, instead of just passing `\"Brandenburger Tor\"` as the `popup` argument, you could use, for example, `\"Brandenburger Tor
(Pariser Platz, 10117 Berlin, Germany)\"`. Then, the name appears in bold and the street address is put on the next line. You could use an f-string to parametrize the argument." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "folium.Marker?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m = folium.Marker(\n", + " location=...,\n", + " popup=...,\n", + " tooltip=...,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "type(m)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q19**: Execute the next code cells that add `m` to the `berlin` map!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "m.add_to(berlin)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "berlin" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The `Place` Class (continued): Marker Representation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q20**: Finish the `.as_marker()` method that returns a `Marker` instance when invoked on a `Place` instance! The method takes an optional `color` argument that uses [folium ](https://github.com/python-visualization/folium)'s `Icon` type to control the color of the marker." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Place:\n", + " \"\"\"A place connected to the Google Maps Geocoding API.\"\"\"\n", + "\n", + " # answers from above\n", + "\n", + " # answer to Q20\n", + " def as_marker(self, *, color=\"blue\"):\n", + " \"\"\"Create a Marker representation of the place.\n", + "\n", + " Args:\n", + " color (str): color of the marker, defaults to \"blue\"\n", + "\n", + " Returns:\n", + " marker (folium.Marker)\n", + "\n", + " Raises:\n", + " RuntimeError: if the place is not synchronized with\n", + " the Google Maps Geocoding API\n", + " \"\"\"\n", + " if not self.place_id:\n", + " raise RuntimeError(\"must synchronize with Google first\")\n", + " return folium.Marker(\n", + " location=...,\n", + " popup=...,\n", + " tooltip=...,\n", + " icon=folium.Icon(color=color),\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q21**: Execute the next code cells that create a new `Place` and obtain a `Marker` for it!\n", + "\n", + "Notes: Without synchronization, we get a `RuntimeError`. `.as_marker()` can be chained right after `.sync_from_google()`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "brandenburg_gate = Place(\"Brandenburger Tor, Pariser Platz, Berlin\", client=api)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "brandenburg_gate.as_marker()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "brandenburg_gate.sync_from_google().as_marker()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q22**: Use the alternative `.from_addresses()` constructor to create a `list` named `places` with already synced `Place`s!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "places = Place.from_addresses(sights, client=api, sync=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "places" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The `Map` Class" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "To make [folium ](https://github.com/python-visualization/folium)'s `Map` class work even better with our `Place` instances, we write our own `Map` class wrapping [folium ](https://github.com/python-visualization/folium)'s. Then, we add further functionality to the class throughout this tutorial.\n", + "\n", + "The `.__init__()` method takes mandatory `name`, `center`, `start`, `end`, and `places` arguments. `name` is there for convenience, `center` is the map's initial center, `start` and `end` are `Place` instances, and `places` is a finite iterable of `Place` instances. Also, `.__init__()` accepts an optional `initial_zoom` argument defaulting to `12`.\n", + "\n", + "Upon initialization, a `folium.Map` instance is created and stored as an implementation detail `_map`. Also, `.__init__()` puts markers for each place on the `_map` object: `\"green\"` and `\"red\"` markers for the `start` and `end` locations and `\"blue\"` ones for the `places` to be visited. To do that, `.__init__()` invokes another `.add_marker()` method on the `Map` class, once for every `Place` object. `.add_marker()` itself invokes the `.add_to()` method on the `folium.Marker` representation of a `Place` instance and enables method chaining.\n", + "\n", + "To keep the state in a `Map` instance consistent, all passed in arguments except `name` are treated as implementation details. Otherwise, a user of the `Map` class could, for example, change the `start` attribute, which would not be reflected in the internally kept `folium.Map` object.\n", + "\n", + "**Q23**: Implement the `.__init__()` and `.add_marker()` methods on the `Map` class as described!\n", + "\n", + "**Q24**: Add a `.show()` method on the `Map` class that simply returns the internal `folium.Map` object!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Map:\n", + " \"\"\"A map with plotting and routing capabilities.\"\"\"\n", + "\n", + " # answer to Q23\n", + " def __init__(self, name, center, start, end, places, initial_zoom=12):\n", + " \"\"\"Create a new map.\n", + "\n", + " Args:\n", + " name (str): name of the map\n", + " center (float, float): coordinates of the map's center\n", + " start (Place): start of the tour\n", + " end (Place): end of the tour\n", + " places (iterable of Places): the places to be visitied\n", + " initial_zoom (integer): zoom level according to folium's\n", + " specifications; defaults to 12\n", + " \"\"\"\n", + " self.name = name\n", + " ...\n", + " ...\n", + " ...\n", + " ... = folium.Map(...)\n", + "\n", + " # Add markers to the map.\n", + " ...\n", + " ...\n", + " for place in places:\n", + " ...\n", + "\n", + " def __repr__(self):\n", + " return f\"\"\n", + "\n", + " # answer to Q24\n", + " def show(self):\n", + " \"\"\"Return a folium.Map representation of the map.\"\"\"\n", + " return ...\n", + "\n", + " # answer to Q23\n", + " def add_marker(self, marker):\n", + " \"\"\"Add a marker to the map.\n", + "\n", + " Args:\n", + " marker (folium.Marker): marker to be put on the map\n", + " \"\"\"\n", + " ...\n", + " return ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's put all the sights, the two airports, and three more places, the [Bundeskanzleramt ](https://en.wikipedia.org/wiki/German_Chancellery), the [Olympic Stadium ](https://en.wikipedia.org/wiki/Olympiastadion_%28Berlin%29), and the [East Side Gallery ](https://en.wikipedia.org/wiki/East_Side_Gallery), on the map.\n", + "\n", + "**Q25**: Execute the next code cells to create a map of Berlin with all the places on it!\n", + "\n", + "Note: Because we implemented method chaining everywhere, the code below is only *one* expression written over several lines. It almost looks like a self-explanatory and compact \"language\" on its own." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "berlin = (\n", + " Map(\n", + " \"Sights in Berlin\",\n", + " center=(52.5015154, 13.4066838),\n", + " start=Place(arrival, client=api).sync_from_google(),\n", + " end=Place(departure, client=api).sync_from_google(),\n", + " places=places,\n", + " initial_zoom=10,\n", + " )\n", + " .add_marker(\n", + " Place(\"Bundeskanzleramt, Willy-Brandt-Straße, Berlin\", client=api)\n", + " .sync_from_google()\n", + " .as_marker(color=\"orange\")\n", + " )\n", + " .add_marker(\n", + " Place(\"Olympiastadion Berlin\", client=api)\n", + " .sync_from_google()\n", + " .as_marker(color=\"orange\")\n", + " )\n", + " .add_marker(\n", + " Place(\"East Side Gallery, Berlin\", client=api)\n", + " .sync_from_google()\n", + " .as_marker(color=\"orange\")\n", + " )\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "berlin" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "berlin.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Distance Matrix" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Before we can find out the best order in which to visit all the sights, we must calculate the pairwise distances between all points. While Google also offers a [Directions API](https://developers.google.com/maps/documentation/directions/start) and a [Distance Matrix API](https://developers.google.com/maps/documentation/distance-matrix/start), we choose to calculate the air distances using the third-party library [geopy ](https://github.com/geopy/geopy).\n", + "\n", + "**Q26**: Familiarize yourself with the [documentation](https://geopy.readthedocs.io/en/stable/) and install [geopy ](https://github.com/geopy/geopy) with the `pip` command line tool!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install geopy" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We use [geopy ](https://github.com/geopy/geopy) primarily for converting the `latitude`-`longitude` coordinates into a [distance matrix ](https://en.wikipedia.org/wiki/Distance_matrix).\n", + "\n", + "Because the [earth is not flat ](https://en.wikipedia.org/wiki/Flat_Earth), [geopy ](https://github.com/geopy/geopy) provides a `great_circle()` function that calculates the so-called [orthodromic distance ](https://en.wikipedia.org/wiki/Great-circle_distance) between two places on a sphere." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from geopy.distance import great_circle" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q27**: For quick reference, read the docstring of `great_circle()` and execute the code cells below to calculate the distance between the `arrival` and the `departure`!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "great_circle?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tegel = Place(arrival, client=api).sync_from_google()\n", + "schoenefeld = Place(departure, client=api).sync_from_google()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "great_circle(tegel.location, schoenefeld.location)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "great_circle(tegel.location, schoenefeld.location).km" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "great_circle(tegel.location, schoenefeld.location).meters" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The `Place` Class (continued): Distance to another `Place`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q28**: Finish the `distance_to()` method in the `Place` class that takes a `other` argument and returns the distance in meters! Adhere to the given docstring!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Place:\n", + " \"\"\"A place connected to the Google Maps Geocoding API.\"\"\"\n", + "\n", + " # answers from above\n", + "\n", + " # answer to Q28\n", + " def distance_to(self, other):\n", + " \"\"\"Calculate the distance to another place in meters.\n", + "\n", + " Args:\n", + " other (Place): the other place to calculate the distance to\n", + "\n", + " Returns:\n", + " distance (int)\n", + "\n", + " Raises:\n", + " RuntimeError: if one of the places is not synchronized with\n", + " the Google Maps Geocoding API\n", + " \"\"\"\n", + " if not self.place_id or not other.place_id:\n", + " raise RuntimeError(\"must synchronize places with Google first\")\n", + " return ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q29**: Execute the code cells below to test the new feature!\n", + "\n", + "Note: If done right, object-oriented code reads almost like plain English." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tegel = Place(arrival, client=api).sync_from_google()\n", + "schoenefeld = Place(departure, client=api).sync_from_google()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tegel.distance_to(schoenefeld)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q30**: Execute the next code cell to instantiate the `Place`s in `sights` again!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "places = Place.from_addresses(sights, client=api, sync=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "places" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The `Map` Class (continued): Pairwise Distances" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, we add a read-only `distances` property on our `Map` class. As we are working with air distances, these are *symmetric* which reduces the number of distances we must calculate.\n", + "\n", + "To do so, we use the [combinations() ](https://docs.python.org/3/library/itertools.html#itertools.combinations) generator function in the [itertools ](https://docs.python.org/3/library/itertools.html) module in the [standard library ](https://docs.python.org/3/library/index.html). That produces all possible `r`-`tuple`s from an `iterable` argument. `r` is `2` in our case as we are looking at `origin`-`destination` pairs.\n", + "\n", + "Let's first look at an easy example of [combinations() ](https://docs.python.org/3/library/itertools.html#itertools.combinations) to understand how it works: It gives us all the `2`-`tuple`s from a `list` of five `numbers` disregarding the order of the `tuple`s' elements." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import itertools" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "numbers = [1, 2, 3, 4, 5]\n", + "\n", + "for x, y in itertools.combinations(numbers, 2):\n", + " print(x, y)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`distances` uses the internal `._start`, `._end`, and `._places` attributes and creates a `dict` with the keys consisting of all pairs of `Place`s and the values being their distances in meters. As this operation is rather costly, we cache the distances the first time we calculate them into a hidden instance attribute `._distances`, which must be initialized with `None` in the `.__init__()` method.\n", + "\n", + "**Q31**: Finish the `.distances` property as described!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Map:\n", + " \"\"\"A map with plotting and routing capabilities.\"\"\"\n", + "\n", + " # answers from above with a tiny adjustment\n", + "\n", + " # answer to Q31\n", + " ...\n", + " def distances(self):\n", + " \"\"\"Return a dict with the pairwise distances of all places.\n", + "\n", + " Implementation note: The results of the calculations are cached.\n", + " \"\"\"\n", + " if not self._distances:\n", + " distances = ...\n", + " all_pairs = itertools.combinations(\n", + " ...,\n", + " r=2,\n", + " )\n", + " for origin, destination in all_pairs:\n", + " distance = ...\n", + " distances[origin, destination] = distance\n", + " distances[destination, origin] = distance\n", + " self._distances = ...\n", + " return ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We pretty print the total distance matrix." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "berlin = Map(\n", + " \"Berlin\",\n", + " center=(52.5015154, 13.4066838),\n", + " start=Place(arrival, client=api).sync_from_google(),\n", + " end=Place(departure, client=api).sync_from_google(),\n", + " places=places,\n", + " initial_zoom=10,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pprint(berlin.distances)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "How can we be sure the matrix contains all possible pairs? As we have 9 `sights` plus the `start` and the `end` of the tour, we conclude that there must be `11 * 10 = 110` distances excluding the `0` distances of a `Place` to itself that are not in the distance matrix." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "n_places = len(places) + 2\n", + "\n", + "n_places * (n_places - 1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "len(berlin.distances)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Route Optimization" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let us find the cost minimal order of traveling from the `arrival` airport to the `departure` airport while visiting all the `sights`.\n", + "\n", + "This problem can be expressed as finding the shortest so-called [Hamiltonian path ](https://en.wikipedia.org/wiki/Hamiltonian_path) from the `start` to `end` on the `Map` (i.e., a path that visits each intermediate node exactly once). With the \"hack\" of assuming the distance of traveling from the `end` to the `start` to be `0` and thereby effectively merging the two airports into a single node, the problem can be viewed as a so-called [traveling salesman problem ](https://en.wikipedia.org/wiki/Traveling_salesman_problem) (TSP).\n", + "\n", + "The TSP is a hard problem to solve but also well studied in the literature. Assuming symmetric distances, a TSP with $n$ nodes has $\\frac{(n-1)!}{2}$ possible routes. $(n-1)$ because any node can be the `start` / `end` and divided by $2$ as the problem is symmetric.\n", + "\n", + "Starting with about $n = 20$, the TSP is almost impossible to solve exactly in a reasonable amount of time. Luckily, we do not have that many `sights` to visit, and so we use a [brute force ](https://en.wikipedia.org/wiki/Brute-force_search) approach and simply loop over all possible routes to find the shortest.\n", + "\n", + "In the case of our tourist, we \"only\" need to try out `181_440` possible routes because the two airports are effectively one node and $n$ becomes $10$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import math" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "math.factorial(len(places) + 1 - 1) // 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Analyzing the problem a bit further, all we need is a list of [permutations ](https://en.wikipedia.org/wiki/Permutation) of the sights as the two airports are always the first and last location.\n", + "\n", + "The [permutations() ](https://docs.python.org/3/library/itertools.html#itertools.permutations) generator function in the [itertools ](https://docs.python.org/3/library/itertools.html) module in the [standard library ](https://docs.python.org/3/library/index.html) helps us with the task. Let's see an example to understand how it works." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "numbers = [1, 2, 3]\n", + "\n", + "for permutation in itertools.permutations(numbers):\n", + " print(permutation)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, if we use [permutations() ](https://docs.python.org/3/library/itertools.html#itertools.permutations) as is, we try out *redundant* routes. For example, transferred to our case, the tuples `(1, 2, 3)` and `(3, 2, 1)` represent the *same* route as the distances are symmetric and the tourist could be going in either direction. To obtain the *unique* routes, we use an `if`-clause in a \"hacky\" way by only accepting routes where the first node has a smaller value than the last. Thus, we keep, for example, `(1, 2, 3)` and discard `(3, 2, 1)`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for permutation in itertools.permutations(numbers):\n", + " if permutation[0] < permutation[-1]:\n", + " print(permutation)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In order to compare `Place`s as numbers, we would have to implement, among others, the `.__eq__()` special method. Otherwise, we get a `TypeError`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Place(arrival, client=api) < Place(departure, client=api)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As a quick and dirty solution, we use the `.location` property on a `Place` to do the comparison." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "Place(arrival, client=api).location < Place(departure, client=api).location" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As the code cell below shows, combining [permutations() ](https://docs.python.org/3/library/itertools.html#itertools.permutations) with an `if`-clause results in the correct number of routes to be looped over." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "sum(\n", + " 1\n", + " for route in itertools.permutations(places)\n", + " if route[0].location < route[-1].location\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To implement the brute force algorithm, we split the logic into two methods.\n", + "\n", + "First, we create an `.evaluate()` method that takes a `route` argument that is a sequence of `Place`s and returns the total distance of the route. Internally, this method uses the `.distances` property repeatedly, which is why we built in caching above.\n", + "\n", + "**Q32**: Finish the `.evaluate()` method as described!\n", + "\n", + "Second, we create a `.brute_force()` method that needs no arguments. It loops over all possible routes to find the shortest. As the `start` and `end` of a route are fixed, we only need to look at `permutation`s of inner nodes. Each `permutation` can then be traversed in a forward and a backward order. `.brute_force()` enables method chaining as well.\n", + "\n", + "**Q33**: Finish the `.brute_force()` method as described!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The `Map` Class (continued): Brute Forcing the TSP" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class Map:\n", + " \"\"\"A map with plotting and routing capabilities.\"\"\"\n", + "\n", + " # answers from above\n", + "\n", + " # answer to Q32\n", + " def evaluate(self, route):\n", + " \"\"\"Calculate the total distance of a route.\n", + "\n", + " Args:\n", + " route (sequence of Places): the ordered nodes in a tour\n", + "\n", + " Returns:\n", + " cost (int)\n", + " \"\"\"\n", + " cost = ...\n", + " # Loop over all pairs of consecutive places.\n", + " origin = ...\n", + " for destination in ...:\n", + " cost += self.distances[...]\n", + " ...\n", + "\n", + " return ...\n", + "\n", + " # answer to Q33\n", + " def brute_force(self):\n", + " \"\"\"Calculate the shortest route by brute force.\n", + "\n", + " The route is plotted on the folium.Map.\n", + " \"\"\"\n", + " # Assume a very high cost to begin with.\n", + " min_cost = ...\n", + " best_route = None\n", + "\n", + " # Loop over all permutations of the intermediate nodes to visit.\n", + " for permutation in ...:\n", + " # Skip redundant permutations.\n", + " if ...:\n", + " ...\n", + " # Travel through the routes in both directions.\n", + " for route in (permutation, permutation[::-1]):\n", + " # Add start and end to the route.\n", + " route = (..., *route, ...)\n", + " # Check if a route is cheaper than all routes seen before.\n", + " cost = ...\n", + " if ...:\n", + " min_cost = ...\n", + " best_route = ...\n", + "\n", + " # Plot the route on the map\n", + " folium.PolyLine(\n", + " [x.location for x in best_route],\n", + " color=\"orange\", weight=3, opacity=1\n", + " ).add_to(self._map)\n", + "\n", + " return ..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q34**: Find the best route for our tourist by executing the code cells below!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "berlin = Map(\n", + " \"Berlin\",\n", + " center=(52.4915154, 13.4066838),\n", + " start=Place(arrival, client=api).sync_from_google(),\n", + " end=Place(departure, client=api).sync_from_google(),\n", + " places=places,\n", + " initial_zoom=12,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "berlin.brute_force().show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "320px" + }, + "toc_section_display": true, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index a0f49df..aeb157a 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -270,3 +270,6 @@ If this is not possible, Text Representations; Instance Methods vs. Class Methods; Computed Properties) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/01_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/01_exercises.ipynb) + (A Traveling Salesman Problem) From da1940d8b6febe730e5e5861d3160f0fa389da23 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 26 Oct 2020 23:34:42 +0100 Subject: [PATCH 111/142] Add initial version of chapter 11, part 2 --- 11_classes/02_content.ipynb | 1984 +++++++++++++++++++++++++++++++++++ CONTENTS.md | 7 + 2 files changed, 1991 insertions(+) create mode 100644 11_classes/02_content.ipynb diff --git a/11_classes/02_content.ipynb b/11_classes/02_content.ipynb new file mode 100644 index 0000000..1d99ecf --- /dev/null +++ b/11_classes/02_content.ipynb @@ -0,0 +1,1984 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/02_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 11: Classes & Instances (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In this second part of the chapter, we learn how we make our `Vector` and `Matrix` instances behave like Python's built-in sequence types, for example, `list` or `tuple` objects." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Sequence Emulation" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As discussed in detail in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb#Collections-vs.-Sequences), a sequence is any finite and iterable container type with a *predictable* order of its elements such that we can label each element with an index in the range `0 <= index < len(sequence)`.\n", + "\n", + "To make `Vector` and `Matrix` instances emulate sequences, we implement the `.__len__()` (cf., [reference ](https://docs.python.org/3/reference/datamodel.html#object.__len__)) and `.__getitem__()` (cf., [reference ](https://docs.python.org/3/reference/datamodel.html#object.__getitem__)) methods. While the former returns the total number of elements in a container and is automatically invoked on any object passed to the built-in [len() ](https://docs.python.org/3/library/functions.html#len) function, the latter is invoked by the interpreter behind the scenes when we use the indexing operator `[]`.\n", + "\n", + "In the example, both `.__len__()` and `.__getitem__()` delegate parts of the work to the embedded `list` object named `._entries`. This is a design principle known as [delegation ](https://en.wikipedia.org/wiki/Delegation_%28object-oriented_programming%29) in software engineering. Also, we implicitly invoke the `.__len__()` method inside the `.__init__()` method already via the `len(self)` expression. This reuses code and also ensures that we calculate the number of entries in one way only within the entire class." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "class Vector:\n", + "\n", + " def __init__(self, data):\n", + " self._entries = list(float(x) for x in data)\n", + " if len(self) == 0:\n", + " raise ValueError(\"a vector must have at least one entry\")\n", + "\n", + " def __repr__(self):\n", + " args = \", \".join(f\"{x:.3f}\" for x in self._entries)\n", + " return f\"Vector(({args}))\"\n", + "\n", + " def __len__(self):\n", + " return len(self._entries)\n", + "\n", + " def __getitem__(self, index):\n", + " return self._entries[index]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Now, we may obtain the number of elements with [len() ](https://docs.python.org/3/library/functions.html#len) and index into `Vector` instances." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "v = Vector([1, 2, 3])" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(v)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1.0" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Negative indexes work \"out of the box\" because of the delegation to the internal `list` object." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3.0" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v[-1]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Somehow \"magically\" we can loop over `v` with a `for` statement. This works as Python simply loops over the indexes implied by `len(v)` and obtains the entries one by one." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.0 2.0 3.0 " + ] + } + ], + "source": [ + "for entry in v:\n", + " print(entry, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`v` may also be looped over in reverse order with the [reversed() ](https://docs.python.org/3/library/functions.html#reversed) built-in." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3.0 2.0 1.0 " + ] + } + ], + "source": [ + "for entry in reversed(v):\n", + " print(entry, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Membership testing with the `in` operator also comes \"for free.\" Here, Python compares the object to be searched to each element with the `==` operator and stops early once one compares equal. That constitutes a [linear search ](https://en.wikipedia.org/wiki/Linear_search) as seen before." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 in v" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "99 in v" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "So far, indexing is a *read-only* operation." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'Vector' 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[0mv\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\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: 'Vector' object does not support item assignment" + ] + } + ], + "source": [ + "v[0] = 99" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Because a `Matrix` is two-dimensional, we must decide how we *flatten* the `._entries`. We *choose* to loop over the first row, then the second row, and so on. This is called a **[row major approach ](https://en.wikipedia.org/wiki/Row-_and_column-major_order)**.\n", + "\n", + "In addition to indexing by `int` objects, we also implement indexing by 2-`tuple`s of `int`s where the first element indicates the row and the second the column. Deciding what to do inside a method depending on the *type* of an argument is known as **type dispatching**. We achieve that with the built-in [isinstance() ](https://docs.python.org/3/library/functions.html#isinstance) function.\n", + "\n", + "Lastly, we ensure that integer indexing also works with negative values as we are used to from sequences in general.\n", + "\n", + "Note how all of the methods work together:\n", + "- `.__init__()`, `.__len__()`, and `.__getitem__()` reuse the `.n_rows` and `.n_cols` properties, and\n", + "- `.__init__()` and `.__getitem__()` invoke `.__len__()` via the `len(self)` expressions." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "class Matrix:\n", + "\n", + " def __init__(self, data):\n", + " self._entries = list(list(float(x) for x in r) for r in data)\n", + " for row in self._entries[1:]:\n", + " if len(row) != self.n_cols:\n", + " raise ValueError(\"rows must have the same number of entries\")\n", + " if len(self) == 0:\n", + " raise ValueError(\"a matrix must have at least one entry\")\n", + "\n", + " @property\n", + " def n_rows(self):\n", + " return len(self._entries)\n", + "\n", + " @property\n", + " def n_cols(self):\n", + " return len(self._entries[0])\n", + "\n", + " def __len__(self):\n", + " return self.n_rows * self.n_cols\n", + "\n", + " def __getitem__(self, index):\n", + " if isinstance(index, int):\n", + " if index < 0:\n", + " index += len(self)\n", + " if not (0 <= index < len(self)):\n", + " raise IndexError(\"integer index out of range\")\n", + " row, col = divmod(index, self.n_cols)\n", + " return self._entries[row][col]\n", + " elif (\n", + " isinstance(index, tuple) and len(index) == 2\n", + " and isinstance(index[0], int) and isinstance(index[1], int)\n", + " ):\n", + " return self._entries[index[0]][index[1]]\n", + " raise TypeError(\"index must be either an int or a tuple of two int's\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Now, we may use a `Matrix` instance just like any other sequence ..." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "m = Matrix([(1, 2, 3), (4, 5, 6), (7, 8, 9)])" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "9" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(m)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1.0" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m[0] # entry in the upper left corner" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "9.0" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m[-1] # entry in the lower right corner" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... but also index in the two dimensions separately." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3.0" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m[0, 2] # first row, third column" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "9.0" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m[-1, -1] # last row, last column / lower right corner" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As before, Python figures out the iteration on its own ..." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 " + ] + } + ], + "source": [ + "for entry in m:\n", + " print(entry, end=\" \")" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "9.0 8.0 7.0 6.0 5.0 4.0 3.0 2.0 1.0 " + ] + } + ], + "source": [ + "for entry in reversed(m):\n", + " print(entry, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... and makes the `in` operator do a linear search." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 in m" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "99 in m" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### The Python Data Model" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Sequence emulation itself is *not* a property of object-oriented languages in general. Instead, it is a behavior any data type may or may not exhibit in Python.\n", + "\n", + "The collection of all such behaviors a programming language offers is commonly referred to as its **object model**. In Python, the term **data model** is used instead and all possible behaviors are documented in the [language reference ](https://docs.python.org/3/reference/datamodel.html), in particular, in the section on special methods. We can think of the data model as the collection of all the behaviors we can make our user-defined data types follow. Pythonistas also use the term **protocol** instead of behavior, for example, we may say that \"the `Vector` and `Matrix` classes follow the sequence protocol.\"\n", + "\n", + "So, merely defining the *two* `.__len__()` and `.__getitem__()` methods is enough to make instances of any user-defined type behave like the built-in sequences in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb). Yet, there we defined sequences as all objects having the *four* properties of being finite, iterable, and ordered container types. And, these properties correspond to special methods by the names of `.__len__()`, `.__iter__()`, `.__reversed__()`, and `.__contains__()` as we see in the next section. Thus, Python \"magically\" knows how to derive the logic for the `.__iter__()`, `.__reversed__()`, and `.__contains__()` methods from the combination of the `.__len__()` and `.__getitem__()` methods. In general, while some special methods are related, others are not. Understanding these relationships means understanding the Python data model and vice versa. That is what every aspiring data scientist should aim for.\n", + "\n", + "On the contrary, we could also look at special methods individually. Whereas `.__len__()` is invoked on the object passed to [len() ](https://docs.python.org/3/library/functions.html#len), Python \"translates\" the indexing operator applied on any name like `a[i]`, for example, into the method invocation `a.__getitem__(i)`. So, in both cases, the special methods are executed according to a deterministic rule of the language. In that sense, they act as some sort of syntactic sugar. Thus, they even work if only one of them is defined. For example, without `.__len__()`, iteration with a `for`-loop still works but only in forward direction." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### More on Iteration" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "When implementing the sequence protocol for our `Matrix` class, we had to make the assumption that the user of our class wants to loop over the entries in a rows first fashion. While such assumptions can often be justified by referring to popular conventions (e.g., mathematicians usually look at matrices also in a \"row by column\" way), we could instead provide several iteration methods such that the user may choose one, just like `dict` objects come with several built-in methods that provide iteration.\n", + "\n", + "In the revised `Matrix` class below, we add the `.rows()`, `.cols()`, and `.entries()` methods that return `generator`s providing different and memory efficient ways of looping over the entries. `.rows()` and `.cols()` sequentially produce `Vector` instances representing individual rows and columns. This is in line with a popular idea in linear algebra to view a matrix as a collection of either row or column vectors. Further, `.entries()` by default produces the entries in the matrix one by one in a flat and row major fashion. Called with the optional `row_major=False` flag, it does the same in a column major fashion. The optional `reverse=True` flag allows iteration in backwards order.\n", + "\n", + "We also implement the `.__iter__()` (cf., [reference ](https://docs.python.org/3/reference/datamodel.html#object.__iter__)) and `.__reversed__()` (cf., [reference ](https://docs.python.org/3/reference/datamodel.html#object.__reversed__)) methods that immediately forward invocation to `.entries()`. So, Python does not need to fall back to `.__len__()` and `.__getitem__()` as we described above." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "class Matrix:\n", + "\n", + " def __init__(self, data):\n", + " self._entries = list(list(float(x) for x in r) for r in data)\n", + " # ...\n", + "\n", + " def __repr__(self):\n", + " args = \", \".join(\"(\" + \", \".join(f\"{c:.3f}\" for c in r) + \",)\" for r in self._entries)\n", + " return f\"Matrix(({args}))\"\n", + "\n", + " @property\n", + " def n_rows(self):\n", + " return len(self._entries)\n", + "\n", + " @property\n", + " def n_cols(self):\n", + " return len(self._entries[0])\n", + "\n", + " def rows(self):\n", + " return (Vector(r) for r in self._entries)\n", + "\n", + " def cols(self):\n", + " return (\n", + " Vector(self._entries[r][c] for r in range(self.n_rows)) for c in range(self.n_cols)\n", + " )\n", + "\n", + " def entries(self, *, reverse=False, row_major=True):\n", + " if reverse:\n", + " rows, cols = (range(self.n_rows - 1, -1, -1), range(self.n_cols - 1, -1, -1))\n", + " else:\n", + " rows, cols = range(self.n_rows), range(self.n_cols)\n", + " if row_major:\n", + " return (self._entries[r][c] for r in rows for c in cols)\n", + " return (self._entries[r][c] for c in cols for r in rows)\n", + "\n", + " def __iter__(self):\n", + " return self.entries()\n", + "\n", + " def __reversed__(self):\n", + " return self.entries(reverse=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The revised version of `Vector` below also works without `.__len__()` and `.__getitem__()` methods and leaves the creation of memory efficient `generator`s up to the embedded `list` object in `._entries` by using the built-in [iter() ](https://docs.python.org/3/library/functions.html#iter) and [reversed() ](https://docs.python.org/3/library/functions.html#reversed) functions. Also, `.__repr__()` now relies on the sequence protocol as the instance loops over \"itself\" with `for x in self`, a subtle reuse of code again. " + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "class Vector:\n", + "\n", + " def __init__(self, data):\n", + " self._entries = list(float(x) for x in data)\n", + " # ...\n", + "\n", + " def __repr__(self):\n", + " args = \", \".join(f\"{x:.3f}\" for x in self)\n", + " return f\"Vector(({args}))\"\n", + "\n", + " def __iter__(self):\n", + " return iter(self._entries)\n", + "\n", + " def __reversed__(self):\n", + " return reversed(self._entries)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "m = Matrix([(1, 2, 3), (4, 5, 6), (7, 8, 9)])" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Iteration works as before ..." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 " + ] + } + ], + "source": [ + "for entry in m:\n", + " print(entry, end=\" \")" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "9.0 8.0 7.0 6.0 5.0 4.0 3.0 2.0 1.0 " + ] + } + ], + "source": [ + "for entry in reversed(m):\n", + " print(entry, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... but now we have some ways of customizing it." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Vector((1.000, 2.000, 3.000)) Vector((4.000, 5.000, 6.000)) Vector((7.000, 8.000, 9.000)) " + ] + } + ], + "source": [ + "for row_vector in m.rows():\n", + " print(row_vector, end=\" \")" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Vector((1.000, 4.000, 7.000)) Vector((2.000, 5.000, 8.000)) Vector((3.000, 6.000, 9.000)) " + ] + } + ], + "source": [ + "for col_vector in m.cols():\n", + " print(col_vector, end=\" \")" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 " + ] + } + ], + "source": [ + "for entry in m.entries():\n", + " print(entry, end=\" \")" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.0 4.0 7.0 2.0 5.0 8.0 3.0 6.0 9.0 " + ] + } + ], + "source": [ + "for entry in m.entries(row_major=False):\n", + " print(entry, end=\" \")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Mutability vs. Immutability" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In the above implementations, the instance attribute `._entries` on a `Vector` or `Matrix` instance references either a `list` or a `list` of row `list`s , which is by the convention of the leading underscore `_` an implementation detail. If users of our classes adhere to this convention, `Vector` and `Matrix` instances can be regarded as *immutable*.\n", + "\n", + "In line with the implied immutability, we implemented the `.transpose()` method such that it returns a *new* `Matrix` instance. Instead, we could make the method change the internal `self._entries` attribute *in place* as we do in the next example. To indicate this mutation to the user of the `Matrix` class clearly, the revised `.transpose()` method returns `None`. That mirrors, for example, how the mutating methods of the built-in `list` type behave (cf., [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/01_content.ipynb#List-Methods)).\n", + "\n", + "Such decisions are better made consciously when designing a custom data type. The main trade-off is that immutable data types are typically easier to reason about when reading code whereas mutable data types tend to be more memory efficient and make programs faster as less copying operations take place in memory. However, this trade-off only becomes critical when we deal with big amounts of data." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "class Matrix:\n", + "\n", + " def __init__(self, data):\n", + " self._entries = list(list(float(x) for x in r) for r in data)\n", + " # ...\n", + "\n", + " def __repr__(self):\n", + " args = \", \".join(\"(\" + \", \".join(f\"{c:.3f}\" for c in r) + \",)\" for r in self._entries)\n", + " return f\"Matrix(({args}))\"\n", + "\n", + " def transpose(self):\n", + " self._entries = list(list(float(x) for x in r) for r in (zip(*self._entries)))\n", + " return None" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "m = Matrix([(1, 2, 3), (4, 5, 6), (7, 8, 9)])" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Transposing `m` has *no* cell output ..." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "m.transpose()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... so we must look at `m` again." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((1.000, 4.000, 7.000,), (2.000, 5.000, 8.000,), (3.000, 6.000, 9.000,)))" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A downside of returning `None` is that we can *not* chain repeated invocations of `.transpose()`." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'NoneType' object has no attribute 'transpose'", + "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[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtranspose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtranspose\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: 'NoneType' object has no attribute 'transpose'" + ] + } + ], + "source": [ + "m.transpose().transpose()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Enabling Method Chaining" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To fix the missing method chaining, we end the `.transpose()` method with `return self`, which returns a reference to the instance on which the method is invoked." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "class Matrix:\n", + "\n", + " def __init__(self, data):\n", + " self._entries = list(list(float(x) for x in r) for r in data)\n", + " # ...\n", + "\n", + " def __repr__(self):\n", + " args = \", \".join(\"(\" + \", \".join(f\"{c:.3f}\" for c in r) + \",)\" for r in self._entries)\n", + " return f\"Matrix(({args}))\"\n", + "\n", + " def __iter__(self): # adapted for brevity; uses parts of entries()\n", + " rows, cols = range(len(self._entries)), range(len(self._entries[0]))\n", + " return (self._entries[r][c] for r in rows for c in cols)\n", + "\n", + " def transpose(self):\n", + " self._entries = list(list(float(x) for x in r) for r in (zip(*self._entries)))\n", + " return self" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "m = Matrix([(1, 2, 3), (4, 5, 6), (7, 8, 9)])" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The downside of this approach is that a user may unknowingly end up with *two* references to the *same* instance. That can only be mitigated by clear documentation." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "n = m.transpose()" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((1.000, 4.000, 7.000,), (2.000, 5.000, 8.000,), (3.000, 6.000, 9.000,)))" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((1.000, 4.000, 7.000,), (2.000, 5.000, 8.000,), (3.000, 6.000, 9.000,)))" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "n" + ] + }, + { + "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": [ + "m is n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "### More on Indexing" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Analogous to the `.__getitem__()` method above, there are also the `.__setitem__()` (cf., [reference ](https://docs.python.org/3/reference/datamodel.html#object.__setitem__)) and `.__delitem__()` (cf., [reference ](https://docs.python.org/3/reference/datamodel.html#object.__delitem__)) methods that assign a new element to or delete an existing element from a sequence.\n", + "\n", + "Whereas deleting an individual entry in a `Vector` or `Matrix` instance may *not* really make sense semantically, we interpret this as setting the corresponding entry to \"unknown\" (i.e., `NaN`). Also, we implement changing individual entries via index assignment. Here, `.__setitem__()` delegates the assignment to the embedded `list` object after casting the assigned value as a `float`. While the example below only allows indexing by an integer, it could be generalized to slicing as well." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "class Vector:\n", + "\n", + " def __init__(self, data):\n", + " self._entries = list(float(x) for x in data)\n", + " # ...\n", + "\n", + " def __repr__(self):\n", + " args = \", \".join(f\"{x:.3f}\" for x in self)\n", + " return f\"Vector(({args}))\"\n", + "\n", + " def __getitem__(self, index):\n", + " return self._entries[index]\n", + "\n", + " def __setitem__(self, index, value):\n", + " self._entries[index] = float(value)\n", + "\n", + " def __delitem__(self, index):\n", + " self._entries[index] = float(\"NaN\")" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "v = Vector([1, 2, 3])" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((1.000, 2.000, 3.000))" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`v` can now be changed in place." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "del v[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((nan, 2.000, 3.000))" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "v[0] = 99" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((99.000, 2.000, 3.000))" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "After this discussion of mutable `Vector` and `Matrix` classes, we continue with immutable classes in the rest of this chapter." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Polymorphism" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A function is considered **polymorphic** if it can work with *different* data types. The main advantage is reuse of the function's code. Polymorphism goes hand in hand with the concept of [duck typing ](https://en.wikipedia.org/wiki/Duck_typing), first mentioned in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb#Duck-Typing) in the context of input validation.\n", + "\n", + "We know polymorphic functions already: The built-in [sum() ](https://docs.python.org/3/library/functions.html#sum) function is a trivial example that works with all kinds of `iterable` arguments." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum((1, 2, 3, 4))" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum([1, 2, 3, 4])" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum({1, 2, 3, 4})" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum({1: 996, 2: 997, 3: 998, 4: 999}) # loops over the keys" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As we implemented the `Vector` and `Matrix` classes to be iterable, we may pass them to [sum() ](https://docs.python.org/3/library/functions.html#sum) as well." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10.0" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(Vector([1, 2, 3, 4]))" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10.0" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(Matrix([(1, 2), (3, 4)]))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "A polymorphic function with a semantic meaning in the context of linear algebra would be one that calculates the [Euclidean norm ](https://en.wikipedia.org/wiki/Norm_%28mathematics%29#Euclidean_norm) for vectors, which is a generalization of the popular [Pythagorean theorem ](https://en.wikipedia.org/wiki/Pythagorean_theorem). Extending the same kind of computation to a matrix results in the even more general [Frobenius norm ](https://en.wikipedia.org/wiki/Matrix_norm#Frobenius_norm):" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "$$\\lVert \\bf{X} \\rVert_F = \\sqrt{ \\sum_{i=1}^m \\sum_{j=1}^n x_{ij}^2 }$$" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `norm()` function below can handle both a `Vector` or a `Matrix` instance and is therefore polymorphic. In this sense, `Vector` and `Matrix` instances \"walk\" and \"quack\" alike. In particular, they they both can provide all their entries as a flat sequence." + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "import math" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "def norm(vector_or_matrix):\n", + " \"\"\"Calculate the Frobenius or Euclidean norm of a matrix or vector.\n", + "\n", + " Args:\n", + " vector_or_matrix (Vector/Matrix): the entries whose squares\n", + " are summed up\n", + "\n", + " Returns:\n", + " norm (float)\n", + " \"\"\"\n", + " return math.sqrt(sum(x ** 2 for x in vector_or_matrix))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "While `norm()` is intended to work with `Vector` or `Matrix` instances ..." + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "5.477225575051661" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "norm(Vector([1, 2, 3, 4]))" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "5.477225575051661" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "norm(Matrix([(1, 2), (3, 4)]))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... it also works for any sequence of numbers." + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "5.477225575051661" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "norm([1, 2, 3, 4])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "An important criterion if different classes are compatible in the sense that the same polymorphic function can work with them is that they implement the same **interface**.\n", + "\n", + "Whereas many other programming languages formalize this [concept ](https://en.wikipedia.org/wiki/Protocol_%28object-oriented_programming%29), in Python the term refers to the loose idea that different classes define the same attributes and implement the various protocols behind the special methods in a consistent way. This is what it means to \"walk\" and \"quack\" alike." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index aeb157a..59b18c0 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -273,3 +273,10 @@ If this is not possible, - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/01_exercises.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/01_exercises.ipynb) (A Traveling Salesman Problem) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/02_content.ipynb) + (Sequence Emulation & Iteration; + Python's Data Model; + (Im)mutable Data Types; + Method Chaining; + Polymorphism) From c2e0deafde125a44f8b07d90ab2fb6b5461878ee Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 27 Oct 2020 11:00:34 +0100 Subject: [PATCH 112/142] Fix wrong link --- CONTENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTENTS.md b/CONTENTS.md index 59b18c0..ca1718b 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -263,7 +263,7 @@ If this is not possible, - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/08_resources.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/08_resources.ipynb) - *Chapter 11*: Classes & Instances - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/00_content.ipynb) (`class` Statement; Instantiation; From 2c83a883c5a9f550e7b36f08903cd4fc1b5c119c Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 27 Oct 2020 11:03:29 +0100 Subject: [PATCH 113/142] Add initial version of chapter 11, part 3 --- 11_classes/03_content.ipynb | 2367 +++++++++++++++++++++++++++++++++++ CONTENTS.md | 4 + 2 files changed, 2371 insertions(+) create mode 100644 11_classes/03_content.ipynb diff --git a/11_classes/03_content.ipynb b/11_classes/03_content.ipynb new file mode 100644 index 0000000..7b3a9ae --- /dev/null +++ b/11_classes/03_content.ipynb @@ -0,0 +1,2367 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/03_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 11: Classes & Instances (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The implementations of our `Vector` and `Matrix` classes so far do not know any of the rules of [linear algebra ](https://en.wikipedia.org/wiki/Linear_algebra). In this third part of the chapter, we add a couple of typical vector and matrix operations, for example, vector addition or matrix-vector multiplication, and some others. Before we do so, we briefly talk about how we can often model the *same* underlying data with a *different* data type." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Representations of Data" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "> \"If you change the way you look at things, the things you look at change.\"\n", + "> -- philosopher and personal coach [Dr. Wayne Dyer ](https://en.wikipedia.org/wiki/Wayne_Dyer)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Sometimes, it is helpful to view a `Vector` as a `Matrix` with either one row or one column. On the contrary, such a `Matrix` can always be interpreted as a `Vector` again. Changing the representation of the same underlying data (i.e., the `_entries`) can be viewed as \"changing\" an object's data type, for which, however, there is no built-in syntax.\n", + "\n", + "Thus, we implement the `.as_matrix()` and `.as_vector()` methods below that create *new* `Matrix` or `Vector` instances out of existing `Vector` or `Matrix` instances, respectively. Internally, both methods rely on the sequence protocol again (i.e., `for x in self`). Also, `.as_matrix()` interprets the `Vector` instance as a column vector by default (i.e., the `column=True` flag)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "class Vector:\n", + "\n", + " def __init__(self, data):\n", + " self._entries = list(float(x) for x in data)\n", + " # ...\n", + "\n", + " def __repr__(self):\n", + " args = \", \".join(f\"{x:.3f}\" for x in self)\n", + " return f\"Vector(({args}))\"\n", + "\n", + " def __iter__(self):\n", + " return iter(self._entries)\n", + "\n", + " def as_matrix(self, *, column=True):\n", + " if column:\n", + " return Matrix([x] for x in self)\n", + " return Matrix([(x for x in self)])" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "class Matrix:\n", + "\n", + " def __init__(self, data):\n", + " self._entries = list(list(float(x) for x in r) for r in data)\n", + " # ...\n", + "\n", + " def __repr__(self):\n", + " args = \", \".join(\"(\" + \", \".join(f\"{c:.3f}\" for c in r) + \",)\" for r in self._entries)\n", + " return f\"Matrix(({args}))\"\n", + "\n", + " @property\n", + " def n_rows(self):\n", + " return len(self._entries)\n", + "\n", + " @property\n", + " def n_cols(self):\n", + " return len(self._entries[0])\n", + "\n", + " def __iter__(self): # adapted for brevity; uses parts of .entries()\n", + " return (self._entries[r][c] for r in range(self.n_rows) for c in range(self.n_cols))\n", + "\n", + " def as_vector(self):\n", + " if not (self.n_rows == 1 or self.n_cols == 1):\n", + " raise RuntimeError(\"one dimension (m or n) must be 1\")\n", + " return Vector(x for x in self)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "v = Vector([1, 2, 3])" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((1.000, 2.000, 3.000))" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's interpret `v` as a column vector and create a matrix with dimension $3 \\times 1$." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "m = v.as_matrix()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((1.000,), (2.000,), (3.000,)))" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(3, 1)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m.n_rows, m.n_cols" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "By chaining `.as_matrix()` and `.as_vector()` we get a *new* `Vector` instance back that is equivalent to the given `v`." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((1.000, 2.000, 3.000))" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v.as_matrix().as_vector()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In the same way, we can also interpret `v` as a row vector and create a $1 \\times 3$ matrix." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "m = v.as_matrix(column=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((1.000, 2.000, 3.000,)))" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 3)" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m.n_rows, m.n_cols" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((1.000, 2.000, 3.000))" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v.as_matrix(column=False).as_vector()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Interpreting a matrix as a vector only works if one of the two dimensions, $m$ or $n$, is $1$. If this requirement is not satisfied, we get the `RuntimeError` raised in `.as_vector()` above." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "m = Matrix([(1, 2, 3), (4, 5, 6), (7, 8, 9)])" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "RuntimeError", + "evalue": "one dimension (m or n) must be 1", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mRuntimeError\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[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_vector\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;36mas_vector\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 22\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mas_vector\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\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 23\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_rows\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m1\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_cols\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[0;32m---> 24\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"one dimension (m or n) must be 1\"\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 25\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mVector\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mRuntimeError\u001b[0m: one dimension (m or n) must be 1" + ] + } + ], + "source": [ + "m.as_vector()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Operator Overloading" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "By implementing special methods such as `.__add__()`, `.__sub__()`, `.__mul__()`, and some others, we can make user-defined data types emulate how numeric types operate with each other (cf., [reference ](https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types)): Then, `Vector` and `Matrix` instances can be added together, subtracted from one another, or be multiplied together. We use them to implement the arithmetic rules from linear algebra.\n", + "\n", + "The OOP concept behind this is **[operator overloading ](https://en.wikipedia.org/wiki/Operator_overloading)** as first mentioned in the context of `str` concatenation in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Operator-Overloading)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Arithmetic Operators" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To understand the protocol behind arithmetic operators, we first look at the simple case of how an `int` object and a `float` object are added. The expression `1 + 2.0` is \"translated\" by Python into a method invocation of the form `1.__add__(2.0)`. This is why all the special methods behind binary operators take two arguments, `self` and, by convention, `other`. To allow binary operators to work with objects of *different* data types, Python expects the `.__add__()` method on the `1` object to return `NotImplemented` if it does not know how to deal with the `2.0` object and then proceeds by invoking the *reverse* special method `2.0.__radd__(1)`. With this protocol, one can create *new* data types that know how to execute arithmetic operations with *existing* data types *without* having to change the latter. By convention, the result of a binary operation should always be a *new* instance object and *not* a mutation of an existing one." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3.0" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "1 + 2.0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Before implementing the arithmetic operators, we must first determine what other data types are allowed to interact with our `Vector` and `Matrix` instances and also how the two interact with each other. Conceptually, this is the same as to ask how strict we want the rules from linear algebra to be enforced in our model world. For example, while it is obvious that two vectors with the same number of entries may be added or subtracted, we could also allow a scalar value to be added to a vector. That seems awkward at first because it is an illegal operation in linear algebra. However, for convenience in our programs, we could interpret any scalar as a \"constants\" vector of the \"right size\" and add it to each entry in a `Vector`. This idea can be generalized into what is called **[broadcasting](https://docs.scipy.org/doc/numpy/user/basics.broadcasting.html)** in [numpy](https://docs.scipy.org/doc/numpy/index.html). We often see \"dirty hacks\" like this in code. They are no bugs but features supposed to make the user of a library more productive.\n", + "\n", + "In this chapter, we model the following binary arithmetic operations:" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "- **Addition** / **Subtraction**\n", + " - `Vector` with `Vector` (if number of entries match; commutative)\n", + " - `Matrix` with `Matrix` (if dimensions $m$ and $n$ match; commutative)\n", + " - `Matrix` / `Vector` with scalar (the scalar is broadcasted; non-commutative for subtraction)\n", + "- **Multiplication**\n", + " - `Vector` with `Vector` ([dot product ](https://en.wikipedia.org/wiki/Dot_product) if number of entries match; commutative)\n", + " - `Matrix` with `Vector` (if dimensions are compatible; vector interpreted as column vector; non-commutative)\n", + " - `Vector` with `Matrix` (if dimensions are compatible; vector interpreted as row vector; non-commutative)\n", + " - `Matrix` / `Vector` with scalar ([scalar multiplication ](https://en.wikipedia.org/wiki/Scalar_multiplication); commutative)\n", + " - `Matrix` with `Matrix` ([matrix-matrix multiplication ](https://en.wikipedia.org/wiki/Matrix_multiplication) if dimensions are compatible; generally non-commutative)\n", + "- **Division**\n", + " - `Matrix` / `Vector` by a scalar (inverse of [scalar multiplication ](https://en.wikipedia.org/wiki/Scalar_multiplication); non-commutative)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "This listing shows the conceptual complexity behind the task of writing a \"little\" linear algebra library. Not to mention that some of the operations are [commutative ](https://en.wikipedia.org/wiki/Commutative_property) while others are not.\n", + "\n", + "As the available special methods correspond to the high-level grouping in the listing, we must implement a lot of **type dispatching** within them. This is why you see the built-in [isinstance() ](https://docs.python.org/3/library/functions.html#isinstance) function in most of the methods below. We use it to check if the `other` argument passed in is a `Vector` or `Matrix` instance or a scalar." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(m, Vector)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(v, Vector)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To check if `other` is a scalar, we need to specify what data type constitutes a scalar. We use a goose typing strategy as explained in [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/02_content.ipynb#Goose-Typing): Any object that behaves like a `numbers.Number` from the [numbers ](https://docs.python.org/3/library/numbers.html) module in the [standard library ](https://docs.python.org/3/library/index.html) is considered a scalar.\n", + "\n", + "For example, the integer `1` is an instance of the built-in `int` type. At the same time, [isinstance() ](https://docs.python.org/3/library/functions.html#isinstance) also confirms that it is a `numbers.Number` in the abstract sense." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(1, int)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "import numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "isinstance(1, numbers.Number)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Now with all the preparation work done, let's look at a \"minimal\" implementation of `Vector` that supports all the arithmetic operations specified above. *None* of the special methods inside the `Vector` class is aware that the `Matrix` class exists! Thus, all operations involving at least one `Matrix` instance are implemented only in the `Matrix` class." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "code_folding": [], + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "class Vector:\n", + "\n", + " def __init__(self, data):\n", + " self._entries = list(float(x) for x in data)\n", + " # ...\n", + "\n", + " def __repr__(self):\n", + " args = \", \".join(f\"{x:.3f}\" for x in self)\n", + " return f\"Vector(({args}))\"\n", + "\n", + " def __len__(self):\n", + " return len(self._entries)\n", + "\n", + " def __iter__(self):\n", + " return iter(self._entries)\n", + "\n", + " def __add__(self, other):\n", + " if isinstance(other, self.__class__): # vector addition\n", + " if len(self) != len(other):\n", + " raise ValueError(\"vectors need to be of the same length\")\n", + " return Vector(x + y for (x, y) in zip(self, other))\n", + " elif isinstance(other, numbers.Number): # broadcasting addition\n", + " return Vector(x + other for x in self)\n", + " return NotImplemented\n", + "\n", + " def __radd__(self, other):\n", + " return self + other\n", + "\n", + " def __sub__(self, other):\n", + " if isinstance(other, self.__class__): # vector subtraction\n", + " if len(self) != len(other):\n", + " raise ValueError(\"vectors need to be of the same length\")\n", + " return Vector(x - y for (x, y) in zip(self, other))\n", + " elif isinstance(other, numbers.Number): # broadcasting subtraction\n", + " return Vector(x - other for x in self)\n", + " return NotImplemented\n", + "\n", + " def __rsub__(self, other):\n", + " # Reverse vector subtraction is already handled in __sub__().\n", + " if isinstance(other, numbers.Number): # broadcasting subtraction\n", + " return Vector(other - x for x in self)\n", + " return NotImplemented\n", + "\n", + " def __mul__(self, other):\n", + " if isinstance(other, self.__class__): # dot product\n", + " if len(self) != len(other):\n", + " raise ValueError(\"vectors need to be of the same length\")\n", + " return sum(x * y for (x, y) in zip(self, other))\n", + " elif isinstance(other, numbers.Number): # scalar multiplication\n", + " return Vector(x * other for x in self)\n", + " return NotImplemented\n", + "\n", + " def __rmul__(self, other):\n", + " return self * other\n", + "\n", + " def __truediv__(self, other):\n", + " if isinstance(other, numbers.Number):\n", + " return self * (1 / other)\n", + " return NotImplemented\n", + "\n", + " def as_matrix(self, *, column=True):\n", + " if column:\n", + " return Matrix([x] for x in self)\n", + " return Matrix([(x for x in self)])" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "v = Vector([1, 2, 3])" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "w = Vector([4, 5])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`.__mul__()` implements both scalar multiplication and the dot product of two `Vector`s. As both operations are commutative, `.__rmul__()` dispatches to `.__mul__()` via the `self * other` expression." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((2.000, 4.000, 6.000))" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "2 * v" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((3.000, 6.000, 9.000))" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v * 3" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "14.0" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v * v" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "If two `Vector`s do *not* have a matching number of entries, a `ValueError` is raised." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "vectors need to be of the same length", + "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[0mv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mw\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__mul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 45\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# dot product\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 46\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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---> 47\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"vectors need to be of the same length\"\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 48\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0msum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0my\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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 49\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mNumber\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# scalar multiplication\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: vectors need to be of the same length" + ] + } + ], + "source": [ + "v * w" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`.__truediv__()` implements the ordinary division operator `/` while `.__floordiv__()` would implement the integer division operator `//`. Here, `.__truediv__()` dispatches to `.__mul__()` after inverting the `other` argument via the `self * (1 / other)` expression." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((0.333, 0.667, 1.000))" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v / 3" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "`.__add__()` and `.__sub__()` implement vector addition and subtraction according to standard linear algebra rules, meaning that both `Vector`s must have the same number of entries or a `ValueError` is raised. Furthermore, both methods are able to broadcast the `other` argument to the dimension of a `Vector` and then execute either vector addition or subtraction. As addition is commutative, `.__radd__()` dispatches to `.__add__()`. For now, we have to explicitly implement `.__rsub__()`. Further below, we see how it can be re-factored to be commutative." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((2.000, 4.000, 6.000))" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v + v" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((0.000, 0.000, 0.000))" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v - v" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "vectors need to be of the same length", + "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[0mv\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mw\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__add__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# vector addition\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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---> 20\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"vectors need to be of the same length\"\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 21\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mVector\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0my\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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 22\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mNumber\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# broadcasting addition\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: vectors need to be of the same length" + ] + } + ], + "source": [ + "v + w" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((101.000, 102.000, 103.000))" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v + 100" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((99.000, 98.000, 97.000))" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "100 - v" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For `Matrix` instances, the implementation is a bit more involved as we need to distinguish between matrix-matrix, matrix-vector, vector-matrix, and scalar multiplication and check for compatible dimensions. To review the underlying rules, check this [article ](https://en.wikipedia.org/wiki/Matrix_multiplication) or watch the video below." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAUDBAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChALCAgOCggIDRUNDhERExMTCAsWGBYSGBASExIBBQUFCAcIDwkJDhIODw8SEhIVEhISFRISEhISEhIVEhISFhISEhISEhISEhISEhISEhISEhISEhISEhISEhIeEv/AABEIAWgB4AMBIgACEQEDEQH/xAAdAAEAAgIDAQEAAAAAAAAAAAAABQgGBwEDBAIJ/8QAURAAAQQBAgMCCQcIBggGAgMAAQACAwQFBhESEyEHMQgUFSI0QVF0swkyVGFxktQjNkJSdYGRtCQ1cqGxwRY3Q2J2grK1M1Njc4PRRKIXJWT/xAAbAQEBAQEBAQEBAAAAAAAAAAAAAgMBBAYHBf/EADMRAQACAQMBBQYEBgMAAAAAAAABEQIDITESBEFRYXEFMoGRsfAGE9HxFCIjQsHhM1Jy/9oADAMBAAIRAxEAPwCmSIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICKXt4ZkUkkT71QPje6NwDbxHExxa7Yir1G4K6vJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VPJ0P0+p9y9+FQRqKS8nQ/T6n3L34VdtTDMlkjiZeqF8j2xtBbeA4nuDW7k1eg3IQdWqPTrvvdn4z1GqS1R6dd97s/Geo1AREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQFJaX9Ope91vjMUapLS/p1L3ut8ZiBqj06773Z+M9RqktUenXfe7PxnqNQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBSWl/TqXvdb4zFGqS0v6dS97rfGYgao9Ou+92fjPUapLVHp133uz8Z6jUBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAUlpf06l73W+MxRqktL+nUve63xmIGqPTrvvdn4z1GqS1R6dd97s/Geo1AREQEREBERAREQEREBERBy0E9y7Z6ksY3fHIwb7bvY5o39gJHepDRx//saHvtX48a/VztR0dVz+Ju4m2PydyEsa/YOdBMNnQWGD9eOQMd9fDsehKD8jUUrq3AWcXet464wx2qc8tednXbjicW8TCQOOJw2c13c5rmkdCvPgcVYvWq9OrG6azamjrwRN24pJZXhkbBv0G7iOp6DvQdENSV+/BG94HQljHOAPsJaOhXU4EEgggg7EHoQR3gj2r9Z+xrQ1fTeFpYivwuNePexM0bGzbk8+zYO/XZ0hdsCT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uDm9CNd9ofbzPb1fV1Xiq7qctavXhFew4StlayORliKXl7cUMjZXs6bEDYjY7baURBbzJeFNpmR7cu3SLHakZG3l2pxUfHFM1vAx/jgbz3tYOgPLa7YbAt7xqjth7cp8vqPF6lx0L8fcxtKpCGvc2RpsQTWZpSAO+u/xlzOE9S3ffvWmkQW4n8JnSWQMORzOjmWc3CxoEzWU54XOi6xnnz7SBgPUB7Hlm/Qnbc6u7Ou2etjdZWdUSYpsUFnxvfHUHhrYTZjDd2PlGznFwL3HZoLpHEBo2aNMIg2/gO1yvW12/Vzqczq7rt2z4mJGc7htVJ6zW8wjg4gZg4/2SFzle12vNrxurhTmFYXK1nxMyM5/DBUiqlvMA4OImMu/eAtPog2x2r64l1bq+DI4mCWpZtz4yrRjkkY6VtthhhgfxNHCN5uEjvVlPlB9eOp4ejgGSDxnKPbYu8HQeKVHNcBw77tbLa4SO/pWkCp92Sa0dp7L1cuypBclp810UNhz2x8ySJ8Qk3jIPE3jJH1gH1Ar0dsvaHc1RlpstcayJ744YYoIi4xV4YWcLY4y/wA7YvMkh3/Sld9iDDEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAUlpf06l73W+MxRqktL+nUve63xmIGqPTrvvdn4z1Grvv2XTSyzOADpZHyODdw0OkcXENBJO259q6EBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAUlpf06l73W+MxRq76Fl0MsUzQC6KRkjQ7ctLo3BwDgCDtuPag6EREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQf/2Q==\n", + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from IPython.display import YouTubeVideo\n", + "YouTubeVideo(\"OMA2Mwo0aZg\", width=\"60%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To summarize the video, the multiplication of two matrices $\\bf{A}$ and $\\bf{B}$ with dimensions $m$ by $n$ and $n$ by $p$ yields a matrix $\\bf{C}$ with dimensions $m$ and $p$. To obtain an entry $c_{ij}$ of matrix $\\bf{C}$ where $i$ and $j$ represent the index labels of the rows and columns, we have to calculate the dot product of the $i$th row vector of $\\bf{A}$ with the $j$th column vector of $\\bf{B}$. So, it makes a difference if we multiply $\\bf{A}$ with $\\bf{B}$ from the right or left.\n", + "\n", + "When multiplying a `Matrix` with a `Vector`, we follow the convention that a `Vector` on the left is interpreted as a row vector and a `Vector` on the right as a column vector. The `Vector`'s length must match the `Matrix`'s corresponding dimension. As row and column vectors can be viewed as a `Matrix` as well, matrix-vector multiplication is a special case of matrix-matrix multiplication." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In the revised `Matrix` class, the `.__add__()`, `.__radd__()`, `.__sub__()`, `.__rsub__()`, and `.__truediv__()` methods follow the same logic as in the `Vector` class above.\n", + "\n", + "Besides implementing scalar multiplication, `.__mul__()` and `.__rmul__()` are responsible for converting `Vector`s into `Matrix`s and back. In particular, all the different ways of performing a multiplication are reduced into *one* generic form, which is a matrix-matrix multiplication. That is achieved by the `._matrix_multiply()` method, another implementation detail." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "class Matrix:\n", + "\n", + " def __init__(self, data):\n", + " self._entries = list(list(float(x) for x in r) for r in data)\n", + " # ...\n", + "\n", + " def __repr__(self):\n", + " args = \", \".join(\"(\" + \", \".join(f\"{c:.3f}\" for c in r) + \",)\" for r in self._entries)\n", + " return f\"Matrix(({args}))\"\n", + "\n", + " @property\n", + " def n_rows(self):\n", + " return len(self._entries)\n", + "\n", + " @property\n", + " def n_cols(self):\n", + " return len(self._entries[0])\n", + "\n", + " def rows(self):\n", + " return (Vector(r) for r in self._entries)\n", + "\n", + " def cols(self):\n", + " return (\n", + " Vector(self._entries[r][c] for r in range(self.n_rows)) for c in range(self.n_cols)\n", + " )\n", + "\n", + " def __iter__(self): # adapted for brevity; uses parts of entries()\n", + " return (self._entries[r][c] for r in range(self.n_rows) for c in range(self.n_cols))\n", + "\n", + " def __add__(self, other):\n", + " if isinstance(other, self.__class__): # matrix addition\n", + " if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):\n", + " raise ValueError(\"matrices need to be of the same dimensions\")\n", + " return Matrix((s_col + o_col for (s_col, o_col) in zip(s_row, o_row))\n", + " for (s_row, o_row) in zip(self._entries, other._entries))\n", + " elif isinstance(other, numbers.Number): # broadcasting addition\n", + " return Matrix((c + other for c in r) for r in self._entries)\n", + " return NotImplemented\n", + "\n", + " def __radd__(self, other):\n", + " if isinstance(other, Vector):\n", + " raise TypeError(\"vectors and matrices cannot be added\")\n", + " return self + other\n", + "\n", + " def __sub__(self, other):\n", + " if isinstance(other, self.__class__): # matrix subtraction\n", + " if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):\n", + " raise ValueError(\"matrices need to be of the same dimensions\")\n", + " return Matrix((s_col - o_col for (s_col, o_col) in zip(s_row, o_row))\n", + " for (s_row, o_row) in zip(self._entries, other._entries))\n", + " elif isinstance(other, numbers.Number): # broadcasting subtraction\n", + " return Matrix((c - other for c in r) for r in self._entries)\n", + " return NotImplemented\n", + "\n", + " def __rsub__(self, other):\n", + " if isinstance(other, Vector):\n", + " raise TypeError(\"vectors and matrices cannot be subtracted\")\n", + " # Reverse matrix subtraction is already handled in __sub__().\n", + " if isinstance(other, numbers.Number): # broadcasting subtraction\n", + " return Matrix((other - c for c in r) for r in self._entries)\n", + " return NotImplemented\n", + " \n", + " def _matrix_multiply(self, other):\n", + " if self.n_cols != other.n_rows:\n", + " raise ValueError(\"matrices need to have compatible dimensions\")\n", + " return Matrix((rv * cv for cv in other.cols()) for rv in self.rows())\n", + "\n", + " def __mul__(self, other):\n", + " if isinstance(other, numbers.Number):\n", + " return Matrix((x * other for x in r) for r in self._entries)\n", + " elif isinstance(other, Vector):\n", + " return self._matrix_multiply(other.as_matrix()).as_vector()\n", + " elif isinstance(other, self.__class__):\n", + " return self._matrix_multiply(other)\n", + " return NotImplemented\n", + "\n", + " def __rmul__(self, other):\n", + " if isinstance(other, numbers.Number):\n", + " return self * other\n", + " elif isinstance(other, Vector):\n", + " return other.as_matrix(column=False)._matrix_multiply(self).as_vector()\n", + " return NotImplemented\n", + "\n", + " def __truediv__(self, other):\n", + " if isinstance(other, numbers.Number):\n", + " return self * (1 / other)\n", + " return NotImplemented\n", + "\n", + " def as_vector(self):\n", + " if not (self.n_rows == 1 or self.n_cols == 1):\n", + " raise RuntimeError(\"one dimension (m or n) must be 1\")\n", + " return Vector(x for x in self)" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "m = Matrix([(1, 2, 3), (4, 5, 6), (7, 8, 9)])\n", + "n = Matrix([(10, 11, 12), (13, 14, 15)])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Scalar multiplication, addition, and subtraction work as before." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((10.000, 20.000, 30.000,), (40.000, 50.000, 60.000,), (70.000, 80.000, 90.000,)))" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "10 * m" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(2 * m + m * 3) / 5" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((0.000, 0.000, 0.000,), (0.000, 0.000, 0.000,), (0.000, 0.000, 0.000,)))" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m - m" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Matrix-matrix multiplication works if the dimensions are compatible ..." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((138.000, 171.000, 204.000,), (174.000, 216.000, 258.000,)))" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "n * m" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "... and results in a `ValueError` otherwise." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "matrices need to have compatible dimensions", + "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[0mm\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mn\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__mul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 72\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_matrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_vector\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 73\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\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---> 74\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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 75\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mNotImplemented\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 76\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36m_matrix_multiply\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 63\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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 64\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_cols\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_rows\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 65\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"matrices need to have compatible dimensions\"\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 66\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mMatrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcols\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mrv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrows\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 67\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: matrices need to have compatible dimensions" + ] + } + ], + "source": [ + "m * n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The same holds for matrix-vector and vector-matrix multiplication. These operations always return `Vector` instances in line with standard linear algebra. If a `Vector`'s length is not compatible with the respective dimension of a `Matrix`, we receive a `ValueError`." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((14.000, 32.000, 50.000))" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m * v" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((30.000, 36.000, 42.000))" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v * m" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((68.000, 86.000))" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "n * v" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "matrices need to have compatible dimensions", + "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[0mv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mn\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__rmul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 79\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 80\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mVector\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---> 81\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_matrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcolumn\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_vector\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 82\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mNotImplemented\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 83\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36m_matrix_multiply\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 63\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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 64\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_cols\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_rows\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 65\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"matrices need to have compatible dimensions\"\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 66\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mMatrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcols\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mrv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrows\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 67\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: matrices need to have compatible dimensions" + ] + } + ], + "source": [ + "v * n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Lastly, the broadcasting addition and subtraction also work for our `Matrix` instances." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((101.000, 102.000, 103.000,), (104.000, 105.000, 106.000,), (107.000, 108.000, 109.000,)))" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m + 100" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((99.000, 98.000, 97.000,), (96.000, 95.000, 94.000,), (93.000, 92.000, 91.000,)))" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "100 - m" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We do not allow addition or subtraction of matrices with vectors and raise a `TypeError` instead. Alternatively, we could have implemented broadcasting here as well, just like [numpy](https://www.numpy.org/) does." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "vectors and matrices cannot be added", + "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[0mm\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mv\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__radd__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 25\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 26\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__radd__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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---> 27\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 28\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 29\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__sub__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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__radd__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 40\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__radd__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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 41\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mVector\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---> 42\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"vectors and matrices cannot be added\"\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 43\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 44\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: vectors and matrices cannot be added" + ] + } + ], + "source": [ + "m + v" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### Relational Operators" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "As we have seen before, two different `Vector`s with the same `._entries` do *not* compare equal. The reason is that for user-defined types Python by default only assumes two instances to be equal if they are actually the same object. This is, of course, semantically wrong for our `Vector`s and `Matrix`s." + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "v = Vector([1, 2, 3])\n", + "w = Vector([1, 2, 3])" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v == w" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v == v" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We implement the `.__eq__()` (cf., [reference ](https://docs.python.org/3/reference/datamodel.html#object.__eq__)) method to control how the comparison operator `==` is carried out. For brevity, we show this only for the `Vector` class. The `.__eq__()` method exits early as soon as the first pair of entries does not match. Also, for reasons discussed in [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/01_content.ipynb#Imprecision), we compare the absolute difference of two corresponding entries to a very small `zero_threshold` that is stored as a class attribute shared among all `Vector` instances. If the `Vector`s differ in their numbers of entries, we fail loudly and raise a `ValueError`." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "class Vector:\n", + "\n", + " zero_threshold = 1e-12\n", + "\n", + " def __init__(self, data):\n", + " self._entries = list(float(x) for x in data)\n", + " # ...\n", + "\n", + " def __repr__(self):\n", + " args = \", \".join(f\"{x:.3f}\" for x in self)\n", + " return f\"Vector(({args}))\"\n", + "\n", + " def __len__(self):\n", + " return len(self._entries)\n", + "\n", + " def __iter__(self):\n", + " return iter(self._entries)\n", + "\n", + " def __eq__(self, other):\n", + " if isinstance(other, self.__class__):\n", + " if len(self) != len(other):\n", + " raise ValueError(\"vectors need to be of the same length\")\n", + " for x, y in zip(self, other):\n", + " if abs(x - y) > self.zero_threshold:\n", + " return False # exit early if two corresponding entries differ\n", + " return True\n", + " return NotImplemented" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "v = Vector([1, 2, 3])\n", + "w = Vector([1, 2, 3])" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v == w" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Besides `.__eq__()`, there are other special methods to implement the `!=`, `<`, `>`, `<=`, and `>=` operators, the last four of which are not semantically meaningful in the context of linear algebra.\n", + "\n", + "Python implicitly creates the `!=` for us in that it just inverts the result of `.__eq__()`." + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [], + "source": [ + "v = Vector([1, 2, 3])\n", + "w = Vector([1, 2, 4])" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v == w" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v != w" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Number Emulation" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Our `Vector` and `Matrix` classes do not fully behave like a `numbers.Number` in the abstract sense. Besides the not yet talked about but useful unary `+` and `-` operators, numbers in Python usually support being passed to built-in functions like [abs() ](https://docs.python.org/3/library/functions.html#abs), [bin() ](https://docs.python.org/3/library/functions.html#bin), [bool() ](https://docs.python.org/3/library/functions.html#bool), [complex() ](https://docs.python.org/3/library/functions.html#complex), [float() ](https://docs.python.org/3/library/functions.html#float), [hex() ](https://docs.python.org/3/library/functions.html#hex), [int() ](https://docs.python.org/3/library/functions.html#int), and others (cf., the [reference ](https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types) for an exhaustive list).\n", + "\n", + "To see that our classes lack simple but expected behaviors, let's try to invert the signs of all entries in the vector `v` ..." + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((1.000, 2.000, 3.000))" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "bad operand type for unary -: 'Vector'", + "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[0mv\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: bad operand type for unary -: 'Vector'" + ] + } + ], + "source": [ + "-v" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "... or pass `v` to [abs() ](https://docs.python.org/3/library/functions.html#abs)." + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "bad operand type for abs(): 'Vector'", + "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[0mabs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv\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: bad operand type for abs(): 'Vector'" + ] + } + ], + "source": [ + "abs(v)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For our example, we decide to implement the unary `+` and `-` operators, [abs() ](https://docs.python.org/3/library/functions.html#abs) as the Euclidean / Frobenius norm (i.e., the [magnitude ](https://en.wikipedia.org/wiki/Magnitude_%28mathematics%29#Euclidean_vector_space)), [bool() ](https://docs.python.org/3/library/functions.html#bool) to check if that norm is greater than `0` or not, and [float() ](https://docs.python.org/3/library/functions.html#float) to obtain a single scalar if the vector or matrix consists of only one entry. To achieve that, we implement the `.__pos__()`, `.__neg__()`, `.__abs__()`, `.__bool__()`, and `.__float__()` methods. For brevity, we do this only for the `Vector` class." + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [], + "source": [ + "import math" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": {}, + "outputs": [], + "source": [ + "def norm(vector_or_matrix):\n", + " \"\"\"Calculate the Frobenius or Euclidean norm of a matrix or vector.\n", + " \n", + " ...\n", + " \"\"\"\n", + " return math.sqrt(sum(x ** 2 for x in vector_or_matrix))" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "class Vector:\n", + "\n", + " def __init__(self, data):\n", + " self._entries = list(float(x) for x in data)\n", + " # ...\n", + "\n", + " def __repr__(self):\n", + " args = \", \".join(f\"{x:.3f}\" for x in self)\n", + " return f\"Vector(({args}))\"\n", + "\n", + " def __iter__(self):\n", + " return iter(self._entries)\n", + "\n", + " def __len__(self):\n", + " return len(self._entries)\n", + "\n", + " def __pos__(self):\n", + " return self\n", + "\n", + " def __neg__(self):\n", + " return Vector(-x for x in self)\n", + "\n", + " def __abs__(self):\n", + " return norm(self)\n", + "\n", + " def __bool__(self):\n", + " return bool(abs(self))\n", + "\n", + " def __float__(self):\n", + " if len(self) != 1:\n", + " raise RuntimeError(\"vector must have one entry to become a scalar\")\n", + " return self._entries[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "v = Vector([1, 2, 3])\n", + "w = Vector([3, 4])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The unary `+` operator is not only conceptually an identity operator but simply returns the instance itself." + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((1.000, 2.000, 3.000))" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "+v" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "+v is v" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((-3.000, -4.000))" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "-w" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The magnitude of a `Vector` is only `0` if *all* entries are `0`." + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "5.0" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "abs(w)" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": {}, + "outputs": [], + "source": [ + "z = Vector([0, 0])" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0.0" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "abs(z)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Only an all `0`s `Vector` is `False` in a boolean context. As mentioned in [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb#Truthy-vs.-Falsy), commonly we view an *empty* sequence as falsy; however, as we do not allow `Vector`s without any entries, we choose the all `0`s alternative. In that regard, the `Vector` class does not behave like the built-in sequence types." + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bool(v)" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bool(z)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Lastly, single entry `Vector`s can be casted as scalars." + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": {}, + "outputs": [], + "source": [ + "s = Vector([42])" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42.0" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "float(s)" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "RuntimeError", + "evalue": "vector must have one entry to become a scalar", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mRuntimeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfloat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv\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__float__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 29\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__float__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\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 30\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\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;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 31\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"vector must have one entry to become a scalar\"\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 32\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\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;31mRuntimeError\u001b[0m: vector must have one entry to become a scalar" + ] + } + ], + "source": [ + "float(v)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index ca1718b..fed0b64 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -280,3 +280,7 @@ If this is not possible, (Im)mutable Data Types; Method Chaining; Polymorphism) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/03_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/03_content.ipynb) + (Operator Overloading: Arithmetic & Relational Operators; + Number Emulation) From 6bcfff481cc90c13938a68f55a516eeda64238b1 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 27 Oct 2020 16:41:22 +0100 Subject: [PATCH 114/142] Add sample_package with linear algebra tools - add sample_package: + __init__.py => high-level description and imports + matrix.py => define a Matrix class + vector.py => define a Vector class + utils.py => package-wide utilities - streamline the code snippets in the chapter 11 notebooks to align with the sample_package --- 11_classes/00_content.ipynb | 10 +- 11_classes/02_content.ipynb | 7 +- 11_classes/03_content.ipynb | 59 ++-- 11_classes/sample_package/__init__.py | 30 ++ 11_classes/sample_package/matrix.py | 416 ++++++++++++++++++++++++++ 11_classes/sample_package/utils.py | 35 +++ 11_classes/sample_package/vector.py | 253 ++++++++++++++++ 7 files changed, 770 insertions(+), 40 deletions(-) create mode 100644 11_classes/sample_package/__init__.py create mode 100644 11_classes/sample_package/matrix.py create mode 100644 11_classes/sample_package/utils.py create mode 100644 11_classes/sample_package/vector.py diff --git a/11_classes/00_content.ipynb b/11_classes/00_content.ipynb index 692af9c..6852453 100644 --- a/11_classes/00_content.ipynb +++ b/11_classes/00_content.ipynb @@ -272,7 +272,7 @@ "outputs": [], "source": [ "class Vector:\n", - " \"\"\"A standard one-dimensional vector from linear algebra.\"\"\"\n", + " \"\"\"A one-dimensional vector from linear algebra.\"\"\"\n", "\n", " dummy_variable = \"I am a vector\"\n", "\n", @@ -403,7 +403,7 @@ "data": { "text/plain": [ "\u001b[0;31mInit signature:\u001b[0m \u001b[0mVector\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;31mDocstring:\u001b[0m A standard one-dimensional vector from linear algebra.\n", + "\u001b[0;31mDocstring:\u001b[0m A one-dimensional vector from linear algebra.\n", "\u001b[0;31mType:\u001b[0m type\n", "\u001b[0;31mSubclasses:\u001b[0m \n" ] @@ -432,7 +432,7 @@ "Help on class Vector in module __main__:\n", "\n", "class Vector(builtins.object)\n", - " | A standard one-dimensional vector from linear algebra.\n", + " | A one-dimensional vector from linear algebra.\n", " | \n", " | Methods defined here:\n", " | \n", @@ -484,7 +484,7 @@ "data": { "text/plain": [ "mappingproxy({'__module__': '__main__',\n", - " '__doc__': 'A standard one-dimensional vector from linear algebra.',\n", + " '__doc__': 'A one-dimensional vector from linear algebra.',\n", " 'dummy_variable': 'I am a vector',\n", " 'dummy_method': ,\n", " '__dict__': ,\n", @@ -687,7 +687,7 @@ "outputs": [], "source": [ "class Vector:\n", - " \"\"\"A standard one-dimensional vector from linear algebra.\n", + " \"\"\"A one-dimensional vector from linear algebra.\n", "\n", " All entries are converted to floats.\n", " \"\"\"\n", diff --git a/11_classes/02_content.ipynb b/11_classes/02_content.ipynb index 1d99ecf..520339f 100644 --- a/11_classes/02_content.ipynb +++ b/11_classes/02_content.ipynb @@ -1815,17 +1815,16 @@ }, "outputs": [], "source": [ - "def norm(vector_or_matrix):\n", + "def norm(vec_or_mat):\n", " \"\"\"Calculate the Frobenius or Euclidean norm of a matrix or vector.\n", "\n", " Args:\n", - " vector_or_matrix (Vector/Matrix): the entries whose squares\n", - " are summed up\n", + " vec_or_mat (Vector / Matrix): object whose entries are squared and summed up\n", "\n", " Returns:\n", " norm (float)\n", " \"\"\"\n", - " return math.sqrt(sum(x ** 2 for x in vector_or_matrix))" + " return math.sqrt(sum(x ** 2 for x in vec_or_mat))" ] }, { diff --git a/11_classes/03_content.ipynb b/11_classes/03_content.ipynb index 7b3a9ae..9dc08a2 100644 --- a/11_classes/03_content.ipynb +++ b/11_classes/03_content.ipynb @@ -694,7 +694,7 @@ " def __add__(self, other):\n", " if isinstance(other, self.__class__): # vector addition\n", " if len(self) != len(other):\n", - " raise ValueError(\"vectors need to be of the same length\")\n", + " raise ValueError(\"vectors must be of the same length\")\n", " return Vector(x + y for (x, y) in zip(self, other))\n", " elif isinstance(other, numbers.Number): # broadcasting addition\n", " return Vector(x + other for x in self)\n", @@ -706,7 +706,7 @@ " def __sub__(self, other):\n", " if isinstance(other, self.__class__): # vector subtraction\n", " if len(self) != len(other):\n", - " raise ValueError(\"vectors need to be of the same length\")\n", + " raise ValueError(\"vectors must be of the same length\")\n", " return Vector(x - y for (x, y) in zip(self, other))\n", " elif isinstance(other, numbers.Number): # broadcasting subtraction\n", " return Vector(x - other for x in self)\n", @@ -721,7 +721,7 @@ " def __mul__(self, other):\n", " if isinstance(other, self.__class__): # dot product\n", " if len(self) != len(other):\n", - " raise ValueError(\"vectors need to be of the same length\")\n", + " raise ValueError(\"vectors must be of the same length\")\n", " return sum(x * y for (x, y) in zip(self, other))\n", " elif isinstance(other, numbers.Number): # scalar multiplication\n", " return Vector(x * other for x in self)\n", @@ -872,14 +872,14 @@ "outputs": [ { "ename": "ValueError", - "evalue": "vectors need to be of the same length", + "evalue": "vectors must be of the same length", "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[0mv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mw\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__mul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 45\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# dot product\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 46\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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---> 47\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"vectors need to be of the same length\"\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 48\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0msum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0my\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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 49\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mNumber\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# scalar multiplication\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mValueError\u001b[0m: vectors need to be of the same length" + "\u001b[0;32m\u001b[0m in \u001b[0;36m__mul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 45\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# dot product\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 46\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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---> 47\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"vectors must be of the same length\"\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 48\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0msum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0my\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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 49\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mNumber\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# scalar multiplication\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: vectors must be of the same length" ] } ], @@ -992,14 +992,14 @@ "outputs": [ { "ename": "ValueError", - "evalue": "vectors need to be of the same length", + "evalue": "vectors must be of the same length", "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[0mv\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mw\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__add__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# vector addition\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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---> 20\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"vectors need to be of the same length\"\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 21\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mVector\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0my\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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 22\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mNumber\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# broadcasting addition\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mValueError\u001b[0m: vectors need to be of the same length" + "\u001b[0;32m\u001b[0m in \u001b[0;36m__add__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# vector addition\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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---> 20\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"vectors must be of the same length\"\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 21\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mVector\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0my\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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 22\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mNumber\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# broadcasting addition\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: vectors must be of the same length" ] } ], @@ -1171,7 +1171,7 @@ " def __add__(self, other):\n", " if isinstance(other, self.__class__): # matrix addition\n", " if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):\n", - " raise ValueError(\"matrices need to be of the same dimensions\")\n", + " raise ValueError(\"matrices must have the same dimensions\")\n", " return Matrix((s_col + o_col for (s_col, o_col) in zip(s_row, o_row))\n", " for (s_row, o_row) in zip(self._entries, other._entries))\n", " elif isinstance(other, numbers.Number): # broadcasting addition\n", @@ -1186,7 +1186,7 @@ " def __sub__(self, other):\n", " if isinstance(other, self.__class__): # matrix subtraction\n", " if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):\n", - " raise ValueError(\"matrices need to be of the same dimensions\")\n", + " raise ValueError(\"matrices must have the same dimensions\")\n", " return Matrix((s_col - o_col for (s_col, o_col) in zip(s_row, o_row))\n", " for (s_row, o_row) in zip(self._entries, other._entries))\n", " elif isinstance(other, numbers.Number): # broadcasting subtraction\n", @@ -1203,7 +1203,7 @@ " \n", " def _matrix_multiply(self, other):\n", " if self.n_cols != other.n_rows:\n", - " raise ValueError(\"matrices need to have compatible dimensions\")\n", + " raise ValueError(\"matrices must have compatible dimensions\")\n", " return Matrix((rv * cv for cv in other.cols()) for rv in self.rows())\n", "\n", " def __mul__(self, other):\n", @@ -1387,15 +1387,15 @@ "outputs": [ { "ename": "ValueError", - "evalue": "matrices need to have compatible dimensions", + "evalue": "matrices must have compatible dimensions", "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[0mm\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mn\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__mul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 72\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_matrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_vector\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 73\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\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---> 74\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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 75\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mNotImplemented\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 76\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36m_matrix_multiply\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 63\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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 64\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_cols\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_rows\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 65\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"matrices need to have compatible dimensions\"\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 66\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mMatrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcols\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mrv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrows\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 67\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mValueError\u001b[0m: matrices need to have compatible dimensions" + "\u001b[0;32m\u001b[0m in \u001b[0;36m__mul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 72\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_matrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_vector\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 73\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\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---> 74\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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 75\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mNotImplemented\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 76\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36m_matrix_multiply\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 63\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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 64\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_cols\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_rows\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 65\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"matrices must have compatible dimensions\"\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 66\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mMatrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcols\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mrv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrows\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 67\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: matrices must have compatible dimensions" ] } ], @@ -1497,15 +1497,15 @@ "outputs": [ { "ename": "ValueError", - "evalue": "matrices need to have compatible dimensions", + "evalue": "matrices must have compatible dimensions", "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[0mv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mn\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__rmul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 79\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 80\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mVector\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---> 81\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_matrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcolumn\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_vector\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 82\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mNotImplemented\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 83\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36m_matrix_multiply\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 63\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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 64\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_cols\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_rows\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 65\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"matrices need to have compatible dimensions\"\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 66\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mMatrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcols\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mrv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrows\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 67\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mValueError\u001b[0m: matrices need to have compatible dimensions" + "\u001b[0;32m\u001b[0m in \u001b[0;36m__rmul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 79\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 80\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mVector\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---> 81\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_matrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcolumn\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_vector\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 82\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mNotImplemented\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 83\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36m_matrix_multiply\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 63\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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 64\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_cols\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_rows\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 65\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"matrices must have compatible dimensions\"\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 66\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mMatrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcols\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mrv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrows\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 67\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: matrices must have compatible dimensions" ] } ], @@ -1736,7 +1736,7 @@ " def __eq__(self, other):\n", " if isinstance(other, self.__class__):\n", " if len(self) != len(other):\n", - " raise ValueError(\"vectors need to be of the same length\")\n", + " raise ValueError(\"vectors must be of the same length\")\n", " for x, y in zip(self, other):\n", " if abs(x - y) > self.zero_threshold:\n", " return False # exit early if two corresponding entries differ\n", @@ -1972,12 +1972,9 @@ "metadata": {}, "outputs": [], "source": [ - "def norm(vector_or_matrix):\n", - " \"\"\"Calculate the Frobenius or Euclidean norm of a matrix or vector.\n", - " \n", - " ...\n", - " \"\"\"\n", - " return math.sqrt(sum(x ** 2 for x in vector_or_matrix))" + "def norm(vec_or_mat):\n", + " \"\"\"Calculate the Frobenius or Euclidean norm of a matrix or vector.\"\"\"\n", + " return math.sqrt(sum(x ** 2 for x in vec_or_mat))" ] }, { @@ -2020,7 +2017,7 @@ "\n", " def __float__(self):\n", " if len(self) != 1:\n", - " raise RuntimeError(\"vector must have one entry to become a scalar\")\n", + " raise RuntimeError(\"vector must have exactly one entry to become a scalar\")\n", " return self._entries[0]" ] }, @@ -2303,14 +2300,14 @@ "outputs": [ { "ename": "RuntimeError", - "evalue": "vector must have one entry to become a scalar", + "evalue": "vector must have exactly one entry to become a scalar", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mRuntimeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfloat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv\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__float__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 29\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__float__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\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 30\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\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;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 31\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"vector must have one entry to become a scalar\"\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 32\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\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;31mRuntimeError\u001b[0m: vector must have one entry to become a scalar" + "\u001b[0;32m\u001b[0m in \u001b[0;36m__float__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 29\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__float__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\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 30\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\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;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 31\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"vector must have exactly one entry to become a scalar\"\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 32\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\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;31mRuntimeError\u001b[0m: vector must have exactly one entry to become a scalar" ] } ], diff --git a/11_classes/sample_package/__init__.py b/11_classes/sample_package/__init__.py new file mode 100644 index 0000000..1e8d12c --- /dev/null +++ b/11_classes/sample_package/__init__.py @@ -0,0 +1,30 @@ +"""This package provides linear algebra functionalities. + +The package is split into three modules: +- matrix: defines the Matrix class +- vector: defines the Vector class +- utils: defines the norm() function that is shared by Matrix and Vector + and package-wide constants + +The classes implement arithmetic operations involving vectors and matrices. + +See the docstrings in the modules and classes for further info. +""" + +# Import the classes here so that they are available +# from the package's top level. That means that a user +# who imports this package with `import sample_package` +# may then refer to, for example, the Matrix class with +# simply `sample_package.Matrix` instead of the longer +# `sample_package.matrix.Matrix`. +from sample_package.matrix import Matrix +from sample_package.vector import Vector + + +# Define meta information for the package. +# There are other (and more modern) ways of +# doing this, but specifying the following +# dunder variables here is the traditional way. +__name__ = "linear_algebra_tools" +__version__ = "0.1.0" # see https://semver.org/ for how the format works +__author__ = "Alexander Hess" diff --git a/11_classes/sample_package/matrix.py b/11_classes/sample_package/matrix.py new file mode 100644 index 0000000..b64e50d --- /dev/null +++ b/11_classes/sample_package/matrix.py @@ -0,0 +1,416 @@ +"""This module defines a Matrix class.""" + +import numbers + +# Note the import at the bottom of this file, and +# see the comments about imports in the matrix module. +from sample_package import utils + + +class Matrix: + """An m-by-n-dimensional matrix from linear algebra. + + All entries are converted to floats, or whatever is set in the typing attribute. + + Attributes: + storage (callable): data type used to store the entries internally; + defaults to tuple + typing (callable): type casting applied to all entries upon creation; + defaults to float + zero_threshold (float): max. tolerance when comparing an entry to zero; + defaults to 1e-12 + """ + + storage = utils.DEFAULT_ENTRIES_STORAGE + typing = utils.DEFAULT_ENTRY_TYPE + zero_threshold = utils.ZERO_THRESHOLD + + def __init__(self, data): + """Create a new matrix. + + Args: + data (sequence of sequences): the matrix's entries; + viewed as a sequence of the matrix's rows (i.e., row-major order); + use the .from_columns() class method if the data come as a sequence + of the matrix's columns (i.e., column-major order) + + Raises: + ValueError: + - if no entries are provided + - if the number of columns is inconsistent across the rows + """ + self._entries = self.storage( + self.storage(self.typing(x) for x in r) for r in data + ) + for row in self._entries[1:]: + if len(row) != self.n_cols: + raise ValueError("rows must have the same number of entries") + if len(self) == 0: + raise ValueError("a matrix must have at least one entry") + + @classmethod + def from_columns(cls, data): + """Create a new matrix. + + This is an alternative constructor for data provided in column-major order. + + Args: + data (sequence of sequences): the matrix's entries; + viewed as a sequence of the matrix's columns (i.e., column-major order); + use the normal constructor method if the data come as a sequence + of the matrix's rows (i.e., row-major order) + + Raises: + ValueError: + - if no entries are provided + - if the number of rows is inconsistent across the columns + """ + return cls(data).transpose() + + @classmethod + def from_rows(cls, data): + """See docstring for .__init__().""" + # Some users may want to use this .from_rows() constructor + # to explicitly communicate that the data are in row-major order. + # Otherwise, this method is redundant. + return cls(data) + + def __repr__(self): + """Text representation of a Matrix.""" + name = self.__class__.__name__ + args = ", ".join( + "(" + ", ".join(f"{c:.3f}" for c in r) + ",)" for r in self._entries + ) + return f"{name}(({args}))" + + def __str__(self): + """Human-readable text representation of a Matrix.""" + name = self.__class__.__name__ + first, last, m, n = self[0], self[-1], self.n_rows, self.n_cols + return f"{name}(({first:.1f}, ...), ..., (..., {last:.1f}))[{m:d}x{n:d}]" + + @property + def n_rows(self): + """Number of rows in a Matrix.""" + return len(self._entries) + + @property + def n_cols(self): + """Number of columns in a Matrix.""" + return len(self._entries[0]) + + def __len__(self): + """Number of entries in a Matrix.""" + return self.n_rows * self.n_cols + + def __getitem__(self, index): + """Obtain an individual entry of a Matrix. + + Args: + index (int / tuple of int's): if index is an integer, + the Matrix is viewed as a sequence in row-major order; + if index is a tuple of integers, the first one refers to + the row and the second one to the column of the entry + + Returns: + entry (Matrix.typing) + + Example Usage: + >>> m = Matrix([(1, 2), (3, 4)]) + >>> m[0] + 1.0 + >>> m[-1] + 4.0 + >>> m[0, 1] + 3.0 + """ + # Sequence-like indexing (one-dimensional) + if isinstance(index, int): + if index < 0: + index += len(self) + if not (0 <= index < len(self)): + raise IndexError("integer index out of range") + row, col = divmod(index, self.n_cols) + return self._entries[row][col] + # Mathematical-like indexing (two-dimensional) + elif ( + isinstance(index, tuple) + and len(index) == 2 + and isinstance(index[0], int) + and isinstance(index[1], int) + ): + return self._entries[index[0]][index[1]] + raise TypeError("index must be either an int or a tuple of two int's") + + def rows(self): + """Loop over a Matrix's rows. + + Returns: + rows (generator): produces a Matrix's rows as Vectors + """ + return (Vector(r) for r in self._entries) + + def cols(self): + """Loop over a Matrix's columns. + + Returns: + columns (generator): produces a Matrix's columns as Vectors + """ + return ( + 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): + """Loop over a Matrix's entries. + + Args: + reverse (bool): flag to loop backwards; defaults to False + row_major (bool): flag to loop in row-major order; defaults to True + + Returns: + entries (generator): produces a Matrix's entries + """ + if reverse: + rows = range(self.n_rows - 1, -1, -1) + cols = range(self.n_cols - 1, -1, -1) + else: + rows, cols = range(self.n_rows), range(self.n_cols) + if row_major: + return (self._entries[r][c] for r in rows for c in cols) + return (self._entries[r][c] for c in cols for r in rows) + + def __iter__(self): + """Loop over a Matrix's entries. + + See .entries() for more customization options. + """ + return self.entries() + + def __reversed__(self): + """Loop over a Matrix's entries in reverse order. + + See .entries() for more customization options. + """ + return self.entries(reverse=True) + + def __add__(self, other): + """Handle `self + other` and `other + self`. + + This may be either matrix addition or broadcasting addition. + + Example Usage: + >>> Matrix([(1, 2), (3, 4)]) + Matrix([(2, 3), (4, 5)]) + Matrix(((3.0, 5.0), (7.0, 9.0))) + + >>> Matrix([(1, 2), (3, 4)]) + 5 + Matrix(((6.0, 7.0), (8.0, 9.0))) + + >>> 10 + Matrix([(1, 2), (3, 4)]) + Matrix(((11.0, 12.0), (13.0, 14.0))) + """ + # Matrix addition + if isinstance(other, self.__class__): + if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols): + raise ValueError("matrices must have the same dimensions") + return self.__class__( + (s_col + o_col for (s_col, o_col) in zip(s_row, o_row)) + for (s_row, o_row) in zip(self._entries, other._entries) + ) + # Broadcasting addition + elif isinstance(other, numbers.Number): + return self.__class__((c + other for c in r) for r in self._entries) + return NotImplemented + + def __radd__(self, other): + """See docstring for .__add__().""" + if isinstance(other, Vector): + raise TypeError("vectors and matrices cannot be added") + # As both matrix and broadcasting addition are commutative, + # we dispatch to .__add__(). + return self + other + + def __sub__(self, other): + """Handle `self - other` and `other - self`. + + This may be either matrix subtraction or broadcasting subtraction. + + Example Usage: + >>> Matrix([(2, 3), (4, 5)]) - Matrix([(1, 2), (3, 4)]) + Matrix(((1.0, 1.0), (1.0, 1.0))) + + >>> Matrix([(1, 2), (3, 4)]) - 1 + Matrix(((0.0, 1.0), (2.0, 3.0))) + + >>> 10 - Matrix([(1, 2), (3, 4)]) + Matrix(((9.0, 8.0), (7.0, 6.0))) + """ + # As subtraction is the inverse of addition, + # we first dispatch to .__neg__() to invert the signs of + # all entries in other and then dispatch to .__add__(). + return self + (-other) + + def __rsub__(self, other): + """See docstring for .__sub__().""" + if isinstance(other, Vector): + raise TypeError("vectors and matrices cannot be subtracted") + # Same comments as in .__sub__() apply + # with the roles of self and other swapped. + return (-self) + other + + def _matrix_multiply(self, other): + """Internal utility method to multiply to Matrix instances.""" + if self.n_cols != other.n_rows: + raise ValueError("matrices must have compatible dimensions") + # Matrix-matrix multiplication means that each entry of the resulting + # Matrix is the dot product of the respective row of the "left" Matrix + # and column of the "right" Matrix. So, the rows/columns are represented + # by the Vector instances provided by the .cols() and .rows() methods. + return self.__class__((rv * cv for cv in other.cols()) for rv in self.rows()) + + def __mul__(self, other): + """Handle `self * other` and `other * self`. + + This may be either scalar multiplication, matrix-vector multiplication, + vector-matrix multiplication, or matrix-matrix multiplication. + + Example Usage: + >>> Matrix([(1, 2), (3, 4)]) * Matrix([(1, 2), (3, 4)]) + Matrix(((7.0, 10.0), (15.0, 22.0))) + + >>> 2 * Matrix([(1, 2), (3, 4)]) + Matrix(((2.0, 4.0), (6.0, 8.0))) + + >>> Matrix([(1, 2), (3, 4)]) * 3 + Matrix(((3.0, 6.0), (9.0, 12.0))) + + Matrix-vector and vector-matrix multiplication are not commutative. + + >>> Matrix([(1, 2), (3, 4)]) * Vector([5, 6]) + Vector((17.0, 39.0)) + + >>> Vector([5, 6]) * Matrix([(1, 2), (3, 4)]) + Vector((23.0, 34.0)) + """ + # Scalar multiplication + if isinstance(other, numbers.Number): + return self.__class__((x * other for x in r) for r in self._entries) + # Matrix-vector multiplication: Vector is a column Vector + elif isinstance(other, Vector): + # First, cast the other Vector as a Matrix, then do matrix-matrix + # multiplication, and lastly return the result as a Vector again. + return self._matrix_multiply(other.as_matrix()).as_vector() + # Matrix-matrix multiplication + elif isinstance(other, self.__class__): + return self._matrix_multiply(other) + return NotImplemented + + def __rmul__(self, other): + """See docstring for .__mul__().""" + # As scalar multiplication is commutative, we dispatch to .__mul__(). + if isinstance(other, numbers.Number): + return self * other + # Vector-matrix multiplication: Vector is a row Vector + elif isinstance(other, Vector): + return other.as_matrix(column=False)._matrix_multiply(self).as_vector() + return NotImplemented + + def __truediv__(self, other): + """Handle `self / other`. + + Divide a Matrix by a scalar. + + Example Usage: + >>> Matrix([(1, 2), (3, 4)]) / 4 + Matrix([(0.25, 0.5), (0.75, 1.0)]) + """ + # As scalar division division is the same as multiplication + # with the inverse, we dispatch to .__mul__(). + if isinstance(other, numbers.Number): + return self * (1 / other) + return NotImplemented + + def __eq__(self, other): + """Handle `self == other`. + + Compare two Matrix instances for equality. + + Example Usage: + >>> Matrix([(1, 2), (3, 4)]) == Matrix([(1, 2), (3, 4)]) + True + + >>> Matrix([(1, 2), (3, 4)]) == Matrix([(5, 6), (7, 8)]) + False + """ + if isinstance(other, self.__class__): + if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols): + raise ValueError("matrices must have the same dimensions") + for x, y in zip(self, other): + if abs(x - y) > self.zero_threshold: + return False # exit early if two corresponding entries differ + return True + return NotImplemented + + def __pos__(self): + """Handle `+self`. + + This is simply an identity operator returning the Matrix itself. + """ + return self + + def __neg__(self): + """Handle `-self`. + + Negate all entries of a Matrix. + """ + return self.__class__((-x for x in r) for r in self._entries) + + def __abs__(self): + """The Frobenius norm of a Matrix.""" + return utils.norm(self) # use the norm() function shared with the Vector class + + def __bool__(self): + """A Matrix is truthy if its Frobenius norm is strictly positive.""" + return bool(abs(self)) + + def __float__(self): + """Cast a Matrix as a scalar. + + Returns: + scalar (float) + + Raises: + RuntimeError: if the Matrix has more than one entry + """ + if not (self.n_rows == 1 and self.n_cols == 1): + raise RuntimeError("matrix must have exactly one entry to become a scalar") + return self[0] + + def as_vector(self): + """Get a Vector representation of a Matrix. + + Returns: + vector (Vector) + + Raises: + RuntimeError: if one of the two dimensions, .n_rows or .n_cols, is not 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): + """Switch the rows and columns of a Matrix. + + Returns: + matrix (Matrix) + """ + return self.__class__(zip(*self._entries)) + + +# This import needs to be made here as otherwise an ImportError is raised. +# That is so as both the matrix and vector modules import a class from each other. +# We call that a circular import. Whereas Python handles "circular" references +# (e.g., both the Matrix and Vector classes have methods that reference the +# respective other class), that is forbidden for imports. +from sample_package.vector import Vector diff --git a/11_classes/sample_package/utils.py b/11_classes/sample_package/utils.py new file mode 100644 index 0000000..2720372 --- /dev/null +++ b/11_classes/sample_package/utils.py @@ -0,0 +1,35 @@ +"""This module provides utilities for the whole package. + +The defined constants are used as defaults in the Vector and Matrix classes. + +The norm() function is shared by Vector.__abs__() and Matrix.__abs__(). +""" + +import math + + +# Define constants (i.e., normal variables that are, by convention, named in UPPERCASE) +# that are used as the defaults for class attributes within Vector and Matrix. +DEFAULT_ENTRIES_STORAGE = tuple +DEFAULT_ENTRY_TYPE = float +ZERO_THRESHOLD = 1e-12 + + +def norm(vec_or_mat): + """Calculate the Frobenius or Euclidean norm of a matrix or vector. + + Find more infos here: https://en.wikipedia.org/wiki/Matrix_norm#Frobenius_norm + + Args: + vec_or_mat (Vector / Matrix): object whose entries are squared and summed up + + Returns: + norm (float) + + Example Usage: + As Vector and Matrix objects are by design non-empty sequences, + norm() may be called, for example, with `[3, 4]` as the argument: + >>> norm([3, 4]) + 5.0 + """ + return math.sqrt(sum(x ** 2 for x in vec_or_mat)) diff --git a/11_classes/sample_package/vector.py b/11_classes/sample_package/vector.py new file mode 100644 index 0000000..1b6e305 --- /dev/null +++ b/11_classes/sample_package/vector.py @@ -0,0 +1,253 @@ +"""This module defines a Vector class.""" + +# Imports from the standard library go first ... +import numbers + +# ... and are followed by project-internal ones. +# If third-party libraries are needed, they are +# put into a group on their own in between. +# Within a group, imports are sorted lexicographically. +from sample_package import utils +from sample_package.matrix import Matrix + + +class Vector: + """A one-dimensional vector from linear algebra. + + All entries are converted to floats, or whatever is set in the typing attribute. + + Attributes: + storage (callable): data type used to store the entries internally; + defaults to tuple + typing (callable): type casting applied to all entries upon creation; + defaults to float + zero_threshold (float): max. tolerance when comparing an entry to zero; + defaults to 1e-12 + """ + + storage = utils.DEFAULT_ENTRIES_STORAGE + typing = utils.DEFAULT_ENTRY_TYPE + zero_threshold = utils.ZERO_THRESHOLD + + def __init__(self, data): + """Create a new vector. + + Args: + data (sequence): the vector's entries + + Raises: + ValueError: if no entries are provided + + Example Usage: + >>> Vector([1, 2, 3]) + Vector((1.0, 2.0, 3.0)) + + >>> Vector(range(3)) + Vector((0.0, 1.0, 2.0)) + """ + self._entries = self.storage(self.typing(x) for x in data) + if len(self) == 0: + raise ValueError("a vector must have at least one entry") + + def __repr__(self): + """Text representation of a Vector.""" + name = self.__class__.__name__ + args = ", ".join(f"{x:.3f}" for x in self) + return f"{name}(({args}))" + + def __str__(self): + """Human-readable text representation of a Vector.""" + name = self.__class__.__name__ + first, last, n_entries = self[0], self[-1], len(self) + return f"{name}({first:.1f}, ..., {last:.1f})[{n_entries:d}]" + + def __len__(self): + """Number of entries in a Vector.""" + return len(self._entries) + + def __getitem__(self, index): + """Obtain an individual entry of a Vector.""" + if not isinstance(index, int): + raise TypeError("index must be an integer") + return self._entries[index] + + def __iter__(self): + """Loop over a Vector's entries.""" + return iter(self._entries) + + def __reversed__(self): + """Loop over a Vector's entries in reverse order.""" + return reversed(self._entries) + + def __add__(self, other): + """Handle `self + other` and `other + self`. + + This may be either vector addition or broadcasting addition. + + Example Usage: + >>> Vector([1, 2, 3]) + Vector([2, 3, 4]) + Vector((3, 5, 7)) + + >>> Vector([1, 2, 3]) + 4 + Vector((5, 6, 7)) + + >>> 10 + Vector([1, 2, 3]) + Vector((11, 12, 13)) + """ + # Vector addition + if isinstance(other, self.__class__): + if len(self) != len(other): + raise ValueError("vectors must be of the same length") + return self.__class__(x + y for (x, y) in zip(self, other)) + # Broadcasting addition + elif isinstance(other, numbers.Number): + return self.__class__(x + other for x in self) + return NotImplemented + + def __radd__(self, other): + """See docstring for .__add__().""" + # As both vector and broadcasting addition are commutative, + # we dispatch to .__add__(). + return self + other + + def __sub__(self, other): + """Handle `self - other` and `other - self`. + + This may be either vector subtraction or broadcasting subtraction. + + Example Usage: + >>> Vector([7, 8, 9]) - Vector([1, 2, 3]) + Vector((6, 6, 6)) + + >>> Vector([1, 2, 3]) - 1 + Vector((0, 1, 2)) + + >>> 10 - Vector([1, 2, 3]) + Vector((9, 8, 7)) + """ + # As subtraction is the inverse of addition, + # we first dispatch to .__neg__() to invert the signs of + # all entries in other and then dispatch to .__add__(). + return self + (-other) + + def __rsub__(self, other): + """See docstring for .__sub__().""" + # Same comments as in .__sub__() apply + # with the roles of self and other swapped. + return (-self) + other + + def __mul__(self, other): + """Handle `self * other` and `other * self`. + + This may be either the dot product of two vectors or scalar multiplication. + + Example Usage: + >>> Vector([1, 2, 3]) * Vector([2, 3, 4]) + 14 + + >>> 2 * Vector([1, 2, 3]) + Vector((2.0, 4.0, 6.0)) + + >>> Vector([1, 2, 3]) * 3 + Vector((3.0, 6.0, 9.0)) + """ + # Dot product + if isinstance(other, self.__class__): + if len(self) != len(other): + raise ValueError("vectors must be of the same length") + return sum(x * y for (x, y) in zip(self, other)) + # Scalar multiplication + elif isinstance(other, numbers.Number): + return self.__class__(x * other for x in self) + return NotImplemented + + def __rmul__(self, other): + """See docstring for .__mul__().""" + # As both dot product and scalar multiplication are commutative, + # we dispatch to .__mul__(). + return self * other + + def __truediv__(self, other): + """Handle `self / other`. + + Divide a Vector by a scalar. + + Example Usage: + >>> Vector([9, 6, 12]) / 3 + Vector((3.0, 2.0, 4.0)) + """ + # As scalar division division is the same as multiplication + # with the inverse, we dispatch to .__mul__(). + if isinstance(other, numbers.Number): + return self * (1 / other) + return NotImplemented + + def __eq__(self, other): + """Handle `self == other`. + + Compare two Vectors for equality. + + Example Usage: + >>> Vector([1, 2, 3]) == Vector([1, 2, 3]) + True + + >>> Vector([1, 2, 3]) == Vector([4, 5, 6]) + False + """ + if isinstance(other, self.__class__): + if len(self) != len(other): + raise ValueError("vectors must be of the same length") + for x, y in zip(self, other): + if abs(x - y) > self.zero_threshold: + return False # exit early if two corresponding entries differ + return True + return NotImplemented + + def __pos__(self): + """Handle `+self`. + + This is simply an identity operator returning the Vector itself. + """ + return self + + def __neg__(self): + """Handle `-self`. + + Negate all entries of a Vector. + """ + return self.__class__(-x for x in self) + + def __abs__(self): + """The Euclidean norm of a vector.""" + return utils.norm(self) # use the norm() function shared with the Matrix class + + def __bool__(self): + """A Vector is truthy if its Euclidean norm is strictly positive.""" + return bool(abs(self)) + + def __float__(self): + """Cast a Vector as a scalar. + + Returns: + scalar (float) + + Raises: + RuntimeError: if the Vector has more than one entry + """ + if len(self) != 1: + raise RuntimeError("vector must have exactly one entry to become a scalar") + return self[0] + + def as_matrix(self, *, column=True): + """Get a Matrix representation of a Vector. + + Args: + column (bool): if the vector is interpreted as a + column vector or a row vector; defaults to True + + Returns: + matrix (Matrix) + """ + if column: + return Matrix([x] for x in self) + return Matrix([(x for x in self)]) From 3abae203cb3f770f3b28da9507e016a443d33e34 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 27 Oct 2020 17:02:28 +0100 Subject: [PATCH 115/142] Pin the dependencies - upgrade jupyterlab - upgrade RISE - upgrade transient dependencies: + nest-asyncio + notebook + pandocfilters + pygments + virtualenv - pin transient dependencies: + flake8 + socks + webpdf --- poetry.lock | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8e454f7..98d06c2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -267,7 +267,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" parso = ">=0.7.0,<0.8.0" [package.extras] -qa = ["flake8 (3.7.9)"] +qa = ["flake8 (==3.7.9)"] testing = ["Django (<3.1)", "colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"] [[package]] @@ -428,7 +428,7 @@ test = ["jupyter-contrib-core", "nose", "requests", "selenium", "mock"] [[package]] name = "jupyterlab" -version = "2.2.8" +version = "2.2.9" description = "The JupyterLab notebook server extension." category = "main" optional = false @@ -547,11 +547,11 @@ testpath = "*" traitlets = ">=4.2" [package.extras] -all = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (0.2.2)", "tornado (>=4.0)", "sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"] +all = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (==0.2.2)", "tornado (>=4.0)", "sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"] docs = ["sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"] serve = ["tornado (>=4.0)"] -test = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (0.2.2)"] -webpdf = ["pyppeteer (0.2.2)"] +test = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (==0.2.2)"] +webpdf = ["pyppeteer (==0.2.2)"] [[package]] name = "nbformat" @@ -573,7 +573,7 @@ test = ["fastjsonschema", "testpath", "pytest", "pytest-cov"] [[package]] name = "nest-asyncio" -version = "1.4.1" +version = "1.4.2" description = "Patch asyncio to allow nested event loops" category = "main" optional = false @@ -654,11 +654,11 @@ six = "*" [[package]] name = "pandocfilters" -version = "1.4.2" +version = "1.4.3" description = "Utilities for writing pandoc filters in python" category = "main" optional = false -python-versions = "*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "parso" @@ -754,7 +754,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pygments" -version = "2.7.1" +version = "2.7.2" description = "Pygments is a syntax highlighting package written in Python." category = "main" optional = false @@ -835,18 +835,18 @@ urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" [package.extras] security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] [[package]] name = "rise" -version = "5.6.1" +version = "5.7.0" description = "Reveal.js - Jupyter/IPython Slideshow Extension" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4" [package.dependencies] -notebook = ">=5.5.0" +notebook = ">=6.0" [[package]] name = "send2trash" @@ -929,11 +929,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] brotli = ["brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "virtualenv" -version = "20.0.35" +version = "20.1.0" description = "Virtual Python Environment builder" category = "dev" optional = false @@ -1157,8 +1157,8 @@ jupyter-nbextensions-configurator = [ {file = "jupyter_nbextensions_configurator-0.4.1.tar.gz", hash = "sha256:e5e86b5d9d898e1ffb30ebb08e4ad8696999f798fef3ff3262d7b999076e4e83"}, ] jupyterlab = [ - {file = "jupyterlab-2.2.8-py3-none-any.whl", hash = "sha256:95d0509557881cfa8a5fcdf225f2fca46faf1bc52fc56a28e0b72fcc594c90ab"}, - {file = "jupyterlab-2.2.8.tar.gz", hash = "sha256:c8377bee30504919c1e79949f9fe35443ab7f5c4be622c95307e8108410c8b8c"}, + {file = "jupyterlab-2.2.9-py3-none-any.whl", hash = "sha256:59af02c26a15ec2d2862a15bc72e41ae304b406a0b0d3f4f705eeb7caf91902b"}, + {file = "jupyterlab-2.2.9.tar.gz", hash = "sha256:3be8f8edea173753dd838c1b6d3bbcb6f5c801121f824a477025c1b6a1d33dc6"}, ] jupyterlab-pygments = [ {file = "jupyterlab_pygments-0.1.2-py2.py3-none-any.whl", hash = "sha256:abfb880fd1561987efaefcb2d2ac75145d2a5d0139b1876d5be806e32f630008"}, @@ -1258,8 +1258,8 @@ nbformat = [ {file = "nbformat-5.0.8.tar.gz", hash = "sha256:f545b22138865bfbcc6b1ffe89ed5a2b8e2dc5d4fe876f2ca60d8e6f702a30f8"}, ] nest-asyncio = [ - {file = "nest_asyncio-1.4.1-py3-none-any.whl", hash = "sha256:a4487c4f49f2d11a7bb89a512a6886b6a5045f47097f49815b2851aaa8599cf0"}, - {file = "nest_asyncio-1.4.1.tar.gz", hash = "sha256:b86c3193abda5b2eeccf8c79894bc71c680369a178f4b068514ac00720b14e01"}, + {file = "nest_asyncio-1.4.2-py3-none-any.whl", hash = "sha256:c2d3bdc76ba235a7ad215128afe31d74a320d25790c50cd94685ec5ea221b94d"}, + {file = "nest_asyncio-1.4.2.tar.gz", hash = "sha256:c614fcfaca72b1f04778bc0e73f49c84500b3d045c49d149fc46f1566643c175"}, ] nodeenv = [ {file = "nodeenv-1.5.0-py2.py3-none-any.whl", hash = "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9"}, @@ -1306,7 +1306,7 @@ packaging = [ {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, ] pandocfilters = [ - {file = "pandocfilters-1.4.2.tar.gz", hash = "sha256:b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"}, + {file = "pandocfilters-1.4.3.tar.gz", hash = "sha256:bc63fbb50534b4b1f8ebe1860889289e8af94a23bff7445259592df25a3906eb"}, ] parso = [ {file = "parso-0.7.1-py2.py3-none-any.whl", hash = "sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea"}, @@ -1345,8 +1345,8 @@ pycparser = [ {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, ] pygments = [ - {file = "Pygments-2.7.1-py3-none-any.whl", hash = "sha256:307543fe65c0947b126e83dd5a61bd8acbd84abec11f43caebaf5534cbc17998"}, - {file = "Pygments-2.7.1.tar.gz", hash = "sha256:926c3f319eda178d1bd90851e4317e6d8cdb5e292a3386aac9bd75eca29cf9c7"}, + {file = "Pygments-2.7.2-py3-none-any.whl", hash = "sha256:88a0bbcd659fcb9573703957c6b9cff9fab7295e6e76db54c9d00ae42df32773"}, + {file = "Pygments-2.7.2.tar.gz", hash = "sha256:381985fcc551eb9d37c52088a32914e00517e57f4a21609f48141ba08e193fa0"}, ] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, @@ -1433,8 +1433,8 @@ requests = [ {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"}, ] rise = [ - {file = "rise-5.6.1-py2.py3-none-any.whl", hash = "sha256:e9637ee5499ad7801474da53a2c830350a44b2192c2f113594e4426190e55ad4"}, - {file = "rise-5.6.1.tar.gz", hash = "sha256:1343f068d01adc4dd0226d9b278ce93fc92f365d827431a57e8d5679eb39f4d6"}, + {file = "rise-5.7.0-py2.py3-none-any.whl", hash = "sha256:b500c7c3f7b09c8194a66feeffd60ead6da0132d9fa18a2bb7352587778ef482"}, + {file = "rise-5.7.0.tar.gz", hash = "sha256:6c00721189e0b457ca40ab4eb0abef8edbba6c71bc04d7f04ad813a214ddea74"}, ] send2trash = [ {file = "Send2Trash-1.5.0-py3-none-any.whl", hash = "sha256:f1691922577b6fa12821234aeb57599d887c4900b9ca537948d2dac34aea888b"}, @@ -1476,8 +1476,8 @@ urllib3 = [ {file = "urllib3-1.25.11.tar.gz", hash = "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2"}, ] virtualenv = [ - {file = "virtualenv-20.0.35-py2.py3-none-any.whl", hash = "sha256:0ebc633426d7468664067309842c81edab11ae97fcaf27e8ad7f5748c89b431b"}, - {file = "virtualenv-20.0.35.tar.gz", hash = "sha256:2a72c80fa2ad8f4e2985c06e6fc12c3d60d060e410572f553c90619b0f6efaf3"}, + {file = "virtualenv-20.1.0-py2.py3-none-any.whl", hash = "sha256:b0011228208944ce71052987437d3843e05690b2f23d1c7da4263fde104c97a2"}, + {file = "virtualenv-20.1.0.tar.gz", hash = "sha256:b8d6110f493af256a40d65e29846c69340a947669eec8ce784fcf3dd3af28380"}, ] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, From a4c32f5811b604e211ea90088f64abd25f852364 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 27 Oct 2020 17:07:07 +0100 Subject: [PATCH 116/142] Add xdoctest to the develop dependencies --- poetry.lock | 24 +++++++++++++++++++++++- pyproject.toml | 3 +++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 98d06c2..e50d7f0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -965,10 +965,28 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "xdoctest" +version = "0.15.0" +description = "A rewrite of the builtin doctest module" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +six = "*" + +[package.extras] +all = ["six", "pytest", "pytest-cov", "codecov", "scikit-build", "cmake", "ninja", "pybind11", "pygments", "colorama", "nbformat", "nbconvert", "jupyter-client", "ipython", "ipykernel"] +colors = ["pygments", "colorama"] +jupyter = ["nbformat", "nbconvert", "jupyter-client", "ipython", "ipykernel"] +optional = ["pygments", "colorama", "nbformat", "nbconvert", "jupyter-client", "ipython", "ipykernel"] +tests = ["pytest", "pytest-cov", "codecov", "scikit-build", "cmake", "ninja", "pybind11", "nbformat", "nbconvert", "jupyter-client", "ipython", "ipykernel"] + [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "abc30be36880f8b203c72895f70f906ef451fca6b4e5113891c9487bc95e02e5" +content-hash = "0483228a1bbf92c52f5045db05de80b951dbd7302db756ca6f65cd56b482b814" [metadata.files] appdirs = [ @@ -1487,3 +1505,7 @@ webencodings = [ {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, ] +xdoctest = [ + {file = "xdoctest-0.15.0-py2.py3-none-any.whl", hash = "sha256:695ea04303a48cbb319709270d43f7bae7f3de3701aec73f09d90a216499992e"}, + {file = "xdoctest-0.15.0.tar.gz", hash = "sha256:7f0a184d403b69b166ebec1aadb13c98c96c59101e974ae2e4db4c3a803ec371"}, +] diff --git a/pyproject.toml b/pyproject.toml index 588c1b4..aa92bb6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,9 @@ numpy = "^1.19.2" nox = "^2020.8.22" pre-commit = "^2.7.1" +# Testing +xdoctest = "^0.15.0" + # Live coding during presentation mode jupyter-contrib-nbextensions = "^0.5.1" rise = "^5.6.1" From 4c123d5d01adc6f5ac5ecd7bd4f4c31c7a10174b Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 27 Oct 2020 17:38:20 +0100 Subject: [PATCH 117/142] Streamline the doctests in the sample_package - add a couple of doctests - fix failing existing doctests --- 11_classes/sample_package/matrix.py | 45 ++++++++++++++++++++--------- 11_classes/sample_package/vector.py | 31 ++++++++++++-------- 2 files changed, 51 insertions(+), 25 deletions(-) diff --git a/11_classes/sample_package/matrix.py b/11_classes/sample_package/matrix.py index b64e50d..65f5fc1 100644 --- a/11_classes/sample_package/matrix.py +++ b/11_classes/sample_package/matrix.py @@ -38,6 +38,10 @@ class Matrix: ValueError: - if no entries are provided - if the number of columns is inconsistent across the rows + + Example Usage: + >>> Matrix([(1, 2), (3, 4)]) + Matrix(((1.000, 2.000,), (3.000, 4.000,))) """ self._entries = self.storage( self.storage(self.typing(x) for x in r) for r in data @@ -64,6 +68,10 @@ class Matrix: ValueError: - if no entries are provided - if the number of rows is inconsistent across the columns + + Example Usage: + >>> Matrix.from_columns([(1, 2), (3, 4)]) + Matrix(((1.000, 3.000,), (2.000, 4.000,))) """ return cls(data).transpose() @@ -122,7 +130,7 @@ class Matrix: >>> m[-1] 4.0 >>> m[0, 1] - 3.0 + 2.0 """ # Sequence-like indexing (one-dimensional) if isinstance(index, int): @@ -201,13 +209,13 @@ class Matrix: Example Usage: >>> Matrix([(1, 2), (3, 4)]) + Matrix([(2, 3), (4, 5)]) - Matrix(((3.0, 5.0), (7.0, 9.0))) + Matrix(((3.000, 5.000,), (7.000, 9.000,))) >>> Matrix([(1, 2), (3, 4)]) + 5 - Matrix(((6.0, 7.0), (8.0, 9.0))) + Matrix(((6.000, 7.000,), (8.000, 9.000,))) >>> 10 + Matrix([(1, 2), (3, 4)]) - Matrix(((11.0, 12.0), (13.0, 14.0))) + Matrix(((11.000, 12.000,), (13.000, 14.000,))) """ # Matrix addition if isinstance(other, self.__class__): @@ -237,13 +245,13 @@ class Matrix: Example Usage: >>> Matrix([(2, 3), (4, 5)]) - Matrix([(1, 2), (3, 4)]) - Matrix(((1.0, 1.0), (1.0, 1.0))) + Matrix(((1.000, 1.000,), (1.000, 1.000,))) >>> Matrix([(1, 2), (3, 4)]) - 1 - Matrix(((0.0, 1.0), (2.0, 3.0))) + Matrix(((0.000, 1.000,), (2.000, 3.000,))) >>> 10 - Matrix([(1, 2), (3, 4)]) - Matrix(((9.0, 8.0), (7.0, 6.0))) + Matrix(((9.000, 8.000,), (7.000, 6.000,))) """ # As subtraction is the inverse of addition, # we first dispatch to .__neg__() to invert the signs of @@ -276,21 +284,21 @@ class Matrix: Example Usage: >>> Matrix([(1, 2), (3, 4)]) * Matrix([(1, 2), (3, 4)]) - Matrix(((7.0, 10.0), (15.0, 22.0))) + Matrix(((7.000, 10.000,), (15.000, 22.000,))) >>> 2 * Matrix([(1, 2), (3, 4)]) - Matrix(((2.0, 4.0), (6.0, 8.0))) + Matrix(((2.000, 4.000,), (6.000, 8.000,))) >>> Matrix([(1, 2), (3, 4)]) * 3 - Matrix(((3.0, 6.0), (9.0, 12.0))) + Matrix(((3.000, 6.000,), (9.000, 12.000,))) Matrix-vector and vector-matrix multiplication are not commutative. >>> Matrix([(1, 2), (3, 4)]) * Vector([5, 6]) - Vector((17.0, 39.0)) + Vector((17.000, 39.000)) >>> Vector([5, 6]) * Matrix([(1, 2), (3, 4)]) - Vector((23.0, 34.0)) + Vector((23.000, 34.000)) """ # Scalar multiplication if isinstance(other, numbers.Number): @@ -322,7 +330,7 @@ class Matrix: Example Usage: >>> Matrix([(1, 2), (3, 4)]) / 4 - Matrix([(0.25, 0.5), (0.75, 1.0)]) + Matrix(((0.250, 0.500,), (0.750, 1.000,))) """ # As scalar division division is the same as multiplication # with the inverse, we dispatch to .__mul__(). @@ -394,6 +402,10 @@ class Matrix: Raises: RuntimeError: if one of the two dimensions, .n_rows or .n_cols, is not 1 + + Example Usage: + >>> Matrix([(1, 2, 3)]).as_vector() + Vector((1.000, 2.000, 3.000)) """ if not (self.n_rows == 1 or self.n_cols == 1): raise RuntimeError("one dimension (m or n) must be 1") @@ -404,6 +416,13 @@ class Matrix: Returns: matrix (Matrix) + + Example Usage: + >>> m = Matrix([(1, 2), (3, 4)]) + >>> m + Matrix(((1.000, 2.000,), (3.000, 4.000,))) + >>> m.transpose() + Matrix(((1.000, 3.000,), (2.000, 4.000,))) """ return self.__class__(zip(*self._entries)) diff --git a/11_classes/sample_package/vector.py b/11_classes/sample_package/vector.py index 1b6e305..874d29a 100644 --- a/11_classes/sample_package/vector.py +++ b/11_classes/sample_package/vector.py @@ -40,10 +40,10 @@ class Vector: Example Usage: >>> Vector([1, 2, 3]) - Vector((1.0, 2.0, 3.0)) + Vector((1.000, 2.000, 3.000)) >>> Vector(range(3)) - Vector((0.0, 1.0, 2.0)) + Vector((0.000, 1.000, 2.000)) """ self._entries = self.storage(self.typing(x) for x in data) if len(self) == 0: @@ -86,13 +86,13 @@ class Vector: Example Usage: >>> Vector([1, 2, 3]) + Vector([2, 3, 4]) - Vector((3, 5, 7)) + Vector((3.000, 5.000, 7.000)) >>> Vector([1, 2, 3]) + 4 - Vector((5, 6, 7)) + Vector((5.000, 6.000, 7.000)) >>> 10 + Vector([1, 2, 3]) - Vector((11, 12, 13)) + Vector((11.000, 12.000, 13.000)) """ # Vector addition if isinstance(other, self.__class__): @@ -117,13 +117,13 @@ class Vector: Example Usage: >>> Vector([7, 8, 9]) - Vector([1, 2, 3]) - Vector((6, 6, 6)) + Vector((6.000, 6.000, 6.000)) >>> Vector([1, 2, 3]) - 1 - Vector((0, 1, 2)) + Vector((0.000, 1.000, 2.000)) >>> 10 - Vector([1, 2, 3]) - Vector((9, 8, 7)) + Vector((9.000, 8.000, 7.000)) """ # As subtraction is the inverse of addition, # we first dispatch to .__neg__() to invert the signs of @@ -143,13 +143,13 @@ class Vector: Example Usage: >>> Vector([1, 2, 3]) * Vector([2, 3, 4]) - 14 + 20.0 >>> 2 * Vector([1, 2, 3]) - Vector((2.0, 4.0, 6.0)) + Vector((2.000, 4.000, 6.000)) >>> Vector([1, 2, 3]) * 3 - Vector((3.0, 6.0, 9.0)) + Vector((3.000, 6.000, 9.000)) """ # Dot product if isinstance(other, self.__class__): @@ -174,7 +174,7 @@ class Vector: Example Usage: >>> Vector([9, 6, 12]) / 3 - Vector((3.0, 2.0, 4.0)) + Vector((3.000, 2.000, 4.000)) """ # As scalar division division is the same as multiplication # with the inverse, we dispatch to .__mul__(). @@ -247,6 +247,13 @@ class Vector: Returns: matrix (Matrix) + + Example Usage: + >>> v = Vector([1, 2, 3]) + >>> v.as_matrix() + Matrix(((1.000,), (2.000,), (3.000,))) + >>> v.as_matrix(column=False) + Matrix(((1.000, 2.000, 3.000,))) """ if column: return Matrix([x] for x in self) From 8dc67c5bdd33bbd6466a4371ae2f19fa2f7303fd Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Tue, 27 Oct 2020 17:56:38 +0100 Subject: [PATCH 118/142] Add a nox session for running xdoctest - add a session "doctests" to nox - run it as a pre-commit hook --- .pre-commit-config.yaml | 6 ++++++ noxfile.py | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 40d4f46..676b025 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,6 +3,12 @@ fail_fast: true repos: - repo: local hooks: + - id: doctests + name: Run the xdoctests in the source files + entry: poetry run nox -s doctests -- + language: system + stages: [commit, merge-commit] + types: [python] - id: fix-branch-references name: Check for wrong branch references entry: poetry run nox -s fix-branch-references -- diff --git a/noxfile.py b/noxfile.py index e77cc26..869cdab 100644 --- a/noxfile.py +++ b/noxfile.py @@ -4,6 +4,8 @@ Nox provides the following tasks: - "init-project": install the pre-commit hooks +- "doctests": run the xdoctests in the source files + - "fix-branch-references": adjusts links with git branch references in various files (e.g., Mardown or notebooks) @@ -22,6 +24,11 @@ import nox REPOSITORY = "webartifex/intro-to-python" +SRC_LOCATIONS = ( + "02_functions/sample_module.py", + "11_classes/sample_package", +) + # Use a unified .cache/ folder for all develop tools. nox.options.envdir = ".cache/nox" @@ -45,6 +52,13 @@ def init_project(session): ) +@nox.session(venv_backend="none") +def doctests(session): + """Run the xdoctests in the source files.""" + for location in SRC_LOCATIONS: + session.run("poetry", "run", "xdoctest", "--silent", location) + + @nox.session(name="fix-branch-references", venv_backend="none") def fix_branch_references(_session): """Change git branch references. From 85b7fd274cdad5e79296ad2245d6868b796823cc Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 28 Oct 2020 16:18:15 +0100 Subject: [PATCH 119/142] Add initial version of chapter 11, part 4 --- 02_functions/02_content.ipynb | 2 +- 11_classes/00_content.ipynb | 351 ++-- 11_classes/02_content.ipynb | 2 +- 11_classes/03_content.ipynb | 26 +- 11_classes/04_content.ipynb | 2719 +++++++++++++++++++++++++ 11_classes/sample_package/__init__.py | 4 + 11_classes/sample_package/matrix.py | 28 +- 11_classes/sample_package/vector.py | 12 +- CONTENTS.md | 5 + 9 files changed, 2927 insertions(+), 222 deletions(-) create mode 100644 11_classes/04_content.ipynb diff --git a/02_functions/02_content.ipynb b/02_functions/02_content.ipynb index 06f1765..5162e9c 100644 --- a/02_functions/02_content.ipynb +++ b/02_functions/02_content.ipynb @@ -1571,7 +1571,7 @@ } }, "source": [ - "Packages are a generalization of modules, and we look at one in detail in [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb). You may, however, already look at a [sample package ](https://github.com/webartifex/intro-to-python/tree/develop/11_classes/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 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/04_content.ipynb#Packages-vs.-Modules).\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/11_classes/00_content.ipynb b/11_classes/00_content.ipynb index 6852453..87a9bae 100644 --- a/11_classes/00_content.ipynb +++ b/11_classes/00_content.ipynb @@ -808,7 +808,7 @@ } }, "source": [ - "Every instance comes with a special `.__class__` attribute that also references the corresponding class object." + "`v`'s semantic \"value\" is not so clear yet. We fix this in the next section." ] }, { @@ -819,41 +819,6 @@ "slide_type": "fragment" } }, - "outputs": [ - { - "data": { - "text/plain": [ - "__main__.Vector" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "v.__class__" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "`v`'s semantic \"value\" is not so clear yet. We fix this in the next section." - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, "outputs": [ { "data": { @@ -861,7 +826,7 @@ "<__main__.Vector at 0x7f9b9416d760>" ] }, - "execution_count": 22, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } @@ -885,7 +850,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 22, "metadata": { "slideshow": { "slide_type": "slide" @@ -910,7 +875,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 23, "metadata": { "slideshow": { "slide_type": "fragment" @@ -946,7 +911,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 24, "metadata": { "slideshow": { "slide_type": "slide" @@ -983,7 +948,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 25, "metadata": { "slideshow": { "slide_type": "slide" @@ -996,7 +961,7 @@ "[1.0, 2.0, 3.0]" ] }, - "execution_count": 26, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } @@ -1031,7 +996,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 26, "metadata": { "slideshow": { "slide_type": "slide" @@ -1042,6 +1007,30 @@ "x = 1, 2, 3" ] }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 2, 3)" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x" + ] + }, { "cell_type": "code", "execution_count": 28, @@ -1062,30 +1051,6 @@ "output_type": "execute_result" } ], - "source": [ - "x" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "(1, 2, 3)" - ] - }, - "execution_count": 29, - "metadata": {}, - "output_type": "execute_result" - } - ], "source": [ "(1, 2, 3)" ] @@ -1103,7 +1068,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 29, "metadata": { "slideshow": { "slide_type": "slide" @@ -1144,7 +1109,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 30, "metadata": { "slideshow": { "slide_type": "slide" @@ -1157,7 +1122,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 31, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1170,7 +1135,7 @@ "Vector((1.000, 2.000, 3.000))" ] }, - "execution_count": 32, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } @@ -1192,7 +1157,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 32, "metadata": { "slideshow": { "slide_type": "skip" @@ -1205,7 +1170,7 @@ "Vector((1.000, 2.000, 3.000))" ] }, - "execution_count": 33, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -1227,7 +1192,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 33, "metadata": { "slideshow": { "slide_type": "slide" @@ -1240,7 +1205,7 @@ "'Vector((1.000, 2.000, 3.000))'" ] }, - "execution_count": 34, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" } @@ -1266,7 +1231,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 34, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1279,7 +1244,7 @@ "'Vector(1.0, ..., 3.0)[3]'" ] }, - "execution_count": 35, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } @@ -1301,7 +1266,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 35, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1342,14 +1307,14 @@ } }, "source": [ - "Below is a first implementation of the `Matrix` class that stores the entries internally as a `list` of `list`s.\n", + "Below is a first implementation of the `Matrix` class that stores the `._entries` internally as a `list` of `list`s.\n", "\n", "The `.__init__()` method ensures that all the rows come with the same number of columns. Again, we do not allow `Matrix` instances without any entries." ] }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 36, "metadata": { "code_folding": [], "slideshow": { @@ -1391,7 +1356,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 37, "metadata": { "slideshow": { "slide_type": "skip" @@ -1404,7 +1369,7 @@ "94113690738160" ] }, - "execution_count": 38, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } @@ -1413,6 +1378,30 @@ "id(Matrix)" ] }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "type" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(Matrix)" + ] + }, { "cell_type": "code", "execution_count": 39, @@ -1421,30 +1410,6 @@ "slide_type": "skip" } }, - "outputs": [ - { - "data": { - "text/plain": [ - "type" - ] - }, - "execution_count": 39, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(Matrix)" - ] - }, - { - "cell_type": "code", - "execution_count": 40, - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, "outputs": [ { "data": { @@ -1452,7 +1417,7 @@ "__main__.Matrix" ] }, - "execution_count": 40, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } @@ -1474,7 +1439,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 40, "metadata": { "slideshow": { "slide_type": "slide" @@ -1487,7 +1452,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 41, "metadata": { "slideshow": { "slide_type": "skip" @@ -1500,7 +1465,7 @@ "140306180401856" ] }, - "execution_count": 42, + "execution_count": 41, "metadata": {}, "output_type": "execute_result" } @@ -1511,7 +1476,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 42, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1524,7 +1489,7 @@ "__main__.Matrix" ] }, - "execution_count": 43, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } @@ -1546,7 +1511,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 43, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1559,7 +1524,7 @@ "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" ] }, - "execution_count": 44, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } @@ -1570,7 +1535,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 44, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1602,7 +1567,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 45, "metadata": { "slideshow": { "slide_type": "skip" @@ -1628,7 +1593,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 46, "metadata": { "slideshow": { "slide_type": "slide" @@ -1673,12 +1638,14 @@ "source": [ "The methods we have seen so far are all **instance methods**. The characteristic idea behind an instance method is that the behavior it provides either depends on the state of a concrete instance or mutates it. In other words, an instance method *always* works with attributes on the `self` argument. If a method does *not* need access to `self` to do its job, it is conceptually *not* an instance method and we should probably convert it into another kind of method as shown below.\n", "\n", - "An example of an instance method from linear algebra is the `.transpose()` method below that switches the rows and columns of an *existing* `Matrix` instance and returns a *new* `Matrix` instance based off that. It is implemented by passing the *iterator* created with the [zip() ](https://docs.python.org/3/library/functions.html#zip) built-in as the `data` argument to the `Matrix` constructor: The expression `zip(*self._entries)` may be a bit hard to understand because of the involved unpacking but simply flips a `Vector`'s rows and columns. The built-in [list() ](https://docs.python.org/3/library/functions.html#func-list) constructor within the `.__init__()` method then materializes the iterator into the `._entries`. Without a concrete `Matrix`'s rows and columns, `.transpose()` does not make sense, conceptually speaking." + "An example of an instance method from linear algebra is the `.transpose()` method below that switches the rows and columns of an *existing* `Matrix` instance and returns a *new* `Matrix` instance based off that. It is implemented by passing the *iterator* created with the [zip() ](https://docs.python.org/3/library/functions.html#zip) built-in as the `data` argument to the `Matrix` constructor: The expression `zip(*self._entries)` may be a bit hard to understand because of the involved unpacking but simply flips a `Matrix`'s rows and columns. The built-in [list() ](https://docs.python.org/3/library/functions.html#func-list) constructor within the `.__init__()` method then materializes the iterator into the `._entries` attribute. Without a concrete `Matrix`'s rows and columns, `.transpose()` does not make sense, conceptually speaking.\n", + "\n", + "Also, we see that it is ok to reference a class from within one of its methods. While this seems trivial to some readers, others may find this confusing. The final versions of the `Vector` and `Matrix` classes in the [fourth part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/04_content.ipynb#The-final-Vector-and-Matrix-Classes) of this chapter show how this \"hard coded\" redundancy can be avoided." ] }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 47, "metadata": { "slideshow": { "slide_type": "slide" @@ -1713,7 +1680,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 48, "metadata": { "slideshow": { "slide_type": "slide" @@ -1724,6 +1691,30 @@ "m = Matrix([(1, 2, 3), (4, 5, 6), (7, 8, 9)])" ] }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m" + ] + }, { "cell_type": "code", "execution_count": 50, @@ -1732,30 +1723,6 @@ "slide_type": "fragment" } }, - "outputs": [ - { - "data": { - "text/plain": [ - "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" - ] - }, - "execution_count": 50, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "m" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, "outputs": [ { "data": { @@ -1763,7 +1730,7 @@ "Matrix(((1.000, 4.000, 7.000,), (2.000, 5.000, 8.000,), (3.000, 6.000, 9.000,)))" ] }, - "execution_count": 51, + "execution_count": 50, "metadata": {}, "output_type": "execute_result" } @@ -1785,7 +1752,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 51, "metadata": { "slideshow": { "slide_type": "slide" @@ -1796,6 +1763,30 @@ "n = m.transpose().transpose()" ] }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "n" + ] + }, { "cell_type": "code", "execution_count": 53, @@ -1808,7 +1799,7 @@ { "data": { "text/plain": [ - "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + "False" ] }, "execution_count": 53, @@ -1817,7 +1808,18 @@ } ], "source": [ - "n" + "m is n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Unintuitively, the comparison operator `==` returns a wrong result as `m` and `n` have `_entries` attributes that compare equal. We fix this in the \"*Operator Overloading*\" section later in this chapter." ] }, { @@ -1840,41 +1842,6 @@ "output_type": "execute_result" } ], - "source": [ - "m is n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "slideshow": { - "slide_type": "skip" - } - }, - "source": [ - "Unintuitively, the comparison operator `==` returns a wrong result as `m` and `n` have `_entries` attributes that compare equal. We fix this in the \"*Operator Overloading*\" section later in this chapter." - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "metadata": { - "slideshow": { - "slide_type": "fragment" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 55, - "metadata": {}, - "output_type": "execute_result" - } - ], "source": [ "m == n" ] @@ -1894,7 +1861,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 55, "metadata": { "slideshow": { "slide_type": "slide" @@ -1933,7 +1900,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 56, "metadata": { "slideshow": { "slide_type": "slide" @@ -1946,7 +1913,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 57, "metadata": { "slideshow": { "slide_type": "fragment" @@ -1959,7 +1926,7 @@ "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" ] }, - "execution_count": 58, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" } @@ -2007,7 +1974,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 58, "metadata": { "slideshow": { "slide_type": "slide" @@ -2051,7 +2018,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 59, "metadata": { "slideshow": { "slide_type": "slide" @@ -2064,7 +2031,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 60, "metadata": { "slideshow": { "slide_type": "fragment" @@ -2077,7 +2044,7 @@ "(2, 3)" ] }, - "execution_count": 61, + "execution_count": 60, "metadata": {}, "output_type": "execute_result" } @@ -2099,7 +2066,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 61, "metadata": { "slideshow": { "slide_type": "fragment" diff --git a/11_classes/02_content.ipynb b/11_classes/02_content.ipynb index 520339f..644fad2 100644 --- a/11_classes/02_content.ipynb +++ b/11_classes/02_content.ipynb @@ -1577,7 +1577,7 @@ } }, "source": [ - "After this discussion of mutable `Vector` and `Matrix` classes, we continue with immutable classes in the rest of this chapter." + "After this discussion of mutable `Vector` and `Matrix` classes, we continue with immutable implementations in the rest of this chapter. To lower the chance that we accidently design parts of our classes to be mutable, we replace the built-in [list() ](https://docs.python.org/3/library/functions.html#func-list) constructor with [tuple() ](https://docs.python.org/3/library/functions.html#func-tuple) in the `.__init__()` methods. As we learn in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/03_content.ipynb#Tuples-are-like-%22Immutable-Lists%22), `tuple`s are like immutable `list`s." ] }, { diff --git a/11_classes/03_content.ipynb b/11_classes/03_content.ipynb index 9dc08a2..def4be5 100644 --- a/11_classes/03_content.ipynb +++ b/11_classes/03_content.ipynb @@ -82,7 +82,7 @@ "class Vector:\n", "\n", " def __init__(self, data):\n", - " self._entries = list(float(x) for x in data)\n", + " self._entries = tuple(float(x) for x in data)\n", " # ...\n", "\n", " def __repr__(self):\n", @@ -111,7 +111,7 @@ "class Matrix:\n", "\n", " def __init__(self, data):\n", - " self._entries = list(list(float(x) for x in r) for r in data)\n", + " self._entries = tuple(tuple(float(x) for x in r) for r in data)\n", " # ...\n", "\n", " def __repr__(self):\n", @@ -678,7 +678,7 @@ "class Vector:\n", "\n", " def __init__(self, data):\n", - " self._entries = list(float(x) for x in data)\n", + " self._entries = tuple(float(x) for x in data)\n", " # ...\n", "\n", " def __repr__(self):\n", @@ -692,7 +692,7 @@ " return iter(self._entries)\n", "\n", " def __add__(self, other):\n", - " if isinstance(other, self.__class__): # vector addition\n", + " if isinstance(other, Vector): # vector addition\n", " if len(self) != len(other):\n", " raise ValueError(\"vectors must be of the same length\")\n", " return Vector(x + y for (x, y) in zip(self, other))\n", @@ -704,7 +704,7 @@ " return self + other\n", "\n", " def __sub__(self, other):\n", - " if isinstance(other, self.__class__): # vector subtraction\n", + " if isinstance(other, Vector): # vector subtraction\n", " if len(self) != len(other):\n", " raise ValueError(\"vectors must be of the same length\")\n", " return Vector(x - y for (x, y) in zip(self, other))\n", @@ -719,7 +719,7 @@ " return NotImplemented\n", "\n", " def __mul__(self, other):\n", - " if isinstance(other, self.__class__): # dot product\n", + " if isinstance(other, Vector): # dot product\n", " if len(self) != len(other):\n", " raise ValueError(\"vectors must be of the same length\")\n", " return sum(x * y for (x, y) in zip(self, other))\n", @@ -1142,7 +1142,7 @@ "class Matrix:\n", "\n", " def __init__(self, data):\n", - " self._entries = list(list(float(x) for x in r) for r in data)\n", + " self._entries = tuple(tuple(float(x) for x in r) for r in data)\n", " # ...\n", "\n", " def __repr__(self):\n", @@ -1169,7 +1169,7 @@ " return (self._entries[r][c] for r in range(self.n_rows) for c in range(self.n_cols))\n", "\n", " def __add__(self, other):\n", - " if isinstance(other, self.__class__): # matrix addition\n", + " if isinstance(other, Matrix): # matrix addition\n", " if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):\n", " raise ValueError(\"matrices must have the same dimensions\")\n", " return Matrix((s_col + o_col for (s_col, o_col) in zip(s_row, o_row))\n", @@ -1184,7 +1184,7 @@ " return self + other\n", "\n", " def __sub__(self, other):\n", - " if isinstance(other, self.__class__): # matrix subtraction\n", + " if isinstance(other, Matrix): # matrix subtraction\n", " if (self.n_rows != other.n_rows) or (self.n_cols != other.n_cols):\n", " raise ValueError(\"matrices must have the same dimensions\")\n", " return Matrix((s_col - o_col for (s_col, o_col) in zip(s_row, o_row))\n", @@ -1211,7 +1211,7 @@ " return Matrix((x * other for x in r) for r in self._entries)\n", " elif isinstance(other, Vector):\n", " return self._matrix_multiply(other.as_matrix()).as_vector()\n", - " elif isinstance(other, self.__class__):\n", + " elif isinstance(other, Matrix):\n", " return self._matrix_multiply(other)\n", " return NotImplemented\n", "\n", @@ -1720,7 +1720,7 @@ " zero_threshold = 1e-12\n", "\n", " def __init__(self, data):\n", - " self._entries = list(float(x) for x in data)\n", + " self._entries = tuple(float(x) for x in data)\n", " # ...\n", "\n", " def __repr__(self):\n", @@ -1734,7 +1734,7 @@ " return iter(self._entries)\n", "\n", " def __eq__(self, other):\n", - " if isinstance(other, self.__class__):\n", + " if isinstance(other, Vector):\n", " if len(self) != len(other):\n", " raise ValueError(\"vectors must be of the same length\")\n", " for x, y in zip(self, other):\n", @@ -1990,7 +1990,7 @@ "class Vector:\n", "\n", " def __init__(self, data):\n", - " self._entries = list(float(x) for x in data)\n", + " self._entries = tuple(float(x) for x in data)\n", " # ...\n", "\n", " def __repr__(self):\n", diff --git a/11_classes/04_content.ipynb b/11_classes/04_content.ipynb new file mode 100644 index 0000000..51b0818 --- /dev/null +++ b/11_classes/04_content.ipynb @@ -0,0 +1,2719 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/04_content.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 11: Classes & Instances (continued)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "In this fourth part of the chapter, we finalize our `Vector` and `Matrix` classes. As both `class` definitions have become rather lengthy, we learn how we to organize them into a Python package and import them in this Jupyter notebook. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Packages vs. Modules" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/02_content.ipynb#Local-Modules-and-Packages), we introduce the concept of a Python module that is imported with the `import` statement. Essentially, a **module** is a single plain text \\*.py file on disk that contains Python code (e.g., [*sample_module.py* ](https://github.com/webartifex/intro-to-python/blob/develop/02_functions/sample_module.py) in [Chapter 2's folder ](https://github.com/webartifex/intro-to-python/tree/develop/02_functions)).\n", + "\n", + "Conceptually, a **package** is a generalization of a module whose code is split across several \\*.py to achieve a better organization of the individual parts. The \\*.py files are stored within a folder (e.g., [*sample_package* ](https://github.com/webartifex/intro-to-python/tree/develop/11_classes/sample_package) in [Chapter 11's folder ](https://github.com/webartifex/intro-to-python/tree/develop/11_classes)). In addition to that, a \"*\\_\\_init\\_\\_.py*\" file that may be empty must be put inside the folder. The latter is what the Python interpreter looks for to decide if a folder is a package or not.\n", + "\n", + "Let's look at an example with the final version of our `Vector` and `Matrix` classes.\n", + "\n", + "`!pwd` shows the location of this Jupyter notebook on the computer you are running [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) on: It is the local equivalent of [Chapter 11's folder ](https://github.com/webartifex/intro-to-python/tree/develop/11_classes) in this book's [GitHub repository ](https://github.com/webartifex/intro-to-python)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/home/webartifex/repos/intro-to-python/11_classes\n" + ] + } + ], + "source": [ + "!pwd" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`!ls` lists all the files and folders in the current location: These are Chapter 11's Jupyter notebooks (i.e., the \\*.ipynb files) and the [*sample_package* ](https://github.com/webartifex/intro-to-python/tree/develop/11_classes/sample_package) folder. " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "00_content.ipynb 02_content.ipynb 04_content.ipynb\n", + "01_exercises.ipynb 03_content.ipynb sample_package\n" + ] + } + ], + "source": [ + "!ls" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If we run `!ls` with the `sample_package` folder as the argument, we see the folder's contents: Four \\*.py files. Alternatively, you can use [JupyterLab' File Browser](https://jupyterlab.readthedocs.io/en/stable/user/interface.html?highlight=file%20browser#left-sidebar) on the left to navigate into the package." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "__init__.py matrix.py\tutils.py vector.py\n" + ] + } + ], + "source": [ + "!ls sample_package" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The package is organized such that the [*matrix.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/matrix.py) and [*vector.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/vector.py) modules each define just one class, `Matrix` and `Vector`. That is intentional as both classes consist of several hundred lines of code and comments.\n", + "\n", + "The [*utils.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/utils.py) module contains code that is shared by both classes. Such code snippets are commonly called \"utilities\" or \"helpers,\" which explains the module's name.\n", + "\n", + "Finally, the [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/__init__.py) file contains mostly meta information and defines what objects should be importable from the package's top level.\n", + "\n", + "With the `import` statement, we can import the entire package just as we would import a module from the [standard library ](https://docs.python.org/3/library/index.html)." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "import sample_package as pkg" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The above cell runs the code in the [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/__init__.py) file from top to bottom, which in turn runs the [*matrix.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/matrix.py), [*utils.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/utils.py), and [*vector.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/vector.py) modules (cf., look at the `import` statements in the four \\*.py files to get the idea). As both [*matrix.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/matrix.py) and [*vector.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/vector.py) depend on each other (i.e., the `Matrix` class needs the `Vector` class to work and vice versa), understanding the order in that the modules are executed is not trivial. Without going into detail, we mention that Python guarantees that each \\*.py file is run only once and figures out the order on its own. If Python is unable to do that, for example, due to unresolvable cirular imports, it aborts with an `ImportError`.\n", + "\n", + "Below, `pkg` is an object of type `module` ..." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pkg" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "module" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(pkg)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "... and we use the built-in [dir() ](https://docs.python.org/3/library/functions.html#dir) function to check what attributes `pkg` comes with." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['Matrix',\n", + " 'Vector',\n", + " '__all__',\n", + " '__author__',\n", + " '__builtins__',\n", + " '__cached__',\n", + " '__doc__',\n", + " '__file__',\n", + " '__loader__',\n", + " '__name__',\n", + " '__package__',\n", + " '__path__',\n", + " '__spec__',\n", + " '__version__',\n", + " 'matrix',\n", + " 'utils',\n", + " 'vector']" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dir(pkg)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The package's meta information and documentation are automatically parsed from the [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/__init__.py) file." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on package linear_algebra_tools:\n", + "\n", + "NAME\n", + " linear_algebra_tools - This package provides linear algebra functionalities.\n", + "\n", + "DESCRIPTION\n", + " The package is split into three modules:\n", + " - matrix: defines the Matrix class\n", + " - vector: defines the Vector class\n", + " - utils: defines the norm() function that is shared by Matrix and Vector\n", + " and package-wide constants\n", + " \n", + " The classes implement arithmetic operations involving vectors and matrices.\n", + " \n", + " See the docstrings in the modules and classes for further info.\n", + "\n", + "PACKAGE CONTENTS\n", + " matrix\n", + " utils\n", + " vector\n", + "\n", + "CLASSES\n", + " builtins.object\n", + " sample_package.matrix.Matrix\n", + " sample_package.vector.Vector\n", + " \n", + " class Matrix(builtins.object)\n", + " | Matrix(data)\n", + " | \n", + " | An m-by-n-dimensional matrix from linear algebra.\n", + " | \n", + " | All entries are converted to floats, or whatever is set in the typing attribute.\n", + " | \n", + " | Attributes:\n", + " | storage (callable): data type used to store the entries internally;\n", + " | defaults to tuple\n", + " | typing (callable): type casting applied to all entries upon creation;\n", + " | defaults to float\n", + " | vector_cls (vector.Vector): a reference to the Vector class to work with\n", + " | zero_threshold (float): max. tolerance when comparing an entry to zero;\n", + " | defaults to 1e-12\n", + " | \n", + " | Methods defined here:\n", + " | \n", + " | __abs__(self)\n", + " | The Frobenius norm of a Matrix.\n", + " | \n", + " | __add__(self, other)\n", + " | Handle `self + other` and `other + self`.\n", + " | \n", + " | This may be either matrix addition or broadcasting addition.\n", + " | \n", + " | Example Usage:\n", + " | >>> Matrix([(1, 2), (3, 4)]) + Matrix([(2, 3), (4, 5)])\n", + " | Matrix(((3.000, 5.000,), (7.000, 9.000,)))\n", + " | \n", + " | >>> Matrix([(1, 2), (3, 4)]) + 5\n", + " | Matrix(((6.000, 7.000,), (8.000, 9.000,)))\n", + " | \n", + " | >>> 10 + Matrix([(1, 2), (3, 4)])\n", + " | Matrix(((11.000, 12.000,), (13.000, 14.000,)))\n", + " | \n", + " | __bool__(self)\n", + " | A Matrix is truthy if its Frobenius norm is strictly positive.\n", + " | \n", + " | __eq__(self, other)\n", + " | Handle `self == other`.\n", + " | \n", + " | Compare two Matrix instances for equality.\n", + " | \n", + " | Example Usage:\n", + " | >>> Matrix([(1, 2), (3, 4)]) == Matrix([(1, 2), (3, 4)])\n", + " | True\n", + " | \n", + " | >>> Matrix([(1, 2), (3, 4)]) == Matrix([(5, 6), (7, 8)])\n", + " | False\n", + " | \n", + " | __float__(self)\n", + " | Cast a Matrix as a scalar.\n", + " | \n", + " | Returns:\n", + " | scalar (float)\n", + " | \n", + " | Raises:\n", + " | RuntimeError: if the Matrix has more than one entry\n", + " | \n", + " | __getitem__(self, index)\n", + " | Obtain an individual entry of a Matrix.\n", + " | \n", + " | Args:\n", + " | index (int / tuple of int's): if index is an integer,\n", + " | the Matrix is viewed as a sequence in row-major order;\n", + " | if index is a tuple of integers, the first one refers to\n", + " | the row and the second one to the column of the entry\n", + " | \n", + " | Returns:\n", + " | entry (Matrix.typing)\n", + " | \n", + " | Example Usage:\n", + " | >>> m = Matrix([(1, 2), (3, 4)])\n", + " | >>> m[0]\n", + " | 1.0\n", + " | >>> m[-1]\n", + " | 4.0\n", + " | >>> m[0, 1]\n", + " | 2.0\n", + " | \n", + " | __init__(self, data)\n", + " | Create a new matrix.\n", + " | \n", + " | Args:\n", + " | data (sequence of sequences): the matrix's entries;\n", + " | viewed as a sequence of the matrix's rows (i.e., row-major order);\n", + " | use the .from_columns() class method if the data come as a sequence\n", + " | of the matrix's columns (i.e., column-major order)\n", + " | \n", + " | Raises:\n", + " | ValueError:\n", + " | - if no entries are provided\n", + " | - if the number of columns is inconsistent across the rows\n", + " | \n", + " | Example Usage:\n", + " | >>> Matrix([(1, 2), (3, 4)])\n", + " | Matrix(((1.000, 2.000,), (3.000, 4.000,)))\n", + " | \n", + " | __iter__(self)\n", + " | Loop over a Matrix's entries.\n", + " | \n", + " | See .entries() for more customization options.\n", + " | \n", + " | __len__(self)\n", + " | Number of entries in a Matrix.\n", + " | \n", + " | __mul__(self, other)\n", + " | Handle `self * other` and `other * self`.\n", + " | \n", + " | This may be either scalar multiplication, matrix-vector multiplication,\n", + " | vector-matrix multiplication, or matrix-matrix multiplication.\n", + " | \n", + " | Example Usage:\n", + " | >>> Matrix([(1, 2), (3, 4)]) * Matrix([(1, 2), (3, 4)])\n", + " | Matrix(((7.000, 10.000,), (15.000, 22.000,)))\n", + " | \n", + " | >>> 2 * Matrix([(1, 2), (3, 4)])\n", + " | Matrix(((2.000, 4.000,), (6.000, 8.000,)))\n", + " | \n", + " | >>> Matrix([(1, 2), (3, 4)]) * 3\n", + " | Matrix(((3.000, 6.000,), (9.000, 12.000,)))\n", + " | \n", + " | Matrix-vector and vector-matrix multiplication are not commutative.\n", + " | \n", + " | >>> Matrix([(1, 2), (3, 4)]) * Vector([5, 6])\n", + " | Vector((17.000, 39.000))\n", + " | \n", + " | >>> Vector([5, 6]) * Matrix([(1, 2), (3, 4)])\n", + " | Vector((23.000, 34.000))\n", + " | \n", + " | __neg__(self)\n", + " | Handle `-self`.\n", + " | \n", + " | Negate all entries of a Matrix.\n", + " | \n", + " | __pos__(self)\n", + " | Handle `+self`.\n", + " | \n", + " | This is simply an identity operator returning the Matrix itself.\n", + " | \n", + " | __radd__(self, other)\n", + " | See docstring for .__add__().\n", + " | \n", + " | __repr__(self)\n", + " | Text representation of a Matrix.\n", + " | \n", + " | __reversed__(self)\n", + " | Loop over a Matrix's entries in reverse order.\n", + " | \n", + " | See .entries() for more customization options.\n", + " | \n", + " | __rmul__(self, other)\n", + " | See docstring for .__mul__().\n", + " | \n", + " | __rsub__(self, other)\n", + " | See docstring for .__sub__().\n", + " | \n", + " | __str__(self)\n", + " | Human-readable text representation of a Matrix.\n", + " | \n", + " | __sub__(self, other)\n", + " | Handle `self - other` and `other - self`.\n", + " | \n", + " | This may be either matrix subtraction or broadcasting subtraction.\n", + " | \n", + " | Example Usage:\n", + " | >>> Matrix([(2, 3), (4, 5)]) - Matrix([(1, 2), (3, 4)])\n", + " | Matrix(((1.000, 1.000,), (1.000, 1.000,)))\n", + " | \n", + " | >>> Matrix([(1, 2), (3, 4)]) - 1\n", + " | Matrix(((0.000, 1.000,), (2.000, 3.000,)))\n", + " | \n", + " | >>> 10 - Matrix([(1, 2), (3, 4)])\n", + " | Matrix(((9.000, 8.000,), (7.000, 6.000,)))\n", + " | \n", + " | __truediv__(self, other)\n", + " | Handle `self / other`.\n", + " | \n", + " | Divide a Matrix by a scalar.\n", + " | \n", + " | Example Usage:\n", + " | >>> Matrix([(1, 2), (3, 4)]) / 4\n", + " | Matrix(((0.250, 0.500,), (0.750, 1.000,)))\n", + " | \n", + " | as_vector(self)\n", + " | Get a Vector representation of a Matrix.\n", + " | \n", + " | Returns:\n", + " | vector (vector.Vector)\n", + " | \n", + " | Raises:\n", + " | RuntimeError: if one of the two dimensions, .n_rows or .n_cols, is not 1\n", + " | \n", + " | Example Usage:\n", + " | >>> Matrix([(1, 2, 3)]).as_vector()\n", + " | Vector((1.000, 2.000, 3.000))\n", + " | \n", + " | cols(self)\n", + " | Loop over a Matrix's columns.\n", + " | \n", + " | Returns:\n", + " | columns (generator): produces a Matrix's columns as Vectors\n", + " | \n", + " | entries(self, *, reverse=False, row_major=True)\n", + " | Loop over a Matrix's entries.\n", + " | \n", + " | Args:\n", + " | reverse (bool): flag to loop backwards; defaults to False\n", + " | row_major (bool): flag to loop in row-major order; defaults to True\n", + " | \n", + " | Returns:\n", + " | entries (generator): produces a Matrix's entries\n", + " | \n", + " | rows(self)\n", + " | Loop over a Matrix's rows.\n", + " | \n", + " | Returns:\n", + " | rows (generator): produces a Matrix's rows as Vectors\n", + " | \n", + " | transpose(self)\n", + " | Switch the rows and columns of a Matrix.\n", + " | \n", + " | Returns:\n", + " | matrix (Matrix)\n", + " | \n", + " | Example Usage:\n", + " | >>> m = Matrix([(1, 2), (3, 4)])\n", + " | >>> m\n", + " | Matrix(((1.000, 2.000,), (3.000, 4.000,)))\n", + " | >>> m.transpose()\n", + " | Matrix(((1.000, 3.000,), (2.000, 4.000,)))\n", + " | \n", + " | ----------------------------------------------------------------------\n", + " | Class methods defined here:\n", + " | \n", + " | from_columns(data) from builtins.type\n", + " | Create a new matrix.\n", + " | \n", + " | This is an alternative constructor for data provided in column-major order.\n", + " | \n", + " | Args:\n", + " | data (sequence of sequences): the matrix's entries;\n", + " | viewed as a sequence of the matrix's columns (i.e., column-major order);\n", + " | use the normal constructor method if the data come as a sequence\n", + " | of the matrix's rows (i.e., row-major order)\n", + " | \n", + " | Raises:\n", + " | ValueError:\n", + " | - if no entries are provided\n", + " | - if the number of rows is inconsistent across the columns\n", + " | \n", + " | Example Usage:\n", + " | >>> Matrix.from_columns([(1, 2), (3, 4)])\n", + " | Matrix(((1.000, 3.000,), (2.000, 4.000,)))\n", + " | \n", + " | from_rows(data) from builtins.type\n", + " | See docstring for .__init__().\n", + " | \n", + " | ----------------------------------------------------------------------\n", + " | Readonly properties defined here:\n", + " | \n", + " | n_cols\n", + " | Number of columns in a Matrix.\n", + " | \n", + " | n_rows\n", + " | Number of rows in a Matrix.\n", + " | \n", + " | ----------------------------------------------------------------------\n", + " | Data descriptors defined here:\n", + " | \n", + " | __dict__\n", + " | dictionary for instance variables (if defined)\n", + " | \n", + " | __weakref__\n", + " | list of weak references to the object (if defined)\n", + " | \n", + " | ----------------------------------------------------------------------\n", + " | Data and other attributes defined here:\n", + " | \n", + " | __hash__ = None\n", + " | \n", + " | storage = \n", + " | Built-in immutable sequence.\n", + " | \n", + " | If no argument is given, the constructor returns an empty tuple.\n", + " | If iterable is specified the tuple is initialized from iterable's items.\n", + " | \n", + " | If the argument is a tuple, the return value is the same object.\n", + " | \n", + " | typing = \n", + " | Convert a string or number to a floating point number, if possible.\n", + " | \n", + " | vector_cls = \n", + " | A one-dimensional vector from linear algebra.\n", + " | \n", + " | All entries are converted to floats, or whatever is set in the typing attribute.\n", + " | \n", + " | Attributes:\n", + " | matrix_cls (matrix.Matrix): a reference to the Matrix class to work with\n", + " | storage (callable): data type used to store the entries internally;\n", + " | defaults to tuple\n", + " | typing (callable): type casting applied to all entries upon creation;\n", + " | defaults to float\n", + " | zero_threshold (float): max. tolerance when comparing an entry to zero;\n", + " | defaults to 1e-12\n", + " | \n", + " | zero_threshold = 1e-12\n", + " \n", + " class Vector(builtins.object)\n", + " | Vector(data)\n", + " | \n", + " | A one-dimensional vector from linear algebra.\n", + " | \n", + " | All entries are converted to floats, or whatever is set in the typing attribute.\n", + " | \n", + " | Attributes:\n", + " | matrix_cls (matrix.Matrix): a reference to the Matrix class to work with\n", + " | storage (callable): data type used to store the entries internally;\n", + " | defaults to tuple\n", + " | typing (callable): type casting applied to all entries upon creation;\n", + " | defaults to float\n", + " | zero_threshold (float): max. tolerance when comparing an entry to zero;\n", + " | defaults to 1e-12\n", + " | \n", + " | Methods defined here:\n", + " | \n", + " | __abs__(self)\n", + " | The Euclidean norm of a vector.\n", + " | \n", + " | __add__(self, other)\n", + " | Handle `self + other` and `other + self`.\n", + " | \n", + " | This may be either vector addition or broadcasting addition.\n", + " | \n", + " | Example Usage:\n", + " | >>> Vector([1, 2, 3]) + Vector([2, 3, 4])\n", + " | Vector((3.000, 5.000, 7.000))\n", + " | \n", + " | >>> Vector([1, 2, 3]) + 4\n", + " | Vector((5.000, 6.000, 7.000))\n", + " | \n", + " | >>> 10 + Vector([1, 2, 3])\n", + " | Vector((11.000, 12.000, 13.000))\n", + " | \n", + " | __bool__(self)\n", + " | A Vector is truthy if its Euclidean norm is strictly positive.\n", + " | \n", + " | __eq__(self, other)\n", + " | Handle `self == other`.\n", + " | \n", + " | Compare two Vectors for equality.\n", + " | \n", + " | Example Usage:\n", + " | >>> Vector([1, 2, 3]) == Vector([1, 2, 3])\n", + " | True\n", + " | \n", + " | >>> Vector([1, 2, 3]) == Vector([4, 5, 6])\n", + " | False\n", + " | \n", + " | __float__(self)\n", + " | Cast a Vector as a scalar.\n", + " | \n", + " | Returns:\n", + " | scalar (float)\n", + " | \n", + " | Raises:\n", + " | RuntimeError: if the Vector has more than one entry\n", + " | \n", + " | __getitem__(self, index)\n", + " | Obtain an individual entry of a Vector.\n", + " | \n", + " | __init__(self, data)\n", + " | Create a new vector.\n", + " | \n", + " | Args:\n", + " | data (sequence): the vector's entries\n", + " | \n", + " | Raises:\n", + " | ValueError: if no entries are provided\n", + " | \n", + " | Example Usage:\n", + " | >>> Vector([1, 2, 3])\n", + " | Vector((1.000, 2.000, 3.000))\n", + " | \n", + " | >>> Vector(range(3))\n", + " | Vector((0.000, 1.000, 2.000))\n", + " | \n", + " | __iter__(self)\n", + " | Loop over a Vector's entries.\n", + " | \n", + " | __len__(self)\n", + " | Number of entries in a Vector.\n", + " | \n", + " | __mul__(self, other)\n", + " | Handle `self * other` and `other * self`.\n", + " | \n", + " | This may be either the dot product of two vectors or scalar multiplication.\n", + " | \n", + " | Example Usage:\n", + " | >>> Vector([1, 2, 3]) * Vector([2, 3, 4])\n", + " | 20.0\n", + " | \n", + " | >>> 2 * Vector([1, 2, 3])\n", + " | Vector((2.000, 4.000, 6.000))\n", + " | \n", + " | >>> Vector([1, 2, 3]) * 3\n", + " | Vector((3.000, 6.000, 9.000))\n", + " | \n", + " | __neg__(self)\n", + " | Handle `-self`.\n", + " | \n", + " | Negate all entries of a Vector.\n", + " | \n", + " | __pos__(self)\n", + " | Handle `+self`.\n", + " | \n", + " | This is simply an identity operator returning the Vector itself.\n", + " | \n", + " | __radd__(self, other)\n", + " | See docstring for .__add__().\n", + " | \n", + " | __repr__(self)\n", + " | Text representation of a Vector.\n", + " | \n", + " | __reversed__(self)\n", + " | Loop over a Vector's entries in reverse order.\n", + " | \n", + " | __rmul__(self, other)\n", + " | See docstring for .__mul__().\n", + " | \n", + " | __rsub__(self, other)\n", + " | See docstring for .__sub__().\n", + " | \n", + " | __str__(self)\n", + " | Human-readable text representation of a Vector.\n", + " | \n", + " | __sub__(self, other)\n", + " | Handle `self - other` and `other - self`.\n", + " | \n", + " | This may be either vector subtraction or broadcasting subtraction.\n", + " | \n", + " | Example Usage:\n", + " | >>> Vector([7, 8, 9]) - Vector([1, 2, 3])\n", + " | Vector((6.000, 6.000, 6.000))\n", + " | \n", + " | >>> Vector([1, 2, 3]) - 1\n", + " | Vector((0.000, 1.000, 2.000))\n", + " | \n", + " | >>> 10 - Vector([1, 2, 3])\n", + " | Vector((9.000, 8.000, 7.000))\n", + " | \n", + " | __truediv__(self, other)\n", + " | Handle `self / other`.\n", + " | \n", + " | Divide a Vector by a scalar.\n", + " | \n", + " | Example Usage:\n", + " | >>> Vector([9, 6, 12]) / 3\n", + " | Vector((3.000, 2.000, 4.000))\n", + " | \n", + " | as_matrix(self, *, column=True)\n", + " | Get a Matrix representation of a Vector.\n", + " | \n", + " | Args:\n", + " | column (bool): if the vector is interpreted as a\n", + " | column vector or a row vector; defaults to True\n", + " | \n", + " | Returns:\n", + " | matrix (matrix.Matrix)\n", + " | \n", + " | Example Usage:\n", + " | >>> v = Vector([1, 2, 3])\n", + " | >>> v.as_matrix()\n", + " | Matrix(((1.000,), (2.000,), (3.000,)))\n", + " | >>> v.as_matrix(column=False)\n", + " | Matrix(((1.000, 2.000, 3.000,)))\n", + " | \n", + " | ----------------------------------------------------------------------\n", + " | Data descriptors defined here:\n", + " | \n", + " | __dict__\n", + " | dictionary for instance variables (if defined)\n", + " | \n", + " | __weakref__\n", + " | list of weak references to the object (if defined)\n", + " | \n", + " | ----------------------------------------------------------------------\n", + " | Data and other attributes defined here:\n", + " | \n", + " | __hash__ = None\n", + " | \n", + " | matrix_cls = \n", + " | An m-by-n-dimensional matrix from linear algebra.\n", + " | \n", + " | All entries are converted to floats, or whatever is set in the typing attribute.\n", + " | \n", + " | Attributes:\n", + " | storage (callable): data type used to store the entries internally;\n", + " | defaults to tuple\n", + " | typing (callable): type casting applied to all entries upon creation;\n", + " | defaults to float\n", + " | vector_cls (vector.Vector): a reference to the Vector class to work with\n", + " | zero_threshold (float): max. tolerance when comparing an entry to zero;\n", + " | defaults to 1e-12\n", + " | \n", + " | storage = \n", + " | Built-in immutable sequence.\n", + " | \n", + " | If no argument is given, the constructor returns an empty tuple.\n", + " | If iterable is specified the tuple is initialized from iterable's items.\n", + " | \n", + " | If the argument is a tuple, the return value is the same object.\n", + " | \n", + " | typing = \n", + " | Convert a string or number to a floating point number, if possible.\n", + " | \n", + " | zero_threshold = 1e-12\n", + "\n", + "DATA\n", + " __all__ = ['Matrix', 'Vector']\n", + "\n", + "VERSION\n", + " 0.1.0\n", + "\n", + "AUTHOR\n", + " Alexander Hess\n", + "\n", + "FILE\n", + " /home/webartifex/repos/intro-to-python/11_classes/sample_package/__init__.py\n", + "\n", + "\n" + ] + } + ], + "source": [ + "help(pkg)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The meta information could also be accessed separately and individually." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'linear_algebra_tools'" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pkg.__name__" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'0.1.0'" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pkg.__version__ # follows the semantic versioning format" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We create `Vector` and `Matrix` instances in the usual way by calling the `Vector` and `Matrix` classes from the package's top level." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((1.000, 2.000, 3.000))" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pkg.Vector([1, 2, 3])" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pkg.Matrix([(1, 2, 3), (4, 5, 6), (7, 8, 9)])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A common practice by package authors is to put all the objects on the package's top level that they want the package users to work with directly. That is achieved via the `import` statements in the [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/__init__.py) file.\n", + "\n", + "However, users can always reach into a package and work with its internals.\n", + "\n", + "For example, the `Vector` and `Matrix` classes are also available via their **qualified name** (cf., [PEP 3155 ](https://www.python.org/dev/peps/pep-3155/)): First, we access the `vector` and `matrix` modules on `pkg`, and then the `Vector` and `Matrix` classes on the modules." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "sample_package.vector.Vector" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pkg.vector.Vector" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "sample_package.matrix.Matrix" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pkg.matrix.Matrix" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Also, let's import the [*utils.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/utils.py) module with the `norm()` function into the global scope. As this function is integrated into the `Vector.__abs__()` and `Matrix.__abs__()` methods, there is actually no need to work with it explicitly." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "from sample_package import utils" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on function norm in module sample_package.utils:\n", + "\n", + "norm(vec_or_mat)\n", + " Calculate the Frobenius or Euclidean norm of a matrix or vector.\n", + " \n", + " Find more infos here: https://en.wikipedia.org/wiki/Matrix_norm#Frobenius_norm\n", + " \n", + " Args:\n", + " vec_or_mat (Vector / Matrix): object whose entries are squared and summed up\n", + " \n", + " Returns:\n", + " norm (float)\n", + " \n", + " Example Usage:\n", + " As Vector and Matrix objects are by design non-empty sequences,\n", + " norm() may be called, for example, with `[3, 4]` as the argument:\n", + " >>> norm([3, 4])\n", + " 5.0\n", + "\n" + ] + } + ], + "source": [ + "help(utils.norm)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Many tutorials on the internet begin by importing \"everything\" from a package into the global scope with `from ... import *`.\n", + "\n", + "That is commonly considered a *bad* practice as it may overwrite already existing variables. However, if the package's [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/__init__.py) file defines an `__all__` attribute, a `list` with all the names to be \"exported,\" the **star import** is safe to be used, in particular, in *interactive* sessions like Jupyter notebooks. We emphasize that the star import should *not* be used *within* packages and modules as then it is not directly evident from a name where the corresponding object is defined.\n", + "\n", + "For more best practices regarding importing we refer to, among others, [Google's Python Style Guide](https://google.github.io/styleguide/pyguide.html#22-imports).\n", + "\n", + "The following `import` statement makes the `Vector` and `Matrix` classes available in the global scope." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "from sample_package import *" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "sample_package.vector.Vector" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Vector" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "sample_package.matrix.Matrix" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Matrix" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For further information on modules and packages, we refer to the [official tutorial ](https://docs.python.org/3/tutorial/modules.html)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## The final `Vector` and `Matrix` Classes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The final implementations of the `Vector` and `Matrix` classes are in the [*matrix.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/matrix.py) and [*vector.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/vector.py) files: They integrate all of the functionalities introduced in this chapter. In addition, the code is cleaned up and fully documented, including examples of common usages.\n", + "\n", + "We strongly suggest the eager student go over the files in the [*sample_package* ](https://github.com/webartifex/intro-to-python/tree/develop/11_classes/sample_package) in detail at some point to understand what well-written and (re-)usable code looks like." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "v = Vector([1, 2, 3])" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((1.000, 2.000, 3.000))" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "sample_package.vector.Vector" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(v)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "m = Matrix([(1, 2, 3), (4, 5, 6), (7, 8, 9)])" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "sample_package.matrix.Matrix" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(m)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Furthermore, the classes are designed for easier maintenence in the long-run.\n", + "\n", + "For example, the `Matrix/Vector.storage` and `Matrix/Vector.typing` class attributes replace the \"hard coded\" [tuple() ](https://docs.python.org/3/library/functions.html#func-tuple) and [float() ](https://docs.python.org/3/library/functions.html#float) built-ins in the `.__init__()` methods: As `self.storage` and `self.typing` are not defined on the *instances*, Python automatically looks them up on the *classes*." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tuple" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Vector.storage" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "float" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Vector.typing" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\u001b[0;31mSignature:\u001b[0m \u001b[0mVector\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\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[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\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\"\"\"Create a new vector.\u001b[0m\n", + "\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m Args:\u001b[0m\n", + "\u001b[0;34m data (sequence): the vector's entries\u001b[0m\n", + "\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m Raises:\u001b[0m\n", + "\u001b[0;34m ValueError: if no entries are provided\u001b[0m\n", + "\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m Example Usage:\u001b[0m\n", + "\u001b[0;34m >>> Vector([1, 2, 3])\u001b[0m\n", + "\u001b[0;34m Vector((1.000, 2.000, 3.000))\u001b[0m\n", + "\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m >>> Vector(range(3))\u001b[0m\n", + "\u001b[0;34m Vector((0.000, 1.000, 2.000))\u001b[0m\n", + "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstorage\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtyping\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\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[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"a vector must have at least one entry\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mFile:\u001b[0m ~/repos/intro-to-python/11_classes/sample_package/vector.py\n", + "\u001b[0;31mType:\u001b[0m function\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "Vector.__init__??" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Both `Matrix/Vector.storage` and `Matrix/Vector.typing` themselves reference the `DEFAULT_ENTRIES_STORAGE` and `DEFAULT_ENTRY_TYPE` constants in the [*utils.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/utils.py) module. This way, we could, for example, change only the constants and thereby also change how the `._entries` are stored internally in both classes. Also, this single **[single source of truth ](https://en.wikipedia.org/wiki/Single_source_of_truth)** ensures that both classes are consistent with each other at all times." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tuple" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "utils.DEFAULT_ENTRIES_STORAGE" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "float" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "utils.DEFAULT_ENTRY_TYPE" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For the same reasons, we also replace the \"hard coded\" references to the `Vector` and `Matrix` classes within the various methods.\n", + "\n", + "Every instance object has an automatically set `.__class__` attribute referencing its class." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "sample_package.matrix.Matrix" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m.__class__" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Of course, we could also use the [type() ](https://docs.python.org/3/library/functions.html#type) built-in instead." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "sample_package.matrix.Matrix" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(m)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So, for example, the `Matrix.transpose()` method makes a `self.__class__(...)` instead of a `Matrix(...)` call." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\u001b[0;31mSignature:\u001b[0m \u001b[0mMatrix\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtranspose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\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[0mtranspose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\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\"\"\"Switch the rows and columns of a Matrix.\u001b[0m\n", + "\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m Returns:\u001b[0m\n", + "\u001b[0;34m matrix (Matrix)\u001b[0m\n", + "\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m Example Usage:\u001b[0m\n", + "\u001b[0;34m >>> m = Matrix([(1, 2), (3, 4)])\u001b[0m\n", + "\u001b[0;34m >>> m\u001b[0m\n", + "\u001b[0;34m Matrix(((1.000, 2.000,), (3.000, 4.000,)))\u001b[0m\n", + "\u001b[0;34m >>> m.transpose()\u001b[0m\n", + "\u001b[0;34m Matrix(((1.000, 3.000,), (2.000, 4.000,)))\u001b[0m\n", + "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\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;31mFile:\u001b[0m ~/repos/intro-to-python/11_classes/sample_package/matrix.py\n", + "\u001b[0;31mType:\u001b[0m function\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "Matrix.transpose??" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Whenever we need a `str` representation of a class's name, we use the `.__name__` attribute on the class, ..." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Matrix'" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Matrix.__name__" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "... or access it via the `.__class__` attribute on an instance." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Matrix'" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m.__class__.__name__" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For example, the `.__repr__()` and `.__str__()` methods make use of that." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\u001b[0;31mSignature:\u001b[0m \u001b[0mMatrix\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__repr__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\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[0m__repr__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\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\"\"\"Text representation of a Matrix.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0mname\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__name__\u001b[0m\u001b[0;34m\u001b[0m\n", + "\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[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n", + "\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[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"{c:.3f}\"\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mc\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mr\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\",)\"\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mr\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;34mf\"{name}(({args}))\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mFile:\u001b[0m ~/repos/intro-to-python/11_classes/sample_package/matrix.py\n", + "\u001b[0;31mType:\u001b[0m function\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "Matrix.__repr__??" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In order to not have to \"hard code\" the name of *another* class (e.g., the `Vector.as_matrix()` method references the `Matrix` class), we apply the following \"hack:\" First, we store a reference to the other class as a class attribute (e.g., `Matrix.vector_cls` and `Vector.matrix_cls`), and then reference that attribute within the methods, just like `.storage` and `.typing` above." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "sample_package.vector.Vector" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Matrix.vector_cls" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "sample_package.matrix.Matrix" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Vector.matrix_cls" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As an example, the `Vector.as_matrix()` method makes a `self.matrix_cls(...)` instead of a `Matrix(...)` call." + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\u001b[0;31mSignature:\u001b[0m \u001b[0mVector\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_matrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcolumn\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\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[0mas_matrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcolumn\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\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\"\"\"Get a Matrix representation of a Vector.\u001b[0m\n", + "\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m Args:\u001b[0m\n", + "\u001b[0;34m column (bool): if the vector is interpreted as a\u001b[0m\n", + "\u001b[0;34m column vector or a row vector; defaults to True\u001b[0m\n", + "\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m Returns:\u001b[0m\n", + "\u001b[0;34m matrix (matrix.Matrix)\u001b[0m\n", + "\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m Example Usage:\u001b[0m\n", + "\u001b[0;34m >>> v = Vector([1, 2, 3])\u001b[0m\n", + "\u001b[0;34m >>> v.as_matrix()\u001b[0m\n", + "\u001b[0;34m Matrix(((1.000,), (2.000,), (3.000,)))\u001b[0m\n", + "\u001b[0;34m >>> v.as_matrix(column=False)\u001b[0m\n", + "\u001b[0;34m Matrix(((1.000, 2.000, 3.000,)))\u001b[0m\n", + "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcolumn\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmatrix_cls\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", + "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmatrix_cls\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\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;31mFile:\u001b[0m ~/repos/intro-to-python/11_classes/sample_package/vector.py\n", + "\u001b[0;31mType:\u001b[0m function\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "Vector.as_matrix??" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "For completeness sake, we mention that in the final `Vector` and `Matrix` classes, the `.__sub__()` and `.__rsub__()` methods use the negation operator implemented in `.__neg__()` and then dispatch to `.__add__()` instead of implementing the subtraction logic themselves." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## \"Real-life\" Experiment" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Let's do some math with bigger `Matrix` and `Vector` instances." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import random" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "random.seed(42)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We initialize `m` as a $100x50$ dimensional `Matrix` with random numbers in the range between `0` and `1_000`." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "m = Matrix((1_000 * random.random() for _ in range(50)) for _ in range(100))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We quickly lose track with all the numbers in the `Matrix`, which is why we implemented the `__str__()` method as a summary representation." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((639.427, 25.011, 275.029, 223.211, 736.471, 676.699, 892.180, 86.939, 421.922, 29.797, 218.638, 505.355, 26.536, 198.838, 649.884, 544.941, 220.441, 589.266, 809.430, 6.499, 805.819, 698.139, 340.251, 155.479, 957.213, 336.595, 92.746, 96.716, 847.494, 603.726, 807.128, 729.732, 536.228, 973.116, 378.534, 552.041, 829.405, 618.520, 861.707, 577.352, 704.572, 45.824, 227.898, 289.388, 79.792, 232.791, 101.001, 277.974, 635.684, 364.832,), (370.181, 209.507, 266.978, 936.655, 648.035, 609.131, 171.139, 729.127, 163.402, 379.455, 989.523, 640.000, 556.950, 684.614, 842.852, 776.000, 229.048, 32.100, 315.453, 267.741, 210.983, 942.910, 876.368, 314.678, 655.439, 395.632, 914.548, 458.852, 264.880, 246.628, 561.368, 262.742, 584.586, 897.823, 399.401, 219.321, 997.538, 509.526, 90.909, 47.116, 109.649, 627.446, 792.079, 422.160, 63.528, 381.619, 996.121, 529.114, 971.078, 860.780,), (11.481, 720.722, 681.710, 536.970, 266.825, 640.962, 111.552, 434.765, 453.724, 953.816, 875.853, 263.389, 500.586, 178.652, 912.628, 870.519, 298.445, 638.949, 608.970, 152.839, 762.511, 539.379, 778.626, 530.354, 0.572, 324.156, 19.477, 929.099, 878.722, 831.666, 307.514, 57.925, 878.010, 946.949, 85.653, 485.990, 69.213, 760.602, 765.834, 128.391, 475.282, 549.804, 265.057, 872.433, 423.138, 211.798, 539.296, 729.931, 201.151, 311.716,), (995.149, 649.878, 438.100, 517.576, 121.004, 224.697, 338.086, 588.309, 230.115, 220.217, 70.993, 631.103, 228.942, 905.420, 859.635, 70.857, 238.005, 668.978, 214.237, 132.312, 935.514, 571.043, 472.671, 784.619, 807.497, 190.410, 96.931, 431.051, 423.579, 467.025, 729.076, 673.365, 984.165, 98.418, 402.621, 339.303, 861.673, 248.656, 190.209, 448.614, 421.882, 278.545, 249.806, 923.266, 443.131, 861.349, 550.325, 50.588, 999.282, 836.028,), (968.996, 926.367, 848.696, 166.311, 485.641, 213.747, 401.040, 58.635, 378.973, 985.309, 265.203, 784.071, 455.008, 423.007, 957.318, 995.423, 555.768, 718.408, 154.797, 296.708, 968.709, 579.180, 542.195, 747.976, 57.165, 584.178, 502.850, 852.720, 157.433, 960.779, 80.111, 185.825, 595.035, 675.213, 235.204, 119.887, 890.287, 246.215, 594.519, 619.382, 419.225, 583.672, 522.783, 934.706, 204.259, 716.192, 238.686, 395.786, 671.690, 299.997,), (316.177, 751.864, 72.543, 458.286, 998.454, 996.096, 73.261, 213.154, 265.200, 933.259, 880.864, 879.270, 369.527, 157.747, 833.745, 703.540, 611.678, 987.233, 653.976, 7.823, 817.104, 299.379, 663.389, 938.930, 134.291, 115.429, 107.036, 553.224, 272.348, 604.830, 717.612, 203.597, 634.238, 263.984, 488.532, 905.336, 846.104, 92.298, 423.576, 276.680, 3.546, 771.119, 637.113, 261.955, 741.231, 551.680, 427.687, 9.670, 75.244, 883.106,), (903.929, 545.590, 834.595, 582.510, 148.094, 127.446, 308.258, 898.981, 796.122, 860.703, 898.925, 210.077, 249.530, 102.794, 780.116, 884.135, 406.377, 620.662, 154.553, 929.881, 864.606, 976.206, 810.772, 881.416, 24.786, 736.564, 332.185, 930.816, 802.235, 864.064, 810.749, 266.806, 787.375, 108.096, 872.167, 858.593, 222.434, 816.587, 460.303, 305.191, 795.345, 227.595, 23.664, 193.130, 328.262, 864.353, 966.889, 279.125, 641.482, 399.678,), (981.150, 536.216, 939.237, 115.342, 970.401, 178.568, 962.534, 265.466, 108.403, 434.564, 728.545, 313.677, 606.209, 511.423, 385.195, 576.588, 254.723, 708.785, 1.691, 925.575, 538.452, 719.430, 741.950, 670.629, 364.221, 69.974, 664.238, 330.200, 313.916, 848.015, 719.754, 300.322, 309.285, 408.393, 402.400, 295.655, 127.288, 420.446, 940.364, 677.318, 902.806, 615.515, 300.950, 547.937, 0.406, 286.914, 429.888, 579.985, 654.706, 464.988,), (442.160, 213.701, 473.186, 901.181, 796.025, 169.691, 84.796, 515.452, 632.941, 335.188, 818.423, 751.138, 672.796, 224.641, 199.130, 24.425, 244.843, 475.136, 849.738, 72.828, 414.441, 629.765, 194.435, 696.354, 494.377, 243.984, 656.058, 5.545, 750.964, 770.046, 106.587, 425.146, 175.887, 957.966, 517.958, 50.218, 249.198, 848.336, 456.462, 801.417, 667.578, 987.892, 595.452, 950.040, 891.426, 612.652, 719.274, 504.778, 830.569, 547.872,), (897.208, 743.655, 474.674, 259.192, 247.240, 637.661, 765.814, 521.300, 626.748, 274.597, 77.483, 285.728, 271.715, 319.710, 540.152, 138.374, 231.261, 693.950, 706.419, 64.229, 407.599, 542.611, 415.774, 206.834, 420.144, 904.838, 584.079, 695.523, 856.732, 765.595, 380.381, 5.896, 351.759, 753.475, 853.448, 953.430, 419.021, 747.516, 546.132, 603.253, 220.539, 219.422, 435.836, 29.025, 336.130, 679.142, 404.317, 165.045, 467.390, 127.628,), (622.257, 26.966, 394.020, 564.392, 27.102, 642.750, 135.699, 461.698, 50.285, 379.104, 211.660, 326.846, 761.230, 379.126, 752.010, 831.924, 252.272, 81.906, 19.383, 539.419, 999.908, 349.960, 650.144, 781.233, 651.755, 754.233, 949.612, 199.361, 20.380, 152.382, 126.221, 669.459, 563.970, 217.965, 699.465, 766.898, 167.789, 607.247, 747.926, 114.533, 819.301, 964.721, 108.099, 25.678, 311.957, 677.347, 958.173, 396.654, 715.015, 75.996,), (690.614, 627.242, 101.901, 772.481, 850.293, 600.412, 121.055, 983.844, 782.635, 347.204, 428.378, 370.571, 505.961, 341.231, 849.576, 822.331, 105.539, 960.788, 635.585, 828.707, 707.309, 435.487, 733.795, 965.474, 270.082, 808.199, 538.173, 483.498, 435.574, 731.026, 268.396, 851.713, 830.731, 86.663, 881.631, 243.863, 464.708, 610.332, 378.989, 28.700, 850.953, 181.840, 212.120, 797.832, 340.339, 880.320, 701.184, 276.269, 10.151, 948.063,), (85.613, 720.075, 488.578, 758.165, 690.609, 645.903, 490.821, 792.933, 93.053, 221.596, 691.787, 306.206, 581.556, 473.260, 530.922, 425.504, 745.935, 330.791, 702.855, 270.916, 251.404, 120.656, 192.584, 119.555, 535.864, 762.190, 185.150, 216.385, 484.199, 724.585, 976.607, 524.637, 282.999, 100.526, 194.118, 227.483, 179.442, 14.148, 534.135, 274.311, 974.295, 553.359, 697.417, 126.279, 868.461, 490.879, 872.720, 574.064, 469.397, 440.469,), (184.364, 51.377, 941.064, 477.729, 822.116, 400.707, 74.082, 629.446, 53.609, 149.198, 562.840, 303.836, 993.918, 118.452, 764.443, 606.318, 790.741, 225.687, 522.573, 450.514, 442.721, 860.167, 990.031, 305.380, 621.027, 609.631, 740.089, 947.590, 207.788, 211.025, 660.428, 157.057, 173.814, 75.065, 2.676, 450.504, 593.811, 291.259, 231.476, 706.956, 702.988, 454.031, 687.385, 923.911, 787.828, 625.058, 661.183, 933.668, 425.139, 544.562,), (647.635, 908.411, 826.631, 71.410, 165.923, 307.612, 748.958, 569.207, 288.611, 124.354, 688.678, 699.734, 942.676, 500.472, 493.795, 80.442, 39.861, 432.029, 322.322, 250.368, 91.327, 961.911, 835.959, 575.199, 950.786, 999.572, 672.282, 269.511, 40.232, 756.269, 470.501, 651.509, 916.073, 181.489, 585.330, 634.785, 491.726, 91.242, 347.961, 333.308, 670.134, 857.733, 329.804, 693.674, 288.218, 945.194, 813.566, 550.097, 454.826, 314.517,), (323.274, 970.185, 404.175, 514.596, 988.119, 657.660, 542.594, 413.248, 187.583, 361.779, 756.443, 625.409, 759.991, 203.558, 549.220, 927.673, 438.116, 698.250, 121.426, 973.147, 608.872, 239.297, 158.378, 550.839, 552.251, 93.209, 992.257, 912.930, 461.448, 117.466, 832.143, 498.376, 716.603, 508.872, 273.425, 834.724, 980.245, 243.731, 551.265, 383.586, 921.868, 508.241, 879.326, 864.027, 276.247, 790.006, 414.942, 934.248, 507.738, 820.549,), (282.839, 298.556, 586.938, 998.902, 489.640, 148.595, 538.581, 345.124, 551.917, 543.430, 455.345, 321.777, 188.652, 697.498, 571.798, 233.562, 775.544, 43.647, 744.705, 705.228, 811.409, 386.079, 663.689, 820.748, 980.818, 495.329, 37.020, 502.291, 590.180, 869.700, 874.190, 440.306, 525.951, 456.928, 722.444, 409.979, 654.781, 154.361, 469.491, 969.204, 338.561, 692.705, 649.837, 851.765, 852.341, 859.342, 380.009, 316.661, 718.717, 759.402,), (872.383, 35.899, 68.421, 631.161, 920.929, 997.426, 746.766, 433.971, 98.443, 633.748, 872.579, 443.679, 694.001, 903.424, 45.991, 796.143, 293.368, 374.841, 145.570, 531.166, 565.928, 792.519, 169.984, 78.968, 870.840, 619.710, 240.830, 912.829, 143.118, 461.150, 253.977, 255.327, 9.397, 804.633, 901.209, 677.611, 157.976, 441.730, 345.566, 587.572, 638.939, 424.309, 250.098, 845.304, 199.217, 384.693, 483.208, 237.206, 571.923, 574.812,), (992.692, 295.231, 977.944, 658.230, 274.480, 565.929, 685.799, 744.669, 49.044, 606.406, 496.727, 904.155, 286.194, 798.860, 607.065, 352.321, 636.618, 620.891, 677.764, 720.928, 659.182, 838.337, 628.248, 903.404, 646.341, 308.933, 440.823, 579.574, 732.360, 90.133, 295.110, 747.481, 175.640, 132.160, 539.408, 971.490, 530.852, 913.487, 830.473, 256.970, 824.690, 481.848, 806.488, 746.559, 338.715, 115.170, 962.893, 140.757, 966.500, 860.141,), (724.217, 979.942, 967.270, 804.588, 365.775, 790.682, 13.919, 536.572, 454.786, 672.828, 672.341, 584.560, 822.417, 940.292, 108.346, 233.822, 25.025, 884.235, 561.407, 915.256, 221.367, 63.217, 823.855, 909.388, 302.190, 408.296, 139.777, 946.262, 304.365, 492.625, 97.192, 887.259, 135.664, 453.644, 670.486, 743.140, 945.974, 419.127, 742.269, 154.523, 414.885, 99.022, 489.347, 408.116, 951.522, 32.716, 370.530, 443.383, 950.555, 855.450,), (99.355, 685.680, 544.466, 977.843, 358.674, 398.140, 189.809, 122.160, 848.033, 454.717, 662.769, 641.704, 597.146, 21.357, 786.795, 243.569, 125.924, 564.578, 68.610, 765.157, 207.157, 215.951, 869.695, 328.560, 147.554, 900.531, 2.836, 858.406, 144.688, 129.992, 250.654, 174.497, 661.058, 25.780, 14.860, 789.985, 237.932, 323.771, 174.246, 52.399, 741.718, 526.086, 745.665, 476.246, 778.017, 513.238, 109.054, 503.839, 945.416, 43.365,), (783.227, 866.981, 521.451, 458.043, 964.026, 60.825, 478.982, 401.617, 686.097, 490.269, 909.701, 73.491, 80.790, 608.297, 65.682, 275.016, 633.077, 548.356, 325.185, 994.628, 530.557, 453.715, 605.427, 99.178, 701.779, 852.793, 650.917, 768.963, 720.840, 215.023, 451.555, 228.494, 338.932, 453.499, 415.990, 95.086, 426.764, 665.108, 374.301, 152.639, 922.985, 67.133, 831.772, 93.230, 96.564, 738.796, 811.769, 556.371, 586.465, 561.586,), (329.646, 122.231, 353.598, 665.341, 750.284, 868.092, 721.061, 968.399, 600.410, 351.646, 577.919, 212.739, 656.736, 224.245, 108.218, 845.373, 367.561, 762.606, 574.100, 807.221, 845.155, 974.547, 818.427, 613.573, 642.699, 26.254, 929.084, 829.461, 267.448, 180.416, 702.699, 308.985, 339.825, 6.106, 869.863, 566.321, 400.784, 141.875, 633.172, 30.657, 746.112, 215.133, 419.832, 340.896, 370.053, 721.596, 776.836, 567.594, 84.957, 52.609,), (157.410, 617.838, 673.969, 272.103, 661.939, 485.662, 442.044, 273.167, 754.943, 113.818, 429.914, 283.246, 678.486, 486.633, 667.133, 45.417, 395.263, 599.325, 7.687, 301.419, 211.234, 137.235, 255.520, 328.122, 7.730, 747.014, 175.695, 380.207, 703.671, 500.262, 833.354, 806.200, 72.075, 861.764, 42.302, 18.742, 921.162, 862.110, 575.759, 573.400, 709.499, 417.694, 115.173, 20.857, 324.768, 801.322, 618.125, 832.026, 919.770, 88.130,), (844.484, 243.316, 588.871, 523.963, 395.767, 310.275, 339.513, 333.069, 168.133, 510.483, 114.027, 509.952, 905.923, 349.375, 727.379, 818.949, 815.037, 236.269, 146.444, 197.272, 602.399, 760.215, 655.509, 177.146, 772.848, 494.117, 754.446, 759.877, 448.905, 924.154, 564.492, 635.298, 624.522, 864.247, 627.217, 150.957, 68.286, 442.208, 302.820, 274.674, 56.172, 507.337, 310.408, 451.914, 56.890, 831.697, 76.731, 864.250, 855.293, 615.008,), (507.068, 462.712, 554.316, 791.818, 895.877, 449.734, 809.816, 651.837, 321.527, 475.629, 150.861, 61.874, 103.502, 899.127, 343.438, 714.316, 504.549, 172.559, 247.744, 437.758, 439.422, 522.748, 158.746, 372.852, 282.894, 408.769, 338.367, 597.886, 789.227, 647.305, 65.912, 94.506, 678.379, 284.147, 723.734, 656.564, 906.343, 873.280, 333.362, 582.740, 141.428, 349.821, 967.697, 698.480, 391.958, 595.041, 938.002, 309.582, 376.679, 791.662,), (813.185, 670.116, 828.959, 738.775, 685.414, 526.393, 646.025, 423.406, 361.828, 362.598, 180.263, 214.193, 947.668, 486.271, 226.543, 137.565, 77.165, 844.428, 101.141, 770.875, 835.120, 883.682, 37.747, 336.764, 766.308, 131.049, 376.720, 162.247, 831.345, 771.098, 809.044, 165.539, 437.673, 410.859, 676.363, 237.530, 444.199, 284.928, 748.537, 448.928, 534.011, 309.468, 808.624, 469.016, 835.113, 367.841, 947.130, 984.440, 461.680, 281.772,), (381.872, 527.460, 966.268, 816.891, 801.259, 138.399, 250.003, 641.179, 874.117, 554.541, 102.590, 845.892, 851.166, 285.063, 763.117, 272.791, 905.306, 147.349, 437.473, 946.413, 222.038, 451.128, 349.585, 26.670, 53.257, 502.007, 235.778, 994.525, 374.913, 28.188, 930.826, 839.176, 649.961, 791.381, 137.600, 286.879, 829.762, 696.072, 138.793, 705.536, 448.601, 5.251, 79.226, 255.924, 834.963, 548.804, 727.235, 527.772, 111.187, 288.102,), (301.151, 47.749, 419.826, 793.899, 457.114, 110.858, 905.147, 596.739, 16.435, 515.376, 241.938, 143.577, 429.239, 614.810, 240.564, 416.568, 664.371, 85.614, 974.654, 67.679, 526.059, 507.328, 988.331, 554.152, 390.454, 470.135, 635.671, 981.039, 253.650, 16.242, 788.520, 344.802, 732.941, 628.257, 771.501, 735.187, 332.519, 44.336, 546.014, 813.509, 175.089, 779.143, 464.623, 695.389, 631.736, 811.498, 63.101, 776.190, 457.680, 293.443,), (43.806, 199.470, 41.906, 933.371, 515.384, 989.123, 543.031, 253.314, 753.291, 191.103, 356.974, 780.842, 865.798, 331.925, 124.475, 368.019, 889.487, 743.308, 894.637, 386.645, 973.724, 496.203, 497.523, 924.310, 519.276, 801.148, 727.081, 78.927, 602.453, 822.341, 545.474, 321.211, 80.069, 660.919, 306.496, 602.622, 426.116, 689.765, 351.547, 42.355, 870.037, 352.559, 998.151, 274.555, 980.027, 947.904, 75.041, 637.513, 363.311, 801.096,), (679.411, 952.789, 142.779, 607.573, 781.312, 34.799, 67.233, 778.515, 366.328, 382.854, 567.245, 605.095, 679.062, 948.824, 372.013, 763.084, 573.922, 529.460, 398.034, 649.561, 249.612, 113.449, 735.675, 499.044, 386.987, 561.673, 261.777, 260.290, 446.273, 996.365, 285.577, 916.479, 491.200, 122.637, 852.826, 452.043, 898.679, 445.111, 87.791, 681.929, 845.521, 319.588, 347.425, 64.939, 542.171, 891.332, 851.362, 711.809, 927.324, 637.700,), (793.696, 508.756, 121.362, 200.980, 138.877, 790.373, 26.284, 554.021, 368.911, 803.662, 551.647, 611.948, 86.215, 309.291, 999.595, 718.870, 525.696, 769.165, 823.339, 73.751, 972.380, 642.339, 449.974, 680.109, 344.515, 877.960, 780.263, 639.794, 181.963, 966.265, 432.618, 910.712, 55.413, 124.161, 153.015, 164.657, 322.661, 709.332, 346.023, 940.904, 894.926, 845.934, 250.605, 635.057, 550.841, 125.170, 302.825, 533.478, 502.573, 168.636,), (941.607, 154.194, 658.733, 720.633, 605.139, 842.530, 563.618, 825.236, 28.373, 45.462, 641.454, 576.771, 651.130, 766.959, 416.587, 638.991, 498.038, 627.164, 289.672, 956.650, 482.945, 804.688, 684.991, 297.434, 72.973, 59.913, 439.605, 484.251, 204.023, 606.660, 312.582, 718.363, 734.200, 860.777, 975.374, 130.766, 370.540, 561.651, 319.116, 466.473, 267.472, 247.919, 96.812, 290.212, 384.150, 615.377, 248.270, 865.308, 159.700, 327.436,), (577.687, 312.715, 763.121, 498.266, 514.725, 498.760, 308.540, 23.176, 945.233, 505.444, 966.687, 215.144, 352.895, 50.540, 494.894, 882.339, 654.260, 470.587, 536.691, 847.172, 430.928, 882.456, 727.508, 763.857, 365.937, 400.582, 570.282, 194.655, 553.223, 73.532, 504.256, 764.404, 279.721, 989.091, 680.399, 118.811, 975.083, 393.904, 794.897, 339.085, 938.949, 754.965, 199.058, 509.123, 500.078, 45.303, 137.036, 333.041, 473.744, 456.989,), (606.261, 515.506, 327.966, 613.068, 162.502, 990.616, 739.319, 299.234, 336.373, 828.289, 532.340, 708.740, 299.791, 815.749, 368.358, 673.806, 979.898, 583.702, 796.755, 725.324, 688.044, 26.647, 474.590, 967.071, 782.904, 776.162, 577.634, 721.400, 583.523, 170.512, 629.025, 619.736, 841.167, 147.776, 680.727, 31.571, 948.205, 109.896, 18.937, 313.692, 151.431, 690.500, 410.377, 774.972, 920.521, 872.818, 735.837, 62.281, 138.082, 207.342,), (325.050, 662.227, 525.477, 313.753, 173.182, 912.124, 342.327, 354.287, 771.990, 720.925, 643.309, 693.313, 610.077, 192.264, 246.519, 558.087, 224.867, 972.911, 297.615, 289.004, 207.278, 704.988, 317.041, 348.803, 933.700, 795.405, 273.458, 121.874, 676.622, 379.694, 980.161, 818.377, 954.609, 804.616, 290.453, 287.630, 714.141, 346.364, 442.376, 256.444, 479.079, 202.068, 538.578, 933.024, 696.171, 137.273, 615.677, 586.830, 242.458, 669.834,), (531.041, 637.945, 52.491, 413.301, 717.358, 100.545, 770.766, 5.181, 550.353, 929.100, 406.907, 935.032, 878.400, 477.449, 199.456, 963.914, 321.168, 645.898, 907.937, 89.461, 574.133, 535.152, 723.118, 936.669, 913.230, 175.065, 882.245, 175.789, 919.635, 997.172, 396.995, 495.384, 936.609, 962.131, 926.040, 876.743, 9.267, 567.962, 107.301, 982.994, 284.562, 989.099, 543.300, 493.912, 938.561, 851.060, 468.021, 192.811, 112.647, 162.494,), (458.914, 257.265, 186.199, 736.618, 790.768, 567.781, 757.283, 175.495, 856.147, 897.043, 826.990, 515.281, 86.738, 669.256, 184.781, 140.612, 323.602, 248.047, 260.785, 235.521, 753.757, 954.035, 301.946, 722.883, 11.436, 653.683, 692.769, 62.124, 118.225, 306.806, 405.417, 502.520, 895.118, 703.557, 310.978, 117.416, 916.130, 295.038, 614.625, 219.129, 133.569, 153.186, 747.735, 605.739, 415.846, 549.235, 470.828, 537.518, 664.094, 218.412,), (247.465, 754.740, 873.135, 81.870, 446.748, 703.766, 78.103, 564.169, 61.758, 547.649, 505.487, 572.702, 149.852, 328.118, 520.342, 116.240, 205.401, 583.148, 90.942, 510.375, 808.692, 453.432, 513.248, 456.798, 57.737, 462.378, 806.915, 723.280, 395.949, 816.453, 745.804, 578.311, 45.290, 344.529, 63.760, 994.124, 934.583, 69.019, 933.776, 31.735, 408.867, 768.972, 765.828, 978.333, 645.881, 420.362, 992.857, 382.480, 869.620, 906.767,), (375.646, 682.730, 661.793, 539.300, 653.534, 347.770, 178.474, 537.258, 528.843, 727.858, 222.690, 3.473, 22.735, 298.363, 673.500, 544.445, 531.934, 823.360, 247.512, 346.160, 275.650, 937.410, 725.024, 112.845, 809.478, 419.241, 766.053, 883.757, 15.646, 206.082, 100.897, 33.576, 597.785, 703.286, 48.676, 740.541, 402.265, 234.339, 217.269, 863.730, 56.444, 503.896, 289.263, 815.786, 731.517, 318.904, 597.918, 672.532, 320.665, 301.764,), (143.260, 660.212, 221.043, 300.501, 60.958, 948.520, 879.714, 911.578, 625.993, 427.201, 495.621, 972.290, 941.586, 671.343, 785.805, 318.734, 416.325, 149.218, 376.460, 754.416, 473.519, 849.341, 300.736, 707.577, 805.776, 914.741, 562.386, 967.786, 557.287, 134.093, 242.859, 203.337, 646.706, 922.226, 847.133, 92.464, 724.585, 190.482, 268.462, 673.672, 602.922, 873.620, 188.163, 761.696, 724.305, 558.850, 479.394, 869.474, 332.964, 957.020,), (15.334, 937.160, 962.078, 117.316, 999.572, 478.921, 242.593, 604.402, 204.513, 915.126, 552.079, 775.514, 380.662, 533.650, 359.260, 261.562, 512.817, 497.277, 98.608, 981.318, 469.490, 839.731, 914.330, 370.705, 413.930, 562.525, 221.274, 145.923, 260.774, 934.758, 579.143, 417.578, 152.411, 329.865, 379.840, 833.363, 499.301, 654.608, 684.847, 257.327, 821.592, 966.508, 641.694, 490.596, 168.234, 794.976, 169.266, 720.314, 488.316, 916.899,), (542.137, 641.809, 58.732, 33.824, 846.697, 945.188, 668.216, 764.339, 412.392, 842.545, 231.433, 707.170, 9.141, 505.733, 373.201, 617.835, 666.755, 616.519, 483.204, 487.854, 6.612, 551.644, 11.851, 529.418, 274.741, 977.479, 17.143, 813.157, 674.033, 806.168, 909.773, 107.016, 96.314, 148.897, 191.932, 526.456, 815.214, 267.325, 396.896, 373.052, 406.027, 565.002, 990.233, 225.857, 684.042, 847.867, 653.736, 858.219, 759.586, 93.501,), (379.264, 552.701, 56.115, 9.450, 171.384, 499.858, 433.910, 784.376, 565.857, 857.960, 95.362, 528.159, 42.552, 211.417, 868.117, 887.554, 475.500, 46.562, 74.348, 925.585, 899.312, 563.510, 32.902, 928.766, 314.485, 961.469, 587.036, 752.254, 712.711, 398.296, 76.937, 162.450, 240.472, 834.651, 389.157, 896.526, 331.730, 755.609, 139.951, 988.478, 724.164, 500.793, 974.323, 53.696, 437.088, 838.675, 340.593, 769.006, 954.858, 396.703,), (773.555, 29.626, 273.327, 992.586, 490.603, 355.811, 941.143, 431.848, 679.695, 660.672, 85.694, 618.616, 798.055, 713.109, 82.038, 154.221, 711.677, 633.901, 739.655, 316.678, 106.551, 5.195, 308.267, 359.917, 269.766, 132.507, 187.392, 448.844, 554.740, 408.044, 26.262, 353.914, 93.064, 598.044, 324.430, 385.238, 291.847, 387.800, 84.700, 901.136, 905.208, 978.173, 571.960, 169.583, 380.732, 138.840, 301.131, 493.124, 63.267, 434.676,), (421.102, 484.231, 76.921, 251.700, 246.590, 625.034, 593.806, 195.548, 106.972, 304.658, 948.823, 332.217, 620.192, 804.076, 329.542, 334.736, 815.475, 859.508, 974.225, 136.124, 320.665, 947.279, 200.851, 314.183, 964.575, 968.725, 291.448, 694.958, 491.007, 575.879, 242.424, 376.055, 816.495, 392.935, 113.888, 563.851, 592.227, 545.629, 681.713, 550.099, 953.005, 461.622, 708.367, 438.455, 291.331, 692.835, 818.966, 795.657, 409.142, 499.303,), (633.336, 242.021, 658.663, 715.236, 789.077, 73.965, 990.701, 479.235, 400.805, 506.613, 920.392, 691.709, 543.645, 790.721, 359.529, 895.502, 536.906, 638.180, 84.982, 768.954, 657.602, 355.009, 647.000, 44.297, 983.608, 677.472, 399.618, 752.683, 965.717, 430.456, 10.548, 258.738, 510.676, 518.798, 580.518, 575.235, 445.779, 391.134, 772.342, 588.590, 500.466, 344.967, 24.563, 104.549, 415.975, 961.728, 116.069, 940.676, 141.675, 311.890,), (455.333, 206.867, 482.926, 476.163, 438.166, 696.763, 318.909, 300.264, 810.186, 115.085, 849.180, 647.970, 677.139, 164.354, 983.900, 243.913, 174.453, 160.136, 559.849, 958.463, 231.856, 405.047, 184.452, 640.479, 432.134, 29.192, 614.107, 197.324, 592.203, 388.836, 704.736, 205.784, 752.325, 808.730, 62.564, 101.752, 871.979, 186.960, 325.985, 457.550, 262.353, 862.637, 527.715, 639.109, 596.971, 611.308, 587.005, 347.925, 845.518, 617.363,), (813.738, 705.988, 297.445, 614.485, 84.752, 133.948, 117.862, 305.380, 183.045, 693.437, 510.825, 418.239, 137.867, 383.710, 185.754, 635.502, 693.433, 645.260, 999.900, 554.913, 489.642, 140.297, 314.580, 451.001, 53.611, 359.039, 9.583, 136.535, 815.216, 963.829, 505.438, 494.970, 684.697, 415.630, 839.892, 488.700, 82.671, 30.861, 761.057, 292.090, 274.853, 537.609, 168.209, 457.321, 742.518, 765.920, 549.726, 113.211, 114.207, 775.113,), (823.283, 366.862, 822.611, 41.611, 718.980, 546.353, 989.776, 102.416, 830.071, 751.345, 297.709, 999.313, 449.732, 348.577, 816.729, 439.070, 993.958, 775.632, 236.946, 810.703, 587.924, 350.631, 710.754, 632.771, 165.982, 139.235, 206.620, 206.943, 59.358, 350.815, 281.085, 538.769, 323.654, 704.054, 289.333, 267.343, 858.017, 985.488, 679.299, 95.225, 962.772, 785.691, 918.769, 992.486, 867.048, 126.888, 866.079, 249.677, 711.395, 828.482,), (761.474, 676.235, 489.459, 577.426, 268.717, 414.225, 451.992, 633.628, 880.125, 93.095, 515.613, 278.226, 936.336, 369.071, 950.254, 327.289, 2.473, 774.135, 732.724, 730.932, 458.449, 664.144, 358.223, 63.331, 534.424, 217.830, 429.643, 211.851, 268.537, 828.344, 337.755, 577.934, 566.142, 485.338, 343.740, 682.552, 48.409, 99.575, 783.890, 459.582, 124.237, 857.652, 441.286, 0.676, 958.032, 202.318, 688.592, 131.913, 649.997, 158.977,), (932.726, 274.019, 654.588, 250.389, 371.844, 903.800, 165.525, 396.342, 305.509, 699.441, 234.144, 655.485, 703.698, 1.086, 476.807, 132.700, 226.191, 679.983, 9.287, 695.597, 817.109, 988.155, 422.314, 132.175, 70.828, 383.070, 730.763, 102.427, 313.351, 880.989, 137.129, 773.460, 753.158, 133.146, 992.940, 142.853, 530.508, 8.475, 650.020, 440.099, 722.432, 628.080, 151.374, 411.710, 686.566, 859.963, 86.688, 100.465, 752.446, 589.574,), (384.032, 963.249, 314.504, 139.830, 276.968, 84.249, 553.397, 600.008, 607.593, 778.970, 690.476, 847.892, 658.405, 301.649, 517.749, 509.523, 747.844, 295.542, 54.569, 897.913, 954.672, 494.888, 112.744, 499.583, 593.930, 528.287, 977.697, 986.883, 933.924, 131.983, 860.814, 568.380, 365.412, 682.942, 762.726, 954.453, 770.367, 16.689, 67.533, 262.185, 39.827, 60.469, 789.290, 506.611, 628.571, 501.049, 415.432, 701.811, 82.428, 536.565,), (616.047, 277.468, 309.907, 511.305, 203.197, 808.060, 536.390, 390.732, 634.294, 834.526, 681.056, 66.115, 698.676, 729.958, 846.468, 57.906, 86.213, 434.484, 453.372, 608.832, 309.290, 741.694, 740.658, 119.443, 707.901, 701.500, 163.833, 953.016, 523.005, 782.987, 720.766, 166.963, 126.931, 781.124, 268.764, 886.415, 771.430, 29.356, 807.108, 271.981, 63.872, 712.323, 576.652, 77.070, 455.198, 360.124, 499.610, 566.886, 367.688, 255.116,), (102.905, 573.912, 722.778, 228.404, 508.825, 44.038, 862.928, 244.551, 471.765, 382.979, 150.085, 931.160, 857.485, 552.865, 913.948, 740.666, 419.369, 321.802, 416.257, 720.288, 271.258, 77.887, 372.808, 502.041, 901.941, 179.344, 804.338, 981.414, 954.079, 68.926, 465.094, 282.307, 844.847, 327.301, 553.091, 7.969, 200.671, 563.807, 303.909, 622.718, 463.927, 591.691, 493.361, 772.613, 195.422, 900.443, 760.482, 245.127, 6.378, 410.036,), (232.998, 346.424, 839.574, 877.199, 950.990, 1.462, 657.304, 849.006, 727.215, 103.949, 529.814, 238.168, 492.029, 59.895, 996.959, 711.652, 93.027, 921.274, 897.287, 519.759, 700.847, 372.492, 974.555, 84.902, 95.577, 133.514, 819.963, 74.830, 567.821, 434.983, 964.218, 236.772, 260.991, 315.009, 800.817, 700.726, 735.353, 318.058, 271.956, 74.687, 202.713, 779.937, 584.708, 155.411, 164.375, 466.055, 406.513, 535.925, 964.635, 207.636,), (308.308, 265.008, 119.914, 157.615, 686.055, 826.387, 696.878, 40.330, 835.925, 327.794, 91.213, 248.219, 355.743, 513.460, 677.183, 260.167, 990.677, 31.082, 404.392, 452.201, 748.078, 249.853, 462.043, 803.897, 139.793, 11.957, 830.372, 982.567, 130.715, 823.673, 372.240, 630.298, 644.685, 582.324, 258.821, 812.747, 21.800, 64.472, 902.496, 443.431, 128.792, 905.078, 829.356, 331.550, 42.696, 460.995, 167.989, 573.884, 821.685, 394.999,), (29.482, 683.213, 172.807, 214.716, 187.151, 279.855, 883.424, 34.649, 619.177, 245.785, 295.105, 411.992, 550.689, 60.979, 279.776, 137.236, 199.458, 884.653, 525.814, 630.754, 802.168, 794.846, 989.404, 781.916, 359.112, 544.518, 484.679, 912.677, 502.393, 388.381, 179.816, 318.878, 219.020, 895.765, 778.538, 58.591, 991.531, 529.432, 766.842, 999.606, 973.980, 100.134, 656.864, 266.527, 816.285, 917.259, 55.909, 996.392, 219.412, 846.505,), (797.391, 354.805, 839.222, 845.221, 176.100, 592.520, 806.210, 697.627, 913.980, 28.207, 700.561, 947.559, 563.606, 563.109, 188.234, 988.006, 881.626, 492.227, 309.053, 490.436, 90.257, 232.623, 218.809, 526.449, 0.683, 917.896, 201.464, 130.490, 716.938, 918.781, 844.284, 323.589, 21.913, 586.609, 917.224, 774.366, 846.481, 860.669, 960.559, 373.591, 941.923, 395.596, 101.032, 301.765, 136.452, 157.504, 948.694, 791.843, 960.662, 649.180,), (174.203, 968.743, 693.560, 928.845, 786.992, 223.237, 588.950, 175.352, 306.823, 688.498, 127.348, 728.831, 948.788, 948.698, 391.601, 994.283, 965.184, 32.383, 602.389, 921.038, 967.532, 220.895, 565.550, 936.688, 140.643, 745.343, 237.996, 982.380, 167.887, 885.320, 88.726, 708.966, 639.103, 886.654, 446.630, 265.212, 249.541, 67.795, 256.658, 108.035, 1.281, 385.937, 732.584, 969.102, 884.552, 493.082, 378.713, 546.024, 101.429, 479.489,), (864.051, 650.970, 687.160, 162.657, 73.709, 845.762, 294.909, 318.717, 951.662, 74.200, 170.105, 375.458, 731.985, 547.046, 898.124, 93.105, 594.031, 613.645, 482.737, 30.993, 942.442, 164.997, 889.753, 157.104, 101.258, 205.550, 189.984, 697.197, 722.038, 729.974, 265.265, 281.523, 237.548, 49.635, 571.770, 839.855, 153.590, 360.829, 427.606, 294.549, 662.020, 600.216, 199.675, 25.736, 170.832, 291.811, 81.936, 843.769, 308.353, 397.467,), (489.097, 661.040, 91.137, 544.126, 184.881, 885.493, 369.402, 445.775, 263.296, 465.058, 226.242, 268.562, 61.639, 752.235, 667.468, 85.707, 343.791, 541.449, 970.706, 589.726, 553.602, 840.797, 818.405, 418.632, 535.514, 866.894, 474.824, 881.654, 476.262, 78.955, 902.777, 714.306, 502.101, 900.455, 800.450, 677.535, 620.273, 120.282, 757.189, 172.879, 984.368, 972.165, 808.461, 126.186, 423.375, 988.280, 435.393, 997.269, 627.226, 834.108,), (257.886, 910.804, 914.174, 67.012, 388.099, 397.355, 326.127, 276.201, 458.157, 873.466, 786.551, 623.058, 522.562, 419.594, 414.442, 148.200, 587.934, 758.377, 939.650, 924.928, 562.684, 100.990, 286.093, 535.633, 343.869, 410.890, 383.043, 485.585, 608.962, 37.466, 275.414, 143.853, 608.655, 693.661, 38.783, 889.574, 331.494, 237.579, 745.750, 920.835, 897.031, 20.233, 817.386, 303.055, 280.347, 491.619, 696.183, 98.217, 868.900, 134.386,), (974.219, 443.111, 825.823, 269.420, 416.773, 645.555, 187.936, 211.391, 823.969, 740.945, 759.493, 867.189, 821.017, 515.270, 158.972, 311.123, 506.795, 135.650, 851.295, 879.333, 28.948, 192.763, 832.930, 836.977, 249.490, 456.449, 918.031, 704.634, 273.977, 823.506, 505.126, 635.410, 123.873, 30.563, 372.467, 594.044, 177.584, 870.481, 586.880, 349.760, 163.514, 894.492, 748.961, 688.851, 285.069, 386.566, 162.907, 572.258, 964.918, 857.111,), (647.380, 677.695, 269.084, 409.507, 20.052, 780.304, 767.573, 8.898, 911.515, 647.372, 601.142, 8.464, 252.390, 805.086, 305.460, 967.024, 642.740, 423.807, 376.475, 348.709, 251.999, 466.698, 677.196, 824.311, 397.153, 102.312, 511.514, 662.355, 843.284, 374.230, 647.342, 609.050, 298.476, 108.105, 63.837, 988.361, 640.595, 861.492, 261.147, 711.094, 892.374, 298.791, 149.929, 765.474, 899.687, 805.430, 802.364, 600.033, 660.530, 680.756,), (721.283, 655.401, 997.469, 259.426, 418.566, 388.275, 35.319, 708.069, 572.042, 189.915, 726.550, 222.363, 534.635, 784.897, 906.527, 671.868, 507.315, 845.419, 840.639, 876.495, 181.136, 97.603, 127.944, 258.652, 808.344, 762.918, 183.068, 679.712, 335.632, 89.300, 355.283, 744.210, 307.085, 788.090, 331.317, 260.559, 294.051, 851.214, 470.537, 866.393, 583.575, 944.301, 71.216, 889.426, 500.477, 867.498, 381.669, 298.356, 54.062, 854.240,), (137.365, 200.297, 409.192, 569.404, 906.621, 457.571, 316.383, 715.668, 778.941, 487.581, 631.033, 176.824, 634.499, 4.714, 273.528, 761.193, 168.606, 764.484, 489.577, 763.569, 88.041, 614.480, 633.488, 403.393, 965.328, 383.316, 37.725, 199.414, 373.101, 14.077, 322.213, 833.229, 190.577, 676.748, 626.687, 248.823, 693.525, 344.350, 128.931, 383.550, 588.671, 167.020, 823.844, 298.202, 290.828, 727.832, 596.370, 337.835, 887.974, 995.472,), (342.733, 901.384, 359.251, 188.426, 948.084, 918.205, 403.392, 228.418, 727.169, 131.206, 734.077, 589.693, 168.985, 366.591, 650.540, 37.363, 876.544, 255.789, 534.733, 48.587, 994.680, 661.970, 653.568, 19.742, 689.750, 416.776, 380.254, 547.282, 474.396, 153.127, 695.192, 630.311, 301.116, 661.665, 662.483, 269.977, 605.634, 137.164, 830.653, 104.914, 718.766, 117.735, 114.013, 106.264, 198.647, 199.749, 262.994, 523.146, 201.673, 703.437,), (295.351, 39.406, 496.355, 207.694, 933.124, 330.604, 2.738, 671.633, 906.880, 835.232, 669.022, 149.144, 90.083, 511.705, 723.564, 101.290, 255.892, 231.160, 988.666, 296.022, 464.277, 99.809, 174.702, 39.445, 290.567, 801.596, 312.707, 738.540, 94.998, 758.204, 45.889, 851.999, 663.355, 170.512, 357.527, 437.715, 621.807, 878.476, 92.951, 814.964, 182.869, 400.778, 962.310, 271.833, 385.715, 850.672, 799.899, 648.846, 796.910, 113.057,), (696.170, 58.646, 942.467, 159.395, 416.028, 590.750, 802.265, 678.393, 181.261, 379.751, 358.599, 28.816, 684.464, 838.536, 973.445, 130.655, 920.398, 112.937, 411.284, 45.972, 261.613, 314.238, 704.568, 677.929, 767.529, 576.649, 565.012, 977.896, 669.844, 338.301, 523.103, 700.580, 95.242, 661.713, 248.577, 345.749, 676.296, 384.877, 839.033, 558.344, 987.792, 54.566, 643.399, 156.927, 848.846, 851.871, 869.416, 74.865, 491.648, 240.892,), (970.145, 50.351, 222.707, 643.317, 403.277, 235.000, 459.095, 801.261, 448.099, 856.589, 447.068, 118.707, 497.373, 653.373, 102.643, 412.339, 557.131, 0.170, 90.896, 604.203, 619.066, 304.598, 508.338, 206.851, 671.474, 950.355, 363.342, 54.275, 222.918, 454.460, 560.151, 619.758, 473.134, 657.167, 715.914, 114.023, 759.320, 221.747, 341.357, 829.884, 964.434, 291.984, 521.985, 702.096, 45.778, 164.155, 140.449, 716.856, 721.559, 106.962,), (610.852, 187.543, 930.216, 392.896, 457.046, 781.420, 716.788, 107.766, 414.470, 926.627, 837.466, 588.811, 772.145, 450.505, 658.457, 956.190, 134.636, 498.829, 530.364, 48.572, 935.308, 838.816, 482.932, 508.333, 921.162, 177.202, 578.554, 730.495, 128.382, 387.406, 600.546, 882.696, 504.110, 384.663, 979.501, 915.910, 762.375, 273.596, 963.594, 970.492, 452.846, 133.372, 412.745, 700.014, 748.427, 298.913, 701.494, 860.708, 711.874, 935.512,), (632.583, 200.889, 624.168, 290.579, 345.294, 672.374, 981.335, 650.086, 958.326, 503.893, 694.312, 322.381, 115.405, 352.238, 480.370, 570.638, 667.157, 417.478, 747.869, 841.389, 285.928, 847.301, 808.305, 522.730, 25.272, 145.327, 670.254, 199.902, 750.185, 160.935, 286.616, 250.561, 839.382, 690.595, 295.141, 753.594, 32.306, 814.026, 102.480, 868.035, 739.492, 864.841, 742.413, 561.271, 237.595, 784.307, 798.822, 287.639, 664.494, 926.485,), (387.555, 956.702, 975.823, 312.654, 552.125, 12.965, 251.341, 620.562, 780.925, 870.118, 829.983, 911.486, 704.629, 647.632, 755.108, 546.615, 603.387, 776.147, 964.290, 294.178, 177.426, 682.659, 186.977, 173.795, 513.847, 377.198, 428.508, 556.599, 130.067, 583.986, 255.016, 330.563, 709.689, 154.806, 153.703, 322.630, 50.855, 932.382, 615.609, 661.890, 490.551, 572.677, 358.086, 784.031, 317.859, 219.962, 183.869, 68.176, 505.140, 415.931,), (537.036, 91.946, 221.198, 213.354, 331.884, 360.798, 218.445, 752.656, 530.499, 996.631, 823.688, 981.133, 8.804, 668.938, 445.667, 904.496, 613.701, 621.132, 958.897, 682.551, 320.937, 915.932, 944.988, 385.876, 540.237, 282.829, 911.336, 822.109, 374.958, 802.817, 445.557, 43.852, 898.282, 192.512, 513.895, 948.212, 167.395, 956.276, 537.975, 7.331, 65.472, 670.335, 773.582, 864.951, 424.209, 103.927, 537.754, 702.743, 976.224, 775.207,), (646.561, 939.751, 746.891, 153.755, 459.225, 331.715, 87.521, 54.268, 794.215, 558.084, 574.962, 227.646, 258.648, 388.033, 630.217, 433.025, 16.891, 673.299, 534.834, 641.423, 618.349, 755.961, 585.608, 700.882, 72.589, 928.048, 106.136, 786.892, 301.351, 86.505, 765.274, 437.676, 395.113, 660.723, 473.988, 533.468, 136.381, 390.993, 797.485, 545.724, 960.134, 144.076, 677.289, 914.169, 795.017, 729.325, 373.131, 949.465, 553.524, 555.070,), (123.235, 5.030, 596.655, 537.152, 946.979, 304.691, 748.297, 903.546, 343.852, 412.654, 645.932, 512.553, 160.954, 220.788, 834.748, 194.326, 181.358, 799.660, 852.251, 851.388, 932.370, 994.313, 461.265, 549.665, 290.653, 67.363, 98.202, 725.589, 487.190, 330.851, 128.215, 655.473, 100.045, 619.516, 900.119, 317.786, 450.646, 616.342, 305.675, 584.170, 565.552, 364.493, 316.333, 428.307, 4.832, 246.175, 221.526, 739.816, 436.124, 840.110,), (134.306, 732.973, 877.886, 462.848, 358.742, 305.472, 551.672, 175.770, 606.628, 841.793, 858.714, 140.002, 538.618, 263.235, 886.336, 76.462, 75.400, 18.630, 507.178, 31.194, 581.890, 405.134, 590.036, 906.304, 551.594, 543.469, 998.728, 472.104, 775.197, 365.819, 223.307, 772.145, 733.227, 290.999, 464.701, 510.412, 396.785, 502.167, 662.678, 847.982, 806.540, 614.036, 165.844, 514.137, 446.220, 179.145, 948.712, 659.413, 967.736, 735.920,), (483.503, 358.796, 218.819, 487.642, 62.861, 369.435, 42.808, 206.774, 907.726, 361.251, 469.674, 454.643, 46.437, 980.590, 324.071, 703.736, 521.373, 829.647, 834.872, 263.316, 544.350, 173.850, 653.675, 365.297, 653.337, 835.469, 516.663, 376.329, 907.242, 516.372, 352.898, 867.719, 486.862, 482.110, 604.211, 501.837, 138.968, 165.606, 77.088, 643.477, 211.517, 186.906, 362.223, 717.303, 118.330, 230.346, 811.173, 720.256, 481.517, 478.815,), (210.704, 161.246, 833.365, 22.446, 43.144, 573.487, 161.129, 629.540, 39.191, 572.269, 55.912, 258.260, 180.038, 958.238, 599.358, 563.605, 18.620, 719.547, 661.745, 283.455, 85.749, 449.203, 992.776, 867.302, 170.567, 830.364, 600.839, 794.616, 820.453, 181.884, 659.649, 264.521, 724.187, 342.671, 453.470, 590.596, 229.814, 385.462, 108.570, 202.349, 859.592, 504.360, 419.795, 149.561, 96.299, 475.869, 614.748, 39.037, 783.868, 503.329,), (117.516, 482.241, 130.329, 603.475, 827.522, 896.969, 774.870, 649.232, 522.044, 370.377, 43.438, 529.817, 229.486, 802.300, 789.556, 381.236, 589.048, 741.394, 761.539, 696.708, 97.717, 133.883, 477.141, 232.513, 859.053, 283.267, 876.766, 408.647, 189.021, 709.149, 789.422, 577.987, 117.829, 7.194, 654.546, 687.767, 315.902, 362.270, 154.779, 651.576, 253.633, 854.977, 423.562, 365.644, 275.561, 680.606, 754.972, 413.228, 783.788, 482.468,), (370.213, 554.911, 253.778, 306.653, 344.356, 705.495, 735.782, 854.999, 659.283, 747.814, 446.595, 699.311, 161.795, 214.458, 400.561, 338.438, 550.576, 697.057, 706.790, 160.738, 964.570, 5.291, 91.089, 145.328, 925.862, 435.377, 64.080, 221.770, 80.040, 37.755, 384.326, 984.448, 613.966, 521.006, 711.606, 597.473, 945.716, 820.229, 640.291, 438.972, 201.707, 656.884, 803.910, 286.007, 33.609, 599.515, 515.814, 231.671, 169.483, 36.061,), (239.257, 0.287, 157.445, 996.817, 779.673, 353.128, 395.143, 586.080, 517.743, 736.926, 68.617, 90.166, 284.494, 829.765, 915.009, 348.095, 963.288, 276.508, 606.363, 194.525, 929.714, 574.907, 261.108, 407.135, 106.465, 72.386, 294.252, 947.015, 802.942, 956.709, 876.113, 813.147, 574.894, 694.192, 966.055, 563.169, 769.967, 757.473, 961.239, 458.848, 460.720, 586.827, 27.351, 116.268, 67.554, 633.662, 994.198, 676.913, 229.930, 315.731,), (955.446, 516.504, 9.723, 832.177, 248.258, 93.009, 673.698, 821.075, 76.029, 931.396, 476.555, 353.538, 894.316, 269.076, 947.118, 683.107, 909.927, 498.999, 199.667, 735.429, 872.742, 206.779, 202.767, 249.695, 618.597, 155.996, 106.689, 920.188, 675.812, 663.424, 613.827, 764.244, 541.474, 42.263, 482.292, 620.667, 492.340, 985.361, 897.000, 868.769, 490.805, 984.395, 916.007, 280.267, 222.093, 576.665, 54.148, 799.270, 478.320, 540.768,), (502.475, 393.717, 686.339, 174.800, 976.507, 698.245, 460.071, 689.211, 11.820, 210.752, 581.008, 325.436, 612.778, 259.701, 548.563, 237.139, 471.345, 613.084, 365.924, 498.770, 210.532, 700.713, 372.310, 854.326, 279.630, 179.889, 131.139, 575.893, 228.567, 99.870, 269.949, 235.805, 432.219, 380.883, 145.844, 959.148, 149.548, 805.668, 176.607, 499.598, 995.562, 849.390, 517.007, 720.560, 785.333, 300.034, 562.138, 567.835, 398.402, 690.543,), (60.036, 813.818, 476.539, 629.862, 449.855, 334.657, 360.962, 560.350, 931.845, 257.715, 19.835, 121.237, 867.369, 963.117, 198.958, 575.894, 649.131, 174.539, 780.309, 355.109, 673.193, 487.494, 736.526, 889.632, 381.071, 286.524, 631.712, 144.848, 167.576, 807.717, 337.388, 631.273, 571.638, 848.901, 71.344, 161.999, 228.218, 316.878, 291.356, 267.472, 644.644, 271.248, 444.898, 862.806, 363.165, 586.920, 965.526, 413.989, 183.886, 23.101,), (727.772, 662.023, 940.437, 700.697, 80.065, 166.910, 103.009, 63.792, 878.691, 548.434, 26.365, 397.013, 764.996, 83.251, 262.452, 155.274, 654.833, 885.471, 309.371, 247.397, 284.175, 626.481, 131.277, 838.518, 28.571, 663.549, 860.369, 325.201, 476.155, 974.982, 540.584, 272.414, 444.845, 969.542, 697.623, 177.037, 597.584, 631.048, 635.851, 563.984, 523.460, 639.528, 309.661, 347.168, 539.668, 804.708, 441.860, 365.783, 259.807, 303.573,), (0.092, 815.380, 847.946, 343.885, 469.198, 8.317, 922.088, 946.958, 477.067, 9.133, 430.435, 292.923, 231.215, 7.218, 373.644, 411.736, 560.554, 394.752, 163.269, 737.118, 389.723, 378.358, 262.980, 422.239, 239.562, 764.538, 909.954, 807.575, 684.640, 284.677, 742.927, 808.809, 412.904, 853.482, 182.407, 289.595, 636.893, 617.729, 272.134, 622.754, 187.789, 19.395, 49.632, 534.971, 185.938, 102.273, 269.195, 715.196, 727.107, 232.810,), (150.684, 493.580, 341.913, 311.577, 799.462, 997.963, 463.684, 791.425, 330.249, 843.546, 951.644, 56.038, 775.653, 71.385, 470.147, 192.192, 841.539, 817.251, 828.252, 121.962, 768.146, 248.962, 771.225, 442.961, 737.619, 33.510, 460.835, 770.996, 521.139, 982.116, 472.826, 681.442, 312.109, 323.063, 629.164, 42.186, 937.421, 520.926, 253.313, 638.520, 198.724, 887.520, 863.658, 217.788, 112.928, 632.909, 324.513, 167.360, 276.100, 119.650,), (789.144, 8.930, 41.993, 783.475, 475.271, 596.212, 371.399, 89.480, 157.698, 91.402, 618.645, 930.849, 996.973, 625.132, 59.775, 644.575, 701.328, 791.466, 125.827, 232.675, 981.676, 788.562, 756.698, 805.525, 438.719, 192.965, 689.250, 358.167, 134.732, 898.663, 485.302, 437.500, 294.715, 697.038, 189.776, 186.627, 347.665, 732.402, 275.324, 831.350, 914.474, 554.520, 68.877, 155.025, 295.673, 263.349, 367.113, 0.720, 638.026, 379.044,), (185.534, 18.720, 856.829, 776.017, 238.787, 721.004, 658.289, 538.957, 387.491, 524.070, 497.549, 551.468, 613.039, 322.600, 640.904, 110.464, 523.201, 66.680, 835.193, 129.774, 873.493, 202.079, 485.370, 98.754, 571.012, 836.300, 657.921, 525.821, 701.314, 255.939, 366.354, 605.954, 70.874, 930.211, 208.738, 491.406, 889.767, 79.957, 801.020, 60.700, 558.020, 938.244, 415.953, 369.069, 708.434, 823.643, 265.816, 40.653, 70.388, 303.905,), (944.603, 960.587, 97.119, 725.172, 531.661, 189.460, 526.192, 248.763, 625.325, 178.898, 749.194, 415.185, 105.514, 638.624, 357.963, 458.721, 665.242, 882.824, 166.809, 180.397, 401.557, 337.513, 158.176, 998.434, 443.012, 352.324, 315.155, 991.463, 324.654, 371.720, 764.184, 430.588, 725.868, 608.413, 563.145, 213.928, 765.708, 926.147, 254.089, 961.652, 447.500, 397.091, 726.313, 982.956, 595.763, 517.609, 993.412, 301.842, 300.334, 221.887,), (855.720, 21.701, 821.940, 689.719, 275.957, 553.663, 556.323, 925.922, 154.715, 37.736, 355.656, 138.408, 367.083, 582.156, 232.994, 811.074, 91.905, 399.798, 917.882, 734.266, 723.581, 792.904, 172.902, 825.705, 689.595, 576.232, 907.656, 595.231, 300.393, 730.789, 576.284, 78.477, 55.923, 770.902, 347.930, 817.142, 416.522, 867.831, 869.787, 226.718, 652.779, 602.302, 11.434, 777.419, 382.487, 304.783, 41.182, 539.930, 149.580, 502.402,), (220.797, 50.520, 731.579, 392.883, 445.616, 595.132, 504.729, 222.086, 289.783, 394.322, 132.189, 82.545, 571.441, 49.311, 399.191, 85.079, 501.823, 773.825, 130.375, 134.871, 559.296, 487.861, 652.248, 196.099, 615.997, 735.668, 246.246, 71.644, 776.772, 323.412, 924.138, 89.595, 671.748, 423.541, 348.308, 320.738, 593.877, 24.207, 304.819, 987.652, 616.221, 990.159, 442.210, 145.818, 44.878, 818.172, 199.685, 373.821, 757.734, 852.764,), (112.372, 54.538, 948.941, 926.730, 868.752, 820.134, 13.733, 693.795, 111.278, 450.062, 22.748, 209.010, 538.005, 203.801, 523.266, 258.658, 483.026, 729.924, 141.347, 698.755, 18.389, 583.005, 663.528, 43.482, 170.320, 284.014, 789.188, 617.965, 53.085, 654.758, 8.334, 388.645, 271.310, 852.083, 660.100, 864.282, 19.078, 867.410, 649.411, 231.169, 380.701, 976.612, 99.604, 315.456, 866.773, 531.561, 186.417, 500.651, 457.986, 926.350,), (21.488, 247.392, 529.415, 333.570, 393.400, 157.005, 346.780, 351.913, 625.231, 236.205, 978.244, 501.251, 811.893, 625.792, 878.677, 890.402, 814.859, 29.466, 554.939, 280.194, 151.708, 897.175, 656.932, 87.795, 382.207, 960.606, 612.448, 625.525, 227.428, 240.361, 152.749, 970.537, 909.735, 329.285, 542.234, 206.757, 138.577, 541.354, 800.105, 862.588, 308.998, 705.136, 523.805, 135.252, 995.686, 975.777, 145.152, 933.030, 917.112, 317.496,), (557.340, 948.602, 118.387, 317.598, 879.638, 727.080, 765.435, 880.132, 414.040, 411.252, 443.004, 933.769, 894.130, 933.250, 273.796, 779.108, 106.771, 184.746, 762.447, 611.982, 266.863, 567.043, 230.911, 232.183, 687.378, 359.261, 688.141, 476.602, 502.325, 604.712, 712.019, 373.959, 852.129, 491.446, 137.513, 193.260, 32.230, 764.539, 15.014, 269.859, 413.044, 742.367, 988.243, 757.777, 66.136, 927.103, 985.628, 867.129, 489.943, 324.896,), (457.545, 246.784, 404.867, 41.826, 735.332, 380.374, 312.897, 611.505, 742.468, 593.806, 525.231, 875.783, 786.920, 520.649, 451.610, 827.010, 42.416, 995.836, 518.703, 395.611, 735.170, 557.701, 516.127, 630.649, 49.295, 291.179, 398.040, 304.554, 827.621, 461.347, 422.462, 613.134, 54.547, 516.970, 142.240, 829.894, 451.698, 722.647, 113.172, 778.731, 937.842, 696.114, 135.307, 413.559, 450.886, 178.879, 590.315, 712.634, 201.932, 454.683,), (250.082, 691.717, 907.162, 796.486, 718.375, 123.544, 114.165, 449.398, 362.943, 523.817, 384.087, 791.067, 511.666, 949.754, 378.679, 380.607, 768.260, 912.253, 565.492, 659.530, 150.090, 868.819, 178.867, 712.047, 419.582, 309.518, 768.630, 446.258, 626.453, 117.554, 131.571, 202.852, 622.560, 253.111, 458.643, 855.411, 543.501, 5.751, 882.703, 237.878, 588.862, 475.712, 410.151, 79.405, 600.102, 244.713, 547.342, 619.766, 557.549, 825.253,), (48.919, 148.256, 653.277, 36.721, 853.829, 666.997, 838.014, 298.442, 920.385, 49.031, 416.957, 178.248, 672.090, 611.090, 691.514, 594.969, 787.308, 177.419, 455.338, 578.966, 931.480, 93.126, 299.294, 368.569, 378.939, 67.667, 427.324, 550.471, 292.833, 134.546, 694.619, 274.419, 527.053, 524.646, 695.829, 612.054, 109.302, 729.729, 628.979, 987.189, 483.663, 688.654, 933.892, 986.278, 287.187, 608.845, 316.489, 525.391, 995.024, 353.853,)))" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Matrix((639.4, ...), ..., (..., 353.9))[100x50]\n" + ] + } + ], + "source": [ + "print(m)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Similarily, `v` is now a `Vector` with $50$ entries." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "v = Vector(1_000 * random.random() for _ in range(50))" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((129.713, 562.634, 519.706, 631.858, 492.504, 179.907, 609.406, 708.587, 979.258, 1.581, 23.987, 625.461, 117.926, 848.070, 799.564, 998.987, 414.041, 333.792, 560.416, 637.504, 11.297, 201.187, 281.627, 790.196, 307.773, 506.690, 323.924, 6.131, 685.836, 341.362, 724.397, 615.993, 29.117, 175.629, 330.515, 337.937, 672.473, 916.163, 797.254, 645.652, 481.496, 627.200, 892.058, 536.968, 335.110, 783.989, 413.953, 742.585, 835.106, 299.344))" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Vector(129.7, ..., 299.3)[50]\n" + ] + } + ], + "source": [ + "print(v)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The arithmetic works as before." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "w = m * v" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Vector(11378937.3, ..., 13593029.3)[100]\n" + ] + } + ], + "source": [ + "print(w)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We can multiply `m` with its transpose or the other way round." + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "n = m * m.transpose()" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Matrix((14370711.3, ...), ..., (..., 16545418.2))[100x100]\n" + ] + } + ], + "source": [ + "print(n)" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "o = m.transpose() * m" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Matrix((32618511.5, ...), ..., (..., 32339164.8))[50x50]\n" + ] + } + ], + "source": [ + "print(o)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Comparison with [numpy](https://www.numpy.org/)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We started out in this chapter by realizing that Python provides us no good data type to model a vector $\\vec{x}$ or a matrix $\\bf{A}$. Then, we built up two custom data types, `Vector` and `Matrix`, that wrap a simple `tuple` object for $\\vec{x}$ and a `tuple` of `tuple`s for $\\bf{A}$ so that we can interact with their `._entries` in a \"natural\" way, which is similar to how we write linear algebra tasks by hand. By doing this, we extend Python with our own little \"dialect\" or **[domain-specific language ](https://en.wikipedia.org/wiki/Domain-specific_language)** (DSL).\n", + "\n", + "If we feel like sharing our linear algebra library with the world, we could easily do so on either [GitHub ](https://github.com) or [PyPI](https://pypi.org). However, for the domain of linear algebra this would be rather pointless as there is already a widely adopted library with [numpy](https://www.numpy.org/) that not only has a lot more features than ours but also is implemented in C, which makes it a lot faster with big data.\n", + "\n", + "Let's model the example in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb#Example:-Vectors-&-Matrices) with both [numpy](https://www.numpy.org/) and our own DSL and compare them." + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "x = (1, 2, 3)\n", + "A = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "can't multiply sequence by non-int of type 'tuple'", + "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[0mA\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: can't multiply sequence by non-int of type 'tuple'" + ] + } + ], + "source": [ + "A * x" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The creation of vectors and matrices is similar to our DSL. However, numpy uses the more general concept of an **n-dimensional array** (i.e., the `ndarray` type) where a vector is only a special case of a matrix and a matrix is yet another special case of an even higher dimensional structure." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import numpy as np" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "x_arr = np.array(x)\n", + "A_arr = np.array(A)" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [], + "source": [ + "x_vec = Vector(x)\n", + "A_mat = Matrix(A)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The text representations are very similar. However, [numpy](https://www.numpy.org/)'s `ndarray`s keep the entries as `int`s while our `Vector` and `Matrix` objects contain `float`s." + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([1, 2, 3])" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x_arr" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((1.000, 2.000, 3.000))" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x_vec" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1, 2, 3],\n", + " [4, 5, 6],\n", + " [7, 8, 9]])" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "A_arr" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "A_mat" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "[numpy](https://www.numpy.org/)'s `ndarray`s come with a `.shape` instance attribute that returns a `tuple` with the dimensions ..." + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(3,)" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x_arr.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(3, 3)" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "A_arr.shape" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "... while `Matrix` objects come with `.n_rows` and `.n_cols` properties." + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(3, 3)" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "A_mat.n_rows, A_mat.n_cols" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The built-in [len() ](https://docs.python.org/3/library/functions.html#len) function does not return the number of entries in an `ndarray` but the number of the rows instead. This is equivalent to the first element in the `.shape` attribute." + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(x_arr)" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(x_vec)" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(A_arr)" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "9" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(A_mat)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "The `.transpose()` method also exists for `ndarray`s." + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1, 4, 7],\n", + " [2, 5, 8],\n", + " [3, 6, 9]])" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "A_arr.transpose()" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Matrix(((1.000, 4.000, 7.000,), (2.000, 5.000, 8.000,), (3.000, 6.000, 9.000,)))" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "A_mat.transpose()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "To perform matrix-matrix, matrix-vector, or vector-matrix multiplication in [numpy](https://www.numpy.org/), we use the `.dot()` method. If we use the `*` operator with `ndarray`s, an *entry-wise* multiplication is performed." + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([14, 32, 50])" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "A_arr.dot(x_arr)" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 1, 4, 9],\n", + " [ 4, 10, 18],\n", + " [ 7, 16, 27]])" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "A_arr * x_arr" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((14.000, 32.000, 50.000))" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "A_mat * x_vec" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Scalar multiplication, however, works as expected." + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([10, 20, 30])" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "10 * x_arr" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Vector((10.000, 20.000, 30.000))" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "10 * x_vec" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "Because we implemented our classes to support the sequence protocol, [numpy](https://www.numpy.org/)'s *one*-dimensional `ndarray`s are actually able to work with them: The `*` operator is applied on a per-entry basis." + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([2., 4., 6.])" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x_arr + x_vec" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([1., 4., 9.])" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "x_arr * x_vec" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "ename": "ValueError", + "evalue": "operands could not be broadcast together with shapes (3,3) (9,) ", + "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[0mA_arr\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mA_mat\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: operands could not be broadcast together with shapes (3,3) (9,) " + ] + } + ], + "source": [ + "A_arr + A_mat" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "We conclude that it is rather easy to extend Python in a way that makes the resulting application code read like core Python again. As there are many well established third-party packages out there, it is unlikely that we have to implement a fundamental library ourselves. Yet, we can apply the concepts introduced in this chapter to organize the code in the applications we write." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/11_classes/sample_package/__init__.py b/11_classes/sample_package/__init__.py index 1e8d12c..8e87577 100644 --- a/11_classes/sample_package/__init__.py +++ b/11_classes/sample_package/__init__.py @@ -28,3 +28,7 @@ from sample_package.vector import Vector __name__ = "linear_algebra_tools" __version__ = "0.1.0" # see https://semver.org/ for how the format works __author__ = "Alexander Hess" + +# Define what is imported with the "star import" +# (i.e., with `from sample_package import *`). +__all__ = ["Matrix", "Vector"] diff --git a/11_classes/sample_package/matrix.py b/11_classes/sample_package/matrix.py index 65f5fc1..3c82d26 100644 --- a/11_classes/sample_package/matrix.py +++ b/11_classes/sample_package/matrix.py @@ -17,12 +17,14 @@ class Matrix: defaults to tuple typing (callable): type casting applied to all entries upon creation; defaults to float + vector_cls (vector.Vector): a reference to the Vector class to work with zero_threshold (float): max. tolerance when comparing an entry to zero; defaults to 1e-12 """ storage = utils.DEFAULT_ENTRIES_STORAGE typing = utils.DEFAULT_ENTRY_TYPE + # the `vector_cls` attribute is set at the bottom of this file zero_threshold = utils.ZERO_THRESHOLD def __init__(self, data): @@ -156,7 +158,7 @@ class Matrix: Returns: rows (generator): produces a Matrix's rows as Vectors """ - return (Vector(r) for r in self._entries) + return (self.vector_cls(r) for r in self._entries) def cols(self): """Loop over a Matrix's columns. @@ -165,7 +167,7 @@ class Matrix: columns (generator): produces a Matrix's columns as Vectors """ return ( - Vector(self._entries[r][c] for r in range(self.n_rows)) + self.vector_cls(self._entries[r][c] for r in range(self.n_rows)) for c in range(self.n_cols) ) @@ -232,7 +234,7 @@ class Matrix: def __radd__(self, other): """See docstring for .__add__().""" - if isinstance(other, Vector): + if isinstance(other, self.vector_cls): raise TypeError("vectors and matrices cannot be added") # As both matrix and broadcasting addition are commutative, # we dispatch to .__add__(). @@ -260,7 +262,7 @@ class Matrix: def __rsub__(self, other): """See docstring for .__sub__().""" - if isinstance(other, Vector): + if isinstance(other, self.vector_cls): raise TypeError("vectors and matrices cannot be subtracted") # Same comments as in .__sub__() apply # with the roles of self and other swapped. @@ -294,6 +296,8 @@ class Matrix: Matrix-vector and vector-matrix multiplication are not commutative. + >>> from sample_package import Vector + >>> Matrix([(1, 2), (3, 4)]) * Vector([5, 6]) Vector((17.000, 39.000)) @@ -304,7 +308,7 @@ class Matrix: if isinstance(other, numbers.Number): return self.__class__((x * other for x in r) for r in self._entries) # Matrix-vector multiplication: Vector is a column Vector - elif isinstance(other, Vector): + elif isinstance(other, self.vector_cls): # First, cast the other Vector as a Matrix, then do matrix-matrix # multiplication, and lastly return the result as a Vector again. return self._matrix_multiply(other.as_matrix()).as_vector() @@ -319,7 +323,7 @@ class Matrix: if isinstance(other, numbers.Number): return self * other # Vector-matrix multiplication: Vector is a row Vector - elif isinstance(other, Vector): + elif isinstance(other, self.vector_cls): return other.as_matrix(column=False)._matrix_multiply(self).as_vector() return NotImplemented @@ -375,7 +379,7 @@ class Matrix: def __abs__(self): """The Frobenius norm of a Matrix.""" - return utils.norm(self) # use the norm() function shared with the Vector class + return utils.norm(self) # uses the norm() function shared vector.Vector def __bool__(self): """A Matrix is truthy if its Frobenius norm is strictly positive.""" @@ -398,7 +402,7 @@ class Matrix: """Get a Vector representation of a Matrix. Returns: - vector (Vector) + vector (vector.Vector) Raises: RuntimeError: if one of the two dimensions, .n_rows or .n_cols, is not 1 @@ -409,7 +413,7 @@ class Matrix: """ 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) + return self.vector_cls(x for x in self) def transpose(self): """Switch the rows and columns of a Matrix. @@ -432,4 +436,8 @@ class Matrix: # We call that a circular import. Whereas Python handles "circular" references # (e.g., both the Matrix and Vector classes have methods that reference the # respective other class), that is forbidden for imports. -from sample_package.vector import Vector +from sample_package import vector + +# This attribute cannot be set in the class definition +# as the vector module is only imported down here. +Matrix.vector_cls = vector.Vector diff --git a/11_classes/sample_package/vector.py b/11_classes/sample_package/vector.py index 874d29a..21e3729 100644 --- a/11_classes/sample_package/vector.py +++ b/11_classes/sample_package/vector.py @@ -7,8 +7,8 @@ import numbers # If third-party libraries are needed, they are # put into a group on their own in between. # Within a group, imports are sorted lexicographically. +from sample_package import matrix from sample_package import utils -from sample_package.matrix import Matrix class Vector: @@ -17,6 +17,7 @@ class Vector: All entries are converted to floats, or whatever is set in the typing attribute. Attributes: + matrix_cls (matrix.Matrix): a reference to the Matrix class to work with storage (callable): data type used to store the entries internally; defaults to tuple typing (callable): type casting applied to all entries upon creation; @@ -25,6 +26,7 @@ class Vector: defaults to 1e-12 """ + matrix_cls = matrix.Matrix storage = utils.DEFAULT_ENTRIES_STORAGE typing = utils.DEFAULT_ENTRY_TYPE zero_threshold = utils.ZERO_THRESHOLD @@ -219,7 +221,7 @@ class Vector: def __abs__(self): """The Euclidean norm of a vector.""" - return utils.norm(self) # use the norm() function shared with the Matrix class + return utils.norm(self) # uses the norm() function shared matrix.Matrix def __bool__(self): """A Vector is truthy if its Euclidean norm is strictly positive.""" @@ -246,7 +248,7 @@ class Vector: column vector or a row vector; defaults to True Returns: - matrix (Matrix) + matrix (matrix.Matrix) Example Usage: >>> v = Vector([1, 2, 3]) @@ -256,5 +258,5 @@ class Vector: Matrix(((1.000, 2.000, 3.000,))) """ if column: - return Matrix([x] for x in self) - return Matrix([(x for x in self)]) + return self.matrix_cls([x] for x in self) + return self.matrix_cls([(x for x in self)]) diff --git a/CONTENTS.md b/CONTENTS.md index fed0b64..be3d163 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -284,3 +284,8 @@ If this is not possible, [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/03_content.ipynb) (Operator Overloading: Arithmetic & Relational Operators; Number Emulation) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/04_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/04_content.ipynb) + (Writing one's own Packages; + The final `Vector` & `Matrix` Classes; + Comparison with `numpy`) From ca9df9e440c7521b0dc9975ed2e5ee7260efe73d Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 28 Oct 2020 16:57:48 +0100 Subject: [PATCH 120/142] Show only the necessary decimals --- 11_classes/00_content.ipynb | 32 ++++----- 11_classes/02_content.ipynb | 34 ++++----- 11_classes/03_content.ipynb | 60 ++++++++-------- 11_classes/04_content.ipynb | 108 ++++++++++++++-------------- 11_classes/sample_package/matrix.py | 38 +++++----- 11_classes/sample_package/vector.py | 30 ++++---- 6 files changed, 152 insertions(+), 150 deletions(-) diff --git a/11_classes/00_content.ipynb b/11_classes/00_content.ipynb index 87a9bae..2850088 100644 --- a/11_classes/00_content.ipynb +++ b/11_classes/00_content.ipynb @@ -1083,13 +1083,13 @@ " # ...\n", "\n", " def __repr__(self):\n", - " args = \", \".join(f\"{x:.3f}\" for x in self._entries)\n", + " args = \", \".join(repr(x) for x in self._entries)\n", " return f\"Vector(({args}))\"\n", "\n", " def __str__(self):\n", " first, last = self._entries[0], self._entries[-1]\n", " n_entries = len(self._entries)\n", - " return f\"Vector({first:.1f}, ..., {last:.1f})[{n_entries:d}]\"" + " return f\"Vector({first!r}, ..., {last!r})[{n_entries:d}]\"" ] }, { @@ -1132,7 +1132,7 @@ { "data": { "text/plain": [ - "Vector((1.000, 2.000, 3.000))" + "Vector((1.0, 2.0, 3.0))" ] }, "execution_count": 31, @@ -1167,7 +1167,7 @@ { "data": { "text/plain": [ - "Vector((1.000, 2.000, 3.000))" + "Vector((1.0, 2.0, 3.0))" ] }, "execution_count": 32, @@ -1176,7 +1176,7 @@ } ], "source": [ - "Vector((1.000, 2.000, 3.000))" + "Vector((1.0, 2.0, 3.0))" ] }, { @@ -1202,7 +1202,7 @@ { "data": { "text/plain": [ - "'Vector((1.000, 2.000, 3.000))'" + "'Vector((1.0, 2.0, 3.0))'" ] }, "execution_count": 33, @@ -1334,13 +1334,13 @@ " raise ValueError(\"a matrix must have at least one entry\")\n", "\n", " def __repr__(self):\n", - " args = \", \".join(\"(\" + \", \".join(f\"{c:.3f}\" for c in r) + \",)\" for r in self._entries)\n", + " args = \", \".join(\"(\" + \", \".join(repr(c) for c in r) + \",)\" for r in self._entries)\n", " return f\"Matrix(({args}))\"\n", "\n", " def __str__(self):\n", " first, last = self._entries[0][0], self._entries[-1][-1]\n", " m, n = len(self._entries), len(self._entries[0])\n", - " return f\"Matrix(({first:.1f}, ...), ..., (..., {last:.1f}))[{m:d}x{n:d}]\"" + " return f\"Matrix(({first!r}, ...), ..., (..., {last!r}))[{m:d}x{n:d}]\"" ] }, { @@ -1521,7 +1521,7 @@ { "data": { "text/plain": [ - "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + "Matrix(((1.0, 2.0, 3.0,), (4.0, 5.0, 6.0,), (7.0, 8.0, 9.0,)))" ] }, "execution_count": 43, @@ -1660,7 +1660,7 @@ " # ...\n", "\n", " def __repr__(self):\n", - " args = \", \".join(\"(\" + \", \".join(f\"{c:.3f}\" for c in r) + \",)\" for r in self._entries)\n", + " args = \", \".join(\"(\" + \", \".join(repr(c) for c in r) + \",)\" for r in self._entries)\n", " return f\"Matrix(({args}))\"\n", "\n", " def transpose(self):\n", @@ -1703,7 +1703,7 @@ { "data": { "text/plain": [ - "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + "Matrix(((1.0, 2.0, 3.0,), (4.0, 5.0, 6.0,), (7.0, 8.0, 9.0,)))" ] }, "execution_count": 49, @@ -1727,7 +1727,7 @@ { "data": { "text/plain": [ - "Matrix(((1.000, 4.000, 7.000,), (2.000, 5.000, 8.000,), (3.000, 6.000, 9.000,)))" + "Matrix(((1.0, 4.0, 7.0,), (2.0, 5.0, 8.0,), (3.0, 6.0, 9.0,)))" ] }, "execution_count": 50, @@ -1775,7 +1775,7 @@ { "data": { "text/plain": [ - "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + "Matrix(((1.0, 2.0, 3.0,), (4.0, 5.0, 6.0,), (7.0, 8.0, 9.0,)))" ] }, "execution_count": 52, @@ -1876,7 +1876,7 @@ " # ...\n", "\n", " def __repr__(self):\n", - " args = \", \".join(\"(\" + \", \".join(f\"{c:.3f}\" for c in r) + \",)\" for r in self._entries)\n", + " args = \", \".join(\"(\" + \", \".join(repr(c) for c in r) + \",)\" for r in self._entries)\n", " return f\"Matrix(({args}))\"\n", "\n", " def transpose(self):\n", @@ -1923,7 +1923,7 @@ { "data": { "text/plain": [ - "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + "Matrix(((1.0, 2.0, 3.0,), (4.0, 5.0, 6.0,), (7.0, 8.0, 9.0,)))" ] }, "execution_count": 57, @@ -1993,7 +1993,7 @@ " raise ValueError(\"a matrix must have at least one entry\")\n", "\n", " def __repr__(self):\n", - " args = \", \".join(\"(\" + \", \".join(f\"{c:.3f}\" for c in r) + \",)\" for r in self._entries)\n", + " args = \", \".join(\"(\" + \", \".join(repr(c) for c in r) + \",)\" for r in self._entries)\n", " return f\"Matrix(({args}))\"\n", "\n", " @property\n", diff --git a/11_classes/02_content.ipynb b/11_classes/02_content.ipynb index 644fad2..90d650e 100644 --- a/11_classes/02_content.ipynb +++ b/11_classes/02_content.ipynb @@ -77,7 +77,7 @@ " raise ValueError(\"a vector must have at least one entry\")\n", "\n", " def __repr__(self):\n", - " args = \", \".join(f\"{x:.3f}\" for x in self._entries)\n", + " args = \", \".join(repr(x) for x in self._entries)\n", " return f\"Vector(({args}))\"\n", "\n", " def __len__(self):\n", @@ -762,7 +762,7 @@ " # ...\n", "\n", " def __repr__(self):\n", - " args = \", \".join(\"(\" + \", \".join(f\"{c:.3f}\" for c in r) + \",)\" for r in self._entries)\n", + " args = \", \".join(\"(\" + \", \".join(repr(c) for c in r) + \",)\" for r in self._entries)\n", " return f\"Matrix(({args}))\"\n", "\n", " @property\n", @@ -825,7 +825,7 @@ " # ...\n", "\n", " def __repr__(self):\n", - " args = \", \".join(f\"{x:.3f}\" for x in self)\n", + " args = \", \".join(repr(x) for x in self)\n", " return f\"Vector(({args}))\"\n", "\n", " def __iter__(self):\n", @@ -860,7 +860,7 @@ { "data": { "text/plain": [ - "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + "Matrix(((1.0, 2.0, 3.0,), (4.0, 5.0, 6.0,), (7.0, 8.0, 9.0,)))" ] }, "execution_count": 25, @@ -951,7 +951,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Vector((1.000, 2.000, 3.000)) Vector((4.000, 5.000, 6.000)) Vector((7.000, 8.000, 9.000)) " + "Vector((1.0, 2.0, 3.0)) Vector((4.0, 5.0, 6.0)) Vector((7.0, 8.0, 9.0)) " ] } ], @@ -973,7 +973,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Vector((1.000, 4.000, 7.000)) Vector((2.000, 5.000, 8.000)) Vector((3.000, 6.000, 9.000)) " + "Vector((1.0, 4.0, 7.0)) Vector((2.0, 5.0, 8.0)) Vector((3.0, 6.0, 9.0)) " ] } ], @@ -1069,7 +1069,7 @@ " # ...\n", "\n", " def __repr__(self):\n", - " args = \", \".join(\"(\" + \", \".join(f\"{c:.3f}\" for c in r) + \",)\" for r in self._entries)\n", + " args = \", \".join(\"(\" + \", \".join(repr(c) for c in r) + \",)\" for r in self._entries)\n", " return f\"Matrix(({args}))\"\n", "\n", " def transpose(self):\n", @@ -1102,7 +1102,7 @@ { "data": { "text/plain": [ - "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + "Matrix(((1.0, 2.0, 3.0,), (4.0, 5.0, 6.0,), (7.0, 8.0, 9.0,)))" ] }, "execution_count": 34, @@ -1161,7 +1161,7 @@ { "data": { "text/plain": [ - "Matrix(((1.000, 4.000, 7.000,), (2.000, 5.000, 8.000,), (3.000, 6.000, 9.000,)))" + "Matrix(((1.0, 4.0, 7.0,), (2.0, 5.0, 8.0,), (3.0, 6.0, 9.0,)))" ] }, "execution_count": 36, @@ -1248,7 +1248,7 @@ " # ...\n", "\n", " def __repr__(self):\n", - " args = \", \".join(\"(\" + \", \".join(f\"{c:.3f}\" for c in r) + \",)\" for r in self._entries)\n", + " args = \", \".join(\"(\" + \", \".join(repr(c) for c in r) + \",)\" for r in self._entries)\n", " return f\"Matrix(({args}))\"\n", "\n", " def __iter__(self): # adapted for brevity; uses parts of entries()\n", @@ -1285,7 +1285,7 @@ { "data": { "text/plain": [ - "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + "Matrix(((1.0, 2.0, 3.0,), (4.0, 5.0, 6.0,), (7.0, 8.0, 9.0,)))" ] }, "execution_count": 40, @@ -1333,7 +1333,7 @@ { "data": { "text/plain": [ - "Matrix(((1.000, 4.000, 7.000,), (2.000, 5.000, 8.000,), (3.000, 6.000, 9.000,)))" + "Matrix(((1.0, 4.0, 7.0,), (2.0, 5.0, 8.0,), (3.0, 6.0, 9.0,)))" ] }, "execution_count": 42, @@ -1357,7 +1357,7 @@ { "data": { "text/plain": [ - "Matrix(((1.000, 4.000, 7.000,), (2.000, 5.000, 8.000,), (3.000, 6.000, 9.000,)))" + "Matrix(((1.0, 4.0, 7.0,), (2.0, 5.0, 8.0,), (3.0, 6.0, 9.0,)))" ] }, "execution_count": 43, @@ -1434,7 +1434,7 @@ " # ...\n", "\n", " def __repr__(self):\n", - " args = \", \".join(f\"{x:.3f}\" for x in self)\n", + " args = \", \".join(repr(x) for x in self)\n", " return f\"Vector(({args}))\"\n", "\n", " def __getitem__(self, index):\n", @@ -1472,7 +1472,7 @@ { "data": { "text/plain": [ - "Vector((1.000, 2.000, 3.000))" + "Vector((1.0, 2.0, 3.0))" ] }, "execution_count": 47, @@ -1520,7 +1520,7 @@ { "data": { "text/plain": [ - "Vector((nan, 2.000, 3.000))" + "Vector((nan, 2.0, 3.0))" ] }, "execution_count": 49, @@ -1557,7 +1557,7 @@ { "data": { "text/plain": [ - "Vector((99.000, 2.000, 3.000))" + "Vector((99.0, 2.0, 3.0))" ] }, "execution_count": 51, diff --git a/11_classes/03_content.ipynb b/11_classes/03_content.ipynb index def4be5..5bd0fe2 100644 --- a/11_classes/03_content.ipynb +++ b/11_classes/03_content.ipynb @@ -86,7 +86,7 @@ " # ...\n", "\n", " def __repr__(self):\n", - " args = \", \".join(f\"{x:.3f}\" for x in self)\n", + " args = \", \".join(repr(x) for x in self)\n", " return f\"Vector(({args}))\"\n", "\n", " def __iter__(self):\n", @@ -115,7 +115,7 @@ " # ...\n", "\n", " def __repr__(self):\n", - " args = \", \".join(\"(\" + \", \".join(f\"{c:.3f}\" for c in r) + \",)\" for r in self._entries)\n", + " args = \", \".join(\"(\" + \", \".join(repr(c) for c in r) + \",)\" for r in self._entries)\n", " return f\"Matrix(({args}))\"\n", "\n", " @property\n", @@ -160,7 +160,7 @@ { "data": { "text/plain": [ - "Vector((1.000, 2.000, 3.000))" + "Vector((1.0, 2.0, 3.0))" ] }, "execution_count": 4, @@ -208,7 +208,7 @@ { "data": { "text/plain": [ - "Matrix(((1.000,), (2.000,), (3.000,)))" + "Matrix(((1.0,), (2.0,), (3.0,)))" ] }, "execution_count": 6, @@ -267,7 +267,7 @@ { "data": { "text/plain": [ - "Vector((1.000, 2.000, 3.000))" + "Vector((1.0, 2.0, 3.0))" ] }, "execution_count": 8, @@ -315,7 +315,7 @@ { "data": { "text/plain": [ - "Matrix(((1.000, 2.000, 3.000,)))" + "Matrix(((1.0, 2.0, 3.0,)))" ] }, "execution_count": 10, @@ -363,7 +363,7 @@ { "data": { "text/plain": [ - "Vector((1.000, 2.000, 3.000))" + "Vector((1.0, 2.0, 3.0))" ] }, "execution_count": 12, @@ -682,7 +682,7 @@ " # ...\n", "\n", " def __repr__(self):\n", - " args = \", \".join(f\"{x:.3f}\" for x in self)\n", + " args = \", \".join(repr(x) for x in self)\n", " return f\"Vector(({args}))\"\n", "\n", " def __len__(self):\n", @@ -790,7 +790,7 @@ { "data": { "text/plain": [ - "Vector((2.000, 4.000, 6.000))" + "Vector((2.0, 4.0, 6.0))" ] }, "execution_count": 24, @@ -814,7 +814,7 @@ { "data": { "text/plain": [ - "Vector((3.000, 6.000, 9.000))" + "Vector((3.0, 6.0, 9.0))" ] }, "execution_count": 25, @@ -910,7 +910,7 @@ { "data": { "text/plain": [ - "Vector((0.333, 0.667, 1.000))" + "Vector((0.3333333333333333, 0.6666666666666666, 1.0))" ] }, "execution_count": 28, @@ -945,7 +945,7 @@ { "data": { "text/plain": [ - "Vector((2.000, 4.000, 6.000))" + "Vector((2.0, 4.0, 6.0))" ] }, "execution_count": 29, @@ -969,7 +969,7 @@ { "data": { "text/plain": [ - "Vector((0.000, 0.000, 0.000))" + "Vector((0.0, 0.0, 0.0))" ] }, "execution_count": 30, @@ -1019,7 +1019,7 @@ { "data": { "text/plain": [ - "Vector((101.000, 102.000, 103.000))" + "Vector((101.0, 102.0, 103.0))" ] }, "execution_count": 32, @@ -1043,7 +1043,7 @@ { "data": { "text/plain": [ - "Vector((99.000, 98.000, 97.000))" + "Vector((99.0, 98.0, 97.0))" ] }, "execution_count": 33, @@ -1146,7 +1146,7 @@ " # ...\n", "\n", " def __repr__(self):\n", - " args = \", \".join(\"(\" + \", \".join(f\"{c:.3f}\" for c in r) + \",)\" for r in self._entries)\n", + " args = \", \".join(\"(\" + \", \".join(repr(c) for c in r) + \",)\" for r in self._entries)\n", " return f\"Matrix(({args}))\"\n", "\n", " @property\n", @@ -1270,7 +1270,7 @@ { "data": { "text/plain": [ - "Matrix(((10.000, 20.000, 30.000,), (40.000, 50.000, 60.000,), (70.000, 80.000, 90.000,)))" + "Matrix(((10.0, 20.0, 30.0,), (40.0, 50.0, 60.0,), (70.0, 80.0, 90.0,)))" ] }, "execution_count": 37, @@ -1294,7 +1294,7 @@ { "data": { "text/plain": [ - "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + "Matrix(((1.0, 2.0, 3.0,), (4.0, 5.0, 6.0,), (7.0, 8.0, 9.0,)))" ] }, "execution_count": 38, @@ -1318,7 +1318,7 @@ { "data": { "text/plain": [ - "Matrix(((0.000, 0.000, 0.000,), (0.000, 0.000, 0.000,), (0.000, 0.000, 0.000,)))" + "Matrix(((0.0, 0.0, 0.0,), (0.0, 0.0, 0.0,), (0.0, 0.0, 0.0,)))" ] }, "execution_count": 39, @@ -1353,7 +1353,7 @@ { "data": { "text/plain": [ - "Matrix(((138.000, 171.000, 204.000,), (174.000, 216.000, 258.000,)))" + "Matrix(((138.0, 171.0, 204.0,), (174.0, 216.0, 258.0,)))" ] }, "execution_count": 40, @@ -1426,7 +1426,7 @@ { "data": { "text/plain": [ - "Vector((14.000, 32.000, 50.000))" + "Vector((14.0, 32.0, 50.0))" ] }, "execution_count": 42, @@ -1450,7 +1450,7 @@ { "data": { "text/plain": [ - "Vector((30.000, 36.000, 42.000))" + "Vector((30.0, 36.0, 42.0))" ] }, "execution_count": 43, @@ -1474,7 +1474,7 @@ { "data": { "text/plain": [ - "Vector((68.000, 86.000))" + "Vector((68.0, 86.0))" ] }, "execution_count": 44, @@ -1536,7 +1536,7 @@ { "data": { "text/plain": [ - "Matrix(((101.000, 102.000, 103.000,), (104.000, 105.000, 106.000,), (107.000, 108.000, 109.000,)))" + "Matrix(((101.0, 102.0, 103.0,), (104.0, 105.0, 106.0,), (107.0, 108.0, 109.0,)))" ] }, "execution_count": 46, @@ -1560,7 +1560,7 @@ { "data": { "text/plain": [ - "Matrix(((99.000, 98.000, 97.000,), (96.000, 95.000, 94.000,), (93.000, 92.000, 91.000,)))" + "Matrix(((99.0, 98.0, 97.0,), (96.0, 95.0, 94.0,), (93.0, 92.0, 91.0,)))" ] }, "execution_count": 47, @@ -1724,7 +1724,7 @@ " # ...\n", "\n", " def __repr__(self):\n", - " args = \", \".join(f\"{x:.3f}\" for x in self)\n", + " args = \", \".join(repr(x) for x in self)\n", " return f\"Vector(({args}))\"\n", "\n", " def __len__(self):\n", @@ -1881,7 +1881,7 @@ { "data": { "text/plain": [ - "Vector((1.000, 2.000, 3.000))" + "Vector((1.0, 2.0, 3.0))" ] }, "execution_count": 58, @@ -1994,7 +1994,7 @@ " # ...\n", "\n", " def __repr__(self):\n", - " args = \", \".join(f\"{x:.3f}\" for x in self)\n", + " args = \", \".join(repr(x) for x in self)\n", " return f\"Vector(({args}))\"\n", "\n", " def __iter__(self):\n", @@ -2058,7 +2058,7 @@ { "data": { "text/plain": [ - "Vector((1.000, 2.000, 3.000))" + "Vector((1.0, 2.0, 3.0))" ] }, "execution_count": 65, @@ -2106,7 +2106,7 @@ { "data": { "text/plain": [ - "Vector((-3.000, -4.000))" + "Vector((-3.0, -4.0))" ] }, "execution_count": 67, diff --git a/11_classes/04_content.ipynb b/11_classes/04_content.ipynb index 51b0818..959c160 100644 --- a/11_classes/04_content.ipynb +++ b/11_classes/04_content.ipynb @@ -303,13 +303,13 @@ " | \n", " | Example Usage:\n", " | >>> Matrix([(1, 2), (3, 4)]) + Matrix([(2, 3), (4, 5)])\n", - " | Matrix(((3.000, 5.000,), (7.000, 9.000,)))\n", + " | Matrix(((3.0, 5.0,), (7.0, 9.0,)))\n", " | \n", " | >>> Matrix([(1, 2), (3, 4)]) + 5\n", - " | Matrix(((6.000, 7.000,), (8.000, 9.000,)))\n", + " | Matrix(((6.0, 7.0,), (8.0, 9.0,)))\n", " | \n", " | >>> 10 + Matrix([(1, 2), (3, 4)])\n", - " | Matrix(((11.000, 12.000,), (13.000, 14.000,)))\n", + " | Matrix(((11.0, 12.0,), (13.0, 14.0,)))\n", " | \n", " | __bool__(self)\n", " | A Matrix is truthy if its Frobenius norm is strictly positive.\n", @@ -372,7 +372,7 @@ " | \n", " | Example Usage:\n", " | >>> Matrix([(1, 2), (3, 4)])\n", - " | Matrix(((1.000, 2.000,), (3.000, 4.000,)))\n", + " | Matrix(((1.0, 2.0,), (3.0, 4.0,)))\n", " | \n", " | __iter__(self)\n", " | Loop over a Matrix's entries.\n", @@ -390,21 +390,23 @@ " | \n", " | Example Usage:\n", " | >>> Matrix([(1, 2), (3, 4)]) * Matrix([(1, 2), (3, 4)])\n", - " | Matrix(((7.000, 10.000,), (15.000, 22.000,)))\n", + " | Matrix(((7.0, 10.0,), (15.0, 22.0,)))\n", " | \n", " | >>> 2 * Matrix([(1, 2), (3, 4)])\n", - " | Matrix(((2.000, 4.000,), (6.000, 8.000,)))\n", + " | Matrix(((2.0, 4.0,), (6.0, 8.0,)))\n", " | \n", " | >>> Matrix([(1, 2), (3, 4)]) * 3\n", - " | Matrix(((3.000, 6.000,), (9.000, 12.000,)))\n", + " | Matrix(((3.0, 6.0,), (9.0, 12.0,)))\n", " | \n", " | Matrix-vector and vector-matrix multiplication are not commutative.\n", " | \n", + " | >>> from sample_package import Vector\n", + " | \n", " | >>> Matrix([(1, 2), (3, 4)]) * Vector([5, 6])\n", - " | Vector((17.000, 39.000))\n", + " | Vector((17.0, 39.0))\n", " | \n", " | >>> Vector([5, 6]) * Matrix([(1, 2), (3, 4)])\n", - " | Vector((23.000, 34.000))\n", + " | Vector((23.0, 34.0))\n", " | \n", " | __neg__(self)\n", " | Handle `-self`.\n", @@ -443,13 +445,13 @@ " | \n", " | Example Usage:\n", " | >>> Matrix([(2, 3), (4, 5)]) - Matrix([(1, 2), (3, 4)])\n", - " | Matrix(((1.000, 1.000,), (1.000, 1.000,)))\n", + " | Matrix(((1.0, 1.0,), (1.0, 1.0,)))\n", " | \n", " | >>> Matrix([(1, 2), (3, 4)]) - 1\n", - " | Matrix(((0.000, 1.000,), (2.000, 3.000,)))\n", + " | Matrix(((0.0, 1.0,), (2.0, 3.0,)))\n", " | \n", " | >>> 10 - Matrix([(1, 2), (3, 4)])\n", - " | Matrix(((9.000, 8.000,), (7.000, 6.000,)))\n", + " | Matrix(((9.0, 8.0,), (7.0, 6.0,)))\n", " | \n", " | __truediv__(self, other)\n", " | Handle `self / other`.\n", @@ -458,7 +460,7 @@ " | \n", " | Example Usage:\n", " | >>> Matrix([(1, 2), (3, 4)]) / 4\n", - " | Matrix(((0.250, 0.500,), (0.750, 1.000,)))\n", + " | Matrix(((0.25, 0.5,), (0.75, 1.0,)))\n", " | \n", " | as_vector(self)\n", " | Get a Vector representation of a Matrix.\n", @@ -471,7 +473,7 @@ " | \n", " | Example Usage:\n", " | >>> Matrix([(1, 2, 3)]).as_vector()\n", - " | Vector((1.000, 2.000, 3.000))\n", + " | Vector((1.0, 2.0, 3.0))\n", " | \n", " | cols(self)\n", " | Loop over a Matrix's columns.\n", @@ -504,9 +506,9 @@ " | Example Usage:\n", " | >>> m = Matrix([(1, 2), (3, 4)])\n", " | >>> m\n", - " | Matrix(((1.000, 2.000,), (3.000, 4.000,)))\n", + " | Matrix(((1.0, 2.0,), (3.0, 4.0,)))\n", " | >>> m.transpose()\n", - " | Matrix(((1.000, 3.000,), (2.000, 4.000,)))\n", + " | Matrix(((1.0, 3.0,), (2.0, 4.0,)))\n", " | \n", " | ----------------------------------------------------------------------\n", " | Class methods defined here:\n", @@ -529,7 +531,7 @@ " | \n", " | Example Usage:\n", " | >>> Matrix.from_columns([(1, 2), (3, 4)])\n", - " | Matrix(((1.000, 3.000,), (2.000, 4.000,)))\n", + " | Matrix(((1.0, 3.0,), (2.0, 4.0,)))\n", " | \n", " | from_rows(data) from builtins.type\n", " | See docstring for .__init__().\n", @@ -612,13 +614,13 @@ " | \n", " | Example Usage:\n", " | >>> Vector([1, 2, 3]) + Vector([2, 3, 4])\n", - " | Vector((3.000, 5.000, 7.000))\n", + " | Vector((3.0, 5.0, 7.0))\n", " | \n", " | >>> Vector([1, 2, 3]) + 4\n", - " | Vector((5.000, 6.000, 7.000))\n", + " | Vector((5.0, 6.0, 7.0))\n", " | \n", " | >>> 10 + Vector([1, 2, 3])\n", - " | Vector((11.000, 12.000, 13.000))\n", + " | Vector((11.0, 12.0, 13.0))\n", " | \n", " | __bool__(self)\n", " | A Vector is truthy if its Euclidean norm is strictly positive.\n", @@ -658,10 +660,10 @@ " | \n", " | Example Usage:\n", " | >>> Vector([1, 2, 3])\n", - " | Vector((1.000, 2.000, 3.000))\n", + " | Vector((1.0, 2.0, 3.0))\n", " | \n", " | >>> Vector(range(3))\n", - " | Vector((0.000, 1.000, 2.000))\n", + " | Vector((0.0, 1.0, 2.0))\n", " | \n", " | __iter__(self)\n", " | Loop over a Vector's entries.\n", @@ -679,10 +681,10 @@ " | 20.0\n", " | \n", " | >>> 2 * Vector([1, 2, 3])\n", - " | Vector((2.000, 4.000, 6.000))\n", + " | Vector((2.0, 4.0, 6.0))\n", " | \n", " | >>> Vector([1, 2, 3]) * 3\n", - " | Vector((3.000, 6.000, 9.000))\n", + " | Vector((3.0, 6.0, 9.0))\n", " | \n", " | __neg__(self)\n", " | Handle `-self`.\n", @@ -719,13 +721,13 @@ " | \n", " | Example Usage:\n", " | >>> Vector([7, 8, 9]) - Vector([1, 2, 3])\n", - " | Vector((6.000, 6.000, 6.000))\n", + " | Vector((6.0, 6.0, 6.0))\n", " | \n", " | >>> Vector([1, 2, 3]) - 1\n", - " | Vector((0.000, 1.000, 2.000))\n", + " | Vector((0.0, 1.0, 2.0))\n", " | \n", " | >>> 10 - Vector([1, 2, 3])\n", - " | Vector((9.000, 8.000, 7.000))\n", + " | Vector((9.0, 8.0, 7.0))\n", " | \n", " | __truediv__(self, other)\n", " | Handle `self / other`.\n", @@ -734,7 +736,7 @@ " | \n", " | Example Usage:\n", " | >>> Vector([9, 6, 12]) / 3\n", - " | Vector((3.000, 2.000, 4.000))\n", + " | Vector((3.0, 2.0, 4.0))\n", " | \n", " | as_matrix(self, *, column=True)\n", " | Get a Matrix representation of a Vector.\n", @@ -749,9 +751,9 @@ " | Example Usage:\n", " | >>> v = Vector([1, 2, 3])\n", " | >>> v.as_matrix()\n", - " | Matrix(((1.000,), (2.000,), (3.000,)))\n", + " | Matrix(((1.0,), (2.0,), (3.0,)))\n", " | >>> v.as_matrix(column=False)\n", - " | Matrix(((1.000, 2.000, 3.000,)))\n", + " | Matrix(((1.0, 2.0, 3.0,)))\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data descriptors defined here:\n", @@ -876,7 +878,7 @@ { "data": { "text/plain": [ - "Vector((1.000, 2.000, 3.000))" + "Vector((1.0, 2.0, 3.0))" ] }, "execution_count": 11, @@ -896,7 +898,7 @@ { "data": { "text/plain": [ - "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + "Matrix(((1.0, 2.0, 3.0,), (4.0, 5.0, 6.0,), (7.0, 8.0, 9.0,)))" ] }, "execution_count": 12, @@ -1116,7 +1118,7 @@ { "data": { "text/plain": [ - "Vector((1.000, 2.000, 3.000))" + "Vector((1.0, 2.0, 3.0))" ] }, "execution_count": 21, @@ -1165,7 +1167,7 @@ { "data": { "text/plain": [ - "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + "Matrix(((1.0, 2.0, 3.0,), (4.0, 5.0, 6.0,), (7.0, 8.0, 9.0,)))" ] }, "execution_count": 24, @@ -1271,10 +1273,10 @@ "\u001b[0;34m\u001b[0m\n", "\u001b[0;34m Example Usage:\u001b[0m\n", "\u001b[0;34m >>> Vector([1, 2, 3])\u001b[0m\n", - "\u001b[0;34m Vector((1.000, 2.000, 3.000))\u001b[0m\n", + "\u001b[0;34m Vector((1.0, 2.0, 3.0))\u001b[0m\n", "\u001b[0;34m\u001b[0m\n", "\u001b[0;34m >>> Vector(range(3))\u001b[0m\n", - "\u001b[0;34m Vector((0.000, 1.000, 2.000))\u001b[0m\n", + "\u001b[0;34m Vector((0.0, 1.0, 2.0))\u001b[0m\n", "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstorage\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtyping\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", @@ -1420,9 +1422,9 @@ "\u001b[0;34m Example Usage:\u001b[0m\n", "\u001b[0;34m >>> m = Matrix([(1, 2), (3, 4)])\u001b[0m\n", "\u001b[0;34m >>> m\u001b[0m\n", - "\u001b[0;34m Matrix(((1.000, 2.000,), (3.000, 4.000,)))\u001b[0m\n", + "\u001b[0;34m Matrix(((1.0, 2.0,), (3.0, 4.0,)))\u001b[0m\n", "\u001b[0;34m >>> m.transpose()\u001b[0m\n", - "\u001b[0;34m Matrix(((1.000, 3.000,), (2.000, 4.000,)))\u001b[0m\n", + "\u001b[0;34m Matrix(((1.0, 3.0,), (2.0, 4.0,)))\u001b[0m\n", "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\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;31mFile:\u001b[0m ~/repos/intro-to-python/11_classes/sample_package/matrix.py\n", @@ -1512,7 +1514,7 @@ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Text representation of a Matrix.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mname\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__name__\u001b[0m\u001b[0;34m\u001b[0m\n", "\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[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n", - "\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[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"{c:.3f}\"\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mc\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mr\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\",)\"\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mr\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\u001b[0m\u001b[0;34m\u001b[0m\n", + "\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[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrepr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mc\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mc\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mr\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\",)\"\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mr\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;34mf\"{name}(({args}))\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mFile:\u001b[0m ~/repos/intro-to-python/11_classes/sample_package/matrix.py\n", @@ -1604,9 +1606,9 @@ "\u001b[0;34m Example Usage:\u001b[0m\n", "\u001b[0;34m >>> v = Vector([1, 2, 3])\u001b[0m\n", "\u001b[0;34m >>> v.as_matrix()\u001b[0m\n", - "\u001b[0;34m Matrix(((1.000,), (2.000,), (3.000,)))\u001b[0m\n", + "\u001b[0;34m Matrix(((1.0,), (2.0,), (3.0,)))\u001b[0m\n", "\u001b[0;34m >>> v.as_matrix(column=False)\u001b[0m\n", - "\u001b[0;34m Matrix(((1.000, 2.000, 3.000,)))\u001b[0m\n", + "\u001b[0;34m Matrix(((1.0, 2.0, 3.0,)))\u001b[0m\n", "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcolumn\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmatrix_cls\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", @@ -1725,7 +1727,7 @@ { "data": { "text/plain": [ - "Matrix(((639.427, 25.011, 275.029, 223.211, 736.471, 676.699, 892.180, 86.939, 421.922, 29.797, 218.638, 505.355, 26.536, 198.838, 649.884, 544.941, 220.441, 589.266, 809.430, 6.499, 805.819, 698.139, 340.251, 155.479, 957.213, 336.595, 92.746, 96.716, 847.494, 603.726, 807.128, 729.732, 536.228, 973.116, 378.534, 552.041, 829.405, 618.520, 861.707, 577.352, 704.572, 45.824, 227.898, 289.388, 79.792, 232.791, 101.001, 277.974, 635.684, 364.832,), (370.181, 209.507, 266.978, 936.655, 648.035, 609.131, 171.139, 729.127, 163.402, 379.455, 989.523, 640.000, 556.950, 684.614, 842.852, 776.000, 229.048, 32.100, 315.453, 267.741, 210.983, 942.910, 876.368, 314.678, 655.439, 395.632, 914.548, 458.852, 264.880, 246.628, 561.368, 262.742, 584.586, 897.823, 399.401, 219.321, 997.538, 509.526, 90.909, 47.116, 109.649, 627.446, 792.079, 422.160, 63.528, 381.619, 996.121, 529.114, 971.078, 860.780,), (11.481, 720.722, 681.710, 536.970, 266.825, 640.962, 111.552, 434.765, 453.724, 953.816, 875.853, 263.389, 500.586, 178.652, 912.628, 870.519, 298.445, 638.949, 608.970, 152.839, 762.511, 539.379, 778.626, 530.354, 0.572, 324.156, 19.477, 929.099, 878.722, 831.666, 307.514, 57.925, 878.010, 946.949, 85.653, 485.990, 69.213, 760.602, 765.834, 128.391, 475.282, 549.804, 265.057, 872.433, 423.138, 211.798, 539.296, 729.931, 201.151, 311.716,), (995.149, 649.878, 438.100, 517.576, 121.004, 224.697, 338.086, 588.309, 230.115, 220.217, 70.993, 631.103, 228.942, 905.420, 859.635, 70.857, 238.005, 668.978, 214.237, 132.312, 935.514, 571.043, 472.671, 784.619, 807.497, 190.410, 96.931, 431.051, 423.579, 467.025, 729.076, 673.365, 984.165, 98.418, 402.621, 339.303, 861.673, 248.656, 190.209, 448.614, 421.882, 278.545, 249.806, 923.266, 443.131, 861.349, 550.325, 50.588, 999.282, 836.028,), (968.996, 926.367, 848.696, 166.311, 485.641, 213.747, 401.040, 58.635, 378.973, 985.309, 265.203, 784.071, 455.008, 423.007, 957.318, 995.423, 555.768, 718.408, 154.797, 296.708, 968.709, 579.180, 542.195, 747.976, 57.165, 584.178, 502.850, 852.720, 157.433, 960.779, 80.111, 185.825, 595.035, 675.213, 235.204, 119.887, 890.287, 246.215, 594.519, 619.382, 419.225, 583.672, 522.783, 934.706, 204.259, 716.192, 238.686, 395.786, 671.690, 299.997,), (316.177, 751.864, 72.543, 458.286, 998.454, 996.096, 73.261, 213.154, 265.200, 933.259, 880.864, 879.270, 369.527, 157.747, 833.745, 703.540, 611.678, 987.233, 653.976, 7.823, 817.104, 299.379, 663.389, 938.930, 134.291, 115.429, 107.036, 553.224, 272.348, 604.830, 717.612, 203.597, 634.238, 263.984, 488.532, 905.336, 846.104, 92.298, 423.576, 276.680, 3.546, 771.119, 637.113, 261.955, 741.231, 551.680, 427.687, 9.670, 75.244, 883.106,), (903.929, 545.590, 834.595, 582.510, 148.094, 127.446, 308.258, 898.981, 796.122, 860.703, 898.925, 210.077, 249.530, 102.794, 780.116, 884.135, 406.377, 620.662, 154.553, 929.881, 864.606, 976.206, 810.772, 881.416, 24.786, 736.564, 332.185, 930.816, 802.235, 864.064, 810.749, 266.806, 787.375, 108.096, 872.167, 858.593, 222.434, 816.587, 460.303, 305.191, 795.345, 227.595, 23.664, 193.130, 328.262, 864.353, 966.889, 279.125, 641.482, 399.678,), (981.150, 536.216, 939.237, 115.342, 970.401, 178.568, 962.534, 265.466, 108.403, 434.564, 728.545, 313.677, 606.209, 511.423, 385.195, 576.588, 254.723, 708.785, 1.691, 925.575, 538.452, 719.430, 741.950, 670.629, 364.221, 69.974, 664.238, 330.200, 313.916, 848.015, 719.754, 300.322, 309.285, 408.393, 402.400, 295.655, 127.288, 420.446, 940.364, 677.318, 902.806, 615.515, 300.950, 547.937, 0.406, 286.914, 429.888, 579.985, 654.706, 464.988,), (442.160, 213.701, 473.186, 901.181, 796.025, 169.691, 84.796, 515.452, 632.941, 335.188, 818.423, 751.138, 672.796, 224.641, 199.130, 24.425, 244.843, 475.136, 849.738, 72.828, 414.441, 629.765, 194.435, 696.354, 494.377, 243.984, 656.058, 5.545, 750.964, 770.046, 106.587, 425.146, 175.887, 957.966, 517.958, 50.218, 249.198, 848.336, 456.462, 801.417, 667.578, 987.892, 595.452, 950.040, 891.426, 612.652, 719.274, 504.778, 830.569, 547.872,), (897.208, 743.655, 474.674, 259.192, 247.240, 637.661, 765.814, 521.300, 626.748, 274.597, 77.483, 285.728, 271.715, 319.710, 540.152, 138.374, 231.261, 693.950, 706.419, 64.229, 407.599, 542.611, 415.774, 206.834, 420.144, 904.838, 584.079, 695.523, 856.732, 765.595, 380.381, 5.896, 351.759, 753.475, 853.448, 953.430, 419.021, 747.516, 546.132, 603.253, 220.539, 219.422, 435.836, 29.025, 336.130, 679.142, 404.317, 165.045, 467.390, 127.628,), (622.257, 26.966, 394.020, 564.392, 27.102, 642.750, 135.699, 461.698, 50.285, 379.104, 211.660, 326.846, 761.230, 379.126, 752.010, 831.924, 252.272, 81.906, 19.383, 539.419, 999.908, 349.960, 650.144, 781.233, 651.755, 754.233, 949.612, 199.361, 20.380, 152.382, 126.221, 669.459, 563.970, 217.965, 699.465, 766.898, 167.789, 607.247, 747.926, 114.533, 819.301, 964.721, 108.099, 25.678, 311.957, 677.347, 958.173, 396.654, 715.015, 75.996,), (690.614, 627.242, 101.901, 772.481, 850.293, 600.412, 121.055, 983.844, 782.635, 347.204, 428.378, 370.571, 505.961, 341.231, 849.576, 822.331, 105.539, 960.788, 635.585, 828.707, 707.309, 435.487, 733.795, 965.474, 270.082, 808.199, 538.173, 483.498, 435.574, 731.026, 268.396, 851.713, 830.731, 86.663, 881.631, 243.863, 464.708, 610.332, 378.989, 28.700, 850.953, 181.840, 212.120, 797.832, 340.339, 880.320, 701.184, 276.269, 10.151, 948.063,), (85.613, 720.075, 488.578, 758.165, 690.609, 645.903, 490.821, 792.933, 93.053, 221.596, 691.787, 306.206, 581.556, 473.260, 530.922, 425.504, 745.935, 330.791, 702.855, 270.916, 251.404, 120.656, 192.584, 119.555, 535.864, 762.190, 185.150, 216.385, 484.199, 724.585, 976.607, 524.637, 282.999, 100.526, 194.118, 227.483, 179.442, 14.148, 534.135, 274.311, 974.295, 553.359, 697.417, 126.279, 868.461, 490.879, 872.720, 574.064, 469.397, 440.469,), (184.364, 51.377, 941.064, 477.729, 822.116, 400.707, 74.082, 629.446, 53.609, 149.198, 562.840, 303.836, 993.918, 118.452, 764.443, 606.318, 790.741, 225.687, 522.573, 450.514, 442.721, 860.167, 990.031, 305.380, 621.027, 609.631, 740.089, 947.590, 207.788, 211.025, 660.428, 157.057, 173.814, 75.065, 2.676, 450.504, 593.811, 291.259, 231.476, 706.956, 702.988, 454.031, 687.385, 923.911, 787.828, 625.058, 661.183, 933.668, 425.139, 544.562,), (647.635, 908.411, 826.631, 71.410, 165.923, 307.612, 748.958, 569.207, 288.611, 124.354, 688.678, 699.734, 942.676, 500.472, 493.795, 80.442, 39.861, 432.029, 322.322, 250.368, 91.327, 961.911, 835.959, 575.199, 950.786, 999.572, 672.282, 269.511, 40.232, 756.269, 470.501, 651.509, 916.073, 181.489, 585.330, 634.785, 491.726, 91.242, 347.961, 333.308, 670.134, 857.733, 329.804, 693.674, 288.218, 945.194, 813.566, 550.097, 454.826, 314.517,), (323.274, 970.185, 404.175, 514.596, 988.119, 657.660, 542.594, 413.248, 187.583, 361.779, 756.443, 625.409, 759.991, 203.558, 549.220, 927.673, 438.116, 698.250, 121.426, 973.147, 608.872, 239.297, 158.378, 550.839, 552.251, 93.209, 992.257, 912.930, 461.448, 117.466, 832.143, 498.376, 716.603, 508.872, 273.425, 834.724, 980.245, 243.731, 551.265, 383.586, 921.868, 508.241, 879.326, 864.027, 276.247, 790.006, 414.942, 934.248, 507.738, 820.549,), (282.839, 298.556, 586.938, 998.902, 489.640, 148.595, 538.581, 345.124, 551.917, 543.430, 455.345, 321.777, 188.652, 697.498, 571.798, 233.562, 775.544, 43.647, 744.705, 705.228, 811.409, 386.079, 663.689, 820.748, 980.818, 495.329, 37.020, 502.291, 590.180, 869.700, 874.190, 440.306, 525.951, 456.928, 722.444, 409.979, 654.781, 154.361, 469.491, 969.204, 338.561, 692.705, 649.837, 851.765, 852.341, 859.342, 380.009, 316.661, 718.717, 759.402,), (872.383, 35.899, 68.421, 631.161, 920.929, 997.426, 746.766, 433.971, 98.443, 633.748, 872.579, 443.679, 694.001, 903.424, 45.991, 796.143, 293.368, 374.841, 145.570, 531.166, 565.928, 792.519, 169.984, 78.968, 870.840, 619.710, 240.830, 912.829, 143.118, 461.150, 253.977, 255.327, 9.397, 804.633, 901.209, 677.611, 157.976, 441.730, 345.566, 587.572, 638.939, 424.309, 250.098, 845.304, 199.217, 384.693, 483.208, 237.206, 571.923, 574.812,), (992.692, 295.231, 977.944, 658.230, 274.480, 565.929, 685.799, 744.669, 49.044, 606.406, 496.727, 904.155, 286.194, 798.860, 607.065, 352.321, 636.618, 620.891, 677.764, 720.928, 659.182, 838.337, 628.248, 903.404, 646.341, 308.933, 440.823, 579.574, 732.360, 90.133, 295.110, 747.481, 175.640, 132.160, 539.408, 971.490, 530.852, 913.487, 830.473, 256.970, 824.690, 481.848, 806.488, 746.559, 338.715, 115.170, 962.893, 140.757, 966.500, 860.141,), (724.217, 979.942, 967.270, 804.588, 365.775, 790.682, 13.919, 536.572, 454.786, 672.828, 672.341, 584.560, 822.417, 940.292, 108.346, 233.822, 25.025, 884.235, 561.407, 915.256, 221.367, 63.217, 823.855, 909.388, 302.190, 408.296, 139.777, 946.262, 304.365, 492.625, 97.192, 887.259, 135.664, 453.644, 670.486, 743.140, 945.974, 419.127, 742.269, 154.523, 414.885, 99.022, 489.347, 408.116, 951.522, 32.716, 370.530, 443.383, 950.555, 855.450,), (99.355, 685.680, 544.466, 977.843, 358.674, 398.140, 189.809, 122.160, 848.033, 454.717, 662.769, 641.704, 597.146, 21.357, 786.795, 243.569, 125.924, 564.578, 68.610, 765.157, 207.157, 215.951, 869.695, 328.560, 147.554, 900.531, 2.836, 858.406, 144.688, 129.992, 250.654, 174.497, 661.058, 25.780, 14.860, 789.985, 237.932, 323.771, 174.246, 52.399, 741.718, 526.086, 745.665, 476.246, 778.017, 513.238, 109.054, 503.839, 945.416, 43.365,), (783.227, 866.981, 521.451, 458.043, 964.026, 60.825, 478.982, 401.617, 686.097, 490.269, 909.701, 73.491, 80.790, 608.297, 65.682, 275.016, 633.077, 548.356, 325.185, 994.628, 530.557, 453.715, 605.427, 99.178, 701.779, 852.793, 650.917, 768.963, 720.840, 215.023, 451.555, 228.494, 338.932, 453.499, 415.990, 95.086, 426.764, 665.108, 374.301, 152.639, 922.985, 67.133, 831.772, 93.230, 96.564, 738.796, 811.769, 556.371, 586.465, 561.586,), (329.646, 122.231, 353.598, 665.341, 750.284, 868.092, 721.061, 968.399, 600.410, 351.646, 577.919, 212.739, 656.736, 224.245, 108.218, 845.373, 367.561, 762.606, 574.100, 807.221, 845.155, 974.547, 818.427, 613.573, 642.699, 26.254, 929.084, 829.461, 267.448, 180.416, 702.699, 308.985, 339.825, 6.106, 869.863, 566.321, 400.784, 141.875, 633.172, 30.657, 746.112, 215.133, 419.832, 340.896, 370.053, 721.596, 776.836, 567.594, 84.957, 52.609,), (157.410, 617.838, 673.969, 272.103, 661.939, 485.662, 442.044, 273.167, 754.943, 113.818, 429.914, 283.246, 678.486, 486.633, 667.133, 45.417, 395.263, 599.325, 7.687, 301.419, 211.234, 137.235, 255.520, 328.122, 7.730, 747.014, 175.695, 380.207, 703.671, 500.262, 833.354, 806.200, 72.075, 861.764, 42.302, 18.742, 921.162, 862.110, 575.759, 573.400, 709.499, 417.694, 115.173, 20.857, 324.768, 801.322, 618.125, 832.026, 919.770, 88.130,), (844.484, 243.316, 588.871, 523.963, 395.767, 310.275, 339.513, 333.069, 168.133, 510.483, 114.027, 509.952, 905.923, 349.375, 727.379, 818.949, 815.037, 236.269, 146.444, 197.272, 602.399, 760.215, 655.509, 177.146, 772.848, 494.117, 754.446, 759.877, 448.905, 924.154, 564.492, 635.298, 624.522, 864.247, 627.217, 150.957, 68.286, 442.208, 302.820, 274.674, 56.172, 507.337, 310.408, 451.914, 56.890, 831.697, 76.731, 864.250, 855.293, 615.008,), (507.068, 462.712, 554.316, 791.818, 895.877, 449.734, 809.816, 651.837, 321.527, 475.629, 150.861, 61.874, 103.502, 899.127, 343.438, 714.316, 504.549, 172.559, 247.744, 437.758, 439.422, 522.748, 158.746, 372.852, 282.894, 408.769, 338.367, 597.886, 789.227, 647.305, 65.912, 94.506, 678.379, 284.147, 723.734, 656.564, 906.343, 873.280, 333.362, 582.740, 141.428, 349.821, 967.697, 698.480, 391.958, 595.041, 938.002, 309.582, 376.679, 791.662,), (813.185, 670.116, 828.959, 738.775, 685.414, 526.393, 646.025, 423.406, 361.828, 362.598, 180.263, 214.193, 947.668, 486.271, 226.543, 137.565, 77.165, 844.428, 101.141, 770.875, 835.120, 883.682, 37.747, 336.764, 766.308, 131.049, 376.720, 162.247, 831.345, 771.098, 809.044, 165.539, 437.673, 410.859, 676.363, 237.530, 444.199, 284.928, 748.537, 448.928, 534.011, 309.468, 808.624, 469.016, 835.113, 367.841, 947.130, 984.440, 461.680, 281.772,), (381.872, 527.460, 966.268, 816.891, 801.259, 138.399, 250.003, 641.179, 874.117, 554.541, 102.590, 845.892, 851.166, 285.063, 763.117, 272.791, 905.306, 147.349, 437.473, 946.413, 222.038, 451.128, 349.585, 26.670, 53.257, 502.007, 235.778, 994.525, 374.913, 28.188, 930.826, 839.176, 649.961, 791.381, 137.600, 286.879, 829.762, 696.072, 138.793, 705.536, 448.601, 5.251, 79.226, 255.924, 834.963, 548.804, 727.235, 527.772, 111.187, 288.102,), (301.151, 47.749, 419.826, 793.899, 457.114, 110.858, 905.147, 596.739, 16.435, 515.376, 241.938, 143.577, 429.239, 614.810, 240.564, 416.568, 664.371, 85.614, 974.654, 67.679, 526.059, 507.328, 988.331, 554.152, 390.454, 470.135, 635.671, 981.039, 253.650, 16.242, 788.520, 344.802, 732.941, 628.257, 771.501, 735.187, 332.519, 44.336, 546.014, 813.509, 175.089, 779.143, 464.623, 695.389, 631.736, 811.498, 63.101, 776.190, 457.680, 293.443,), (43.806, 199.470, 41.906, 933.371, 515.384, 989.123, 543.031, 253.314, 753.291, 191.103, 356.974, 780.842, 865.798, 331.925, 124.475, 368.019, 889.487, 743.308, 894.637, 386.645, 973.724, 496.203, 497.523, 924.310, 519.276, 801.148, 727.081, 78.927, 602.453, 822.341, 545.474, 321.211, 80.069, 660.919, 306.496, 602.622, 426.116, 689.765, 351.547, 42.355, 870.037, 352.559, 998.151, 274.555, 980.027, 947.904, 75.041, 637.513, 363.311, 801.096,), (679.411, 952.789, 142.779, 607.573, 781.312, 34.799, 67.233, 778.515, 366.328, 382.854, 567.245, 605.095, 679.062, 948.824, 372.013, 763.084, 573.922, 529.460, 398.034, 649.561, 249.612, 113.449, 735.675, 499.044, 386.987, 561.673, 261.777, 260.290, 446.273, 996.365, 285.577, 916.479, 491.200, 122.637, 852.826, 452.043, 898.679, 445.111, 87.791, 681.929, 845.521, 319.588, 347.425, 64.939, 542.171, 891.332, 851.362, 711.809, 927.324, 637.700,), (793.696, 508.756, 121.362, 200.980, 138.877, 790.373, 26.284, 554.021, 368.911, 803.662, 551.647, 611.948, 86.215, 309.291, 999.595, 718.870, 525.696, 769.165, 823.339, 73.751, 972.380, 642.339, 449.974, 680.109, 344.515, 877.960, 780.263, 639.794, 181.963, 966.265, 432.618, 910.712, 55.413, 124.161, 153.015, 164.657, 322.661, 709.332, 346.023, 940.904, 894.926, 845.934, 250.605, 635.057, 550.841, 125.170, 302.825, 533.478, 502.573, 168.636,), (941.607, 154.194, 658.733, 720.633, 605.139, 842.530, 563.618, 825.236, 28.373, 45.462, 641.454, 576.771, 651.130, 766.959, 416.587, 638.991, 498.038, 627.164, 289.672, 956.650, 482.945, 804.688, 684.991, 297.434, 72.973, 59.913, 439.605, 484.251, 204.023, 606.660, 312.582, 718.363, 734.200, 860.777, 975.374, 130.766, 370.540, 561.651, 319.116, 466.473, 267.472, 247.919, 96.812, 290.212, 384.150, 615.377, 248.270, 865.308, 159.700, 327.436,), (577.687, 312.715, 763.121, 498.266, 514.725, 498.760, 308.540, 23.176, 945.233, 505.444, 966.687, 215.144, 352.895, 50.540, 494.894, 882.339, 654.260, 470.587, 536.691, 847.172, 430.928, 882.456, 727.508, 763.857, 365.937, 400.582, 570.282, 194.655, 553.223, 73.532, 504.256, 764.404, 279.721, 989.091, 680.399, 118.811, 975.083, 393.904, 794.897, 339.085, 938.949, 754.965, 199.058, 509.123, 500.078, 45.303, 137.036, 333.041, 473.744, 456.989,), (606.261, 515.506, 327.966, 613.068, 162.502, 990.616, 739.319, 299.234, 336.373, 828.289, 532.340, 708.740, 299.791, 815.749, 368.358, 673.806, 979.898, 583.702, 796.755, 725.324, 688.044, 26.647, 474.590, 967.071, 782.904, 776.162, 577.634, 721.400, 583.523, 170.512, 629.025, 619.736, 841.167, 147.776, 680.727, 31.571, 948.205, 109.896, 18.937, 313.692, 151.431, 690.500, 410.377, 774.972, 920.521, 872.818, 735.837, 62.281, 138.082, 207.342,), (325.050, 662.227, 525.477, 313.753, 173.182, 912.124, 342.327, 354.287, 771.990, 720.925, 643.309, 693.313, 610.077, 192.264, 246.519, 558.087, 224.867, 972.911, 297.615, 289.004, 207.278, 704.988, 317.041, 348.803, 933.700, 795.405, 273.458, 121.874, 676.622, 379.694, 980.161, 818.377, 954.609, 804.616, 290.453, 287.630, 714.141, 346.364, 442.376, 256.444, 479.079, 202.068, 538.578, 933.024, 696.171, 137.273, 615.677, 586.830, 242.458, 669.834,), (531.041, 637.945, 52.491, 413.301, 717.358, 100.545, 770.766, 5.181, 550.353, 929.100, 406.907, 935.032, 878.400, 477.449, 199.456, 963.914, 321.168, 645.898, 907.937, 89.461, 574.133, 535.152, 723.118, 936.669, 913.230, 175.065, 882.245, 175.789, 919.635, 997.172, 396.995, 495.384, 936.609, 962.131, 926.040, 876.743, 9.267, 567.962, 107.301, 982.994, 284.562, 989.099, 543.300, 493.912, 938.561, 851.060, 468.021, 192.811, 112.647, 162.494,), (458.914, 257.265, 186.199, 736.618, 790.768, 567.781, 757.283, 175.495, 856.147, 897.043, 826.990, 515.281, 86.738, 669.256, 184.781, 140.612, 323.602, 248.047, 260.785, 235.521, 753.757, 954.035, 301.946, 722.883, 11.436, 653.683, 692.769, 62.124, 118.225, 306.806, 405.417, 502.520, 895.118, 703.557, 310.978, 117.416, 916.130, 295.038, 614.625, 219.129, 133.569, 153.186, 747.735, 605.739, 415.846, 549.235, 470.828, 537.518, 664.094, 218.412,), (247.465, 754.740, 873.135, 81.870, 446.748, 703.766, 78.103, 564.169, 61.758, 547.649, 505.487, 572.702, 149.852, 328.118, 520.342, 116.240, 205.401, 583.148, 90.942, 510.375, 808.692, 453.432, 513.248, 456.798, 57.737, 462.378, 806.915, 723.280, 395.949, 816.453, 745.804, 578.311, 45.290, 344.529, 63.760, 994.124, 934.583, 69.019, 933.776, 31.735, 408.867, 768.972, 765.828, 978.333, 645.881, 420.362, 992.857, 382.480, 869.620, 906.767,), (375.646, 682.730, 661.793, 539.300, 653.534, 347.770, 178.474, 537.258, 528.843, 727.858, 222.690, 3.473, 22.735, 298.363, 673.500, 544.445, 531.934, 823.360, 247.512, 346.160, 275.650, 937.410, 725.024, 112.845, 809.478, 419.241, 766.053, 883.757, 15.646, 206.082, 100.897, 33.576, 597.785, 703.286, 48.676, 740.541, 402.265, 234.339, 217.269, 863.730, 56.444, 503.896, 289.263, 815.786, 731.517, 318.904, 597.918, 672.532, 320.665, 301.764,), (143.260, 660.212, 221.043, 300.501, 60.958, 948.520, 879.714, 911.578, 625.993, 427.201, 495.621, 972.290, 941.586, 671.343, 785.805, 318.734, 416.325, 149.218, 376.460, 754.416, 473.519, 849.341, 300.736, 707.577, 805.776, 914.741, 562.386, 967.786, 557.287, 134.093, 242.859, 203.337, 646.706, 922.226, 847.133, 92.464, 724.585, 190.482, 268.462, 673.672, 602.922, 873.620, 188.163, 761.696, 724.305, 558.850, 479.394, 869.474, 332.964, 957.020,), (15.334, 937.160, 962.078, 117.316, 999.572, 478.921, 242.593, 604.402, 204.513, 915.126, 552.079, 775.514, 380.662, 533.650, 359.260, 261.562, 512.817, 497.277, 98.608, 981.318, 469.490, 839.731, 914.330, 370.705, 413.930, 562.525, 221.274, 145.923, 260.774, 934.758, 579.143, 417.578, 152.411, 329.865, 379.840, 833.363, 499.301, 654.608, 684.847, 257.327, 821.592, 966.508, 641.694, 490.596, 168.234, 794.976, 169.266, 720.314, 488.316, 916.899,), (542.137, 641.809, 58.732, 33.824, 846.697, 945.188, 668.216, 764.339, 412.392, 842.545, 231.433, 707.170, 9.141, 505.733, 373.201, 617.835, 666.755, 616.519, 483.204, 487.854, 6.612, 551.644, 11.851, 529.418, 274.741, 977.479, 17.143, 813.157, 674.033, 806.168, 909.773, 107.016, 96.314, 148.897, 191.932, 526.456, 815.214, 267.325, 396.896, 373.052, 406.027, 565.002, 990.233, 225.857, 684.042, 847.867, 653.736, 858.219, 759.586, 93.501,), (379.264, 552.701, 56.115, 9.450, 171.384, 499.858, 433.910, 784.376, 565.857, 857.960, 95.362, 528.159, 42.552, 211.417, 868.117, 887.554, 475.500, 46.562, 74.348, 925.585, 899.312, 563.510, 32.902, 928.766, 314.485, 961.469, 587.036, 752.254, 712.711, 398.296, 76.937, 162.450, 240.472, 834.651, 389.157, 896.526, 331.730, 755.609, 139.951, 988.478, 724.164, 500.793, 974.323, 53.696, 437.088, 838.675, 340.593, 769.006, 954.858, 396.703,), (773.555, 29.626, 273.327, 992.586, 490.603, 355.811, 941.143, 431.848, 679.695, 660.672, 85.694, 618.616, 798.055, 713.109, 82.038, 154.221, 711.677, 633.901, 739.655, 316.678, 106.551, 5.195, 308.267, 359.917, 269.766, 132.507, 187.392, 448.844, 554.740, 408.044, 26.262, 353.914, 93.064, 598.044, 324.430, 385.238, 291.847, 387.800, 84.700, 901.136, 905.208, 978.173, 571.960, 169.583, 380.732, 138.840, 301.131, 493.124, 63.267, 434.676,), (421.102, 484.231, 76.921, 251.700, 246.590, 625.034, 593.806, 195.548, 106.972, 304.658, 948.823, 332.217, 620.192, 804.076, 329.542, 334.736, 815.475, 859.508, 974.225, 136.124, 320.665, 947.279, 200.851, 314.183, 964.575, 968.725, 291.448, 694.958, 491.007, 575.879, 242.424, 376.055, 816.495, 392.935, 113.888, 563.851, 592.227, 545.629, 681.713, 550.099, 953.005, 461.622, 708.367, 438.455, 291.331, 692.835, 818.966, 795.657, 409.142, 499.303,), (633.336, 242.021, 658.663, 715.236, 789.077, 73.965, 990.701, 479.235, 400.805, 506.613, 920.392, 691.709, 543.645, 790.721, 359.529, 895.502, 536.906, 638.180, 84.982, 768.954, 657.602, 355.009, 647.000, 44.297, 983.608, 677.472, 399.618, 752.683, 965.717, 430.456, 10.548, 258.738, 510.676, 518.798, 580.518, 575.235, 445.779, 391.134, 772.342, 588.590, 500.466, 344.967, 24.563, 104.549, 415.975, 961.728, 116.069, 940.676, 141.675, 311.890,), (455.333, 206.867, 482.926, 476.163, 438.166, 696.763, 318.909, 300.264, 810.186, 115.085, 849.180, 647.970, 677.139, 164.354, 983.900, 243.913, 174.453, 160.136, 559.849, 958.463, 231.856, 405.047, 184.452, 640.479, 432.134, 29.192, 614.107, 197.324, 592.203, 388.836, 704.736, 205.784, 752.325, 808.730, 62.564, 101.752, 871.979, 186.960, 325.985, 457.550, 262.353, 862.637, 527.715, 639.109, 596.971, 611.308, 587.005, 347.925, 845.518, 617.363,), (813.738, 705.988, 297.445, 614.485, 84.752, 133.948, 117.862, 305.380, 183.045, 693.437, 510.825, 418.239, 137.867, 383.710, 185.754, 635.502, 693.433, 645.260, 999.900, 554.913, 489.642, 140.297, 314.580, 451.001, 53.611, 359.039, 9.583, 136.535, 815.216, 963.829, 505.438, 494.970, 684.697, 415.630, 839.892, 488.700, 82.671, 30.861, 761.057, 292.090, 274.853, 537.609, 168.209, 457.321, 742.518, 765.920, 549.726, 113.211, 114.207, 775.113,), (823.283, 366.862, 822.611, 41.611, 718.980, 546.353, 989.776, 102.416, 830.071, 751.345, 297.709, 999.313, 449.732, 348.577, 816.729, 439.070, 993.958, 775.632, 236.946, 810.703, 587.924, 350.631, 710.754, 632.771, 165.982, 139.235, 206.620, 206.943, 59.358, 350.815, 281.085, 538.769, 323.654, 704.054, 289.333, 267.343, 858.017, 985.488, 679.299, 95.225, 962.772, 785.691, 918.769, 992.486, 867.048, 126.888, 866.079, 249.677, 711.395, 828.482,), (761.474, 676.235, 489.459, 577.426, 268.717, 414.225, 451.992, 633.628, 880.125, 93.095, 515.613, 278.226, 936.336, 369.071, 950.254, 327.289, 2.473, 774.135, 732.724, 730.932, 458.449, 664.144, 358.223, 63.331, 534.424, 217.830, 429.643, 211.851, 268.537, 828.344, 337.755, 577.934, 566.142, 485.338, 343.740, 682.552, 48.409, 99.575, 783.890, 459.582, 124.237, 857.652, 441.286, 0.676, 958.032, 202.318, 688.592, 131.913, 649.997, 158.977,), (932.726, 274.019, 654.588, 250.389, 371.844, 903.800, 165.525, 396.342, 305.509, 699.441, 234.144, 655.485, 703.698, 1.086, 476.807, 132.700, 226.191, 679.983, 9.287, 695.597, 817.109, 988.155, 422.314, 132.175, 70.828, 383.070, 730.763, 102.427, 313.351, 880.989, 137.129, 773.460, 753.158, 133.146, 992.940, 142.853, 530.508, 8.475, 650.020, 440.099, 722.432, 628.080, 151.374, 411.710, 686.566, 859.963, 86.688, 100.465, 752.446, 589.574,), (384.032, 963.249, 314.504, 139.830, 276.968, 84.249, 553.397, 600.008, 607.593, 778.970, 690.476, 847.892, 658.405, 301.649, 517.749, 509.523, 747.844, 295.542, 54.569, 897.913, 954.672, 494.888, 112.744, 499.583, 593.930, 528.287, 977.697, 986.883, 933.924, 131.983, 860.814, 568.380, 365.412, 682.942, 762.726, 954.453, 770.367, 16.689, 67.533, 262.185, 39.827, 60.469, 789.290, 506.611, 628.571, 501.049, 415.432, 701.811, 82.428, 536.565,), (616.047, 277.468, 309.907, 511.305, 203.197, 808.060, 536.390, 390.732, 634.294, 834.526, 681.056, 66.115, 698.676, 729.958, 846.468, 57.906, 86.213, 434.484, 453.372, 608.832, 309.290, 741.694, 740.658, 119.443, 707.901, 701.500, 163.833, 953.016, 523.005, 782.987, 720.766, 166.963, 126.931, 781.124, 268.764, 886.415, 771.430, 29.356, 807.108, 271.981, 63.872, 712.323, 576.652, 77.070, 455.198, 360.124, 499.610, 566.886, 367.688, 255.116,), (102.905, 573.912, 722.778, 228.404, 508.825, 44.038, 862.928, 244.551, 471.765, 382.979, 150.085, 931.160, 857.485, 552.865, 913.948, 740.666, 419.369, 321.802, 416.257, 720.288, 271.258, 77.887, 372.808, 502.041, 901.941, 179.344, 804.338, 981.414, 954.079, 68.926, 465.094, 282.307, 844.847, 327.301, 553.091, 7.969, 200.671, 563.807, 303.909, 622.718, 463.927, 591.691, 493.361, 772.613, 195.422, 900.443, 760.482, 245.127, 6.378, 410.036,), (232.998, 346.424, 839.574, 877.199, 950.990, 1.462, 657.304, 849.006, 727.215, 103.949, 529.814, 238.168, 492.029, 59.895, 996.959, 711.652, 93.027, 921.274, 897.287, 519.759, 700.847, 372.492, 974.555, 84.902, 95.577, 133.514, 819.963, 74.830, 567.821, 434.983, 964.218, 236.772, 260.991, 315.009, 800.817, 700.726, 735.353, 318.058, 271.956, 74.687, 202.713, 779.937, 584.708, 155.411, 164.375, 466.055, 406.513, 535.925, 964.635, 207.636,), (308.308, 265.008, 119.914, 157.615, 686.055, 826.387, 696.878, 40.330, 835.925, 327.794, 91.213, 248.219, 355.743, 513.460, 677.183, 260.167, 990.677, 31.082, 404.392, 452.201, 748.078, 249.853, 462.043, 803.897, 139.793, 11.957, 830.372, 982.567, 130.715, 823.673, 372.240, 630.298, 644.685, 582.324, 258.821, 812.747, 21.800, 64.472, 902.496, 443.431, 128.792, 905.078, 829.356, 331.550, 42.696, 460.995, 167.989, 573.884, 821.685, 394.999,), (29.482, 683.213, 172.807, 214.716, 187.151, 279.855, 883.424, 34.649, 619.177, 245.785, 295.105, 411.992, 550.689, 60.979, 279.776, 137.236, 199.458, 884.653, 525.814, 630.754, 802.168, 794.846, 989.404, 781.916, 359.112, 544.518, 484.679, 912.677, 502.393, 388.381, 179.816, 318.878, 219.020, 895.765, 778.538, 58.591, 991.531, 529.432, 766.842, 999.606, 973.980, 100.134, 656.864, 266.527, 816.285, 917.259, 55.909, 996.392, 219.412, 846.505,), (797.391, 354.805, 839.222, 845.221, 176.100, 592.520, 806.210, 697.627, 913.980, 28.207, 700.561, 947.559, 563.606, 563.109, 188.234, 988.006, 881.626, 492.227, 309.053, 490.436, 90.257, 232.623, 218.809, 526.449, 0.683, 917.896, 201.464, 130.490, 716.938, 918.781, 844.284, 323.589, 21.913, 586.609, 917.224, 774.366, 846.481, 860.669, 960.559, 373.591, 941.923, 395.596, 101.032, 301.765, 136.452, 157.504, 948.694, 791.843, 960.662, 649.180,), (174.203, 968.743, 693.560, 928.845, 786.992, 223.237, 588.950, 175.352, 306.823, 688.498, 127.348, 728.831, 948.788, 948.698, 391.601, 994.283, 965.184, 32.383, 602.389, 921.038, 967.532, 220.895, 565.550, 936.688, 140.643, 745.343, 237.996, 982.380, 167.887, 885.320, 88.726, 708.966, 639.103, 886.654, 446.630, 265.212, 249.541, 67.795, 256.658, 108.035, 1.281, 385.937, 732.584, 969.102, 884.552, 493.082, 378.713, 546.024, 101.429, 479.489,), (864.051, 650.970, 687.160, 162.657, 73.709, 845.762, 294.909, 318.717, 951.662, 74.200, 170.105, 375.458, 731.985, 547.046, 898.124, 93.105, 594.031, 613.645, 482.737, 30.993, 942.442, 164.997, 889.753, 157.104, 101.258, 205.550, 189.984, 697.197, 722.038, 729.974, 265.265, 281.523, 237.548, 49.635, 571.770, 839.855, 153.590, 360.829, 427.606, 294.549, 662.020, 600.216, 199.675, 25.736, 170.832, 291.811, 81.936, 843.769, 308.353, 397.467,), (489.097, 661.040, 91.137, 544.126, 184.881, 885.493, 369.402, 445.775, 263.296, 465.058, 226.242, 268.562, 61.639, 752.235, 667.468, 85.707, 343.791, 541.449, 970.706, 589.726, 553.602, 840.797, 818.405, 418.632, 535.514, 866.894, 474.824, 881.654, 476.262, 78.955, 902.777, 714.306, 502.101, 900.455, 800.450, 677.535, 620.273, 120.282, 757.189, 172.879, 984.368, 972.165, 808.461, 126.186, 423.375, 988.280, 435.393, 997.269, 627.226, 834.108,), (257.886, 910.804, 914.174, 67.012, 388.099, 397.355, 326.127, 276.201, 458.157, 873.466, 786.551, 623.058, 522.562, 419.594, 414.442, 148.200, 587.934, 758.377, 939.650, 924.928, 562.684, 100.990, 286.093, 535.633, 343.869, 410.890, 383.043, 485.585, 608.962, 37.466, 275.414, 143.853, 608.655, 693.661, 38.783, 889.574, 331.494, 237.579, 745.750, 920.835, 897.031, 20.233, 817.386, 303.055, 280.347, 491.619, 696.183, 98.217, 868.900, 134.386,), (974.219, 443.111, 825.823, 269.420, 416.773, 645.555, 187.936, 211.391, 823.969, 740.945, 759.493, 867.189, 821.017, 515.270, 158.972, 311.123, 506.795, 135.650, 851.295, 879.333, 28.948, 192.763, 832.930, 836.977, 249.490, 456.449, 918.031, 704.634, 273.977, 823.506, 505.126, 635.410, 123.873, 30.563, 372.467, 594.044, 177.584, 870.481, 586.880, 349.760, 163.514, 894.492, 748.961, 688.851, 285.069, 386.566, 162.907, 572.258, 964.918, 857.111,), (647.380, 677.695, 269.084, 409.507, 20.052, 780.304, 767.573, 8.898, 911.515, 647.372, 601.142, 8.464, 252.390, 805.086, 305.460, 967.024, 642.740, 423.807, 376.475, 348.709, 251.999, 466.698, 677.196, 824.311, 397.153, 102.312, 511.514, 662.355, 843.284, 374.230, 647.342, 609.050, 298.476, 108.105, 63.837, 988.361, 640.595, 861.492, 261.147, 711.094, 892.374, 298.791, 149.929, 765.474, 899.687, 805.430, 802.364, 600.033, 660.530, 680.756,), (721.283, 655.401, 997.469, 259.426, 418.566, 388.275, 35.319, 708.069, 572.042, 189.915, 726.550, 222.363, 534.635, 784.897, 906.527, 671.868, 507.315, 845.419, 840.639, 876.495, 181.136, 97.603, 127.944, 258.652, 808.344, 762.918, 183.068, 679.712, 335.632, 89.300, 355.283, 744.210, 307.085, 788.090, 331.317, 260.559, 294.051, 851.214, 470.537, 866.393, 583.575, 944.301, 71.216, 889.426, 500.477, 867.498, 381.669, 298.356, 54.062, 854.240,), (137.365, 200.297, 409.192, 569.404, 906.621, 457.571, 316.383, 715.668, 778.941, 487.581, 631.033, 176.824, 634.499, 4.714, 273.528, 761.193, 168.606, 764.484, 489.577, 763.569, 88.041, 614.480, 633.488, 403.393, 965.328, 383.316, 37.725, 199.414, 373.101, 14.077, 322.213, 833.229, 190.577, 676.748, 626.687, 248.823, 693.525, 344.350, 128.931, 383.550, 588.671, 167.020, 823.844, 298.202, 290.828, 727.832, 596.370, 337.835, 887.974, 995.472,), (342.733, 901.384, 359.251, 188.426, 948.084, 918.205, 403.392, 228.418, 727.169, 131.206, 734.077, 589.693, 168.985, 366.591, 650.540, 37.363, 876.544, 255.789, 534.733, 48.587, 994.680, 661.970, 653.568, 19.742, 689.750, 416.776, 380.254, 547.282, 474.396, 153.127, 695.192, 630.311, 301.116, 661.665, 662.483, 269.977, 605.634, 137.164, 830.653, 104.914, 718.766, 117.735, 114.013, 106.264, 198.647, 199.749, 262.994, 523.146, 201.673, 703.437,), (295.351, 39.406, 496.355, 207.694, 933.124, 330.604, 2.738, 671.633, 906.880, 835.232, 669.022, 149.144, 90.083, 511.705, 723.564, 101.290, 255.892, 231.160, 988.666, 296.022, 464.277, 99.809, 174.702, 39.445, 290.567, 801.596, 312.707, 738.540, 94.998, 758.204, 45.889, 851.999, 663.355, 170.512, 357.527, 437.715, 621.807, 878.476, 92.951, 814.964, 182.869, 400.778, 962.310, 271.833, 385.715, 850.672, 799.899, 648.846, 796.910, 113.057,), (696.170, 58.646, 942.467, 159.395, 416.028, 590.750, 802.265, 678.393, 181.261, 379.751, 358.599, 28.816, 684.464, 838.536, 973.445, 130.655, 920.398, 112.937, 411.284, 45.972, 261.613, 314.238, 704.568, 677.929, 767.529, 576.649, 565.012, 977.896, 669.844, 338.301, 523.103, 700.580, 95.242, 661.713, 248.577, 345.749, 676.296, 384.877, 839.033, 558.344, 987.792, 54.566, 643.399, 156.927, 848.846, 851.871, 869.416, 74.865, 491.648, 240.892,), (970.145, 50.351, 222.707, 643.317, 403.277, 235.000, 459.095, 801.261, 448.099, 856.589, 447.068, 118.707, 497.373, 653.373, 102.643, 412.339, 557.131, 0.170, 90.896, 604.203, 619.066, 304.598, 508.338, 206.851, 671.474, 950.355, 363.342, 54.275, 222.918, 454.460, 560.151, 619.758, 473.134, 657.167, 715.914, 114.023, 759.320, 221.747, 341.357, 829.884, 964.434, 291.984, 521.985, 702.096, 45.778, 164.155, 140.449, 716.856, 721.559, 106.962,), (610.852, 187.543, 930.216, 392.896, 457.046, 781.420, 716.788, 107.766, 414.470, 926.627, 837.466, 588.811, 772.145, 450.505, 658.457, 956.190, 134.636, 498.829, 530.364, 48.572, 935.308, 838.816, 482.932, 508.333, 921.162, 177.202, 578.554, 730.495, 128.382, 387.406, 600.546, 882.696, 504.110, 384.663, 979.501, 915.910, 762.375, 273.596, 963.594, 970.492, 452.846, 133.372, 412.745, 700.014, 748.427, 298.913, 701.494, 860.708, 711.874, 935.512,), (632.583, 200.889, 624.168, 290.579, 345.294, 672.374, 981.335, 650.086, 958.326, 503.893, 694.312, 322.381, 115.405, 352.238, 480.370, 570.638, 667.157, 417.478, 747.869, 841.389, 285.928, 847.301, 808.305, 522.730, 25.272, 145.327, 670.254, 199.902, 750.185, 160.935, 286.616, 250.561, 839.382, 690.595, 295.141, 753.594, 32.306, 814.026, 102.480, 868.035, 739.492, 864.841, 742.413, 561.271, 237.595, 784.307, 798.822, 287.639, 664.494, 926.485,), (387.555, 956.702, 975.823, 312.654, 552.125, 12.965, 251.341, 620.562, 780.925, 870.118, 829.983, 911.486, 704.629, 647.632, 755.108, 546.615, 603.387, 776.147, 964.290, 294.178, 177.426, 682.659, 186.977, 173.795, 513.847, 377.198, 428.508, 556.599, 130.067, 583.986, 255.016, 330.563, 709.689, 154.806, 153.703, 322.630, 50.855, 932.382, 615.609, 661.890, 490.551, 572.677, 358.086, 784.031, 317.859, 219.962, 183.869, 68.176, 505.140, 415.931,), (537.036, 91.946, 221.198, 213.354, 331.884, 360.798, 218.445, 752.656, 530.499, 996.631, 823.688, 981.133, 8.804, 668.938, 445.667, 904.496, 613.701, 621.132, 958.897, 682.551, 320.937, 915.932, 944.988, 385.876, 540.237, 282.829, 911.336, 822.109, 374.958, 802.817, 445.557, 43.852, 898.282, 192.512, 513.895, 948.212, 167.395, 956.276, 537.975, 7.331, 65.472, 670.335, 773.582, 864.951, 424.209, 103.927, 537.754, 702.743, 976.224, 775.207,), (646.561, 939.751, 746.891, 153.755, 459.225, 331.715, 87.521, 54.268, 794.215, 558.084, 574.962, 227.646, 258.648, 388.033, 630.217, 433.025, 16.891, 673.299, 534.834, 641.423, 618.349, 755.961, 585.608, 700.882, 72.589, 928.048, 106.136, 786.892, 301.351, 86.505, 765.274, 437.676, 395.113, 660.723, 473.988, 533.468, 136.381, 390.993, 797.485, 545.724, 960.134, 144.076, 677.289, 914.169, 795.017, 729.325, 373.131, 949.465, 553.524, 555.070,), (123.235, 5.030, 596.655, 537.152, 946.979, 304.691, 748.297, 903.546, 343.852, 412.654, 645.932, 512.553, 160.954, 220.788, 834.748, 194.326, 181.358, 799.660, 852.251, 851.388, 932.370, 994.313, 461.265, 549.665, 290.653, 67.363, 98.202, 725.589, 487.190, 330.851, 128.215, 655.473, 100.045, 619.516, 900.119, 317.786, 450.646, 616.342, 305.675, 584.170, 565.552, 364.493, 316.333, 428.307, 4.832, 246.175, 221.526, 739.816, 436.124, 840.110,), (134.306, 732.973, 877.886, 462.848, 358.742, 305.472, 551.672, 175.770, 606.628, 841.793, 858.714, 140.002, 538.618, 263.235, 886.336, 76.462, 75.400, 18.630, 507.178, 31.194, 581.890, 405.134, 590.036, 906.304, 551.594, 543.469, 998.728, 472.104, 775.197, 365.819, 223.307, 772.145, 733.227, 290.999, 464.701, 510.412, 396.785, 502.167, 662.678, 847.982, 806.540, 614.036, 165.844, 514.137, 446.220, 179.145, 948.712, 659.413, 967.736, 735.920,), (483.503, 358.796, 218.819, 487.642, 62.861, 369.435, 42.808, 206.774, 907.726, 361.251, 469.674, 454.643, 46.437, 980.590, 324.071, 703.736, 521.373, 829.647, 834.872, 263.316, 544.350, 173.850, 653.675, 365.297, 653.337, 835.469, 516.663, 376.329, 907.242, 516.372, 352.898, 867.719, 486.862, 482.110, 604.211, 501.837, 138.968, 165.606, 77.088, 643.477, 211.517, 186.906, 362.223, 717.303, 118.330, 230.346, 811.173, 720.256, 481.517, 478.815,), (210.704, 161.246, 833.365, 22.446, 43.144, 573.487, 161.129, 629.540, 39.191, 572.269, 55.912, 258.260, 180.038, 958.238, 599.358, 563.605, 18.620, 719.547, 661.745, 283.455, 85.749, 449.203, 992.776, 867.302, 170.567, 830.364, 600.839, 794.616, 820.453, 181.884, 659.649, 264.521, 724.187, 342.671, 453.470, 590.596, 229.814, 385.462, 108.570, 202.349, 859.592, 504.360, 419.795, 149.561, 96.299, 475.869, 614.748, 39.037, 783.868, 503.329,), (117.516, 482.241, 130.329, 603.475, 827.522, 896.969, 774.870, 649.232, 522.044, 370.377, 43.438, 529.817, 229.486, 802.300, 789.556, 381.236, 589.048, 741.394, 761.539, 696.708, 97.717, 133.883, 477.141, 232.513, 859.053, 283.267, 876.766, 408.647, 189.021, 709.149, 789.422, 577.987, 117.829, 7.194, 654.546, 687.767, 315.902, 362.270, 154.779, 651.576, 253.633, 854.977, 423.562, 365.644, 275.561, 680.606, 754.972, 413.228, 783.788, 482.468,), (370.213, 554.911, 253.778, 306.653, 344.356, 705.495, 735.782, 854.999, 659.283, 747.814, 446.595, 699.311, 161.795, 214.458, 400.561, 338.438, 550.576, 697.057, 706.790, 160.738, 964.570, 5.291, 91.089, 145.328, 925.862, 435.377, 64.080, 221.770, 80.040, 37.755, 384.326, 984.448, 613.966, 521.006, 711.606, 597.473, 945.716, 820.229, 640.291, 438.972, 201.707, 656.884, 803.910, 286.007, 33.609, 599.515, 515.814, 231.671, 169.483, 36.061,), (239.257, 0.287, 157.445, 996.817, 779.673, 353.128, 395.143, 586.080, 517.743, 736.926, 68.617, 90.166, 284.494, 829.765, 915.009, 348.095, 963.288, 276.508, 606.363, 194.525, 929.714, 574.907, 261.108, 407.135, 106.465, 72.386, 294.252, 947.015, 802.942, 956.709, 876.113, 813.147, 574.894, 694.192, 966.055, 563.169, 769.967, 757.473, 961.239, 458.848, 460.720, 586.827, 27.351, 116.268, 67.554, 633.662, 994.198, 676.913, 229.930, 315.731,), (955.446, 516.504, 9.723, 832.177, 248.258, 93.009, 673.698, 821.075, 76.029, 931.396, 476.555, 353.538, 894.316, 269.076, 947.118, 683.107, 909.927, 498.999, 199.667, 735.429, 872.742, 206.779, 202.767, 249.695, 618.597, 155.996, 106.689, 920.188, 675.812, 663.424, 613.827, 764.244, 541.474, 42.263, 482.292, 620.667, 492.340, 985.361, 897.000, 868.769, 490.805, 984.395, 916.007, 280.267, 222.093, 576.665, 54.148, 799.270, 478.320, 540.768,), (502.475, 393.717, 686.339, 174.800, 976.507, 698.245, 460.071, 689.211, 11.820, 210.752, 581.008, 325.436, 612.778, 259.701, 548.563, 237.139, 471.345, 613.084, 365.924, 498.770, 210.532, 700.713, 372.310, 854.326, 279.630, 179.889, 131.139, 575.893, 228.567, 99.870, 269.949, 235.805, 432.219, 380.883, 145.844, 959.148, 149.548, 805.668, 176.607, 499.598, 995.562, 849.390, 517.007, 720.560, 785.333, 300.034, 562.138, 567.835, 398.402, 690.543,), (60.036, 813.818, 476.539, 629.862, 449.855, 334.657, 360.962, 560.350, 931.845, 257.715, 19.835, 121.237, 867.369, 963.117, 198.958, 575.894, 649.131, 174.539, 780.309, 355.109, 673.193, 487.494, 736.526, 889.632, 381.071, 286.524, 631.712, 144.848, 167.576, 807.717, 337.388, 631.273, 571.638, 848.901, 71.344, 161.999, 228.218, 316.878, 291.356, 267.472, 644.644, 271.248, 444.898, 862.806, 363.165, 586.920, 965.526, 413.989, 183.886, 23.101,), (727.772, 662.023, 940.437, 700.697, 80.065, 166.910, 103.009, 63.792, 878.691, 548.434, 26.365, 397.013, 764.996, 83.251, 262.452, 155.274, 654.833, 885.471, 309.371, 247.397, 284.175, 626.481, 131.277, 838.518, 28.571, 663.549, 860.369, 325.201, 476.155, 974.982, 540.584, 272.414, 444.845, 969.542, 697.623, 177.037, 597.584, 631.048, 635.851, 563.984, 523.460, 639.528, 309.661, 347.168, 539.668, 804.708, 441.860, 365.783, 259.807, 303.573,), (0.092, 815.380, 847.946, 343.885, 469.198, 8.317, 922.088, 946.958, 477.067, 9.133, 430.435, 292.923, 231.215, 7.218, 373.644, 411.736, 560.554, 394.752, 163.269, 737.118, 389.723, 378.358, 262.980, 422.239, 239.562, 764.538, 909.954, 807.575, 684.640, 284.677, 742.927, 808.809, 412.904, 853.482, 182.407, 289.595, 636.893, 617.729, 272.134, 622.754, 187.789, 19.395, 49.632, 534.971, 185.938, 102.273, 269.195, 715.196, 727.107, 232.810,), (150.684, 493.580, 341.913, 311.577, 799.462, 997.963, 463.684, 791.425, 330.249, 843.546, 951.644, 56.038, 775.653, 71.385, 470.147, 192.192, 841.539, 817.251, 828.252, 121.962, 768.146, 248.962, 771.225, 442.961, 737.619, 33.510, 460.835, 770.996, 521.139, 982.116, 472.826, 681.442, 312.109, 323.063, 629.164, 42.186, 937.421, 520.926, 253.313, 638.520, 198.724, 887.520, 863.658, 217.788, 112.928, 632.909, 324.513, 167.360, 276.100, 119.650,), (789.144, 8.930, 41.993, 783.475, 475.271, 596.212, 371.399, 89.480, 157.698, 91.402, 618.645, 930.849, 996.973, 625.132, 59.775, 644.575, 701.328, 791.466, 125.827, 232.675, 981.676, 788.562, 756.698, 805.525, 438.719, 192.965, 689.250, 358.167, 134.732, 898.663, 485.302, 437.500, 294.715, 697.038, 189.776, 186.627, 347.665, 732.402, 275.324, 831.350, 914.474, 554.520, 68.877, 155.025, 295.673, 263.349, 367.113, 0.720, 638.026, 379.044,), (185.534, 18.720, 856.829, 776.017, 238.787, 721.004, 658.289, 538.957, 387.491, 524.070, 497.549, 551.468, 613.039, 322.600, 640.904, 110.464, 523.201, 66.680, 835.193, 129.774, 873.493, 202.079, 485.370, 98.754, 571.012, 836.300, 657.921, 525.821, 701.314, 255.939, 366.354, 605.954, 70.874, 930.211, 208.738, 491.406, 889.767, 79.957, 801.020, 60.700, 558.020, 938.244, 415.953, 369.069, 708.434, 823.643, 265.816, 40.653, 70.388, 303.905,), (944.603, 960.587, 97.119, 725.172, 531.661, 189.460, 526.192, 248.763, 625.325, 178.898, 749.194, 415.185, 105.514, 638.624, 357.963, 458.721, 665.242, 882.824, 166.809, 180.397, 401.557, 337.513, 158.176, 998.434, 443.012, 352.324, 315.155, 991.463, 324.654, 371.720, 764.184, 430.588, 725.868, 608.413, 563.145, 213.928, 765.708, 926.147, 254.089, 961.652, 447.500, 397.091, 726.313, 982.956, 595.763, 517.609, 993.412, 301.842, 300.334, 221.887,), (855.720, 21.701, 821.940, 689.719, 275.957, 553.663, 556.323, 925.922, 154.715, 37.736, 355.656, 138.408, 367.083, 582.156, 232.994, 811.074, 91.905, 399.798, 917.882, 734.266, 723.581, 792.904, 172.902, 825.705, 689.595, 576.232, 907.656, 595.231, 300.393, 730.789, 576.284, 78.477, 55.923, 770.902, 347.930, 817.142, 416.522, 867.831, 869.787, 226.718, 652.779, 602.302, 11.434, 777.419, 382.487, 304.783, 41.182, 539.930, 149.580, 502.402,), (220.797, 50.520, 731.579, 392.883, 445.616, 595.132, 504.729, 222.086, 289.783, 394.322, 132.189, 82.545, 571.441, 49.311, 399.191, 85.079, 501.823, 773.825, 130.375, 134.871, 559.296, 487.861, 652.248, 196.099, 615.997, 735.668, 246.246, 71.644, 776.772, 323.412, 924.138, 89.595, 671.748, 423.541, 348.308, 320.738, 593.877, 24.207, 304.819, 987.652, 616.221, 990.159, 442.210, 145.818, 44.878, 818.172, 199.685, 373.821, 757.734, 852.764,), (112.372, 54.538, 948.941, 926.730, 868.752, 820.134, 13.733, 693.795, 111.278, 450.062, 22.748, 209.010, 538.005, 203.801, 523.266, 258.658, 483.026, 729.924, 141.347, 698.755, 18.389, 583.005, 663.528, 43.482, 170.320, 284.014, 789.188, 617.965, 53.085, 654.758, 8.334, 388.645, 271.310, 852.083, 660.100, 864.282, 19.078, 867.410, 649.411, 231.169, 380.701, 976.612, 99.604, 315.456, 866.773, 531.561, 186.417, 500.651, 457.986, 926.350,), (21.488, 247.392, 529.415, 333.570, 393.400, 157.005, 346.780, 351.913, 625.231, 236.205, 978.244, 501.251, 811.893, 625.792, 878.677, 890.402, 814.859, 29.466, 554.939, 280.194, 151.708, 897.175, 656.932, 87.795, 382.207, 960.606, 612.448, 625.525, 227.428, 240.361, 152.749, 970.537, 909.735, 329.285, 542.234, 206.757, 138.577, 541.354, 800.105, 862.588, 308.998, 705.136, 523.805, 135.252, 995.686, 975.777, 145.152, 933.030, 917.112, 317.496,), (557.340, 948.602, 118.387, 317.598, 879.638, 727.080, 765.435, 880.132, 414.040, 411.252, 443.004, 933.769, 894.130, 933.250, 273.796, 779.108, 106.771, 184.746, 762.447, 611.982, 266.863, 567.043, 230.911, 232.183, 687.378, 359.261, 688.141, 476.602, 502.325, 604.712, 712.019, 373.959, 852.129, 491.446, 137.513, 193.260, 32.230, 764.539, 15.014, 269.859, 413.044, 742.367, 988.243, 757.777, 66.136, 927.103, 985.628, 867.129, 489.943, 324.896,), (457.545, 246.784, 404.867, 41.826, 735.332, 380.374, 312.897, 611.505, 742.468, 593.806, 525.231, 875.783, 786.920, 520.649, 451.610, 827.010, 42.416, 995.836, 518.703, 395.611, 735.170, 557.701, 516.127, 630.649, 49.295, 291.179, 398.040, 304.554, 827.621, 461.347, 422.462, 613.134, 54.547, 516.970, 142.240, 829.894, 451.698, 722.647, 113.172, 778.731, 937.842, 696.114, 135.307, 413.559, 450.886, 178.879, 590.315, 712.634, 201.932, 454.683,), (250.082, 691.717, 907.162, 796.486, 718.375, 123.544, 114.165, 449.398, 362.943, 523.817, 384.087, 791.067, 511.666, 949.754, 378.679, 380.607, 768.260, 912.253, 565.492, 659.530, 150.090, 868.819, 178.867, 712.047, 419.582, 309.518, 768.630, 446.258, 626.453, 117.554, 131.571, 202.852, 622.560, 253.111, 458.643, 855.411, 543.501, 5.751, 882.703, 237.878, 588.862, 475.712, 410.151, 79.405, 600.102, 244.713, 547.342, 619.766, 557.549, 825.253,), (48.919, 148.256, 653.277, 36.721, 853.829, 666.997, 838.014, 298.442, 920.385, 49.031, 416.957, 178.248, 672.090, 611.090, 691.514, 594.969, 787.308, 177.419, 455.338, 578.966, 931.480, 93.126, 299.294, 368.569, 378.939, 67.667, 427.324, 550.471, 292.833, 134.546, 694.619, 274.419, 527.053, 524.646, 695.829, 612.054, 109.302, 729.729, 628.979, 987.189, 483.663, 688.654, 933.892, 986.278, 287.187, 608.845, 316.489, 525.391, 995.024, 353.853,)))" + "Matrix(((639.4267984578837, 25.010755222666937, 275.02931836911927, 223.21073814882274, 736.4712141640124, 676.6994874229113, 892.1795677048455, 86.93883262941615, 421.9218196852704, 29.797219438070343, 218.63797480360336, 505.3552881033624, 26.535969683863627, 198.8376506866485, 649.8844377795232, 544.9414806032166, 220.4406220406967, 589.2656838759087, 809.4304566778267, 6.498759678061017, 805.8192518328079, 698.1393949882269, 340.25051651799185, 155.47949981178155, 957.2130722067812, 336.59454511262675, 92.7458433801479, 96.71637683346401, 847.4943663474597, 603.7260313668911, 807.1282732743801, 729.7317866938179, 536.2280914547007, 973.1157639793706, 378.5343772083535, 552.040631273227, 829.4046642529948, 618.5197523642461, 861.7069003107772, 577.352145256762, 704.5718362149235, 45.82438365566222, 227.89827565154687, 289.38796360210716, 79.79197692362749, 232.79088636103018, 101.00142940972911, 277.97360311009214, 635.6844442644002, 364.8321789700842,), (370.18096711688264, 209.50703077148768, 266.97782204911334, 936.6545877124939, 648.0353852465936, 609.1310056669882, 171.13864819809697, 729.1267979503492, 163.40249376192838, 379.4554417576478, 989.5233506365953, 639.9997598540929, 556.9497437746462, 684.6142509898746, 842.8519201898096, 775.9999115462448, 229.04807196410437, 32.10024390403776, 315.45304805908194, 267.74087597570275, 210.98284358632645, 942.9097143350544, 876.3676264726688, 314.67788079847793, 655.43866529488, 395.63190106066423, 914.5475897405435, 458.8518525873988, 264.88016649805246, 246.62750769398346, 561.3681341631508, 262.7416085229353, 584.5859902235405, 897.822883602477, 399.4005051403973, 219.32075915728333, 997.5376064951103, 509.5262936764645, 90.9094121737939, 47.11637542473457, 109.64913035065915, 627.44604170309, 792.0793643629642, 422.159966799684, 63.52770615195713, 381.61928650653675, 996.1213802400968, 529.114345099137, 971.0783776136182, 860.779702234498,), (11.481021942819636, 720.7218193601947, 681.7103690265748, 536.9703304087951, 266.82518995254276, 640.961798579808, 111.55217359587644, 434.765250669105, 453.72370632920644, 953.8159275210801, 875.852940378194, 263.38905075109074, 500.5861130502983, 178.65188053013136, 912.6278393448205, 870.5185698367669, 298.4447914486329, 638.9494948660051, 608.9702114381723, 152.83926854963482, 762.5108000751512, 539.3790301196258, 778.6264786305583, 530.3536721951775, 0.5718961279435053, 324.1560570046731, 19.476742385832303, 929.0986162646171, 878.7218778231842, 831.6655293611794, 307.5141254026614, 57.92516649418755, 878.0095992040406, 946.9494452979941, 85.65345206787877, 485.9904633166138, 69.2125184683836, 760.6021652572316, 765.8344293069878, 128.3914644997628, 475.28237809873133, 549.8035934949439, 265.0566289400591, 872.4330410852574, 423.13794020088693, 211.79820544208206, 539.2960887794584, 729.9310690899762, 201.1510633896959, 311.71629130089497,), (995.1493566608947, 649.8780576394535, 438.10008391450407, 517.5758410355907, 121.00419586826573, 224.69733703155737, 338.08556214745533, 588.3087184572333, 230.114732596577, 220.21738445155947, 70.99308600903254, 631.102957270099, 228.94178381115438, 905.420013006128, 859.6354002537465, 70.85734988865345, 238.00463436899523, 668.9777782962806, 214.2368073704386, 132.311848725025, 935.514240580671, 571.0430933252844, 472.67102631179415, 784.6194242907534, 807.4969977666434, 190.4099143618777, 96.93081422882332, 431.0511824063775, 423.57862301992077, 467.024668036675, 729.0758494598506, 673.3645472933015, 984.1652113659661, 98.41787115195888, 402.62128210226876, 339.30260539496317, 861.6725363527911, 248.65633392028565, 190.2089084408115, 448.6135478331319, 421.8816398344042, 278.5451446669405, 249.80644788210049, 923.2655992760128, 443.13074505345696, 861.3491047618306, 550.3253124498481, 50.58832952488124, 999.2824684127266, 836.0275850799519,), (968.9962572847512, 926.3669830081276, 848.6957344143054, 166.31111060391402, 485.64112545071845, 213.74729919918167, 401.0402925494526, 58.635399972178924, 378.9731189769161, 985.308843779726, 265.20305817215194, 784.0706019485693, 455.0083673391433, 423.00748599016293, 957.3176408596732, 995.4226894927139, 555.7683234056182, 718.408275296326, 154.79682527406413, 296.7078254945642, 968.7093649691589, 579.1802908162562, 542.1952013742742, 747.9755603790641, 57.16527290748308, 584.1775944589713, 502.85038291951355, 852.7198920482854, 157.43272793948327, 960.7789032744504, 80.11146524058688, 185.8249609807232, 595.0351064500277, 675.2125536040902, 235.2038950009312, 119.88661394712419, 890.2873141294375, 246.21534778862485, 594.5191535334412, 619.3815103321031, 419.2249153358725, 583.6722892912247, 522.7827155319588, 934.7062577364272, 204.25919942353644, 716.1918007894149, 238.68595261584602, 395.7858467912545, 671.6902229599713, 299.99707979876223,), (316.17719627185403, 751.8644924144021, 72.54311449315732, 458.2855226185861, 998.4544408544424, 996.0964478550944, 73.260721099633, 213.1543122670404, 265.20041475040136, 933.2593779937091, 880.8641736864396, 879.2702424845428, 369.52708873888395, 157.74683235723197, 833.744954639807, 703.539925087371, 611.6777657259502, 987.2330636315044, 653.9763177107326, 7.823107152157949, 817.1041351154616, 299.3787521999779, 663.3887149660774, 938.9300039271039, 134.2911143933677, 115.4286704191022, 107.03597770941764, 553.223640884816, 272.3482123148163, 604.8298270302239, 717.6121871387979, 203.59731232745293, 634.2379588850797, 263.98390163040943, 488.53185214937656, 905.3364910793232, 846.1037132948554, 92.29846771273343, 423.57577256372633, 276.68022397225167, 3.5456890877823, 771.119223019627, 637.1133773013796, 261.9552624343482, 741.2309083479308, 551.6804211263913, 427.68691898067937, 9.669699608339965, 75.24386007376704, 883.1063933001429,), (903.9285715598932, 545.5902892055224, 834.5950198860166, 582.509566489794, 148.09378556748266, 127.44551928213876, 308.2583499301337, 898.9814887425899, 796.1223048880418, 860.7025820009028, 898.9246365264746, 210.07653833975405, 249.52973922292443, 102.79362167178563, 780.1162418714426, 884.1347014510089, 406.3773898321168, 620.6615101507128, 154.55333833220465, 929.8810156936744, 864.605696219964, 976.2060329309629, 810.7717199403969, 881.4162046633244, 24.786361898188723, 736.5644717550821, 332.1854679464287, 930.8158860483255, 802.2351389371389, 864.0640283752793, 810.749316574389, 266.80570959447203, 787.3745091354712, 108.09562640295711, 872.1667829060897, 858.5932513377817, 222.43371754566442, 816.586605596929, 460.3032346789421, 305.1908673386006, 795.3454991528617, 227.59548740777035, 23.66443470145152, 193.12978832770867, 328.26195119770654, 864.3529420302864, 966.8891040483611, 279.1249927218714, 641.4817386076277, 399.6783843600609,), (981.1496871982602, 536.2157324787219, 939.2371403247157, 115.34175185142759, 970.400611022228, 178.56781617246364, 962.5343157615555, 265.4663625229686, 108.40254721471109, 434.56375856464433, 728.5450606527043, 313.67731419499125, 606.2088533061433, 511.4230596694781, 385.1954333447272, 576.5880434965995, 254.72250613858193, 708.7852838341706, 1.6912782186294661, 925.5751654990827, 538.4519970927919, 719.4299991448455, 741.9500778394765, 670.6285044329995, 364.2214717812642, 69.97381112631018, 664.2376849112724, 330.2000360425964, 313.91564505835964, 848.0152795063354, 719.7542630139502, 300.3222682112642, 309.28466220865323, 408.3929086192168, 402.40038705772463, 295.655202525947, 127.28779905915322, 420.4463337729083, 940.363670730183, 677.3179452727329, 902.8055457325827, 615.5149159513805, 300.9498745655653, 547.9372131356982, 0.4059396972875273, 286.91371686892717, 429.8881499898346, 579.984781195682, 654.7056237030716, 464.9881902470142,), (442.1597993048074, 213.70140098910028, 473.18618590932624, 901.1808258282542, 796.0247601267803, 169.69139619805475, 84.79553672512175, 515.4520099152164, 632.9408557657957, 335.1882554098009, 818.4234645366643, 751.1381375407323, 672.795670557167, 224.64066599728827, 199.12993272657664, 24.425387726826344, 244.84254407835016, 475.1363442188051, 849.737694624732, 72.82822918456911, 414.44101099771933, 629.7653807377137, 194.4352367397093, 696.3542504905049, 494.37716901043694, 243.98443957843884, 656.058011111784, 5.54481813803176, 750.9644766184729, 770.0461885740251, 106.58729656353894, 425.1461939427341, 175.88668170653165, 957.9660422795397, 517.9577504437408, 50.21838514064092, 249.19827965997166, 848.3363473516597, 456.4618254701725, 801.4166017222644, 667.5777325863531, 987.8924530664481, 595.4523184694197, 950.0396084431559, 891.425925810437, 612.6523227617629, 719.273961275967, 504.778164824402, 830.569169721415, 547.8719506108284,), (897.2081032332621, 743.6554421595849, 474.6744368230533, 259.19154846501937, 247.23973750965956, 637.6614367761563, 765.8136842971654, 521.2998128279821, 626.7484369817813, 274.59744691753826, 77.48335386473582, 285.7281508631525, 271.7151070821846, 319.7095684187623, 540.1522225184564, 138.37406151615573, 231.26147972818677, 693.9498122990523, 706.4191416945522, 64.22885071387807, 407.5993696665867, 542.6111405039153, 415.77423410315595, 206.83438951441025, 420.14351777342563, 904.838478340177, 584.0794142042251, 695.5229864979681, 856.7320323039343, 765.5945761180694, 380.38102892771167, 5.8960835839930725, 351.7588026718247, 753.4751250593858, 853.4479505691046, 953.4303384701062, 419.0212826298219, 747.5156689780508, 546.1323097338391, 603.2525889412414, 220.5386943238189, 219.42163462143617, 435.8359760466366, 29.02481994671524, 336.12954369838246, 679.1418850283497, 404.31666913763706, 165.04473120350883, 467.39014923231014, 127.6277972811607,), (622.2569609740647, 26.96645190513769, 394.0202563397047, 564.3919830247742, 27.102046340312434, 642.7496480093357, 135.69948723056424, 461.69844405151173, 50.28463348862755, 379.1038641881396, 211.66028421148152, 326.84580488130854, 761.2297078940271, 379.12621556413626, 752.0098235547848, 831.9242851552726, 252.2715317823806, 81.90623276164256, 19.38328705001069, 539.4190479225337, 999.9078285092093, 349.9603437201839, 650.144093249875, 781.2330496108949, 651.7546552438894, 754.2332040595262, 949.6117327159889, 199.3606823625329, 20.380017320332342, 152.38234578479037, 126.22097487420625, 669.4588446199107, 563.9695819300191, 217.96454090650363, 699.4649712461509, 766.8980983562408, 167.78914336780227, 607.2474938909317, 747.9256519552858, 114.53287137889767, 819.3011743110851, 964.7207730340876, 108.09874965760658, 25.678425497466016, 311.9572443946952, 677.3472868504088, 958.1728382058959, 396.65444151662734, 715.0147050494684, 75.99647784305996,), (690.6144159329804, 627.2423956010444, 101.90130544597653, 772.480884951224, 850.2932390887963, 600.4116148168441, 121.05506506731511, 983.8443515146713, 782.6353463610196, 347.20376530844896, 428.37801323474446, 370.5708762180562, 505.9607896770779, 341.2311748612832, 849.5756269995774, 822.330918090058, 105.53887064399858, 960.7875672145785, 635.585106101446, 828.7073110024583, 707.308643706077, 435.48714500767704, 733.7953040133918, 965.4737312380777, 270.0823963874008, 808.1992188067559, 538.1729064482557, 483.49750388319615, 435.5744930038943, 731.0262143051223, 268.3955380492253, 851.7131600193319, 830.7310188906034, 86.6628980556744, 881.631184002772, 243.863439190956, 464.7084666032317, 610.3317042305206, 378.98930412826405, 28.69999777008958, 850.9528363124591, 181.8398571576918, 212.119850179739, 797.8323568280965, 340.3388431642836, 880.3199797582254, 701.1837503322016, 276.26857575618493, 10.151114438677112, 948.0625777770312,), (85.61296195802126, 720.0746641041943, 488.5778468487874, 758.1646534824664, 690.609339446523, 645.9028997409523, 490.8213350785862, 792.9328681323175, 93.05335055467168, 221.59640047253015, 691.7871552952018, 306.2060301300884, 581.5555853323672, 473.2604887595248, 530.9219311457678, 425.5038127052148, 745.9354367136096, 330.79129719593016, 702.8549421857689, 270.91642697619636, 251.4036762009415, 120.65588475230582, 192.5842934515174, 119.55474125505283, 535.8639653837498, 762.189609484103, 185.14984243766196, 216.38463987860567, 484.1985872127087, 724.5850010930516, 976.6070228830567, 524.6368691086078, 282.998703274955, 100.52610809079077, 194.11757809107343, 227.48316348255472, 179.44154368540333, 14.148367256063832, 534.1350892676633, 274.3113267832673, 974.2949311027379, 553.3589667986083, 697.4173929101944, 126.2794929584089, 868.461197262944, 490.87869452377885, 872.7197349985346, 574.0642196263323, 469.3969449305102, 440.46879813601026,), (184.36367039008172, 51.37671718357784, 941.0635967681033, 477.7291872656877, 822.1156452994304, 400.70744225193977, 74.0821702556398, 629.4457069519983, 53.609074292533144, 149.19758447365516, 562.8395970845642, 303.8355118422277, 993.9181227102201, 118.45156221146158, 764.4434453261943, 606.3176512429628, 790.7408298434194, 225.68713706503462, 522.5725351302178, 450.5144648887542, 442.72100401932835, 860.1666658261801, 990.0312604667979, 305.3802443193604, 621.0273210721485, 609.6309122451303, 740.089305484545, 947.590200325378, 207.78790582355134, 211.025195305393, 660.4281371920366, 157.05709338087715, 173.81354832643447, 75.06486890116793, 2.675722602885733, 450.5037046177024, 593.8111951195633, 291.2592890310276, 231.47623455602584, 706.9558298790332, 702.9875580937172, 454.03132640704325, 687.3849200712427, 923.9110444825487, 787.8280266467314, 625.0580071642219, 661.1830428533679, 933.6684584455132, 425.13896610421165, 544.5623787105786,), (647.6347234024807, 908.411445212606, 826.6311596500597, 71.40983685581415, 165.92278922072467, 307.6118126142325, 748.9577220696233, 569.2070493190923, 288.61058830584176, 124.35365817470823, 688.6779912120937, 699.7336849757173, 942.6762407440408, 500.4721771179256, 493.7952193450843, 80.44185189930097, 39.86078418361305, 432.02866416077256, 322.32158335829996, 250.3679024120039, 91.3268866309852, 961.9111021928367, 835.9586139061813, 575.1991092199305, 950.7862778063524, 999.5724168774514, 672.2815843032392, 269.5110259670539, 40.231673144616174, 756.2688304125571, 470.50082325163356, 651.5094894336863, 916.0727879267675, 181.4891472231259, 585.3296252778543, 634.7847194540868, 491.7258021910134, 91.24240629382719, 347.96105629465046, 333.308393665092, 670.1335095211855, 857.7330944003526, 329.8036635789654, 693.6736739834325, 288.2177953614557, 945.1935395632106, 813.5660347379726, 550.0966089717829, 454.82590860172945, 314.51715717016793,), (323.27378627599467, 970.1847268085771, 404.17505700074565, 514.5962523291051, 988.1192147817542, 657.660386483129, 542.593594357195, 413.2475707993215, 187.5825413945271, 361.77935915167524, 756.4431540555737, 625.408742145009, 759.990536061017, 203.55823835027388, 549.2196390853317, 927.6727608444477, 438.11609507237824, 698.2500291221722, 121.42608336261983, 973.1468158216944, 608.8716670819649, 239.29746232765038, 158.3781638052324, 550.83900700267, 552.251409053733, 93.20920128152255, 992.2571393952196, 912.929878484168, 461.44789417878343, 117.46614908955755, 832.1431737689012, 498.37550470719526, 716.603325991791, 508.87201506951175, 273.42489671327417, 834.7239455766944, 980.2446326033755, 243.73090607513836, 551.2650768804697, 383.58601324714994, 921.8681499315619, 508.240891592894, 879.3262551464761, 864.0269344285881, 276.24740282204095, 790.0061820031135, 414.94242355134514, 934.2483936825201, 507.7376758096565, 820.5494731855783,), (282.83898328282663, 298.5558497717672, 586.937722414161, 998.9023332293388, 489.640346655701, 148.59541838278912, 538.5805777038737, 345.1239416930075, 551.917417070811, 543.430062958673, 455.3446168665011, 321.77735022903744, 188.65237370710543, 697.4984276205713, 571.7976419849011, 233.5624460578931, 775.5444750992589, 43.64729909730192, 744.7051515651959, 705.2278810250026, 811.4089025648768, 386.0787524909823, 663.6888294845347, 820.7475517095551, 980.8181386597851, 495.32864961642355, 37.01961134557619, 502.29115013296587, 590.180429309567, 869.7003133622154, 874.1903740543083, 440.3062097706771, 525.9510868075614, 456.92807447424224, 722.4438275706426, 409.9786197463644, 654.7813264277338, 154.36121877154883, 469.49060098473194, 969.2036305742172, 338.56123405143165, 692.7045985868821, 649.8366525792726, 851.7652923506914, 852.341336565893, 859.3421841682666, 380.00939752265504, 316.66115393339965, 718.717425222983, 759.4018093343636,), (872.3830173985363, 35.89909983768658, 68.42074718155145, 631.1610168546406, 920.9290987802967, 997.4259229562153, 746.766366737927, 433.9714719231257, 98.44312638319796, 633.7478287807912, 872.5792326070907, 443.6785516617058, 694.0011627932346, 903.424062050486, 45.99096867721275, 796.1434651622118, 293.3677759652088, 374.84108977531486, 145.56979569939844, 531.1663181487601, 565.9280619159368, 792.5194738809532, 169.98364906809738, 78.96835065999464, 870.8395986448548, 619.7103685374153, 240.82979185631302, 912.8290160235548, 143.11772012850966, 461.1499133549976, 253.97733941354227, 255.32670815915404, 9.397431454871041, 804.6330769751463, 901.2094235988831, 677.6108856990794, 157.97562207178694, 441.72978360381444, 345.5656244430817, 587.5717051264214, 638.9387023600121, 424.30893846089765, 250.09822440770125, 845.3039251425987, 199.21699910889234, 384.6932489673455, 483.2080610592161, 237.2057019275746, 571.9226923507389, 574.8119301786451,), (992.6920436268975, 295.23075388326936, 977.9444845768627, 658.2298159286806, 274.48038017822773, 565.929016955699, 685.7994927340121, 744.6688411653251, 49.04425077599994, 606.4064930764746, 496.7272865238703, 904.1552908937254, 286.1941514591945, 798.8601195075987, 607.064998164266, 352.3209558353165, 636.6178780058918, 620.8911631303041, 677.7644586233595, 720.928376670709, 659.1815403167537, 838.3371169625166, 628.2481036868983, 903.4037040729679, 646.3406088905228, 308.9328839526361, 440.82319016281156, 579.5738053684028, 732.3597679392383, 90.13337574621893, 295.1104516291553, 747.4808649383715, 175.64007044430662, 132.15979774678354, 539.4077589844211, 971.4895812113399, 530.852373702785, 913.486974481697, 830.4726195673974, 256.97008456326233, 824.6898125424073, 481.8478298737412, 806.4884937937666, 746.559350717045, 338.71525380188626, 115.1697074497594, 962.8932928688776, 140.75701500588266, 966.5002094627521, 860.1405968988219,), (724.2167120755182, 979.9422427819035, 967.2697473000323, 804.5876440205619, 365.7750494055664, 790.6819685889375, 13.918655100901178, 536.5723082690591, 454.7860277338747, 672.8283818737536, 672.3407973510132, 584.5600916521661, 822.4173012267743, 940.2918917795541, 108.34610219923069, 233.82190221158316, 25.024649646482324, 884.2348452148522, 561.4073822498789, 915.2559087431595, 221.36720007399003, 63.21704116019544, 823.8553513904476, 909.387638427892, 302.1901745292502, 408.2958557954127, 139.7770125072211, 946.2615328819462, 304.3645843560052, 492.6246189782059, 97.1919986218216, 887.2593085285023, 135.66404870633653, 453.6437568888926, 670.4862188501712, 743.1401215231715, 945.9740857794321, 419.1267534147228, 742.2690147653158, 154.522902409835, 414.88452743680693, 99.02163471052849, 489.34703778961455, 408.1158856977005, 951.5215253810595, 32.7162868550469, 370.5299587344354, 443.38308606070166, 950.555169851427, 855.4501933059546,), (99.35462460613032, 685.6802654812853, 544.4658614821449, 977.8425294520467, 358.67384121231794, 398.1396427443731, 189.80856216107955, 122.15971908726375, 848.0331884636811, 454.7173685705171, 662.768738061978, 641.7044672332177, 597.145959519545, 21.357454736370627, 786.7945904546167, 243.56889716402364, 125.92388530804288, 564.5779759079634, 68.6101528243559, 765.1573758885845, 207.1573703466585, 215.95135191867476, 869.6954267695447, 328.5595534322304, 147.55417994144372, 900.5310356317582, 2.8355514800163517, 858.4061263801764, 144.687980320508, 129.9921314434368, 250.65419672812382, 174.49712090139346, 661.0576425973168, 25.780149786198358, 14.860327230708847, 789.9846642347538, 237.93160609046305, 323.77146196202244, 174.2462014061772, 52.39901786117651, 741.7180569541495, 526.0855265978671, 745.6652750339957, 476.24596542325025, 778.0170393142027, 513.2379576091917, 109.05401000384552, 503.8386897858214, 945.4156429701063, 43.365036899171706,), (783.2269959803796, 866.9809077598383, 521.4512147130841, 458.042522097682, 964.0261831220287, 60.825407494505825, 478.9819109983633, 401.61725451256046, 686.0974960622327, 490.26885414422526, 909.7008291152293, 73.49071576645049, 80.79047741079192, 608.29742363344, 65.68223332011391, 275.0159995579532, 633.0767243010155, 548.3564340483606, 325.1854433186679, 994.6277558609236, 530.5568374313245, 453.71541757547163, 605.4267915353129, 99.17846167904199, 701.7794185460662, 852.7927372955751, 650.9166648813055, 768.9627301047386, 720.8399166575991, 215.0230663274969, 451.5549159652815, 228.4935743645844, 338.9316188351752, 453.4989029074473, 415.9896502614953, 95.08583927563086, 426.764006260958, 665.1078630603089, 374.3010234355368, 152.63892476872377, 922.9850357343844, 67.13330813664776, 831.7718884748544, 93.2301017036774, 96.56443256578562, 738.7959984887157, 811.7692852773923, 556.3707356153501, 586.465082739477, 561.5864139920724,), (329.6459814162051, 122.23128535455929, 353.59807963376767, 665.3405200029155, 750.2842502514783, 868.0921488690649, 721.0606787461494, 968.3986253114745, 600.410091224677, 351.646185693149, 577.9185183898549, 212.7388056720061, 656.7363029881521, 224.2448691075656, 108.21838192726662, 845.3734186013451, 367.56105061535015, 762.6056319368497, 574.1000043314627, 807.2213711523444, 845.1551613283581, 974.5466021257082, 818.4268595406696, 613.5732805354647, 642.6991638298314, 26.2538314535834, 929.0842909949364, 829.460789959663, 267.4477251541106, 180.4160719608544, 702.6987728656047, 308.98468882991017, 339.8246567772584, 6.10578940365869, 869.8627065364382, 566.3210947613762, 400.78434399951556, 141.87465415126866, 633.1720126555139, 30.65709838090591, 746.1117620057067, 215.13288003351093, 419.83249376450317, 340.89598176933276, 370.05309247700393, 721.5959677426732, 776.835619966741, 567.5935566143972, 84.95703997717929, 52.608826425521784,), (157.40989710715314, 617.8381819260305, 673.968710613113, 272.10284354621893, 661.9386928082612, 485.66170489099625, 442.04418669777914, 273.1668443647695, 754.9431436683707, 113.81750811020174, 429.91363339789035, 283.24647008044934, 678.4862547601331, 486.63275336425085, 667.1325587363496, 45.417362604427524, 395.26339608762896, 599.3249569444504, 7.687085899882873, 301.4193620184368, 211.23397922031228, 137.234805257327, 255.51950402145928, 328.1223559378411, 7.7299065693119395, 747.0141234297678, 175.6948018758423, 380.20744571523625, 703.6712633826636, 500.2623465562132, 833.3542024198782, 806.2001865667638, 72.07549659215783, 861.7643620225886, 42.30226156279138, 18.7415365856457, 921.1624345024125, 862.1100136120664, 575.7591607368311, 573.399680885843, 709.4989615689342, 417.69395984348057, 115.17337266379823, 20.85655902474881, 324.76817944551385, 801.3221543104522, 618.1252633042402, 832.0259130717071, 919.7697517413847, 88.12988129788468,), (844.484359814697, 243.31647482273365, 588.8712883029119, 523.9625430006316, 395.76669685933297, 310.27456183586133, 339.51328114846393, 333.06862249313195, 168.1327080456866, 510.4832845421483, 114.02663983855254, 509.952062322972, 905.9227315800505, 349.3752654723803, 727.3791056739043, 818.9486015248519, 815.0370057500141, 236.2688489467243, 146.44421827769438, 197.27180282398328, 602.3989852731659, 760.2152955468921, 655.5090105187392, 177.14612894722913, 772.8480892475602, 494.11702501738864, 754.4458252469858, 759.8771496077485, 448.905256995063, 924.1542583856593, 564.4917834027992, 635.2983190605934, 624.5217794418948, 864.2468748314161, 627.2174068997718, 150.9574013930769, 68.28625849575609, 442.20806383628485, 302.8204351390129, 274.67366748615916, 56.172120213078045, 507.33688528977814, 310.40785060631094, 451.9138637006822, 56.89005083395238, 831.6966316631215, 76.731001173455, 864.2500339252391, 855.2933714560903, 615.0083884155736,), (507.06781733395513, 462.7116589272723, 554.316371338304, 791.8177972653585, 895.8767655568026, 449.73370365090625, 809.8159176979711, 651.8374546486099, 321.52676288042835, 475.6290279430695, 150.86107669563853, 61.87370010110072, 103.50187727039162, 899.1268339557736, 343.4377758382676, 714.3155491674627, 504.5490015009574, 172.55891143636492, 247.74372359828422, 437.758274309233, 439.4217917626243, 522.7480352655456, 158.74620798154137, 372.8519821013271, 282.8935786144179, 408.7693972473203, 338.3671468414195, 597.8858623829894, 789.2269315636647, 647.3053569693069, 65.91185427971746, 94.50594751509433, 678.379344840081, 284.1469738781137, 723.7336550024618, 656.5640864104779, 906.3426971636773, 873.2796620605388, 333.3620360605566, 582.7395145864195, 141.42838058431784, 349.8207875358442, 967.6965076927446, 698.4799628118809, 391.9579843353603, 595.0412281515748, 938.0021995657609, 309.5818874159609, 376.67930605630016, 791.6619578635435,), (813.184783814801, 670.1163999947225, 828.9589728944645, 738.7746721294827, 685.4144402775762, 526.393339734174, 646.0248207334879, 423.40636632210374, 361.8280963467846, 362.59766898204435, 180.26292287684464, 214.19266120890035, 947.6682675343468, 486.2709208727273, 226.54304653388724, 137.5653531770793, 77.16508430087632, 844.4283886858833, 101.14076366789415, 770.874720363101, 835.1198266345559, 883.6821654925616, 37.74749235756125, 336.76437196428157, 766.3076044472416, 131.049041429508, 376.7198708225247, 162.2472120884505, 831.3450566891255, 771.0978137309884, 809.0437196393011, 165.5391657440587, 437.67340513834677, 410.85861149653346, 676.3629221885329, 237.53020144692505, 444.19870980527566, 284.92793256335824, 748.5365180954363, 448.92796303334035, 534.0111496982611, 309.46789656278963, 808.6238710907863, 469.0156050322527, 835.1133927257074, 367.8409582250328, 947.130170244123, 984.4397935315773, 461.6799784409892, 281.7717327037754,), (381.87243419071046, 527.4597884614826, 966.2681532059473, 816.8912395812401, 801.2592241515476, 138.39853460343122, 250.00321158900707, 641.1790362044471, 874.116945052237, 554.5407454244312, 102.58973174840935, 845.8922767334385, 851.1660480847788, 285.06301405932845, 763.1168302916909, 272.7912995913566, 905.3062089782412, 147.3486559916043, 437.4725601949808, 946.4132630117607, 222.0380069069806, 451.1279902207089, 349.5850781386489, 26.67019080293853, 53.25688717107446, 502.00711469323545, 235.77807386343264, 994.5253512382913, 374.91267341776756, 28.18754552815006, 930.8259047499531, 839.1762876116056, 649.9606842941816, 791.380637482176, 137.59958772587166, 286.87939731326816, 829.7615831528226, 696.0719885759836, 138.7926918181862, 705.5361752890805, 448.6014739822466, 5.251198305617932, 79.22577127072128, 255.9239284370447, 834.963099282381, 548.8042454438354, 727.2347853249317, 527.7715058867243, 111.18686032423841, 288.10157803923056,), (301.15119458621996, 47.74944661885128, 419.82554375344307, 793.8991086394382, 457.1136166364487, 110.857895290015, 905.1468856619986, 596.7390428189469, 16.435352205894425, 515.3757302094061, 241.93813442099332, 143.57684024626005, 429.23889310333374, 614.8095827759506, 240.56423880654353, 416.5675952085398, 664.3713017420091, 85.61395498726176, 974.6544909522216, 67.67932290884605, 526.0594453221705, 507.3276965797866, 988.3314855964675, 554.1519524181955, 390.4537325600641, 470.135078158486, 635.67079146863, 981.0394225515603, 253.65026106921275, 16.242231108947625, 788.5200162795153, 344.80249316339126, 732.9410214506416, 628.2569624756565, 771.5013741098591, 735.1869848123113, 332.5186083719849, 44.33568829520817, 546.0137452076915, 813.5088655560882, 175.08912705170843, 779.1425934782815, 464.62289974703776, 695.389251996064, 631.7358477583379, 811.4976818476064, 63.10053703222462, 776.1903997034217, 457.6795774473532, 293.4425711750313,), (43.806275659123095, 199.46983371518834, 41.905941930382, 933.3709799503973, 515.3835892544988, 989.1227022961234, 543.0306976541859, 253.3137652026174, 753.290918818865, 191.1034307339109, 356.9741760353634, 780.841566978425, 865.7982770780576, 331.92468638134454, 124.4750082443834, 368.019174314673, 889.4865170122814, 743.3077055196212, 894.6374949550533, 386.64476826069216, 973.723584315309, 496.20322653702266, 497.52339249362734, 924.3104666269636, 519.275853534942, 801.1480874017738, 727.0813243426358, 78.92700605546787, 602.4532988302273, 822.3412795398867, 545.4743973446369, 321.21142821459404, 80.06891107499526, 660.9192214581367, 306.49585609075245, 602.6216277305998, 426.11607288304543, 689.7648084454862, 351.54698377199213, 42.355162850129524, 870.0371750563921, 352.5593103084823, 998.1505977730491, 274.5553600748576, 980.0272791942791, 947.9043786030863, 75.04116498815927, 637.5125378832984, 363.3111306509823, 801.0959755621699,), (679.4106078146899, 952.7893962796078, 142.77946836254972, 607.5729033208553, 781.3119697434665, 34.79896579854203, 67.23336306210881, 778.5153735068654, 366.32847310714857, 382.8544016887777, 567.2446417241194, 605.0948288547855, 679.0620569133949, 948.8235292655564, 372.01337847068373, 763.0844717985796, 573.9217783338956, 529.4598815362897, 398.03404595244996, 649.5607367060315, 249.61165309339793, 113.44861258501804, 735.6748594794277, 499.0439602564668, 386.9873800392635, 561.6727107596471, 261.77667658749584, 260.2897711399034, 446.2731124056162, 996.3651121547607, 285.576877698158, 916.4789095418814, 491.20019525422407, 122.63742190397086, 852.8262903843226, 452.04268524539737, 898.6790307862303, 445.11119274245505, 87.79074110473107, 681.9292602506372, 845.5212189746994, 319.58777209997015, 347.42529473856456, 64.93907831607115, 542.1713612255624, 891.3316823538889, 851.3620507531259, 711.8091040072127, 927.3244567231777, 637.7000225640163,), (793.6963838450027, 508.7557451743008, 121.36245507845689, 200.98037117768575, 138.87687203836575, 790.3730608077492, 26.284026808265693, 554.021437259595, 368.9111655012207, 803.6617262885865, 551.6469339264276, 611.9483626205209, 86.21548165193272, 309.29071752846215, 999.5950439343869, 718.8696598907195, 525.6956548303001, 769.164550374698, 823.3393995083907, 73.75071291123902, 972.3797315137659, 642.3385893863814, 449.9744956629961, 680.108990649581, 344.5147807026997, 877.9601516504117, 780.2629288383084, 639.7939293666631, 181.96313655213737, 966.2646139341068, 432.61828648197485, 910.7122709468873, 55.412850017777075, 124.1611206390122, 153.01546681020062, 164.65707989012412, 322.6607511545556, 709.3321325292459, 346.0230823418731, 940.9040549315347, 894.9259168211245, 845.9337061558656, 250.60530898199906, 635.0570913877459, 550.8414154831397, 125.1702951269027, 302.8246061088761, 533.4780297683806, 502.5731447412627, 168.6359017477068,), (941.6069878685959, 154.19426687956317, 658.7328733837543, 720.632768437273, 605.1389078618554, 842.5300041133889, 563.6180344312958, 825.2362673266321, 28.373487898877613, 45.46180329134197, 641.4537338243601, 576.771231477379, 651.1299774855999, 766.9590009163111, 416.5867836632462, 638.9911922088704, 498.0380601361161, 627.1640102980843, 289.67165680160576, 956.650169954405, 482.94481817541947, 804.688154215733, 684.9908411956767, 297.43387142974234, 72.97302550534579, 59.91303293466898, 439.605499920411, 484.2511301073592, 204.0230135633987, 606.6602628206364, 312.582499246933, 718.3628851124375, 734.1997548045468, 860.7773657543253, 975.3741279148802, 130.76615155238648, 370.5401980502913, 561.6512157483692, 319.1158864031572, 466.4725670406364, 267.4716744541824, 247.91888252554563, 96.81165256057145, 290.2120049699015, 384.14983350160605, 615.3774441639687, 248.27028038118658, 865.3075017207177, 159.69966213016096, 327.43582080119495,), (577.6870378935964, 312.71492086227306, 763.1213751386285, 498.2655420599088, 514.7248392314845, 498.75970839765415, 308.5404820544293, 23.176298216351032, 945.2328040692373, 505.44446302377156, 966.6866305524754, 215.1444225262411, 352.89508885635536, 50.54040462789189, 494.89420352517635, 882.3394763682011, 654.2600368896171, 470.5868533681957, 536.690749288927, 847.1723653934679, 430.9277693475405, 882.4557309185928, 727.5080633593919, 763.8567641619895, 365.937352517563, 400.5816210147566, 570.2816438810293, 194.65530188771908, 553.2229266211517, 73.53174974284182, 504.2555291232047, 764.4041147072396, 279.720677623982, 989.0907006207226, 680.3986401188607, 118.81101972358954, 975.082815413784, 393.90371272860716, 794.8972283809458, 339.0852999525653, 938.9485669553754, 754.9651722927939, 199.05788155244997, 509.1225162251821, 500.07790357064385, 45.30335246707473, 137.0363735675204, 333.0407053527672, 473.74415039519005, 456.98855928287395,), (606.2605224038041, 515.505732147254, 327.96584763652623, 613.068121064678, 162.5020458769394, 990.6157375611401, 739.3193605736042, 299.2343425283183, 336.37345215167835, 828.2893859573759, 532.3398298758764, 708.7398064354379, 299.7905647374137, 815.7488332438242, 368.3578098657696, 673.8063924730166, 979.8980311791619, 583.7021418487864, 796.7548139301472, 725.3242125518553, 688.0436512027717, 26.647154963833188, 474.59021408506055, 967.0706961720509, 782.9039914314214, 776.1620251720722, 577.6343958641828, 721.4001122963067, 583.5232770476399, 170.51206174665933, 629.0252411453637, 619.7358055010894, 841.1671249582643, 147.77570830033181, 680.7268950506789, 31.57051334209937, 948.2051707843013, 109.89552133369685, 18.937367506612567, 313.6924834458552, 151.43125811934544, 690.5002609185254, 410.37740932270503, 774.972301807091, 920.5209498972107, 872.8177089123203, 735.8372712686813, 62.28128601443195, 138.0824857852102, 207.34170497124472,), (325.0495344260548, 662.2267997143491, 525.4771514000354, 313.75259873781715, 173.18242385732773, 912.1241609240104, 342.32701768184614, 354.28698864481277, 771.9897841487408, 720.9245613272926, 643.3090999994615, 693.3133100298209, 610.0765800787516, 192.2641691367134, 246.5191355273604, 558.0866508074041, 224.8670379928167, 972.910627590569, 297.6145652769079, 289.0041374035249, 207.27779485464026, 704.9882597401968, 317.04074501844804, 348.8031742013066, 933.7003747708006, 795.4053560023335, 273.45753675542306, 121.87410573271507, 676.6222457825457, 379.69418537438247, 980.1605373213622, 818.3774601305623, 954.6088633914613, 804.6158339565703, 290.45265199293556, 287.6303416141399, 714.1412874983953, 346.3635140956165, 442.37611186483537, 256.44397547348206, 479.0792630164832, 202.06800720059647, 538.5779237086527, 933.0239337833077, 696.1713006468226, 137.27295480092295, 615.6770344144923, 586.8304985708152, 242.4580384102868, 669.8339648931039,), (531.0414897327992, 637.9445734086377, 52.49110683946279, 413.3013660482586, 717.3580562502436, 100.5449047773479, 770.7660580982791, 5.18144640713003, 550.3525657796166, 929.0996801699778, 406.90745154688346, 935.0320985961515, 878.3996215142282, 477.44852044089646, 199.45597466760168, 963.9140388909358, 321.1677021191667, 645.8979178902698, 907.9369587356224, 89.46072051151633, 574.1333531753733, 535.1522768936047, 723.1176782424234, 936.669379750626, 913.2297256698525, 175.0647754809631, 882.2449731648331, 175.78870753886255, 919.6348118509339, 997.1718030885099, 396.99457427829964, 495.3838973217578, 936.6087447777628, 962.1313800833424, 926.039697896443, 876.7431679179234, 9.267168480099786, 567.9618686644953, 107.300690773538, 982.9938883710064, 284.56165235004624, 989.0994700887234, 543.3004835569749, 493.91242034181914, 938.5605017408724, 851.0597385779967, 468.0207690062901, 192.81139827380977, 112.64676102015447, 162.49426253973866,), (458.9144710323612, 257.2648802295191, 186.19906912806772, 736.6178954922569, 790.7676644252557, 567.7812224209393, 757.2827502575612, 175.49491383846473, 856.1465042734577, 897.0427617839752, 826.9898252763096, 515.2806589597959, 86.73776984835591, 669.2558534099542, 184.78120104733998, 140.61187563429846, 323.6016700850828, 248.04708366714502, 260.78527706544384, 235.5212528015339, 753.756651781457, 954.0348226549125, 301.9458396916147, 722.8825284009974, 11.43573419416799, 653.6833670701986, 692.7685904227388, 62.12433180053611, 118.22484780397258, 306.80634200361976, 405.41661296839726, 502.52047123838963, 895.1183698162541, 703.557035102256, 310.9779483846603, 117.41574584477632, 916.1303858506353, 295.0376001562468, 614.625448143849, 219.1286009353439, 133.56878219680414, 153.18556468917922, 747.7348371508565, 605.7389480890694, 415.84561543122464, 549.2345088183648, 470.8280768793115, 537.5176876932009, 664.0944393396638, 218.41162265732595,), (247.46542930857774, 754.7395497248654, 873.1350584877333, 81.87030647327487, 446.7479774964833, 703.7661251558726, 78.10272214744019, 564.1687222734748, 61.75804755668379, 547.6492487109315, 505.4870560925958, 572.7016742278647, 149.8523813824143, 328.11763774220645, 520.341541184787, 116.24002218466423, 205.40148553936154, 583.1476784410175, 90.94164445168784, 510.37535403283454, 808.6920831772371, 453.432300179782, 513.2478432016416, 456.79847571187605, 57.736780932524766, 462.3783057389237, 806.9153525543925, 723.2800798250023, 395.9487099532575, 816.4532259331305, 745.8044828318157, 578.3112650590353, 45.289802727828786, 344.52886656213457, 63.75991211208321, 994.1236604769201, 934.5827988849464, 69.0191461603138, 933.7755625849306, 31.734871023079037, 408.8669358192483, 768.9720625834827, 765.8276829237117, 978.333284924101, 645.8808180971635, 420.3619388823279, 992.8565985808789, 382.47961885137204, 869.6202853107085, 906.7673115245726,), (375.6455338019876, 682.7303541015414, 661.7925381254681, 539.3002639188602, 653.534098418702, 347.7698871391502, 178.47362900411167, 537.2584863980012, 528.8425395440092, 727.8581409061281, 222.6902159662665, 3.473294944907779, 22.735327321966594, 298.36298870298185, 673.4998577765671, 544.4453390250208, 531.9336084967588, 823.3604373757844, 247.51203850377556, 346.15973498762673, 275.6497277951273, 937.4103611350669, 725.0239459089183, 112.84463876647732, 809.4781835391944, 419.2405984917521, 766.0534675139033, 883.7566218453745, 15.645796363549568, 206.08162185021416, 100.89671309250703, 33.57627575428079, 597.7848967315526, 703.2862668649286, 48.6763212437491, 740.5410784798617, 402.2653508109063, 234.33927848756164, 217.26921017755342, 863.7302426700563, 56.44403502446094, 503.8958489409345, 289.26345135391887, 815.7862567633168, 731.5174831160183, 318.903696369192, 597.9176742772623, 672.5319014369137, 320.6651153929831, 301.7644351442033,), (143.26043416332868, 660.2124238107432, 221.04274044603255, 300.5009537574196, 60.957637106762164, 948.5202550267213, 879.7138909638829, 911.5776656205444, 625.9931376483299, 427.2005822945435, 495.62078749302094, 972.2902353436858, 941.5864098319117, 671.3425247457224, 785.804595980518, 318.7344573163122, 416.3246334214447, 149.2176078251637, 376.46018850713205, 754.4160972381253, 473.51882041221995, 849.3409322600405, 300.7364187951478, 707.5767974879722, 805.7761599348869, 914.7411738159054, 562.3859495869091, 967.7861885466267, 557.2867581992731, 134.09275105907804, 242.85851608958575, 203.3367330600544, 646.7058515479447, 922.2261045112224, 847.1333859196119, 92.46399652686365, 724.5847123072019, 190.4816184307383, 268.4615878549812, 673.6719206345784, 602.9220449890759, 873.6204584895396, 188.16329393275532, 761.69641753721, 724.3052398521492, 558.8504762725273, 479.3942064709148, 869.473851524538, 332.9643108188213, 957.0197605266732,), (15.333706228492838, 937.1597632022477, 962.077556114459, 117.31619944333448, 999.5720070178116, 478.9208763423658, 242.59318184574153, 604.4015340787812, 204.5131429778937, 915.1264595935564, 552.0792925478145, 775.5138820637702, 380.66174437292256, 533.6501274162631, 359.2595555515757, 261.5616273853526, 512.8165400237735, 497.27729310098334, 98.60823156548004, 981.3184568465767, 469.4904220225584, 839.7311815636849, 914.3304988974891, 370.7049214312584, 413.9301705786246, 562.5247274643543, 221.27409831622523, 145.92271310254856, 260.77410990976955, 934.7582502963535, 579.1429260374005, 417.5780735551736, 152.41141018414328, 329.8652859778599, 379.83977474115414, 833.3627152869851, 499.30148236932246, 654.607966121022, 684.8466123459061, 257.32675763488277, 821.5919396923563, 966.5082672503437, 641.6944543987072, 490.5955825807824, 168.233645951541, 794.9755143536341, 169.2657108601817, 720.3135307620937, 488.3163212541324, 916.8993896943668,), (542.1368553513552, 641.8094631823666, 58.732051580730136, 33.82375716469055, 846.6973831827223, 945.1881112008982, 668.2155433931528, 764.3388435720192, 412.3922224155927, 842.5447168253485, 231.43339207744552, 707.1695637034599, 9.141461690661767, 505.7329196930651, 373.20069268681186, 617.835235876803, 666.7547295533396, 616.5193610843379, 483.2041536218251, 487.85438400307123, 6.6123569921588965, 551.6435609112568, 11.850968127892436, 529.4176468416506, 274.740737197832, 977.4793482325117, 17.1425976024272, 813.1572209139099, 674.0329521192103, 806.1676989474289, 909.7733659987662, 107.0164286944929, 96.31389025140847, 148.89748574025052, 191.9320569916132, 526.4559854002163, 815.2143907132826, 267.32473667663584, 396.89642249807144, 373.05158346368216, 406.027409873886, 565.0021824479691, 990.2330316397273, 225.85725262742218, 684.0416254703855, 847.8671020315358, 653.7357195591532, 858.2191590211871, 759.5858501768448, 93.50050542710942,), (379.2640222398159, 552.7014395296953, 56.1149391289657, 9.450172654130618, 171.38357522104764, 499.858393112792, 433.9096519716623, 784.3763107901111, 565.856627951035, 857.9603133636695, 95.36183547073007, 528.159185641648, 42.551757617045105, 211.41705588454718, 868.1168905816056, 887.5543070344936, 475.5002876452733, 46.56197074174329, 74.34805992565107, 925.5848100809231, 899.3116508650087, 563.5098640479836, 32.90178015347189, 928.7663612546593, 314.48469322665096, 961.4691898760058, 587.0361040844883, 752.254469846865, 712.7113999493597, 398.296020395122, 76.93749144134587, 162.45025071470587, 240.4721943042868, 834.650560091752, 389.1566073673588, 896.5257670027198, 331.72983962182855, 755.6092645208922, 139.9505942351973, 988.4779579141031, 724.1635700943149, 500.7928516377251, 974.3233274359964, 53.6964319473936, 437.08825284349916, 838.674657613175, 340.59274647510597, 769.0056533654423, 954.8583969146658, 396.7030493089595,), (773.5549161313224, 29.625658945165114, 273.32702862879734, 992.5858784507974, 490.6034561107933, 355.8111977058479, 941.1428449707254, 431.8479462395447, 679.6948580881589, 660.6719075148754, 85.69411763673574, 618.6158902598878, 798.0551738027466, 713.1085326783857, 82.03800967314857, 154.22096386551843, 711.6771547786167, 633.9008819630509, 739.6552889765946, 316.67822868153615, 106.55091771126112, 5.195222370030117, 308.26745513629805, 359.9174973468966, 269.7664432393865, 132.50700449419938, 187.3917835278772, 448.84367702145147, 554.7400006088677, 408.04417508313753, 26.261904013297197, 353.91428734525886, 93.06426045987415, 598.0437977942729, 324.4303305724127, 385.2379173959837, 291.847351296321, 387.7995593783937, 84.69951692852251, 901.136044856433, 905.2075743133588, 978.1730640138873, 571.9604307495395, 169.5829233923255, 380.73202348874315, 138.84005895941675, 301.1312653750687, 493.1239422107847, 63.267150276954396, 434.67626926274494,), (421.1023397725243, 484.2313139339285, 76.92136139515715, 251.69973778890287, 246.59006834700293, 625.0336877688947, 593.8063980390787, 195.54822488026048, 106.972367580902, 304.657995751257, 948.8234623945651, 332.21721531162694, 620.1921878746894, 804.0764619573824, 329.5417162792602, 334.736223746957, 815.4754700030031, 859.5084671008352, 974.2253765046069, 136.1244715782648, 320.66515537508764, 947.2789220161162, 200.8514887020717, 314.18328119544134, 964.5746230947939, 968.7252217466955, 291.4481515800532, 694.9577676721101, 491.00731289210233, 575.8792816249606, 242.4242967805731, 376.0553023241471, 816.4945154329131, 392.93512973945985, 113.88782361199812, 563.850508628786, 592.2270342503855, 545.6290854508221, 681.7126331300876, 550.0991569974728, 953.004611486296, 461.62222283314844, 708.3670512560635, 438.45495430890855, 291.33120798529535, 692.8352793243428, 818.9655680044585, 795.6568359959543, 409.14159024490147, 499.30321528896025,), (633.3360396536635, 242.02116767079883, 658.6629685323181, 715.2363912994261, 789.076763220063, 73.96513558228845, 990.700614436968, 479.23469977262624, 400.80509732937367, 506.61264302950906, 920.3921844782957, 691.7088658981077, 543.6452033874776, 790.7209170492591, 359.5294930162599, 895.501515360899, 536.9059860245878, 638.1803670108411, 84.98193405098775, 768.9540479200405, 657.6016445807657, 355.0088194484896, 646.9998479834043, 44.2966935278496, 983.6082059519828, 677.4718958641856, 399.61774622508227, 752.6827777296058, 965.7167777138283, 430.45554389018224, 10.547772784579967, 258.73837040365544, 510.6762405250447, 518.7977668493495, 580.5182955240041, 575.2353982704261, 445.7785515049068, 391.1341686775028, 772.3422324624321, 588.5899565401472, 500.4657816197345, 344.9673875995183, 24.562653025769322, 104.54935800606168, 415.9754285257126, 961.7278656799704, 116.06933795897467, 940.676158146193, 141.6751768315483, 311.89034389754147,), (455.3326352258771, 206.8673262083145, 482.92599877938846, 476.16253120916355, 438.16593827302705, 696.7632655236661, 318.90947421770045, 300.2640831819403, 810.1859369186981, 115.08526669878594, 849.1800080469097, 647.9699172777136, 677.139332890884, 164.35409285070324, 983.9004705882779, 243.9129465519927, 174.45323282413395, 160.1357112153593, 559.8489524898631, 958.4626217339497, 231.85554741141036, 405.04743628025284, 184.45177515139366, 640.4788766600781, 432.1344524292825, 29.19227434239058, 614.1069373719198, 197.32443578224635, 592.2031583603683, 388.8357803557071, 704.7356159597344, 205.78447936732192, 752.3254953604917, 808.7297886312608, 62.56375146916437, 101.75204872714238, 871.9793300098852, 186.9598356320934, 325.9849115988185, 457.5504222061855, 262.3533954523609, 862.6365474573073, 527.7150196277827, 639.1085856661506, 596.9708292829935, 611.3084211390019, 587.0047145652179, 347.9246374367544, 845.5178026695593, 617.362679336425,), (813.7382542609338, 705.98836094598, 297.4448346993519, 614.4845157129195, 84.7519686230278, 133.94776965071065, 117.8616526616567, 305.38000253549893, 183.0445183314835, 693.4365417164455, 510.82486948716087, 418.2391062112586, 137.86729853455748, 383.709962837687, 185.75369929276565, 635.5016420586353, 693.4329265293553, 645.2600950799504, 999.899552563092, 554.9125758324974, 489.64202619220885, 140.29653509779706, 314.5800146608443, 451.00097074665115, 53.61126263083682, 359.0391721713688, 9.583439563281226, 136.5347146624959, 815.2159406538618, 963.8290894149399, 505.43801973067036, 494.96984786640365, 684.6966705599865, 415.6304352741119, 839.8918021012228, 488.69951193968564, 82.67062578646689, 30.860705643004806, 761.0566185454242, 292.0899095587066, 274.852918273534, 537.6086182048194, 168.2089774365737, 457.3213872734491, 742.5182519297871, 765.9195549436906, 549.7261845380514, 113.21099529202317, 114.2066513589688, 775.1130278639955,), (823.2828079975666, 366.8617721054209, 822.6109277962613, 41.61052227023332, 718.9802411300433, 546.3532747219646, 989.7757278766833, 102.4164388774983, 830.0707165425397, 751.3454947436721, 297.70893510289363, 999.3126692789077, 449.73234283041376, 348.57697682231384, 816.7285851164386, 439.069903383333, 993.9576843186171, 775.6316498807736, 236.94605536668124, 810.7027168394102, 587.9238969768106, 350.6308411139897, 710.7539594937995, 632.7706309271384, 165.9816176902592, 139.23496592677608, 206.61965618677337, 206.94272075176602, 59.35783363934111, 350.8154789528309, 281.085018790198, 538.768546047638, 323.6536158546817, 704.0537617551885, 289.3332434649436, 267.34306627808013, 858.0168449462576, 985.4883022617942, 679.29931592331, 95.22516381434276, 962.771994993792, 785.6910482912973, 918.7687118298253, 992.4862256446744, 867.0475904337784, 126.88816861381025, 866.0787949911568, 249.6772419381398, 711.3948488391691, 828.4818026986326,), (761.473587479857, 676.234553699946, 489.4587259156378, 577.4255293041615, 268.71715208748594, 414.22508936503766, 451.99172255036433, 633.6277633502976, 880.1250813073235, 93.09478404261739, 515.613472087699, 278.2256878517837, 936.3361140885752, 369.071174075422, 950.2540788653826, 327.2892801609303, 2.4730851419847433, 774.1352904376932, 732.724026539487, 730.931937487053, 458.4492566797177, 664.1438208318425, 358.2227293409872, 63.33068606017467, 534.4244643875649, 217.82993501520588, 429.64310068523616, 211.85146640773823, 268.53683831442254, 828.343617048808, 337.75515517078736, 577.9336402609515, 566.1421109171403, 485.33790400850506, 343.7396205526192, 682.5519260932059, 48.40926115172295, 99.57474191620585, 783.8897618405682, 459.58176267356686, 124.23717923039845, 857.6515999286138, 441.2859488764266, 0.6759315121042109, 958.0317693039723, 202.31820639739973, 688.5918819115103, 131.91308738401352, 649.9971993406527, 158.97746290581938,), (932.7255627259242, 274.0194580952822, 654.5879644187941, 250.38927854910887, 371.84376764676574, 903.8002688356579, 165.5250791593438, 396.3415669322332, 305.5092448442307, 699.4413715245736, 234.14384148441945, 655.485228383535, 703.6980397640442, 1.086303691723689, 476.8067082609141, 132.69979203998662, 226.19086145940602, 679.982725121579, 9.28694760846016, 695.5971072880487, 817.1090269132984, 988.154909464272, 422.31393377505987, 132.17515109256084, 70.82830540051211, 383.0699256757727, 730.7633817639711, 102.42717044950666, 313.3514774409062, 880.9889949802706, 137.1292947435456, 773.4604836506242, 753.157800991068, 133.14623118621216, 992.940155246385, 142.85306683489384, 530.508276546681, 8.474741953009568, 650.0202131578069, 440.09942077985187, 722.4320263643224, 628.0800383409025, 151.37413084428098, 411.70989435967806, 686.5661698757399, 859.96252460215, 86.68803346598852, 100.46511247763878, 752.4456465479524, 589.5739177615131,), (384.03190934312073, 963.2487105532641, 314.50366818216935, 139.8301888092337, 276.9676569448454, 84.24871599798666, 553.3966317847301, 600.0078672258529, 607.5930989155232, 778.9696525132599, 690.4760750146538, 847.892104439959, 658.4053700851439, 301.6493335771986, 517.7491277972375, 509.52256611471256, 747.8436409812496, 295.5420512420667, 54.56913101525296, 897.9125603569896, 954.6715113471528, 494.88771994326686, 112.74366260380853, 499.5825412726576, 593.9297681377232, 528.2865009361842, 977.6969478433356, 986.882532843189, 933.9244016742159, 131.98288073270203, 860.8140039367474, 568.3803887729609, 365.4124628480987, 682.9420490024262, 762.7259378955857, 954.4529839408864, 770.3670492246139, 16.689401770856204, 67.53256927276719, 262.18527716206705, 39.82686193718976, 60.46885958820569, 789.2899989930913, 506.61060427492697, 628.570696060576, 501.04896550707514, 415.4319943754019, 701.8106386235163, 82.42781192464588, 536.5648160700154,), (616.047055666748, 277.46773573456005, 309.90688561664194, 511.30469829533143, 203.19749395782782, 808.0600809706533, 536.3901728894583, 390.7315029683497, 634.2942473532567, 834.5264691847527, 681.0556847092104, 66.11508362121499, 698.6758816054913, 729.9583774315414, 846.4681151407763, 57.90594402313043, 86.21279839148932, 434.4835886060191, 453.3718422971427, 608.8324044274435, 309.2900638735262, 741.6935090575543, 740.6581802341782, 119.44300690788745, 707.9006071180984, 701.499611223866, 163.83263093540756, 953.0162017546976, 523.0053627284083, 782.9871015437459, 720.765518093459, 166.96295419238083, 126.93060748112484, 781.1239799369876, 268.76448640924565, 886.414759740656, 771.4302904896247, 29.356251172803493, 807.1078466573698, 271.98059502135374, 63.87198912314807, 712.3232482004954, 576.651674714184, 77.07003128638534, 455.1977776794275, 360.1238955217533, 499.61000576138326, 566.8861282384498, 367.68777785681175, 255.11608103907813,), (102.9049150218696, 573.9123051920565, 722.7783733258665, 228.40442315325106, 508.8248849106468, 44.03790726280954, 862.9284949026113, 244.55145035670344, 471.76458957773815, 382.9794743673991, 150.08466824115007, 931.1600771571855, 857.4851977479724, 552.8647884958646, 913.947669521129, 740.6658281065769, 419.3693043627985, 321.8020144720613, 416.25659850131535, 720.2879214281971, 271.25795800981666, 77.88706996691197, 372.8081667378613, 502.04094255958273, 901.9410092582714, 179.34435027230566, 804.3380194587567, 981.413863941495, 954.0794074604277, 68.9264377727451, 465.0941779168861, 282.30687062090243, 844.846504261328, 327.30092172611893, 553.0913953240675, 7.969173557455522, 200.67115345295582, 563.8067408241869, 303.90930726808296, 622.7175238921045, 463.92669058049927, 591.6909349848162, 493.36067585488485, 772.6132629505244, 195.4224238728235, 900.4432972264233, 760.4822203216966, 245.12687437658286, 6.377882751804065, 410.0361095660199,), (232.99774185974297, 346.4236977394062, 839.5740068961738, 877.198614251575, 950.9902621434433, 1.4620405355777466, 657.3038501759978, 849.0059877789217, 727.2150390409726, 103.94901935155964, 529.8144890600125, 238.16759452086566, 492.0288768498976, 59.895075653189636, 996.9592701075485, 711.6524747001027, 93.02663835808822, 921.2744029721064, 897.2874719314592, 519.759218492037, 700.8469096986796, 372.49197407408076, 974.5547901682511, 84.90241751648952, 95.57700533738556, 133.51385025095698, 819.962891894216, 74.83048432987749, 567.8207282892083, 434.9828784349853, 964.2182747327408, 236.7717347418965, 260.99093892361003, 315.0089154097583, 800.8168219994952, 700.7256063215422, 735.3528687823681, 318.0577192661006, 271.9558718324544, 74.68743580247971, 202.7126262792337, 779.9369824688629, 584.7080358041289, 155.4105513219367, 164.3749806997682, 466.0551124366618, 406.51255190921376, 535.9245490900237, 964.6347222502287, 207.63622561035356,), (308.3077182125046, 265.0079269147082, 119.91355635539358, 157.6153770655162, 686.0548429505152, 826.3866879396497, 696.8779832370764, 40.32980810866338, 835.9247509879863, 327.79365888479197, 91.21290379134861, 248.21878946125508, 355.7427555042837, 513.4596753787295, 677.1833769890718, 260.1667927212239, 990.6766204079072, 31.082093358759554, 404.39222644717466, 452.2011694010627, 748.0784472087092, 249.85262822155107, 462.0432488762389, 803.8967534438501, 139.79303927312236, 11.956923169412992, 830.372331894636, 982.5672374836216, 130.71523754397796, 823.6734151883558, 372.23974381599766, 630.2975486038245, 644.6850157085017, 582.3238014140632, 258.82065234652583, 812.7470916136143, 21.800182738612726, 64.47203604690976, 902.4960819332814, 443.43132383598885, 128.79154899700453, 905.0779703801529, 829.3561772174179, 331.5498912522767, 42.69631909043081, 460.9949220019407, 167.98920872905643, 573.8841998067228, 821.6854015322838, 394.9986882219194,), (29.48229300029148, 683.2129167516829, 172.80721183078919, 214.71610597002666, 187.15111371116166, 279.8545542807238, 883.4237988936261, 34.64944373150591, 619.1765488458958, 245.78477924524543, 295.10516299971647, 411.9922062961651, 550.6886652039806, 60.97906893284277, 279.77598743897136, 137.2360171320648, 199.45780666658663, 884.652516224965, 525.8140582527797, 630.7543888247554, 802.1680827711275, 794.8462443018702, 989.403615761534, 781.915766845425, 359.112109837565, 544.5178060340517, 484.6794854405083, 912.677016598367, 502.3932555437417, 388.3812745693004, 179.81587369786922, 318.87756732937953, 219.02018807309454, 895.7647816143137, 778.5382813284745, 58.59123027811852, 991.5313234305709, 529.4322919654317, 766.8421684502729, 999.6057468113461, 973.9798617335022, 100.13433730495758, 656.8644213762342, 266.52700106568307, 816.2851616087347, 917.2594825704406, 55.908655852449755, 996.3920485064572, 219.41158604392484, 846.5050904651331,), (797.3906873794087, 354.80463456515986, 839.2222523059093, 845.2209023662808, 176.10036757999003, 592.5195091814127, 806.2102253593883, 697.62656359958, 913.9800041285343, 28.20656972715907, 700.5608033514183, 947.5587350700152, 563.6064867831936, 563.1088908640183, 188.2336649052563, 988.0062104046431, 881.6263598977603, 492.2267238954052, 309.05307215121957, 490.4364201274792, 90.25737685090284, 232.62335808323908, 218.80894169157727, 526.4485141485109, 0.6834963838308061, 917.8961859206465, 201.4643833521411, 130.4895456191826, 716.9376326908161, 918.7807873072061, 844.2840864376508, 323.5888113048372, 21.912896243572288, 586.6091750912095, 917.2241474811186, 774.366498819892, 846.4808875791347, 860.6694665915508, 960.5587502757579, 373.5907800833732, 941.9232134680417, 395.5955623538543, 101.03217018068634, 301.7651537254691, 136.45166885749006, 157.50388601684585, 948.6935320263234, 791.8425249998718, 960.6621003687383, 649.180265644814,), (174.20267095553066, 968.7428963331677, 693.560398292543, 928.8452089854253, 786.9917925955768, 223.23721398064245, 588.9501557831036, 175.3516299902208, 306.82322929795805, 688.4984395989571, 127.3483647235355, 728.830955935999, 948.7881042212877, 948.6981598499029, 391.60142727760194, 994.2831305476307, 965.183940287551, 32.38274292708743, 602.3886674509712, 921.0382164180794, 967.5316856159355, 220.89525043701207, 565.5502461203205, 936.6877935542542, 140.64336814856836, 745.3433281019825, 237.99592514939692, 982.3796549403569, 167.88741675158414, 885.3202533951373, 88.72601257012636, 708.9658131452244, 639.102687904879, 886.6535152178218, 446.6299340977938, 265.21244056287617, 249.54086313527878, 67.79478006423378, 256.6579415427148, 108.03460121462926, 1.281157494689933, 385.93709795340993, 732.5844944987687, 969.1018251056852, 884.5519975872274, 493.0819934738476, 378.712574774259, 546.0239676784543, 101.42855963712161, 479.4892503971605,), (864.0507204685769, 650.9695631134773, 687.1604042171144, 162.65651174848838, 73.70938956137418, 845.7624840245603, 294.90861311613014, 318.7169079566575, 951.6621314341206, 74.20040176665933, 170.10520315119072, 375.45788407999635, 731.9850852713168, 547.0462586122314, 898.1241731199735, 93.10468538068662, 594.0305440520949, 613.6447257788939, 482.7367864884782, 30.99320405217498, 942.4422581019638, 164.9971947619141, 889.7532704040436, 157.10377733078406, 101.25781097333564, 205.55047671793713, 189.98425758838545, 697.1967038073572, 722.0381045756757, 729.9740056702899, 265.2647693499766, 281.52307344526525, 237.54828494724566, 49.63544303850986, 571.7698078577415, 839.854504845651, 153.5899345167342, 360.82892122727304, 427.6059574457135, 294.5494489662148, 662.0202260098677, 600.2155280137688, 199.67480076094634, 25.735692731685745, 170.8321916922231, 291.8114660461075, 81.93609471299234, 843.7690743469366, 308.3534001177294, 397.46734850812294,), (489.09681212065726, 661.0400547460049, 91.13687755067923, 544.1260292196804, 184.88122054204726, 885.492692749539, 369.40166493194505, 445.7752707944581, 263.2959666828842, 465.0579561546505, 226.2422802826669, 268.5619932729496, 61.639369660099305, 752.2351421152871, 667.4684591345684, 85.70731183991708, 343.7913865460467, 541.449006006022, 970.7055371735757, 589.7264423036034, 553.6019061579088, 840.7971664302505, 818.4052148653735, 418.63207493445054, 535.5144119789297, 866.8940137055623, 474.82438812192, 881.6538694176228, 476.26228108146864, 78.9550497740832, 902.7772013391541, 714.3056991303599, 502.10147031560484, 900.4550614715919, 800.4503367206856, 677.5348855760302, 620.273114349303, 120.28187475543328, 757.1887556391935, 172.87897280649446, 984.367990249133, 972.1648783377569, 808.4607213193631, 126.18599863853974, 423.37489931559946, 988.2799363369157, 435.39324553250725, 997.2692267787756, 627.2262122323979, 834.1081186760042,), (257.88635998777653, 910.8044857315942, 914.1739368985866, 67.01195304861241, 388.09915301475075, 397.35499435703593, 326.126805369736, 276.2008199072493, 458.15718112261516, 873.466253372757, 786.5506388277718, 623.0579285888082, 522.5620104498092, 419.59383422522865, 414.4423987689787, 148.1997412934446, 587.934133281388, 758.3773878576729, 939.6499849871099, 924.9281667592735, 562.6837594773225, 100.99044282544867, 286.0928540488724, 535.6334811459167, 343.8691785774645, 410.89003729922047, 383.04347287705286, 485.5847922863307, 608.9621519524352, 37.46567049771343, 275.4141420347538, 143.85267062667373, 608.655264085627, 693.661253167092, 38.78253248179509, 889.5742139142575, 331.4940757342707, 237.57929391630717, 745.7498198391361, 920.8349829061652, 897.0313621494225, 20.23288859327188, 817.3859253052775, 303.0549534480966, 280.3466531846416, 491.61921335931703, 696.1828446749748, 98.21739027274123, 868.9000250991986, 134.3855209684405,), (974.2194246033105, 443.11061333142885, 825.8233317797506, 269.4199810087636, 416.77272770538275, 645.5548114276443, 187.9356908926114, 211.3905021058976, 823.9694342551406, 740.944798920008, 759.4932952262567, 867.1889705502257, 821.0173923984603, 515.2697026095568, 158.97244263900046, 311.1230932889089, 506.7952752365554, 135.6498210170598, 851.2953409871922, 879.332591359856, 28.948159320185685, 192.7634468315582, 832.9299330765513, 836.976677608695, 249.49026132427932, 456.4486671280806, 918.0310693411162, 704.6339452200365, 273.9774984743589, 823.5062916891162, 505.12637493716386, 635.409809075844, 123.87285889069999, 30.563192381993566, 372.4667615353072, 594.0435934521513, 177.58369416386165, 870.4807719184629, 586.8802484224268, 349.7599016446545, 163.51361451785084, 894.4919203121954, 748.9611344849333, 688.8505376159543, 285.0693764347619, 386.5656707409847, 162.9074055986205, 572.2580610203007, 964.9176092022551, 857.1111874383023,), (647.379891761482, 677.6952460909707, 269.0835792689571, 409.50719649554213, 20.052357499524387, 780.3036642606434, 767.5727284118514, 8.897986070045771, 911.5154380523718, 647.3715541750241, 601.1419504945338, 8.463727114871311, 252.39044136065147, 805.0855691954863, 305.45969838455676, 967.0244433683766, 642.740311290164, 423.80680065961906, 376.4754522710008, 348.7091992720813, 251.99870927787603, 466.6980154972157, 677.1961563873133, 824.3108214706635, 397.15253997212017, 102.31210296860016, 511.51371746830176, 662.3554525262523, 843.2839500857772, 374.2297631610715, 647.3421200645904, 609.0501046380168, 298.4759262532974, 108.10453058584346, 63.83705389710992, 988.3609658217671, 640.5947456146558, 861.4916002518306, 261.14690792649685, 711.0937755717918, 892.3737976809327, 298.7911119189864, 149.92929635415652, 765.4743883036232, 899.6870447454949, 805.4298226335299, 802.3637444528116, 600.0333011455716, 660.530323681129, 680.7557503873818,), (721.2831975926106, 655.4005704624416, 997.4690995782959, 259.42627271198893, 418.5656860813416, 388.27492100545834, 35.31900482642425, 708.0689593758711, 572.0424340368412, 189.9151604113507, 726.5498765809035, 222.36319397626892, 534.6351015442132, 784.897055296117, 906.5265055050547, 671.8684759624089, 507.31485560681534, 845.4192442445952, 840.6386945964, 876.4947843421105, 181.13585780400078, 97.60306381270934, 127.94413969605334, 258.6518505602079, 808.3438691424701, 762.9182183193025, 183.06835701259504, 679.7123692899282, 335.63246320511877, 89.2998220194392, 355.2833961198324, 744.2099293428081, 307.08527133220855, 788.0904368075993, 331.31713697077646, 260.5592354057036, 294.05112165034274, 851.2138882114102, 470.5365676706802, 866.3933696925396, 583.5746197425805, 944.300984000077, 71.21573740407582, 889.4260614703878, 500.47737449270437, 867.49775469823, 381.6692198055871, 298.35575381746327, 54.06197064257512, 854.2397704033438,), (137.36525917312392, 200.29659079514352, 409.1918467521041, 569.4035879734539, 906.6209345061276, 457.57143306154126, 316.3834576487089, 715.668456110036, 778.9411774338146, 487.58084574810744, 631.0327722626952, 176.82426656346072, 634.4989691526711, 4.713501423993849, 273.5278622014996, 761.1932812149992, 168.60575658290767, 764.4837361457579, 489.5770906300164, 763.5691671054992, 88.0413437590446, 614.4797387250618, 633.4875407025942, 403.39348039795044, 965.3278401300331, 383.31642005346066, 37.725318474937986, 199.41425122811697, 373.10140542737105, 14.077118724632133, 322.21313384806905, 833.228666566564, 190.57670994040456, 676.7481416771101, 626.6869286716226, 248.82312287588192, 693.5247471703972, 344.3497740633209, 128.93080042830275, 383.55047630769366, 588.6706881680719, 167.02018726830937, 823.8438296744104, 298.20225349413874, 290.8277918094866, 727.8319114468128, 596.3699186155023, 337.83515120793294, 887.9740438370417, 995.4724098279283,), (342.73283925755993, 901.3837761445245, 359.25088636663236, 188.42603389086943, 948.0843537069427, 918.2054088414334, 403.3916445011253, 228.4182848358255, 727.1688281363213, 131.20577641718256, 734.0766193115209, 589.6928347992416, 168.98474189014712, 366.5914513998485, 650.5403801212248, 37.363427692595266, 876.5441375994762, 255.78917392529289, 534.7334341938405, 48.586961581737256, 994.6802336570648, 661.9699158378946, 653.5676184007526, 19.741766514145166, 689.749748377426, 416.77556517954383, 380.2537629276146, 547.2823847289255, 474.3963383277372, 153.12709812443637, 695.192029220909, 630.3113401629369, 301.1164547567923, 661.664576090722, 662.4829658888967, 269.9771495371891, 605.6343369240042, 137.16352297980893, 830.6527204708898, 104.91423690009694, 718.7662245161679, 117.73456843723929, 114.01255268424792, 106.26370830639497, 198.6466617062822, 199.74938614635852, 262.9939156137319, 523.1461075925806, 201.67318998218943, 703.4367502462583,), (295.35120100061295, 39.405707628568656, 496.3547133680927, 207.693984271615, 933.1243446518774, 330.6036161967014, 2.7379990573926927, 671.6331324652049, 906.880044269792, 835.2321652632412, 669.0219305016401, 149.1444926667661, 90.08267357592447, 511.7053469646589, 723.5636168984789, 101.29047859295737, 255.89210275066486, 231.16028649174592, 988.6659307576041, 296.0221787104385, 464.27735723614836, 99.80891505761747, 174.7018089453909, 39.444519968693584, 290.56717723767946, 801.5961026904473, 312.70710696276427, 738.5401957921762, 94.99847033058029, 758.2040048412383, 45.88908709341521, 851.9987250744881, 663.355027490844, 170.51227635637534, 357.5274042377443, 437.7145167819347, 621.8070333325072, 878.4755373974737, 92.9513154374223, 814.9642895364964, 182.86942030364273, 400.77751428075294, 962.309930149754, 271.83309075019014, 385.71548975931427, 850.671578036584, 799.8990969489502, 648.8464838722638, 796.9095570354069, 113.05655637764612,), (696.1698735237949, 58.64639566517737, 942.4669879999001, 159.39539748216492, 416.0277166308965, 590.7503029903385, 802.2647840450959, 678.3931362492056, 181.2605605373021, 379.7507791158876, 358.5989148270966, 28.81622957122154, 684.4641333814728, 838.5364765471118, 973.4445802253692, 130.65532954841586, 920.3979856381027, 112.93747937376742, 411.28426373517425, 45.972325379669996, 261.61286722848985, 314.2379448738709, 704.568176480623, 677.9289210201644, 767.5287182899103, 576.6490081233795, 565.0122256873914, 977.8955575507985, 669.8443582143154, 338.30064176842325, 523.103175224674, 700.5801842975543, 95.24213845169471, 661.713262845963, 248.57699485070762, 345.7494874575037, 676.2955683574412, 384.87661740671433, 839.0330212297873, 558.3442367021879, 987.7916247614772, 54.56611522767585, 643.3987165568129, 156.92737330174123, 848.8455747760007, 851.8711514460662, 869.4156346127679, 74.86524247698479, 491.6479611444012, 240.89181669723115,), (970.1450487834002, 50.35071452654394, 222.706523168799, 643.3173148232519, 403.27699346172284, 234.99999999128718, 459.09459474823524, 801.2612877267221, 448.0985856653739, 856.5892241780172, 447.068119913798, 118.70685205363284, 497.373348066501, 653.3730031383718, 102.64332148015242, 412.3385476755494, 557.1310114048331, 0.16971547833644074, 90.89554691153224, 604.2032760617975, 619.0661537463823, 304.5979581036485, 508.3377403194649, 206.85124884989835, 671.4743707791561, 950.3551299933657, 363.34247756981097, 54.27521330556784, 222.9181143963146, 454.4599826035843, 560.1508451060681, 619.7579696770842, 473.1336810935889, 657.1672064311396, 715.9135002831553, 114.0233583557383, 759.3199510791171, 221.74710782019002, 341.3571145768387, 829.8838856344012, 964.43368345434, 291.9835638082815, 521.9850866041359, 702.0960532599854, 45.77810985503427, 164.15536770539086, 140.44918732224886, 716.8561126478827, 721.5590954513159, 106.961505519519,), (610.8517661386837, 187.54340053160334, 930.2162591531453, 392.89648991517345, 457.0460082929889, 781.4204036487547, 716.7879144693429, 107.76574728749199, 414.4703451283303, 926.6265305373107, 837.4662374864649, 588.8111078979014, 772.1451736372288, 450.50459695896404, 658.4566950002027, 956.190321625751, 134.63601443063277, 498.82864210261124, 530.3643475190843, 48.571605631905214, 935.3082808316526, 838.8161106789165, 482.9319635359263, 508.3328990718833, 921.1623140438011, 177.20243850214555, 578.5543857041766, 730.4946617427778, 128.3817820515778, 387.40557922066597, 600.5460218054113, 882.6958635586781, 504.1101193531644, 384.6626387437041, 979.500760205043, 915.9102416970992, 762.3746562617927, 273.5958799561112, 963.5935376797527, 970.4916499121006, 452.84570608136676, 133.37183101609352, 412.7454728076193, 700.0142848971251, 748.426865877916, 298.91313495542425, 701.494310407385, 860.707546129158, 711.874356395145, 935.5120774625358,), (632.5832845072639, 200.88908232534442, 624.1675892053881, 290.5794954792507, 345.29358089858107, 672.3737130513169, 981.3354962348673, 650.0863878069779, 958.3264528051965, 503.89343904324966, 694.3119737273263, 322.3813974138552, 115.40525228362542, 352.23780246287737, 480.3699223906268, 570.6381942530993, 667.1572380522164, 417.4779331255218, 747.8687233765869, 841.3893024169117, 285.92798022536357, 847.3010914205489, 808.304779326696, 522.7299862092023, 25.272141254472082, 145.32671163279554, 670.2541787669418, 199.9024172647168, 750.1850186700538, 160.93539434323, 286.61554815910995, 250.56100745818298, 839.3818144651713, 690.59485036401, 295.140565961606, 753.593948045165, 32.30638324232715, 814.0262175544686, 102.4800063379766, 868.0353736544103, 739.4924801728499, 864.8414852046026, 742.4127358728605, 561.2705527908106, 237.5954725662045, 784.3071728708866, 798.8217524895705, 287.6386545072203, 664.4935685916236, 926.4848111687795,), (387.5546486419903, 956.7019700118268, 975.8227220561263, 312.6539808946341, 552.1245975330635, 12.965237674638308, 251.34112837230572, 620.561695611849, 780.9248928758853, 870.1180923213351, 829.9834819551445, 911.4863770737386, 704.6290688927878, 647.6323855500005, 755.1077052085499, 546.6151367012413, 603.3869772886416, 776.1467451223322, 964.2898231260307, 294.1777218044915, 177.4258984882632, 682.6588517015351, 186.97658153232365, 173.79513185530516, 513.8468527745739, 377.1982270526456, 428.5078144007343, 556.5986205953329, 130.06653011537227, 583.9860576839312, 255.01614813242202, 330.56278874226905, 709.6892963163141, 154.80569779828625, 153.70338356962975, 322.63002989352, 50.85544119522678, 932.3818655711908, 615.60867609933, 661.890210964939, 490.5509411536766, 572.6769905549301, 358.0855881637394, 784.0314485705014, 317.8590053721895, 219.96201542755745, 183.86909753535895, 68.17646910442588, 505.1403504563159, 415.9312437704217,), (537.0359525339087, 91.94555393906644, 221.19808110145277, 213.35381018619591, 331.8835149039815, 360.7978246627327, 218.4452857845147, 752.6556895545169, 530.4988956068246, 996.6310826043388, 823.6884009252022, 981.1331976152965, 8.803696295037412, 668.9384679943867, 445.66724628469245, 904.4964584876922, 613.7010970607944, 621.131697623634, 958.8968160739336, 682.551360768528, 320.9370105070815, 915.9324302986754, 944.9876704318376, 385.8759347840439, 540.2373129425615, 282.8289942793975, 911.335926628618, 822.1090642399662, 374.95804216197917, 802.8167375082938, 445.556897571718, 43.85166091050174, 898.2816089058533, 192.51238793367708, 513.8947001274336, 948.2118196484181, 167.3948092134949, 956.276206473399, 537.974830964251, 7.330586216845125, 65.47182210371993, 670.3351261430566, 773.5822663692767, 864.9508557126312, 424.20945194114057, 103.92680917367014, 537.7542140241612, 702.7428662675412, 976.2239213390278, 775.2067404630612,), (646.5611593183453, 939.7511115083134, 746.8908256742621, 153.7545772176455, 459.22523526004164, 331.7153754672878, 87.52089793419859, 54.268070891441035, 794.2147869758903, 558.0842053178108, 574.9622942627916, 227.64563494165668, 258.64795899117564, 388.03260364477075, 630.2172646257878, 433.02503463927087, 16.890968304141275, 673.2987323277127, 534.8343700610021, 641.4227464816397, 618.3493589106022, 755.9605966951774, 585.6080133133892, 700.8820313830021, 72.58944479897711, 928.0477501663338, 106.13554186997254, 786.8918976625699, 301.3507098448711, 86.50522143779271, 765.2742410204625, 437.676149468225, 395.11325822861767, 660.7231649072405, 473.98767736795145, 533.4677638103494, 136.3814603295468, 390.9925654289903, 797.4846128334658, 545.7244686408226, 960.1342650804345, 144.07553745848313, 677.2890455050625, 914.1689163846179, 795.0171222324592, 729.3245766158448, 373.130514900324, 949.4651035305127, 553.524187764279, 555.0697456402619,), (123.23515893349901, 5.029839074005116, 596.6548874533303, 537.152048554461, 946.9793892288718, 304.6911523495084, 748.2965867327935, 903.5463759745459, 343.8516498486469, 412.65449602624795, 645.9315035600874, 512.552694547587, 160.95435756439502, 220.78796935740797, 834.7483668680621, 194.32621534632543, 181.35798477365006, 799.6600536055314, 852.2511277674757, 851.3877224254742, 932.3698654031485, 994.3130504254334, 461.2652036895939, 549.6646096791668, 290.65266752008534, 67.36275740480136, 98.20240138037195, 725.5892573341403, 487.1904101337485, 330.8509316827234, 128.21514659595158, 655.4729806969527, 100.0450572754229, 619.5157771543329, 900.1185908429218, 317.7859372337254, 450.64597821493504, 616.3424302594531, 305.6754894067207, 584.1699328451972, 565.5520079051098, 364.4934356786292, 316.33315949632913, 428.3073929595911, 4.831882664671383, 246.17513854548412, 221.52570420651975, 739.8162159386168, 436.1240660896885, 840.1100587806552,), (134.30583238288884, 732.9727957514148, 877.8858295079616, 462.84837607561025, 358.74160211359674, 305.47179644533526, 551.6718261601972, 175.77016089522536, 606.6276660708613, 841.7929953382605, 858.7136553707992, 140.0016687161999, 538.6180370780223, 263.2346505935833, 886.3358320788956, 76.46237855721539, 75.39995679337663, 18.630294871235353, 507.1783112855739, 31.194231008309092, 581.8904904939293, 405.134467551114, 590.036311840718, 906.3035773981833, 551.5939985261583, 543.4693239181186, 998.7281750118517, 472.1039507829259, 775.1972388374282, 365.8186301030095, 223.30664762435504, 772.1448435604627, 733.226827212901, 290.9991602143739, 464.7011421704528, 510.4119933437855, 396.78515764970746, 502.16726966643085, 662.6784893786967, 847.9818401038666, 806.5400569278984, 614.0359765601281, 165.84355203215827, 514.1366805163822, 446.22044383900175, 179.14518390862077, 948.7118316129784, 659.4133069529768, 967.735559837513, 735.9198088348245,), (483.50341572743093, 358.79553884580594, 218.81911725902836, 487.64214837364784, 62.861161413469205, 369.43518544747434, 42.808440838646874, 206.77407471750098, 907.7260925729606, 361.2513645596288, 469.67422901020893, 454.64344979488834, 46.437457501178116, 980.5897820631023, 324.0711100639776, 703.7362528476223, 521.3729041335506, 829.6471556536503, 834.8722662173934, 263.3160693841534, 544.3500620883715, 173.84981644645003, 653.6753131338843, 365.29677799104763, 653.3365465568498, 835.4690676766445, 516.6628975539387, 376.3287810435452, 907.2424264468196, 516.3717821675702, 352.89800775972566, 867.7194218712473, 486.86156267417556, 482.11008971312594, 604.2109802856869, 501.83698447074175, 138.96830707712994, 165.60582242899213, 77.08822574124684, 643.4770023883098, 211.51710413575475, 186.9056758087676, 362.22294421509105, 717.3027084540954, 118.33022282354267, 230.34633259932457, 811.1726293729599, 720.2556588749491, 481.51664938948613, 478.81464272008213,), (210.70395105176343, 161.24625986262575, 833.3649963952541, 22.446038154291358, 43.144331335165376, 573.4865113522683, 161.129140367538, 629.540424888155, 39.190865239483564, 572.2692992304645, 55.91200416044417, 258.2596460167085, 180.0375395762971, 958.2383364845537, 599.3575227949298, 563.6047023357652, 18.62003563723691, 719.5472995040886, 661.7450626291582, 283.45462640564045, 85.74850273574452, 449.20336161945676, 992.7758237079788, 867.3023989920944, 170.56678398969626, 830.3635747042808, 600.8387144112, 794.6160702604512, 820.4531801304973, 181.88415187009966, 659.648750057853, 264.5206985452897, 724.1867980777558, 342.6712745603319, 453.47014514009174, 590.5959909323074, 229.81378717877976, 385.46188274184755, 108.5701139015155, 202.34854908817513, 859.5916920106055, 504.35952607071266, 419.7950126716061, 149.56119362645603, 96.2985767232044, 475.86918548947733, 614.7481694803669, 39.03656361812824, 783.8682348387684, 503.3292081384595,), (117.51640657477047, 482.2409351124809, 130.32884077716434, 603.4746508247106, 827.5217786520734, 896.9689528475317, 774.8704230678032, 649.2317351065338, 522.0442296615541, 370.3774502487801, 43.438283336481476, 529.8165189397056, 229.4862563790502, 802.3001960078137, 789.5555851509546, 381.23649619905507, 589.0480861412066, 741.394042282245, 761.5392270474919, 696.7076031574667, 97.71690834678259, 133.88310562460137, 477.14123700454, 232.51289062265568, 859.0532473014114, 283.26666800895583, 876.7663131932245, 408.6467046872152, 189.02101371787072, 709.1488372061078, 789.422482305125, 577.9871458004462, 117.82860398115102, 7.194052450581467, 654.5461208031974, 687.7667367777761, 315.90171462927066, 362.2697897745908, 154.77858056181358, 651.5759347779317, 253.63272623198395, 854.9769262679285, 423.56154743336316, 365.64409443647173, 275.56054287998876, 680.6057050634485, 754.9719006098335, 413.2281582959364, 783.7884169635375, 482.4677570068806,), (370.212968879147, 554.9113562983285, 253.77764544269633, 306.6530866745945, 344.35630421485365, 705.4946079710852, 735.7824306046107, 854.9989228235969, 659.282849126203, 747.8141950798877, 446.5948774152193, 699.3114422950533, 161.79511770813627, 214.45814153808007, 400.56090642572286, 338.4378463093469, 550.5762145968181, 697.0574087244603, 706.7899335328698, 160.73800913321722, 964.5702513745175, 5.290633472571971, 91.08858423870403, 145.3284358357537, 925.8617877978492, 435.377434552764, 64.0804563693106, 221.770244619512, 80.03973669580266, 37.755338285078935, 384.326010426022, 984.4481006312732, 613.9655907167139, 521.0055432205589, 711.6058317089812, 597.4725638140962, 945.7158578456077, 820.2287344282204, 640.291314530137, 438.97204854123584, 201.7072428060146, 656.8842227068429, 803.9099091153836, 286.00651170869537, 33.609351396258425, 599.5149128924895, 515.8136315528284, 231.67097281806159, 169.48289181310216, 36.06113314434878,), (239.25662528737723, 0.28727185462340543, 157.44476085039082, 996.8171438272063, 779.6728789764172, 353.127663356528, 395.1431170512062, 586.0801652096985, 517.7427009892949, 736.925540146395, 68.61717193705897, 90.1658575951505, 284.4937870363794, 829.7648024492999, 915.008973948154, 348.09524824824246, 963.2876650654888, 276.5078486971222, 606.3634079922522, 194.52519308596106, 929.7138408121202, 574.9067217458147, 261.10750567193907, 407.13462610720165, 106.46495639257658, 72.38597052670393, 294.25182446812005, 947.0154651776626, 802.9422425723574, 956.7093495852752, 876.113046243067, 813.1473496063743, 574.8938787739089, 694.1918614713001, 966.0554556331059, 563.1686038049302, 769.9671927338335, 757.4732850466725, 961.2387253016144, 458.848429334124, 460.7204414645122, 586.8265171499795, 27.350697532073866, 116.2683014409226, 67.55426066125358, 633.6617975581885, 994.1980333654756, 676.9129760650999, 229.93013858915413, 315.7306940289286,), (955.4456871233602, 516.5042172048383, 9.723191714424928, 832.1770449232287, 248.25760936207752, 93.00943016209173, 673.6979167818138, 821.0746894925302, 76.02882053368909, 931.3962398356626, 476.5554540892897, 353.53792621617106, 894.3162528702067, 269.07578246879905, 947.1181090634782, 683.1073085237686, 909.9274589878878, 498.9993403636831, 199.6665390943092, 735.4290652428307, 872.7424984082979, 206.77863232200988, 202.76655878827333, 249.69543394695748, 618.5968991818082, 155.99556847615892, 106.6892901053912, 920.1881295329896, 675.8121248765266, 663.42407832045, 613.8269752757525, 764.244212635527, 541.4739546030245, 42.26312673099775, 482.2920812010506, 620.6674748317666, 492.340377704212, 985.3613293244484, 896.999773193295, 868.7688356245534, 490.8053516065467, 984.3946826775544, 916.0068296725573, 280.26662318318705, 222.09278307981327, 576.6651863474098, 54.14817128878413, 799.269917779981, 478.32007661204347, 540.767945591827,), (502.47521212167254, 393.7169228523979, 686.3386465303895, 174.80036211904903, 976.5071952370417, 698.2445680961396, 460.07059004643, 689.2105490596489, 11.819978610448922, 210.7523857014477, 581.0082189794467, 325.43619991542636, 612.7780963617714, 259.700769090298, 548.5634003791886, 237.13915250754525, 471.34536473845543, 613.0840431384478, 365.9241511219268, 498.7700166101968, 210.53248211065633, 700.7126268163007, 372.30962786881605, 854.3260872408196, 279.62953188068406, 179.88922872699587, 131.13916649872394, 575.8927292079621, 228.5667494996686, 99.86974090945311, 269.9491069025366, 235.80517288550763, 432.2187517308349, 380.883382231468, 145.84373533753637, 959.1479207466692, 149.54805726548292, 805.6683615575902, 176.60727665961284, 499.5978261118507, 995.5617702996016, 849.3899153966604, 517.0069749205006, 720.5603295246306, 785.3329366938784, 300.0341821534487, 562.1384520717112, 567.8345192053117, 398.40190186332757, 690.5431759606441,), (60.036377724016596, 813.8180692068463, 476.539424352137, 629.8622123621544, 449.85481906379175, 334.6565385145628, 360.96202105512157, 560.3500348142645, 931.8445757777681, 257.7148254524192, 19.83533361376, 121.23748921670374, 867.3685839741368, 963.1165609179141, 198.9581106368732, 575.8944574733487, 649.130951899575, 174.53883572945838, 780.3090980992371, 355.1090383908918, 673.1928401473501, 487.49374430436166, 736.5261647621551, 889.6324491382205, 381.07099841284577, 286.5235765557891, 631.7121904834689, 144.84833827806742, 167.57553548053383, 807.7171439570685, 337.3883099989331, 631.273408325837, 571.6375738737935, 848.9007719388086, 71.34407817655742, 161.99948894698534, 228.21793938787326, 316.8782416955612, 291.3563527944199, 267.4723908706379, 644.6442931551883, 271.248101351349, 444.89838764797173, 862.8064836470026, 363.1648402504287, 586.9197735260105, 965.5260328224946, 413.98906087367317, 183.886199103384, 23.10109231650759,), (727.7724164324044, 662.0225478086397, 940.4373373379956, 700.6969284246842, 80.06473787554714, 166.91046503965245, 103.00931405688607, 63.79239959116023, 878.6909764497414, 548.4337438182938, 26.364913366241026, 397.0125207601695, 764.9963721335957, 83.25120139068665, 262.45226426498715, 155.27436200016186, 654.8326755433498, 885.4713170963988, 309.3706535499986, 247.39651931519225, 284.175280058089, 626.4806846261897, 131.27711062686387, 838.5177788928746, 28.5712930199129, 663.5491353038045, 860.3688174133925, 325.20131665647875, 476.15474065537353, 974.9815454400946, 540.5841613922831, 272.41442761231417, 444.84493237074383, 969.5422645545461, 697.6228769070474, 177.03663997971208, 597.5841988365606, 631.0475226410597, 635.8511364258356, 563.9844867212875, 523.4598043545075, 639.528248710812, 309.6613281139966, 347.1683001641309, 539.6681221360597, 804.707766246453, 441.8603055765344, 365.7833219483778, 259.80704393772436, 303.57291557308895,), (0.09234639492805563, 815.3797891608882, 847.9464883826706, 343.88491724999426, 469.19795524402616, 8.3171105963733, 922.0883957440528, 946.9575118681244, 477.06707045145714, 9.132599576333167, 430.43531326923346, 292.9226130023878, 231.21539351915854, 7.217985005767535, 373.64387652703357, 411.73586906613457, 560.5543586396154, 394.75207962018334, 163.26949533028878, 737.1178541033773, 389.7232698531959, 378.35806027496176, 262.98027544624904, 422.23938275369267, 239.56181493856056, 764.5382940766145, 909.9544034392849, 807.5749404275756, 684.6402903785192, 284.67685467345297, 742.9268921311517, 808.8086490990372, 412.9038765132473, 853.4820233916041, 182.4071790026428, 289.59529929128547, 636.8927095483467, 617.7294139025154, 272.13413045162406, 622.7537802755535, 187.7894425440112, 19.39539724699635, 49.632072223098625, 534.9707530597663, 185.93759814152565, 102.27348865653974, 269.19513288914555, 715.195942211275, 727.107070447535, 232.81049795595544,), (150.68369369748856, 493.5795531957453, 341.91285386862626, 311.5766852030555, 799.4623519360701, 997.9634461655995, 463.6844672034293, 791.4247142652953, 330.24864703583034, 843.5462992680785, 951.644041732608, 56.038344401892815, 775.6529107125235, 71.38469474905685, 470.1467765987197, 192.19229292645278, 841.5392879402597, 817.2507239995704, 828.2520473943315, 121.96155618577076, 768.1456708511898, 248.96236050723408, 771.2253330273345, 442.96094214292003, 737.6190125535086, 33.51029157027507, 460.8346353122799, 770.9959677901886, 521.1391200572839, 982.1158514056141, 472.8255449915152, 681.4423200578532, 312.1093655840698, 323.0631884829062, 629.1642541306993, 42.18591559473106, 937.4209236322339, 520.9262529175096, 253.31274033508888, 638.5200120420238, 198.72363180239984, 887.5195760435424, 863.6576657496004, 217.78824905510285, 112.92845931623752, 632.9089659017448, 324.5134876300968, 167.36035640828607, 276.1000438384478, 119.64959201073866,), (789.1437801948886, 8.929855889342143, 41.993369021095404, 783.4752402510599, 475.27067876165006, 596.2122839316125, 371.39866330323366, 89.47990274967444, 157.6984561973751, 91.40155230421298, 618.6448584379411, 930.8487845186271, 996.9731029835355, 625.1323302094067, 59.775131609422495, 644.5748966479184, 701.3281400230029, 791.4662443652128, 125.8274576652264, 232.67463085606354, 981.6757109638336, 788.5618257868736, 756.697523201389, 805.5253765865858, 438.71889809659734, 192.96529905015848, 689.2499802840422, 358.1674972504314, 134.73217376095226, 898.6630442700426, 485.30213316251314, 437.5000041650592, 294.7145964408969, 697.0377749956548, 189.77583621965144, 186.6267289491038, 347.6646790182655, 732.4024101227128, 275.32446285830315, 831.3495933911199, 914.4740019780461, 554.5203101660566, 68.87658336115943, 155.02547807532696, 295.67266121073476, 263.349268895432, 367.11270700453946, 0.7198287242494716, 638.0260624435374, 379.04356966657735,), (185.53429009714995, 18.719856588055194, 856.8292883342145, 776.0165677979388, 238.78655821602112, 721.0035626839232, 658.28941737982, 538.9565215934048, 387.49084018073563, 524.0697688104523, 497.54915956954727, 551.4678072492145, 613.0392423505267, 322.60003991279393, 640.9039297850732, 110.46413403940414, 523.2005391932355, 66.67984309626563, 835.1933578982914, 129.7742663222461, 873.4927775805724, 202.07902868878213, 485.3702872366953, 98.75358664853229, 571.012181354699, 836.2995266064913, 657.9212582849684, 525.8213626144117, 701.3140215747126, 255.93853033397806, 366.3540200942962, 605.95362423245, 70.873960950122, 930.211036809051, 208.73809349350148, 491.4055014282741, 889.7673251634665, 79.95680323287202, 801.0199490108841, 60.70018828605151, 558.0198894700217, 938.2438576998607, 415.9532027097531, 369.0686508584735, 708.4337849788369, 823.6429662774005, 265.8156344966652, 40.65315227434285, 70.38812961024409, 303.90454886805543,), (944.6034820072357, 960.5867454371248, 97.11925015766253, 725.1719328091889, 531.6610514024726, 189.46041310667573, 526.1920217936971, 248.76302905401337, 625.325053539152, 178.8978916289171, 749.1943579773724, 415.18460024974024, 105.51418189915573, 638.6242998880421, 357.96336587140377, 458.7205924283744, 665.2423547735436, 882.8240193732261, 166.80931890354745, 180.39653860006823, 401.5571736148952, 337.512870561569, 158.1764558993315, 998.4335720532814, 443.01216999828785, 352.3240752703322, 315.1549961423973, 991.4629906181269, 324.65410862009236, 371.71951243537126, 764.1843679043711, 430.5880727332455, 725.8676836151698, 608.413370257039, 563.1447028042841, 213.9281360437204, 765.7082196342848, 926.1469225926575, 254.08912718302412, 961.6517756313344, 447.49978899824936, 397.09056168940384, 726.3132783372915, 982.9564711785478, 595.7628266237857, 517.6093588403293, 993.4120095545393, 301.8416539323987, 300.3339617281736, 221.8867034824401,), (855.7204255082062, 21.70137145759954, 821.9399224471144, 689.719183909427, 275.95713674360564, 553.6631532853207, 556.3230855067685, 925.9224502117322, 154.7147862187559, 37.73630008376983, 355.65575328549073, 138.40819347641, 367.0828930260639, 582.15570635987, 232.99414855423439, 811.0742367826583, 91.90512128537264, 399.79824847776393, 917.8818033444678, 734.2659538775798, 723.580526744515, 792.90419431428, 172.9024908563226, 825.7052443848854, 689.5951010498992, 576.2324617766981, 907.6556421470943, 595.2312025595801, 300.39325384327464, 730.7893619562772, 576.2836961563387, 78.47739592285386, 55.9227088233194, 770.9015036701385, 347.9302069529332, 817.1417000465422, 416.5217126179194, 867.8310969512203, 869.7865141212199, 226.7176713621516, 652.7791831603508, 602.3016343165425, 11.43438096260352, 777.4186181440473, 382.4869621852632, 304.78339287924285, 41.18225216356319, 539.9297294386691, 149.58001916730123, 502.40161044723277,), (220.79717482028516, 50.51981414582751, 731.5785735649414, 392.8827045190352, 445.61623597771074, 595.1321912948403, 504.7293461120348, 222.0856281514143, 289.78302915668706, 394.3224038780659, 132.18904537087806, 82.54511263858267, 571.4405905106294, 49.31109807008804, 399.1912995469419, 85.0789874682354, 501.8231156057946, 773.8251365562567, 130.37517489517413, 134.87076372595496, 559.2961773135403, 487.8610998043328, 652.2484389791689, 196.09930039322077, 615.9968375942019, 735.6677541453226, 246.24584050711306, 71.64398034108488, 776.7718987916836, 323.41164115621126, 924.1380808950829, 89.59464812673524, 671.7475534497354, 423.54060236564004, 348.3078568465243, 320.7383628526695, 593.8771490410704, 24.206830335269046, 304.8188475463931, 987.6519169637293, 616.2209944545034, 990.1591822713317, 442.2101052342515, 145.81847519027758, 44.87755093137324, 818.1719201318144, 199.68521360575576, 373.82079032056936, 757.7337825930621, 852.7641451792005,), (112.37174078510425, 54.53801336175423, 948.9409150257889, 926.7296951091071, 868.7523516449686, 820.1339564299961, 13.733292693431377, 693.7952250561176, 111.27799231646985, 450.0615706383459, 22.748115151202008, 209.00954112420212, 538.0054579069861, 203.80135287660516, 523.2658814665313, 258.6580034255985, 483.02633034469176, 729.9235835097888, 141.34742092394183, 698.7552320082528, 18.38851255630447, 583.0049433264157, 663.5283419996432, 43.482007499458966, 170.31964704476331, 284.0135410479907, 789.1884945074221, 617.9647763913978, 53.08524865627895, 654.757871815387, 8.334175248427611, 388.6451472369792, 271.3103790803375, 852.0829447572587, 660.1003733293647, 864.2822735599534, 19.078323934194174, 867.4104428544812, 649.4111447522284, 231.16862013781702, 380.7012029628479, 976.6119398898542, 99.60371184253735, 315.45596930877974, 866.7727455281198, 531.5606451632132, 186.41738741751902, 500.65082286887883, 457.98626214778074, 926.3499730264335,), (21.488458214345528, 247.39213974975127, 529.4153638551198, 333.56963463077574, 393.3998178272747, 157.0052505908327, 346.7799029075455, 351.913082267181, 625.2309702510104, 236.20476570721272, 978.2442672445462, 501.25146117715735, 811.8928600997455, 625.792363715763, 878.6772741911603, 890.4020860787615, 814.858637678337, 29.46589845520775, 554.9393515968055, 280.19384462820506, 151.70818076868466, 897.1753149691715, 656.9317589496127, 87.79501713730143, 382.2071218544083, 960.6056934445716, 612.4483933501054, 625.5246004948266, 227.42797745267552, 240.36080817057604, 152.7491541688516, 970.537173095217, 909.7353935641712, 329.2847383705465, 542.2337512058923, 206.75727466100935, 138.5765267985366, 541.353826851015, 800.1050591640936, 862.5876615800681, 308.9983961621827, 705.1363877851007, 523.8054835018718, 135.25249384391523, 995.6858002320087, 975.7769090685993, 145.15239489251186, 933.0299464229068, 917.1121206734174, 317.4962108419391,), (557.3402980935688, 948.6015641647615, 118.38729071717869, 317.59758919121697, 879.6381216162653, 727.0795332601915, 765.4350125691849, 880.131900108576, 414.04015541967885, 411.2517632793493, 443.004487049652, 933.768524391193, 894.1300931338027, 933.2500304158002, 273.79616463152354, 779.1078849727928, 106.77143284340107, 184.74649657456732, 762.4474837329655, 611.981597669024, 266.86261300447956, 567.0430929389746, 230.911348733013, 232.18253426933566, 687.377885178361, 359.26133453455134, 688.141284732716, 476.6022200441854, 502.32458745046125, 604.7117596027515, 712.01912797666, 373.95940113050096, 852.1294324655852, 491.4455970663347, 137.5127223416147, 193.25982947851017, 32.22955322704579, 764.5388541282347, 15.014260451757021, 269.85870264618126, 413.04402219171976, 742.3666616628996, 988.2434174629566, 757.7765897256293, 66.13632423488936, 927.1032061777938, 985.6276395863681, 867.1290026386034, 489.9428985108636, 324.8956268040456,), (457.5449294417121, 246.78384086158633, 404.86676824252845, 41.826275050118156, 735.3317798996605, 380.3741687551209, 312.89749141149747, 611.504874670052, 742.4678547079448, 593.805801392789, 525.2305317028896, 875.7829718598168, 786.9198623188731, 520.6490912237487, 451.6095925636621, 827.0101016593499, 42.41591600909778, 995.8363715240481, 518.7026525875117, 395.6111966358917, 735.1700815502675, 557.7009498134095, 516.1266830947985, 630.6485530927204, 49.29544918127604, 291.17936223412954, 398.0398914545593, 304.5541478222386, 827.6210076057507, 461.347163963963, 422.46218245044145, 613.1342792438925, 54.54666681552644, 516.969650266138, 142.23961877509163, 829.8935044266395, 451.6980878296554, 722.646791492833, 113.17158624359503, 778.7311418760397, 937.8424576992394, 696.1141418397956, 135.30667419036257, 413.55930509761663, 450.8858548470743, 178.8785789436156, 590.315475289654, 712.6343913063026, 201.9320047091576, 454.6832076473508,), (250.08229407723826, 691.7174283309014, 907.1624630054067, 796.4862224757226, 718.374719718563, 123.54421683543349, 114.16493882407775, 449.3984180247218, 362.94338915725456, 523.8173060661488, 384.0873742615717, 791.0665942370252, 511.6664514845053, 949.7538891427195, 378.67863141257976, 380.60700926114674, 768.2603237843883, 912.2527788221324, 565.4915785141691, 659.5303406020439, 150.08973451051176, 868.8185374782038, 178.86676467326868, 712.0474878796226, 419.58169123576727, 309.5183054369818, 768.6304223609426, 446.2582341490725, 626.4528496615293, 117.55397840247983, 131.57103181208575, 202.8517052794463, 622.5603279873416, 253.11097511803925, 458.64265782853664, 855.4110564190316, 543.5011596043003, 5.750548899053909, 882.7029205371946, 237.87835401947677, 588.8623271843092, 475.71198980315387, 410.15136571135633, 79.40491736841615, 600.1021042955892, 244.71317143863337, 547.3416095831464, 619.7660388695853, 557.5486482688888, 825.2525047036826,), (48.918799943029725, 148.25616817969035, 653.2768782155202, 36.72105182241203, 853.8292872341877, 666.996542946195, 838.014435792606, 298.44154664211777, 920.3846982695362, 49.03136274879905, 416.9569238553582, 178.2483917574228, 672.090495261405, 611.089756048654, 691.5136180806667, 594.9689863047196, 787.3075795335859, 177.4192673436795, 455.3378280202657, 578.9662924422967, 931.4798193860056, 93.12628782176957, 299.2944815965134, 368.56942547507407, 378.9393515904601, 67.66667782099533, 427.3238393910946, 550.4710828134854, 292.83271335673953, 134.54578907805103, 694.619438494801, 274.4185427517334, 527.0527334046208, 524.6462092537088, 695.8294973451716, 612.053744928424, 109.30223443632624, 729.7285197626795, 628.9785990785555, 987.1892637585828, 483.662983209447, 688.6542782543451, 933.8916747881832, 986.2784423441115, 287.1873038042605, 608.8446069344726, 316.48855849847223, 525.3912253315947, 995.0243355575512, 353.85334437419004,)))" ] }, "execution_count": 43, @@ -1750,7 +1752,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Matrix((639.4, ...), ..., (..., 353.9))[100x50]\n" + "Matrix((639.4267984578837, ...), ..., (..., 353.85334437419004))[100x50]\n" ] } ], @@ -1790,7 +1792,7 @@ { "data": { "text/plain": [ - "Vector((129.713, 562.634, 519.706, 631.858, 492.504, 179.907, 609.406, 708.587, 979.258, 1.581, 23.987, 625.461, 117.926, 848.070, 799.564, 998.987, 414.041, 333.792, 560.416, 637.504, 11.297, 201.187, 281.627, 790.196, 307.773, 506.690, 323.924, 6.131, 685.836, 341.362, 724.397, 615.993, 29.117, 175.629, 330.515, 337.937, 672.473, 916.163, 797.254, 645.652, 481.496, 627.200, 892.058, 536.968, 335.110, 783.989, 413.953, 742.585, 835.106, 299.344))" + "Vector((129.7131375543026, 562.6343376666077, 519.7058001844938, 631.8576074968361, 492.50445371851595, 179.90723165619127, 609.4057577306154, 708.5870675033931, 979.2576595660704, 1.580917225831202, 23.986797518176004, 625.4607423896532, 117.92572392127187, 848.0697885701867, 799.5643512555062, 998.9870072501308, 414.04113952437183, 333.7922569338235, 560.4155049178265, 637.5035435880566, 11.297267978029769, 201.1871389300902, 281.62670325053864, 790.1955028462116, 307.77254924389706, 506.6896270960092, 323.9238393830701, 6.131262435292828, 685.8357886469579, 341.36158523000273, 724.3966428110118, 615.9933429964842, 29.117381811460618, 175.62908823799773, 330.51483209301, 337.936855273543, 672.4729573660214, 916.1630531735751, 797.2543838289249, 645.6522206645749, 481.4955231203607, 627.2004877076889, 892.0583267899182, 536.9675449723645, 335.10965453438513, 783.9890332085388, 413.95306533418574, 742.5846461655369, 835.1057359656187, 299.3437466393607))" ] }, "execution_count": 46, @@ -1815,7 +1817,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Vector(129.7, ..., 299.3)[50]\n" + "Vector(129.7131375543026, ..., 299.3437466393607)[50]\n" ] } ], @@ -1860,7 +1862,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Vector(11378937.3, ..., 13593029.3)[100]\n" + "Vector(11378937.3105893, ..., 13593029.305789862)[100]\n" ] } ], @@ -1905,7 +1907,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Matrix((14370711.3, ...), ..., (..., 16545418.2))[100x100]\n" + "Matrix((14370711.265265431, ...), ..., (..., 16545418.239505699))[100x100]\n" ] } ], @@ -1939,7 +1941,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Matrix((32618511.5, ...), ..., (..., 32339164.8))[50x50]\n" + "Matrix((32618511.50703142, ...), ..., (..., 32339164.77803234))[50x50]\n" ] } ], @@ -2111,7 +2113,7 @@ { "data": { "text/plain": [ - "Vector((1.000, 2.000, 3.000))" + "Vector((1.0, 2.0, 3.0))" ] }, "execution_count": 60, @@ -2161,7 +2163,7 @@ { "data": { "text/plain": [ - "Matrix(((1.000, 2.000, 3.000,), (4.000, 5.000, 6.000,), (7.000, 8.000, 9.000,)))" + "Matrix(((1.0, 2.0, 3.0,), (4.0, 5.0, 6.0,), (7.0, 8.0, 9.0,)))" ] }, "execution_count": 62, @@ -2419,7 +2421,7 @@ { "data": { "text/plain": [ - "Matrix(((1.000, 4.000, 7.000,), (2.000, 5.000, 8.000,), (3.000, 6.000, 9.000,)))" + "Matrix(((1.0, 4.0, 7.0,), (2.0, 5.0, 8.0,), (3.0, 6.0, 9.0,)))" ] }, "execution_count": 71, @@ -2504,7 +2506,7 @@ { "data": { "text/plain": [ - "Vector((14.000, 32.000, 50.000))" + "Vector((14.0, 32.0, 50.0))" ] }, "execution_count": 74, @@ -2563,7 +2565,7 @@ { "data": { "text/plain": [ - "Vector((10.000, 20.000, 30.000))" + "Vector((10.0, 20.0, 30.0))" ] }, "execution_count": 76, diff --git a/11_classes/sample_package/matrix.py b/11_classes/sample_package/matrix.py index 3c82d26..50a683f 100644 --- a/11_classes/sample_package/matrix.py +++ b/11_classes/sample_package/matrix.py @@ -43,7 +43,7 @@ class Matrix: Example Usage: >>> Matrix([(1, 2), (3, 4)]) - Matrix(((1.000, 2.000,), (3.000, 4.000,))) + Matrix(((1.0, 2.0,), (3.0, 4.0,))) """ self._entries = self.storage( self.storage(self.typing(x) for x in r) for r in data @@ -73,7 +73,7 @@ class Matrix: Example Usage: >>> Matrix.from_columns([(1, 2), (3, 4)]) - Matrix(((1.000, 3.000,), (2.000, 4.000,))) + Matrix(((1.0, 3.0,), (2.0, 4.0,))) """ return cls(data).transpose() @@ -89,7 +89,7 @@ class Matrix: """Text representation of a Matrix.""" name = self.__class__.__name__ args = ", ".join( - "(" + ", ".join(f"{c:.3f}" for c in r) + ",)" for r in self._entries + "(" + ", ".join(repr(c) for c in r) + ",)" for r in self._entries ) return f"{name}(({args}))" @@ -97,7 +97,7 @@ class Matrix: """Human-readable text representation of a Matrix.""" name = self.__class__.__name__ first, last, m, n = self[0], self[-1], self.n_rows, self.n_cols - return f"{name}(({first:.1f}, ...), ..., (..., {last:.1f}))[{m:d}x{n:d}]" + return f"{name}(({first!r}, ...), ..., (..., {last!r}))[{m:d}x{n:d}]" @property def n_rows(self): @@ -211,13 +211,13 @@ class Matrix: Example Usage: >>> Matrix([(1, 2), (3, 4)]) + Matrix([(2, 3), (4, 5)]) - Matrix(((3.000, 5.000,), (7.000, 9.000,))) + Matrix(((3.0, 5.0,), (7.0, 9.0,))) >>> Matrix([(1, 2), (3, 4)]) + 5 - Matrix(((6.000, 7.000,), (8.000, 9.000,))) + Matrix(((6.0, 7.0,), (8.0, 9.0,))) >>> 10 + Matrix([(1, 2), (3, 4)]) - Matrix(((11.000, 12.000,), (13.000, 14.000,))) + Matrix(((11.0, 12.0,), (13.0, 14.0,))) """ # Matrix addition if isinstance(other, self.__class__): @@ -247,13 +247,13 @@ class Matrix: Example Usage: >>> Matrix([(2, 3), (4, 5)]) - Matrix([(1, 2), (3, 4)]) - Matrix(((1.000, 1.000,), (1.000, 1.000,))) + Matrix(((1.0, 1.0,), (1.0, 1.0,))) >>> Matrix([(1, 2), (3, 4)]) - 1 - Matrix(((0.000, 1.000,), (2.000, 3.000,))) + Matrix(((0.0, 1.0,), (2.0, 3.0,))) >>> 10 - Matrix([(1, 2), (3, 4)]) - Matrix(((9.000, 8.000,), (7.000, 6.000,))) + Matrix(((9.0, 8.0,), (7.0, 6.0,))) """ # As subtraction is the inverse of addition, # we first dispatch to .__neg__() to invert the signs of @@ -286,23 +286,23 @@ class Matrix: Example Usage: >>> Matrix([(1, 2), (3, 4)]) * Matrix([(1, 2), (3, 4)]) - Matrix(((7.000, 10.000,), (15.000, 22.000,))) + Matrix(((7.0, 10.0,), (15.0, 22.0,))) >>> 2 * Matrix([(1, 2), (3, 4)]) - Matrix(((2.000, 4.000,), (6.000, 8.000,))) + Matrix(((2.0, 4.0,), (6.0, 8.0,))) >>> Matrix([(1, 2), (3, 4)]) * 3 - Matrix(((3.000, 6.000,), (9.000, 12.000,))) + Matrix(((3.0, 6.0,), (9.0, 12.0,))) Matrix-vector and vector-matrix multiplication are not commutative. >>> from sample_package import Vector >>> Matrix([(1, 2), (3, 4)]) * Vector([5, 6]) - Vector((17.000, 39.000)) + Vector((17.0, 39.0)) >>> Vector([5, 6]) * Matrix([(1, 2), (3, 4)]) - Vector((23.000, 34.000)) + Vector((23.0, 34.0)) """ # Scalar multiplication if isinstance(other, numbers.Number): @@ -334,7 +334,7 @@ class Matrix: Example Usage: >>> Matrix([(1, 2), (3, 4)]) / 4 - Matrix(((0.250, 0.500,), (0.750, 1.000,))) + Matrix(((0.25, 0.5,), (0.75, 1.0,))) """ # As scalar division division is the same as multiplication # with the inverse, we dispatch to .__mul__(). @@ -409,7 +409,7 @@ class Matrix: Example Usage: >>> Matrix([(1, 2, 3)]).as_vector() - Vector((1.000, 2.000, 3.000)) + Vector((1.0, 2.0, 3.0)) """ if not (self.n_rows == 1 or self.n_cols == 1): raise RuntimeError("one dimension (m or n) must be 1") @@ -424,9 +424,9 @@ class Matrix: Example Usage: >>> m = Matrix([(1, 2), (3, 4)]) >>> m - Matrix(((1.000, 2.000,), (3.000, 4.000,))) + Matrix(((1.0, 2.0,), (3.0, 4.0,))) >>> m.transpose() - Matrix(((1.000, 3.000,), (2.000, 4.000,))) + Matrix(((1.0, 3.0,), (2.0, 4.0,))) """ return self.__class__(zip(*self._entries)) diff --git a/11_classes/sample_package/vector.py b/11_classes/sample_package/vector.py index 21e3729..e9315c4 100644 --- a/11_classes/sample_package/vector.py +++ b/11_classes/sample_package/vector.py @@ -42,10 +42,10 @@ class Vector: Example Usage: >>> Vector([1, 2, 3]) - Vector((1.000, 2.000, 3.000)) + Vector((1.0, 2.0, 3.0)) >>> Vector(range(3)) - Vector((0.000, 1.000, 2.000)) + Vector((0.0, 1.0, 2.0)) """ self._entries = self.storage(self.typing(x) for x in data) if len(self) == 0: @@ -54,14 +54,14 @@ class Vector: def __repr__(self): """Text representation of a Vector.""" name = self.__class__.__name__ - args = ", ".join(f"{x:.3f}" for x in self) + args = ", ".join(repr(x) for x in self) return f"{name}(({args}))" def __str__(self): """Human-readable text representation of a Vector.""" name = self.__class__.__name__ first, last, n_entries = self[0], self[-1], len(self) - return f"{name}({first:.1f}, ..., {last:.1f})[{n_entries:d}]" + return f"{name}({first!r}, ..., {last!r})[{n_entries:d}]" def __len__(self): """Number of entries in a Vector.""" @@ -88,13 +88,13 @@ class Vector: Example Usage: >>> Vector([1, 2, 3]) + Vector([2, 3, 4]) - Vector((3.000, 5.000, 7.000)) + Vector((3.0, 5.0, 7.0)) >>> Vector([1, 2, 3]) + 4 - Vector((5.000, 6.000, 7.000)) + Vector((5.0, 6.0, 7.0)) >>> 10 + Vector([1, 2, 3]) - Vector((11.000, 12.000, 13.000)) + Vector((11.0, 12.0, 13.0)) """ # Vector addition if isinstance(other, self.__class__): @@ -119,13 +119,13 @@ class Vector: Example Usage: >>> Vector([7, 8, 9]) - Vector([1, 2, 3]) - Vector((6.000, 6.000, 6.000)) + Vector((6.0, 6.0, 6.0)) >>> Vector([1, 2, 3]) - 1 - Vector((0.000, 1.000, 2.000)) + Vector((0.0, 1.0, 2.0)) >>> 10 - Vector([1, 2, 3]) - Vector((9.000, 8.000, 7.000)) + Vector((9.0, 8.0, 7.0)) """ # As subtraction is the inverse of addition, # we first dispatch to .__neg__() to invert the signs of @@ -148,10 +148,10 @@ class Vector: 20.0 >>> 2 * Vector([1, 2, 3]) - Vector((2.000, 4.000, 6.000)) + Vector((2.0, 4.0, 6.0)) >>> Vector([1, 2, 3]) * 3 - Vector((3.000, 6.000, 9.000)) + Vector((3.0, 6.0, 9.0)) """ # Dot product if isinstance(other, self.__class__): @@ -176,7 +176,7 @@ class Vector: Example Usage: >>> Vector([9, 6, 12]) / 3 - Vector((3.000, 2.000, 4.000)) + Vector((3.0, 2.0, 4.0)) """ # As scalar division division is the same as multiplication # with the inverse, we dispatch to .__mul__(). @@ -253,9 +253,9 @@ class Vector: Example Usage: >>> v = Vector([1, 2, 3]) >>> v.as_matrix() - Matrix(((1.000,), (2.000,), (3.000,))) + Matrix(((1.0,), (2.0,), (3.0,))) >>> v.as_matrix(column=False) - Matrix(((1.000, 2.000, 3.000,))) + Matrix(((1.0, 2.0, 3.0,))) """ if column: return self.matrix_cls([x] for x in self) From 8dd24414ac15b8b0951d36b6a93a9e8e38f4ddfa Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 28 Oct 2020 17:20:55 +0100 Subject: [PATCH 121/142] Streamline slides --- 11_classes/00_content.ipynb | 12 +- 11_classes/03_content.ipynb | 84 ++++++-- 11_classes/04_content.ipynb | 396 ++++++++++++++++++++++++++++++------ 3 files changed, 406 insertions(+), 86 deletions(-) diff --git a/11_classes/00_content.ipynb b/11_classes/00_content.ipynb index 2850088..34e3df3 100644 --- a/11_classes/00_content.ipynb +++ b/11_classes/00_content.ipynb @@ -1287,14 +1287,22 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "From a theoretical point of view, the text representation provided by `.__repr__()` contains all the information (i.e., the $0$s and $1$s in memory) that is needed to model something in a computer. In a way, it is a natural extension from the binary (cf., [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb#Binary-Representations)), hexadecimal (cf., [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb#Hexadecimal-Representations)), and `bytes` (cf., [Chapter 6 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/02_content.ipynb#The-bytes-Type)) representations of information. After all, just like Unicode characters are encoded in `bytes`, the more \"complex\" objects in this chapter are encoded in Unicode characters via their text representations." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "source": [ "### The `Matrix` Class" ] diff --git a/11_classes/03_content.ipynb b/11_classes/03_content.ipynb index 5bd0fe2..32491d8 100644 --- a/11_classes/03_content.ipynb +++ b/11_classes/03_content.ipynb @@ -260,7 +260,7 @@ "execution_count": 8, "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "slide" } }, "outputs": [ @@ -474,7 +474,11 @@ { "cell_type": "code", "execution_count": 15, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [ { "data": { @@ -542,7 +546,11 @@ { "cell_type": "code", "execution_count": 16, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "outputs": [ { "data": { @@ -562,7 +570,11 @@ { "cell_type": "code", "execution_count": 17, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [ { "data": { @@ -597,7 +609,7 @@ "execution_count": 18, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "fragment" } }, "outputs": [ @@ -1798,7 +1810,11 @@ { "cell_type": "code", "execution_count": 55, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [], "source": [ "v = Vector([1, 2, 3])\n", @@ -1808,7 +1824,11 @@ { "cell_type": "code", "execution_count": 56, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -1828,7 +1848,11 @@ { "cell_type": "code", "execution_count": 57, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -1920,7 +1944,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "... or pass `v` to [abs() ](https://docs.python.org/3/library/functions.html#abs)." ] @@ -1928,7 +1956,11 @@ { "cell_type": "code", "execution_count": 60, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "ename": "TypeError", @@ -1960,7 +1992,11 @@ { "cell_type": "code", "execution_count": 61, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [], "source": [ "import math" @@ -1969,7 +2005,11 @@ { "cell_type": "code", "execution_count": 62, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, "outputs": [], "source": [ "def norm(vec_or_mat):\n", @@ -2051,7 +2091,7 @@ "execution_count": 65, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "fragment" } }, "outputs": [ @@ -2134,7 +2174,7 @@ "execution_count": 68, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "fragment" } }, "outputs": [ @@ -2156,7 +2196,11 @@ { "cell_type": "code", "execution_count": 69, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [], "source": [ "z = Vector([0, 0])" @@ -2202,7 +2246,7 @@ "execution_count": 71, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "fragment" } }, "outputs": [ @@ -2259,7 +2303,11 @@ { "cell_type": "code", "execution_count": 73, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [], "source": [ "s = Vector([42])" @@ -2270,7 +2318,7 @@ "execution_count": 74, "metadata": { "slideshow": { - "slide_type": "slide" + "slide_type": "fragment" } }, "outputs": [ diff --git a/11_classes/04_content.ipynb b/11_classes/04_content.ipynb index 959c160..b495a9a 100644 --- a/11_classes/04_content.ipynb +++ b/11_classes/04_content.ipynb @@ -35,14 +35,22 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "source": [ "## Packages vs. Modules" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "In [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/02_content.ipynb#Local-Modules-and-Packages), we introduce the concept of a Python module that is imported with the `import` statement. Essentially, a **module** is a single plain text \\*.py file on disk that contains Python code (e.g., [*sample_module.py* ](https://github.com/webartifex/intro-to-python/blob/develop/02_functions/sample_module.py) in [Chapter 2's folder ](https://github.com/webartifex/intro-to-python/tree/develop/02_functions)).\n", "\n", @@ -56,7 +64,11 @@ { "cell_type": "code", "execution_count": 1, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [ { "name": "stdout", @@ -72,7 +84,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "`!ls` lists all the files and folders in the current location: These are Chapter 11's Jupyter notebooks (i.e., the \\*.ipynb files) and the [*sample_package* ](https://github.com/webartifex/intro-to-python/tree/develop/11_classes/sample_package) folder. " ] @@ -80,7 +96,11 @@ { "cell_type": "code", "execution_count": 2, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "name": "stdout", @@ -97,7 +117,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "If we run `!ls` with the `sample_package` folder as the argument, we see the folder's contents: Four \\*.py files. Alternatively, you can use [JupyterLab' File Browser](https://jupyterlab.readthedocs.io/en/stable/user/interface.html?highlight=file%20browser#left-sidebar) on the left to navigate into the package." ] @@ -105,7 +129,11 @@ { "cell_type": "code", "execution_count": 3, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "name": "stdout", @@ -121,7 +149,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "The package is organized such that the [*matrix.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/matrix.py) and [*vector.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/vector.py) modules each define just one class, `Matrix` and `Vector`. That is intentional as both classes consist of several hundred lines of code and comments.\n", "\n", @@ -135,7 +167,11 @@ { "cell_type": "code", "execution_count": 4, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [], "source": [ "import sample_package as pkg" @@ -143,7 +179,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "The above cell runs the code in the [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/__init__.py) file from top to bottom, which in turn runs the [*matrix.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/matrix.py), [*utils.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/utils.py), and [*vector.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/vector.py) modules (cf., look at the `import` statements in the four \\*.py files to get the idea). As both [*matrix.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/matrix.py) and [*vector.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/vector.py) depend on each other (i.e., the `Matrix` class needs the `Vector` class to work and vice versa), understanding the order in that the modules are executed is not trivial. Without going into detail, we mention that Python guarantees that each \\*.py file is run only once and figures out the order on its own. If Python is unable to do that, for example, due to unresolvable cirular imports, it aborts with an `ImportError`.\n", "\n", @@ -153,7 +193,11 @@ { "cell_type": "code", "execution_count": 5, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -173,7 +217,11 @@ { "cell_type": "code", "execution_count": 6, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -192,7 +240,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "... and we use the built-in [dir() ](https://docs.python.org/3/library/functions.html#dir) function to check what attributes `pkg` comes with." ] @@ -200,7 +252,11 @@ { "cell_type": "code", "execution_count": 7, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [ { "data": { @@ -235,7 +291,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "The package's meta information and documentation are automatically parsed from the [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/__init__.py) file." ] @@ -243,7 +303,11 @@ { "cell_type": "code", "execution_count": 8, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [ { "name": "stdout", @@ -818,7 +882,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "The meta information could also be accessed separately and individually." ] @@ -826,7 +894,11 @@ { "cell_type": "code", "execution_count": 9, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [ { "data": { @@ -846,7 +918,11 @@ { "cell_type": "code", "execution_count": 10, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -865,7 +941,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "We create `Vector` and `Matrix` instances in the usual way by calling the `Vector` and `Matrix` classes from the package's top level." ] @@ -873,7 +953,11 @@ { "cell_type": "code", "execution_count": 11, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [ { "data": { @@ -893,7 +977,11 @@ { "cell_type": "code", "execution_count": 12, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -912,7 +1000,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "A common practice by package authors is to put all the objects on the package's top level that they want the package users to work with directly. That is achieved via the `import` statements in the [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/__init__.py) file.\n", "\n", @@ -924,7 +1016,11 @@ { "cell_type": "code", "execution_count": 13, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [ { "data": { @@ -944,7 +1040,11 @@ { "cell_type": "code", "execution_count": 14, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -963,7 +1063,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "Also, let's import the [*utils.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/utils.py) module with the `norm()` function into the global scope. As this function is integrated into the `Vector.__abs__()` and `Matrix.__abs__()` methods, there is actually no need to work with it explicitly." ] @@ -971,7 +1075,11 @@ { "cell_type": "code", "execution_count": 15, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [], "source": [ "from sample_package import utils" @@ -980,7 +1088,11 @@ { "cell_type": "code", "execution_count": 16, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "name": "stdout", @@ -1014,7 +1126,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "Many tutorials on the internet begin by importing \"everything\" from a package into the global scope with `from ... import *`.\n", "\n", @@ -1028,7 +1144,11 @@ { "cell_type": "code", "execution_count": 17, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [], "source": [ "from sample_package import *" @@ -1037,7 +1157,11 @@ { "cell_type": "code", "execution_count": 18, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -1057,7 +1181,11 @@ { "cell_type": "code", "execution_count": 19, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -1076,7 +1204,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "For further information on modules and packages, we refer to the [official tutorial ](https://docs.python.org/3/tutorial/modules.html)." ] @@ -1094,7 +1226,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "The final implementations of the `Vector` and `Matrix` classes are in the [*matrix.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/matrix.py) and [*vector.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/vector.py) files: They integrate all of the functionalities introduced in this chapter. In addition, the code is cleaned up and fully documented, including examples of common usages.\n", "\n", @@ -1104,7 +1240,11 @@ { "cell_type": "code", "execution_count": 20, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [], "source": [ "v = Vector([1, 2, 3])" @@ -1113,7 +1253,11 @@ { "cell_type": "code", "execution_count": 21, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -1133,7 +1277,11 @@ { "cell_type": "code", "execution_count": 22, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -1153,7 +1301,11 @@ { "cell_type": "code", "execution_count": 23, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [], "source": [ "m = Matrix([(1, 2, 3), (4, 5, 6), (7, 8, 9)])" @@ -1162,7 +1314,11 @@ { "cell_type": "code", "execution_count": 24, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -1182,7 +1338,11 @@ { "cell_type": "code", "execution_count": 25, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -1215,7 +1375,11 @@ { "cell_type": "code", "execution_count": 26, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [ { "data": { @@ -1235,7 +1399,11 @@ { "cell_type": "code", "execution_count": 27, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, "outputs": [ { "data": { @@ -1255,7 +1423,11 @@ { "cell_type": "code", "execution_count": 28, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [ { "data": { @@ -1295,7 +1467,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "Both `Matrix/Vector.storage` and `Matrix/Vector.typing` themselves reference the `DEFAULT_ENTRIES_STORAGE` and `DEFAULT_ENTRY_TYPE` constants in the [*utils.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/utils.py) module. This way, we could, for example, change only the constants and thereby also change how the `._entries` are stored internally in both classes. Also, this single **[single source of truth ](https://en.wikipedia.org/wiki/Single_source_of_truth)** ensures that both classes are consistent with each other at all times." ] @@ -1303,7 +1479,11 @@ { "cell_type": "code", "execution_count": 29, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [ { "data": { @@ -1323,7 +1503,11 @@ { "cell_type": "code", "execution_count": 30, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, "outputs": [ { "data": { @@ -1342,7 +1526,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "For the same reasons, we also replace the \"hard coded\" references to the `Vector` and `Matrix` classes within the various methods.\n", "\n", @@ -1352,7 +1540,11 @@ { "cell_type": "code", "execution_count": 31, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [ { "data": { @@ -1371,7 +1563,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "Of course, we could also use the [type() ](https://docs.python.org/3/library/functions.html#type) built-in instead." ] @@ -1379,7 +1575,11 @@ { "cell_type": "code", "execution_count": 32, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "outputs": [ { "data": { @@ -1398,7 +1598,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "So, for example, the `Matrix.transpose()` method makes a `self.__class__(...)` instead of a `Matrix(...)` call." ] @@ -1406,7 +1610,11 @@ { "cell_type": "code", "execution_count": 33, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -1441,7 +1649,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "Whenever we need a `str` representation of a class's name, we use the `.__name__` attribute on the class, ..." ] @@ -1449,7 +1661,11 @@ { "cell_type": "code", "execution_count": 34, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "outputs": [ { "data": { @@ -1468,7 +1684,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "... or access it via the `.__class__` attribute on an instance." ] @@ -1476,7 +1696,11 @@ { "cell_type": "code", "execution_count": 35, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [ { "data": { @@ -1495,7 +1719,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "For example, the `.__repr__()` and `.__str__()` methods make use of that." ] @@ -1503,7 +1731,11 @@ { "cell_type": "code", "execution_count": 36, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -1531,7 +1763,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "In order to not have to \"hard code\" the name of *another* class (e.g., the `Vector.as_matrix()` method references the `Matrix` class), we apply the following \"hack:\" First, we store a reference to the other class as a class attribute (e.g., `Matrix.vector_cls` and `Vector.matrix_cls`), and then reference that attribute within the methods, just like `.storage` and `.typing` above." ] @@ -1539,7 +1775,11 @@ { "cell_type": "code", "execution_count": 37, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "outputs": [ { "data": { @@ -1559,7 +1799,11 @@ { "cell_type": "code", "execution_count": 38, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "outputs": [ { "data": { @@ -1578,7 +1822,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "As an example, the `Vector.as_matrix()` method makes a `self.matrix_cls(...)` instead of a `Matrix(...)` call." ] @@ -1586,7 +1834,11 @@ { "cell_type": "code", "execution_count": 39, - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "fragment" + } + }, "outputs": [ { "data": { @@ -1638,7 +1890,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "source": [ "## \"Real-life\" Experiment" ] @@ -1762,7 +2018,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "Similarily, `v` is now a `Vector` with $50$ entries." ] @@ -2236,7 +2496,11 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, "source": [ "... while `Matrix` objects come with `.n_rows` and `.n_cols` properties." ] From 30127b35533ecb0b32b5b85e1690e34708b93cc2 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 28 Oct 2020 22:42:08 +0100 Subject: [PATCH 122/142] Add initial version of chapter 11's summary --- 11_classes/05_summary.ipynb | 100 ++++++++++++++++++++++++++++++++++++ CONTENTS.md | 1 + 2 files changed, 101 insertions(+) create mode 100644 11_classes/05_summary.ipynb diff --git a/11_classes/05_summary.ipynb b/11_classes/05_summary.ipynb new file mode 100644 index 0000000..47baf9f --- /dev/null +++ b/11_classes/05_summary.ipynb @@ -0,0 +1,100 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Chapter 11: Classes & Instances (TL;DR)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "source": [ + "With the `class` statement, we can create a *user-defined* data type that we also call a **class**.\n", + "\n", + "Then, to create new **instances** of the data type, we simply call the class, just as we do with the built-in constructors.\n", + "\n", + "Conceptually, a class is the **blueprint** defining the **behavior** each instance exhibits.\n", + "\n", + "In the example used throughout the chapter, the `Vector` and `Matrix` classes implement the linear algebra rules all `Vector` and `Matrix` instances follow *in general*. On the contrary, the instances **encapsulate** the **state** of *concrete* vectors and matrices.\n", + "\n", + "The `class` statement acts as a *namespace* that consists of simple *variable assignments* and *function definitions*:\n", + "1. Variables become the **class attributes** that are shared among all instances.\n", + "2. Functions may take a different role:\n", + " - By default, they become **instance methods** by going through a **binding process** where a reference to the instance on which the method is *invoked* is passed in as the first argument. By convention, the corresponding parameter is called `self`; it embodies an instance's state: That means that instance methods set and get **instance attributes** on and from `self`.\n", + " - They may be declared as **class methods**. Then, the binding process is adjusted such that the first argument passed in is a reference to the class itself and, by convention, named `cls`. A common use case is to design **alternative constructors**.\n", + " - They may also be declared as **properties**. A use case for that are *derived* attributes that follow semantically from an instance's state.\n", + " \n", + "The **Python Data Model** concerns what special methods (i.e., the ones with the dunder names) exists and how they work together.\n", + "\n", + "The instantiation process is controlled by the `.__init__()` method.\n", + "\n", + "The `__repr__()` and `__str__()` methods implement the **text representation** of an instance, which can be regarded as a Unicode encoded representation of all the state encapsulated in an instance.\n", + "\n", + "**Sequence emulation** means that a user-defined data type exhibits the same four properties as the built-in sequences, which are regarded as finite and iterable containers with a predictable order. The `.__len__()`, `.__iter__()`, `__reversed__()`, `__getitem__()`, and some others are used to implement the corresponding behaviors.\n", + "\n", + "Similarly, **number emulation** means that an instance of a user-defined data type behaves like a built-in number. For example, by implementing the `.__abs__()` method, an instance may be passed to the built-in [abs() ](https://docs.python.org/3/library/functions.html#abs) function.\n", + "\n", + "If different data types, built-in or user-defined, share a well-defined set of behaviors, a single function may be written to work with objects of all the data types. We describe such functions as **polymorphic**.\n", + "\n", + "Classes may specify how *operators* are **overloaded**. Examples for that are the `.__add__()`, `.__sub__()`, or `.__eq__()` methods.\n", + "\n", + "**Packages** are folders containing **modules** (i.e., \\*.py files) and a \"*\\_\\_init\\_\\_.py*\" file. We use them to design coherent libraries with reusable code." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "livereveal": { + "auto_select": "code", + "auto_select_fragment": true, + "scroll": true, + "theme": "serif" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "384px" + }, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index be3d163..b545cb4 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -289,3 +289,4 @@ If this is not possible, (Writing one's own Packages; The final `Vector` & `Matrix` Classes; Comparison with `numpy`) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/05_summary.ipynb) From 96ff64cfb5c3ff28aa1ae491f4762b196941f80c Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Wed, 28 Oct 2020 23:10:19 +0100 Subject: [PATCH 123/142] Add initial version of chapter 11's review --- 11_classes/06_review.ipynb | 271 +++++++++++++++++++++++++++++++++++++ CONTENTS.md | 1 + 2 files changed, 272 insertions(+) create mode 100644 11_classes/06_review.ipynb diff --git a/11_classes/06_review.ipynb b/11_classes/06_review.ipynb new file mode 100644 index 0000000..df7af79 --- /dev/null +++ b/11_classes/06_review.ipynb @@ -0,0 +1,271 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 11: Classes & Instances (Review Questions)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb), [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/02_content.ipynb), [third ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/03_content.ipynb), and [fourth ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/04_content.ipynb) part of Chapter 11.\n", + "\n", + "Be concise in your answers! Most questions can be answered in *one* sentence." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Essay Questions " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: How are **classes** a way to manage the **state** in a big program? How should we think of classes conceptually?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: What do we mean with **instantiation**? How do **instances** relate to **classes**?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3:** What is an **implementation detail**? Name two different examples of implementation details regarding the `Vector` and `Matrix` classes!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: How are **instance methods** different from **class methods**? How do **special methods** fit into the picture?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: How do **mutability** and **immutability** come into play when designing a user-defined data type?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: Explain the concept of **method chaining**!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: How can we implement **operator overloading** for a user-defined data type? When do we need to user *reverse* special methods?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## True / False Questions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Motivate your answer with *one short* sentence!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: An instance's **text representation** is a `bytes` object with a special encoding." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: Computed **properties** are special kinds of **instance methods**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: **Sequence emulation** means designing a user-defined data type around the built-in `list` or `tuple` types." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q11**: The **Python Data Model** can be regarded as the \"Theory\" or \"Mental Model\" behind the Python language." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q12**: **Polymorphism** means that two instances have the same data type, be it a built-in or user-defined one." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q13**: **Number emulation** means that two instances of the same user-defined data type can be added together." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q14**: **Packages** are a good place to collect all the code to be reused in a data science project, for example, across different Jupyter notebooks." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/CONTENTS.md b/CONTENTS.md index b545cb4..bdad0d7 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -290,3 +290,4 @@ If this is not possible, The final `Vector` & `Matrix` Classes; Comparison with `numpy`) - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/05_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/06_review.ipynb) From 950909547ccacfe6e5cee83dd5f47a4b4d8bc4ef Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 8 Apr 2024 15:44:35 +0200 Subject: [PATCH 124/142] Update the dependencies to up-to-date versions --- poetry.lock | 2809 ++++++++++++++++++++++++++++++------------------ pyproject.toml | 21 +- 2 files changed, 1775 insertions(+), 1055 deletions(-) diff --git a/poetry.lock b/poetry.lock index e50d7f0..ac23fb3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,1511 +1,2234 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + [[package]] -name = "appdirs" -version = "1.4.4" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" +name = "anyio" +version = "4.3.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false -python-versions = "*" +python-versions = ">=3.8" +files = [ + {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, + {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] [[package]] name = "appnope" -version = "0.1.0" -description = "Disable App Nap on OS X 10.9" -category = "main" +version = "0.1.4" +description = "Disable App Nap on macOS >= 10.9" optional = false -python-versions = "*" +python-versions = ">=3.6" +files = [ + {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, + {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, +] [[package]] name = "argcomplete" -version = "1.12.1" +version = "3.2.3" description = "Bash tab completion for argparse" -category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.8" +files = [ + {file = "argcomplete-3.2.3-py3-none-any.whl", hash = "sha256:c12355e0494c76a2a7b73e3a59b09024ca0ba1e279fb9ed6c1b82d5b74b6a70c"}, + {file = "argcomplete-3.2.3.tar.gz", hash = "sha256:bf7900329262e481be5a15f56f19736b376df6f82ed27576fa893652c5de6c23"}, +] [package.extras] -test = ["coverage", "flake8", "pexpect", "wheel"] +test = ["coverage", "mypy", "pexpect", "ruff", "wheel"] [[package]] name = "argon2-cffi" -version = "20.1.0" -description = "The secure Argon2 password hashing algorithm." -category = "main" +version = "23.1.0" +description = "Argon2 for Python" optional = false -python-versions = "*" +python-versions = ">=3.7" +files = [ + {file = "argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea"}, + {file = "argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08"}, +] [package.dependencies] -cffi = ">=1.0.0" -six = "*" +argon2-cffi-bindings = "*" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest", "sphinx", "wheel", "pre-commit"] -docs = ["sphinx"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"] +dev = ["argon2-cffi[tests,typing]", "tox (>4)"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-copybutton", "sphinx-notfound-page"] +tests = ["hypothesis", "pytest"] +typing = ["mypy"] [[package]] -name = "async-generator" -version = "1.10" -description = "Async generators and context managers for Python 3.5+" -category = "main" +name = "argon2-cffi-bindings" +version = "21.2.0" +description = "Low-level CFFI bindings for Argon2" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" +files = [ + {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, + {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, + {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, + {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, + {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, +] + +[package.dependencies] +cffi = ">=1.0.1" + +[package.extras] +dev = ["cogapp", "pre-commit", "pytest", "wheel"] +tests = ["pytest"] + +[[package]] +name = "arrow" +version = "1.3.0" +description = "Better dates & times for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80"}, + {file = "arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85"}, +] + +[package.dependencies] +python-dateutil = ">=2.7.0" +types-python-dateutil = ">=2.8.10" + +[package.extras] +doc = ["doc8", "sphinx (>=7.0.0)", "sphinx-autobuild", "sphinx-autodoc-typehints", "sphinx_rtd_theme (>=1.3.0)"] +test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock", "pytz (==2021.1)", "simplejson (==3.*)"] + +[[package]] +name = "asttokens" +version = "2.4.1" +description = "Annotate AST trees with source code positions" +optional = false +python-versions = "*" +files = [ + {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, + {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, +] + +[package.dependencies] +six = ">=1.12.0" + +[package.extras] +astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] +test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] + +[[package]] +name = "async-lru" +version = "2.0.4" +description = "Simple LRU cache for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "async-lru-2.0.4.tar.gz", hash = "sha256:b8a59a5df60805ff63220b2a0c5b5393da5521b113cd5465a44eb037d81a5627"}, + {file = "async_lru-2.0.4-py3-none-any.whl", hash = "sha256:ff02944ce3c288c5be660c42dbcca0742b32c3b279d6dceda655190240b99224"}, +] [[package]] name = "attrs" -version = "20.2.0" +version = "23.2.0" description = "Classes Without Boilerplate" -category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "sphinx-rtd-theme", "pre-commit"] -docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] [[package]] -name = "backcall" -version = "0.2.0" -description = "Specifications for callback functions passed in to an API" -category = "main" +name = "babel" +version = "2.14.0" +description = "Internationalization utilities" optional = false -python-versions = "*" +python-versions = ">=3.7" +files = [ + {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, + {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, +] + +[package.extras] +dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] + +[[package]] +name = "beautifulsoup4" +version = "4.12.3" +description = "Screen-scraping library" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, + {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, +] + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] +html5lib = ["html5lib"] +lxml = ["lxml"] [[package]] name = "bleach" -version = "3.2.1" +version = "6.1.0" description = "An easy safelist-based HTML-sanitizing tool." -category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.8" +files = [ + {file = "bleach-6.1.0-py3-none-any.whl", hash = "sha256:3225f354cfc436b9789c66c4ee030194bee0568fbf9cbdad3bc8b5c26c5f12b6"}, + {file = "bleach-6.1.0.tar.gz", hash = "sha256:0a31f1837963c41d46bbf1331b8778e1308ea0791db03cc4e7357b97cf42a8fe"}, +] [package.dependencies] -packaging = "*" six = ">=1.9.0" webencodings = "*" +[package.extras] +css = ["tinycss2 (>=1.1.0,<1.3)"] + [[package]] name = "certifi" -version = "2020.6.20" +version = "2024.2.2" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false -python-versions = "*" +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] [[package]] name = "cffi" -version = "1.14.3" +version = "1.16.0" description = "Foreign Function Interface for Python calling C code." -category = "main" optional = false -python-versions = "*" +python-versions = ">=3.8" +files = [ + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, +] [package.dependencies] pycparser = "*" [[package]] name = "cfgv" -version = "3.2.0" +version = "3.4.0" description = "Validate configuration and produce human readable error messages." -category = "dev" optional = false -python-versions = ">=3.6.1" +python-versions = ">=3.8" +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, +] [[package]] -name = "chardet" -version = "3.0.4" -description = "Universal encoding detector for Python 2 and 3" -category = "main" +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false -python-versions = "*" +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] [[package]] name = "colorama" -version = "0.4.4" +version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] [[package]] name = "colorlog" -version = "4.4.0" -description = "Log formatting with colors!" -category = "dev" +version = "6.8.2" +description = "Add colours to the output of Python's logging module." optional = false -python-versions = "*" +python-versions = ">=3.6" +files = [ + {file = "colorlog-6.8.2-py3-none-any.whl", hash = "sha256:4dcbb62368e2800cb3c5abd348da7e53f6c362dda502ec27c560b2e58a66bd33"}, + {file = "colorlog-6.8.2.tar.gz", hash = "sha256:3e3e079a41feb5a1b64f978b5ea4f46040a94f11f0e8bbb8261e3dbbeca64d44"}, +] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} +[package.extras] +development = ["black", "flake8", "mypy", "pytest", "types-colorama"] + +[[package]] +name = "comm" +version = "0.2.2" +description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." +optional = false +python-versions = ">=3.8" +files = [ + {file = "comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3"}, + {file = "comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e"}, +] + +[package.dependencies] +traitlets = ">=4" + +[package.extras] +test = ["pytest"] + +[[package]] +name = "debugpy" +version = "1.8.1" +description = "An implementation of the Debug Adapter Protocol for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "debugpy-1.8.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:3bda0f1e943d386cc7a0e71bfa59f4137909e2ed947fb3946c506e113000f741"}, + {file = "debugpy-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda73bf69ea479c8577a0448f8c707691152e6c4de7f0c4dec5a4bc11dee516e"}, + {file = "debugpy-1.8.1-cp310-cp310-win32.whl", hash = "sha256:3a79c6f62adef994b2dbe9fc2cc9cc3864a23575b6e387339ab739873bea53d0"}, + {file = "debugpy-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:7eb7bd2b56ea3bedb009616d9e2f64aab8fc7000d481faec3cd26c98a964bcdd"}, + {file = "debugpy-1.8.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:016a9fcfc2c6b57f939673c874310d8581d51a0fe0858e7fac4e240c5eb743cb"}, + {file = "debugpy-1.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd97ed11a4c7f6d042d320ce03d83b20c3fb40da892f994bc041bbc415d7a099"}, + {file = "debugpy-1.8.1-cp311-cp311-win32.whl", hash = "sha256:0de56aba8249c28a300bdb0672a9b94785074eb82eb672db66c8144fff673146"}, + {file = "debugpy-1.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:1a9fe0829c2b854757b4fd0a338d93bc17249a3bf69ecf765c61d4c522bb92a8"}, + {file = "debugpy-1.8.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ebb70ba1a6524d19fa7bb122f44b74170c447d5746a503e36adc244a20ac539"}, + {file = "debugpy-1.8.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2e658a9630f27534e63922ebf655a6ab60c370f4d2fc5c02a5b19baf4410ace"}, + {file = "debugpy-1.8.1-cp312-cp312-win32.whl", hash = "sha256:caad2846e21188797a1f17fc09c31b84c7c3c23baf2516fed5b40b378515bbf0"}, + {file = "debugpy-1.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:edcc9f58ec0fd121a25bc950d4578df47428d72e1a0d66c07403b04eb93bcf98"}, + {file = "debugpy-1.8.1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:7a3afa222f6fd3d9dfecd52729bc2e12c93e22a7491405a0ecbf9e1d32d45b39"}, + {file = "debugpy-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d915a18f0597ef685e88bb35e5d7ab968964b7befefe1aaea1eb5b2640b586c7"}, + {file = "debugpy-1.8.1-cp38-cp38-win32.whl", hash = "sha256:92116039b5500633cc8d44ecc187abe2dfa9b90f7a82bbf81d079fcdd506bae9"}, + {file = "debugpy-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:e38beb7992b5afd9d5244e96ad5fa9135e94993b0c551ceebf3fe1a5d9beb234"}, + {file = "debugpy-1.8.1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:bfb20cb57486c8e4793d41996652e5a6a885b4d9175dd369045dad59eaacea42"}, + {file = "debugpy-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efd3fdd3f67a7e576dd869c184c5dd71d9aaa36ded271939da352880c012e703"}, + {file = "debugpy-1.8.1-cp39-cp39-win32.whl", hash = "sha256:58911e8521ca0c785ac7a0539f1e77e0ce2df753f786188f382229278b4cdf23"}, + {file = "debugpy-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:6df9aa9599eb05ca179fb0b810282255202a66835c6efb1d112d21ecb830ddd3"}, + {file = "debugpy-1.8.1-py2.py3-none-any.whl", hash = "sha256:28acbe2241222b87e255260c76741e1fbf04fdc3b6d094fcf57b6c6f75ce1242"}, + {file = "debugpy-1.8.1.zip", hash = "sha256:f696d6be15be87aef621917585f9bb94b1dc9e8aced570db1b8a6fc14e8f9b42"}, +] + [[package]] name = "decorator" -version = "4.4.2" +version = "5.1.1" description = "Decorators for Humans" -category = "main" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*" +python-versions = ">=3.5" +files = [ + {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, + {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, +] [[package]] name = "defusedxml" -version = "0.6.0" +version = "0.7.1" description = "XML bomb protection for Python stdlib modules" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, + {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, +] [[package]] name = "distlib" -version = "0.3.1" +version = "0.3.8" description = "Distribution utilities" -category = "dev" optional = false python-versions = "*" +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] [[package]] -name = "entrypoints" -version = "0.3" -description = "Discover and load entry points from installed packages." -category = "main" +name = "executing" +version = "2.0.1" +description = "Get the currently executing AST node of a frame, and other information" optional = false -python-versions = ">=2.7" +python-versions = ">=3.5" +files = [ + {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, + {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, +] + +[package.extras] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] + +[[package]] +name = "fastjsonschema" +version = "2.19.1" +description = "Fastest Python implementation of JSON schema" +optional = false +python-versions = "*" +files = [ + {file = "fastjsonschema-2.19.1-py3-none-any.whl", hash = "sha256:3672b47bc94178c9f23dbb654bf47440155d4db9df5f7bc47643315f9c405cd0"}, + {file = "fastjsonschema-2.19.1.tar.gz", hash = "sha256:e3126a94bdc4623d3de4485f8d468a12f02a67921315ddc87836d6e456dc789d"}, +] + +[package.extras] +devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] [[package]] name = "filelock" -version = "3.0.12" +version = "3.13.3" description = "A platform independent file lock." -category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.8" +files = [ + {file = "filelock-3.13.3-py3-none-any.whl", hash = "sha256:5ffa845303983e7a0b7ae17636509bc97997d58afeafa72fb141a17b152284cb"}, + {file = "filelock-3.13.3.tar.gz", hash = "sha256:a79895a25bbefdf55d1a2a0a80968f7dbb28edcd6d4234a0afb3f37ecde4b546"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +typing = ["typing-extensions (>=4.8)"] + +[[package]] +name = "fqdn" +version = "1.5.1" +description = "Validates fully-qualified domain names against RFC 1123, so that they are acceptable to modern bowsers" +optional = false +python-versions = ">=2.7, !=3.0, !=3.1, !=3.2, !=3.3, !=3.4, <4" +files = [ + {file = "fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014"}, + {file = "fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f"}, +] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "httpcore" +version = "1.0.5" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, + {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.13,<0.15" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<0.26.0)"] + +[[package]] +name = "httpx" +version = "0.27.0" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, + {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] [[package]] name = "identify" -version = "1.5.6" +version = "2.5.35" description = "File identification library for Python" -category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +python-versions = ">=3.8" +files = [ + {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"}, + {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"}, +] [package.extras] -license = ["editdistance"] +license = ["ukkonen"] [[package]] name = "idna" -version = "2.10" +version = "3.6" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.5" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] [[package]] name = "ipykernel" -version = "5.3.4" +version = "6.29.4" description = "IPython Kernel for Jupyter" -category = "main" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" +files = [ + {file = "ipykernel-6.29.4-py3-none-any.whl", hash = "sha256:1181e653d95c6808039c509ef8e67c4126b3b3af7781496c7cbfb5ed938a27da"}, + {file = "ipykernel-6.29.4.tar.gz", hash = "sha256:3d44070060f9475ac2092b760123fadf105d2e2493c24848b6691a7c4f42af5c"}, +] [package.dependencies] appnope = {version = "*", markers = "platform_system == \"Darwin\""} -ipython = ">=5.0.0" -jupyter-client = "*" -tornado = ">=4.2" -traitlets = ">=4.1.0" +comm = ">=0.1.1" +debugpy = ">=1.6.5" +ipython = ">=7.23.1" +jupyter-client = ">=6.1.12" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +matplotlib-inline = ">=0.1" +nest-asyncio = "*" +packaging = "*" +psutil = "*" +pyzmq = ">=24" +tornado = ">=6.1" +traitlets = ">=5.4.0" [package.extras] -test = ["pytest (!=5.3.4)", "pytest-cov", "flaky", "nose"] +cov = ["coverage[toml]", "curio", "matplotlib", "pytest-cov", "trio"] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "trio"] +pyqt5 = ["pyqt5"] +pyside6 = ["pyside6"] +test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.23.5)", "pytest-cov", "pytest-timeout"] [[package]] name = "ipython" -version = "7.18.1" +version = "8.23.0" description = "IPython: Productive Interactive Computing" -category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.10" +files = [ + {file = "ipython-8.23.0-py3-none-any.whl", hash = "sha256:07232af52a5ba146dc3372c7bf52a0f890a23edf38d77caef8d53f9cdc2584c1"}, + {file = "ipython-8.23.0.tar.gz", hash = "sha256:7468edaf4f6de3e1b912e57f66c241e6fd3c7099f2ec2136e239e142e800274d"}, +] [package.dependencies] -appnope = {version = "*", markers = "sys_platform == \"darwin\""} -backcall = "*" colorama = {version = "*", markers = "sys_platform == \"win32\""} decorator = "*" -jedi = ">=0.10" -pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} -pickleshare = "*" -prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" -pygments = "*" -traitlets = ">=4.2" +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""} +prompt-toolkit = ">=3.0.41,<3.1.0" +pygments = ">=2.4.0" +stack-data = "*" +traitlets = ">=5.13.0" +typing-extensions = {version = "*", markers = "python_version < \"3.12\""} [package.extras] -all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.14)", "pygments", "qtconsole", "requests", "testpath"] -doc = ["Sphinx (>=1.3)"] +all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] +black = ["black"] +doc = ["docrepr", "exceptiongroup", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "stack-data", "typing-extensions"] kernel = ["ipykernel"] +matplotlib = ["matplotlib"] nbconvert = ["nbconvert"] nbformat = ["nbformat"] -notebook = ["notebook", "ipywidgets"] +notebook = ["ipywidgets", "notebook"] parallel = ["ipyparallel"] qtconsole = ["qtconsole"] -test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.14)"] +test = ["pickleshare", "pytest (<8)", "pytest-asyncio (<0.22)", "testpath"] +test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"] [[package]] -name = "ipython-genutils" -version = "0.2.0" -description = "Vestigial utilities from IPython" -category = "main" +name = "isoduration" +version = "20.11.0" +description = "Operations with ISO 8601 durations" optional = false -python-versions = "*" +python-versions = ">=3.7" +files = [ + {file = "isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042"}, + {file = "isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9"}, +] + +[package.dependencies] +arrow = ">=0.15.0" [[package]] name = "jedi" -version = "0.17.2" +version = "0.19.1" description = "An autocompletion tool for Python that can be used for text editors." -category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6" +files = [ + {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, + {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, +] [package.dependencies] -parso = ">=0.7.0,<0.8.0" +parso = ">=0.8.3,<0.9.0" [package.extras] -qa = ["flake8 (==3.7.9)"] -testing = ["Django (<3.1)", "colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"] +docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] [[package]] name = "jinja2" -version = "2.11.2" +version = "3.1.3" description = "A very fast and expressive template engine." -category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] [package.dependencies] -MarkupSafe = ">=0.23" +MarkupSafe = ">=2.0" [package.extras] -i18n = ["Babel (>=0.8)"] +i18n = ["Babel (>=2.7)"] [[package]] name = "json5" -version = "0.9.5" +version = "0.9.24" description = "A Python implementation of the JSON5 data format." -category = "main" optional = false -python-versions = "*" +python-versions = ">=3.8" +files = [ + {file = "json5-0.9.24-py3-none-any.whl", hash = "sha256:4ca101fd5c7cb47960c055ef8f4d0e31e15a7c6c48c3b6f1473fc83b6c462a13"}, + {file = "json5-0.9.24.tar.gz", hash = "sha256:0c638399421da959a20952782800e5c1a78c14e08e1dc9738fa10d8ec14d58c8"}, +] -[package.extras] -dev = ["hypothesis"] +[[package]] +name = "jsonpointer" +version = "2.4" +description = "Identify specific nodes in a JSON document (RFC 6901)" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" +files = [ + {file = "jsonpointer-2.4-py2.py3-none-any.whl", hash = "sha256:15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a"}, + {file = "jsonpointer-2.4.tar.gz", hash = "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88"}, +] [[package]] name = "jsonschema" -version = "3.2.0" +version = "4.21.1" description = "An implementation of JSON Schema validation for Python" -category = "main" optional = false -python-versions = "*" +python-versions = ">=3.8" +files = [ + {file = "jsonschema-4.21.1-py3-none-any.whl", hash = "sha256:7996507afae316306f9e2290407761157c6f78002dcf7419acb99822143d1c6f"}, + {file = "jsonschema-4.21.1.tar.gz", hash = "sha256:85727c00279f5fa6bedbe6238d2aa6403bedd8b4864ab11207d07df3cc1b2ee5"}, +] [package.dependencies] -attrs = ">=17.4.0" -pyrsistent = ">=0.14.0" -six = ">=1.11.0" +attrs = ">=22.2.0" +fqdn = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} +idna = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} +isoduration = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} +jsonpointer = {version = ">1.13", optional = true, markers = "extra == \"format-nongpl\""} +jsonschema-specifications = ">=2023.03.6" +referencing = ">=0.28.4" +rfc3339-validator = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} +rfc3986-validator = {version = ">0.1.0", optional = true, markers = "extra == \"format-nongpl\""} +rpds-py = ">=0.7.1" +uri-template = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} +webcolors = {version = ">=1.11", optional = true, markers = "extra == \"format-nongpl\""} [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"] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "jsonschema-specifications" +version = "2023.12.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, + {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, +] + +[package.dependencies] +referencing = ">=0.31.0" [[package]] name = "jupyter-client" -version = "6.1.7" +version = "8.6.1" description = "Jupyter protocol implementation and client libraries" -category = "main" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" +files = [ + {file = "jupyter_client-8.6.1-py3-none-any.whl", hash = "sha256:3b7bd22f058434e3b9a7ea4b1500ed47de2713872288c0d511d19926f99b459f"}, + {file = "jupyter_client-8.6.1.tar.gz", hash = "sha256:e842515e2bab8e19186d89fdfea7abd15e39dd581f94e399f00e2af5a1652d3f"}, +] [package.dependencies] -jupyter-core = ">=4.6.0" -python-dateutil = ">=2.1" -pyzmq = ">=13" -tornado = ">=4.1" -traitlets = "*" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +python-dateutil = ">=2.8.2" +pyzmq = ">=23.0" +tornado = ">=6.2" +traitlets = ">=5.3" [package.extras] -test = ["ipykernel", "ipython", "mock", "pytest", "pytest-asyncio", "async-generator", "pytest-timeout"] - -[[package]] -name = "jupyter-contrib-core" -version = "0.3.3" -description = "Common utilities for jupyter-contrib projects." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -jupyter-core = "*" -notebook = ">=4.0" -tornado = "*" -traitlets = "*" - -[package.extras] -testing_utils = ["nose", "mock"] - -[[package]] -name = "jupyter-contrib-nbextensions" -version = "0.5.1" -description = "A collection of Jupyter nbextensions." -category = "dev" -optional = false -python-versions = "*" - -[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 = ["nbformat", "nose", "pip", "requests", "mock"] +docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] +test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] [[package]] name = "jupyter-core" -version = "4.6.3" +version = "5.7.2" description = "Jupyter core package. A base package on which Jupyter projects rely." -category = "main" optional = false -python-versions = "!=3.0,!=3.1,!=3.2,!=3.3,!=3.4,>=2.7" +python-versions = ">=3.8" +files = [ + {file = "jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409"}, + {file = "jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9"}, +] [package.dependencies] -pywin32 = {version = ">=1.0", markers = "sys_platform == \"win32\""} -traitlets = "*" - -[[package]] -name = "jupyter-highlight-selected-word" -version = "0.2.0" -description = "Jupyter notebook extension that enables highlighting every instance of the current word in the notebook." -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "jupyter-latex-envs" -version = "1.4.6" -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..." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -ipython = "*" -jupyter_core = "*" -nbconvert = "*" -notebook = ">=4.0" -traitlets = ">=4.1" - -[[package]] -name = "jupyter-nbextensions-configurator" -version = "0.4.1" -description = "jupyter serverextension providing configuration interfaces for nbextensions." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -jupyter_contrib_core = ">=0.3.3" -jupyter_core = "*" -notebook = ">=4.0" -pyyaml = "*" -tornado = "*" -traitlets = "*" +platformdirs = ">=2.5" +pywin32 = {version = ">=300", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} +traitlets = ">=5.3" [package.extras] -test = ["jupyter-contrib-core", "nose", "requests", "selenium", "mock"] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"] +test = ["ipykernel", "pre-commit", "pytest (<8)", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "jupyter-events" +version = "0.10.0" +description = "Jupyter Event System library" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_events-0.10.0-py3-none-any.whl", hash = "sha256:4b72130875e59d57716d327ea70d3ebc3af1944d3717e5a498b8a06c6c159960"}, + {file = "jupyter_events-0.10.0.tar.gz", hash = "sha256:670b8229d3cc882ec782144ed22e0d29e1c2d639263f92ca8383e66682845e22"}, +] + +[package.dependencies] +jsonschema = {version = ">=4.18.0", extras = ["format-nongpl"]} +python-json-logger = ">=2.0.4" +pyyaml = ">=5.3" +referencing = "*" +rfc3339-validator = "*" +rfc3986-validator = ">=0.1.1" +traitlets = ">=5.3" + +[package.extras] +cli = ["click", "rich"] +docs = ["jupyterlite-sphinx", "myst-parser", "pydata-sphinx-theme", "sphinxcontrib-spelling"] +test = ["click", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.19.0)", "pytest-console-scripts", "rich"] + +[[package]] +name = "jupyter-lsp" +version = "2.2.4" +description = "Multi-Language Server WebSocket proxy for Jupyter Notebook/Lab server" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter-lsp-2.2.4.tar.gz", hash = "sha256:5e50033149344065348e688608f3c6d654ef06d9856b67655bd7b6bac9ee2d59"}, + {file = "jupyter_lsp-2.2.4-py3-none-any.whl", hash = "sha256:da61cb63a16b6dff5eac55c2699cc36eac975645adee02c41bdfc03bf4802e77"}, +] + +[package.dependencies] +jupyter-server = ">=1.1.2" + +[[package]] +name = "jupyter-server" +version = "2.13.0" +description = "The backend—i.e. core services, APIs, and REST endpoints—to Jupyter web applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_server-2.13.0-py3-none-any.whl", hash = "sha256:77b2b49c3831fbbfbdb5048cef4350d12946191f833a24e5f83e5f8f4803e97b"}, + {file = "jupyter_server-2.13.0.tar.gz", hash = "sha256:c80bfb049ea20053c3d9641c2add4848b38073bf79f1729cea1faed32fc1c78e"}, +] + +[package.dependencies] +anyio = ">=3.1.0" +argon2-cffi = "*" +jinja2 = "*" +jupyter-client = ">=7.4.4" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +jupyter-events = ">=0.9.0" +jupyter-server-terminals = "*" +nbconvert = ">=6.4.4" +nbformat = ">=5.3.0" +overrides = "*" +packaging = "*" +prometheus-client = "*" +pywinpty = {version = "*", markers = "os_name == \"nt\""} +pyzmq = ">=24" +send2trash = ">=1.8.2" +terminado = ">=0.8.3" +tornado = ">=6.2.0" +traitlets = ">=5.6.0" +websocket-client = "*" + +[package.extras] +docs = ["ipykernel", "jinja2", "jupyter-client", "jupyter-server", "myst-parser", "nbformat", "prometheus-client", "pydata-sphinx-theme", "send2trash", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-openapi (>=0.8.0)", "sphinxcontrib-spelling", "sphinxemoji", "tornado", "typing-extensions"] +test = ["flaky", "ipykernel", "pre-commit", "pytest (>=7.0)", "pytest-console-scripts", "pytest-jupyter[server] (>=0.7)", "pytest-timeout", "requests"] + +[[package]] +name = "jupyter-server-terminals" +version = "0.5.3" +description = "A Jupyter Server Extension Providing Terminals." +optional = false +python-versions = ">=3.8" +files = [ + {file = "jupyter_server_terminals-0.5.3-py3-none-any.whl", hash = "sha256:41ee0d7dc0ebf2809c668e0fc726dfaf258fcd3e769568996ca731b6194ae9aa"}, + {file = "jupyter_server_terminals-0.5.3.tar.gz", hash = "sha256:5ae0295167220e9ace0edcfdb212afd2b01ee8d179fe6f23c899590e9b8a5269"}, +] + +[package.dependencies] +pywinpty = {version = ">=2.0.3", markers = "os_name == \"nt\""} +terminado = ">=0.8.3" + +[package.extras] +docs = ["jinja2", "jupyter-server", "mistune (<4.0)", "myst-parser", "nbformat", "packaging", "pydata-sphinx-theme", "sphinxcontrib-github-alt", "sphinxcontrib-openapi", "sphinxcontrib-spelling", "sphinxemoji", "tornado"] +test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (>=0.5.3)", "pytest-timeout"] [[package]] name = "jupyterlab" -version = "2.2.9" -description = "The JupyterLab notebook server extension." -category = "main" +version = "4.1.5" +description = "JupyterLab computational environment" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" +files = [ + {file = "jupyterlab-4.1.5-py3-none-any.whl", hash = "sha256:3bc843382a25e1ab7bc31d9e39295a9f0463626692b7995597709c0ab236ab2c"}, + {file = "jupyterlab-4.1.5.tar.gz", hash = "sha256:c9ad75290cb10bfaff3624bf3fbb852319b4cce4c456613f8ebbaa98d03524db"}, +] [package.dependencies] -jinja2 = ">=2.10" -jupyterlab-server = ">=1.1.5,<2.0" -notebook = ">=4.3.1" -tornado = "<6.0.0 || >6.0.0,<6.0.1 || >6.0.1,<6.0.2 || >6.0.2" +async-lru = ">=1.0.0" +httpx = ">=0.25.0" +ipykernel = "*" +jinja2 = ">=3.0.3" +jupyter-core = "*" +jupyter-lsp = ">=2.0.0" +jupyter-server = ">=2.4.0,<3" +jupyterlab-server = ">=2.19.0,<3" +notebook-shim = ">=0.2" +packaging = "*" +tornado = ">=6.2.0" +traitlets = "*" [package.extras] -docs = ["jsx-lexer", "recommonmark", "sphinx", "sphinx-rtd-theme", "sphinx-copybutton"] -test = ["pytest", "pytest-check-links", "requests", "wheel", "virtualenv"] +dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.2.0)"] +docs = ["jsx-lexer", "myst-parser", "pydata-sphinx-theme (>=0.13.0)", "pytest", "pytest-check-links", "pytest-jupyter", "sphinx (>=1.8,<7.3.0)", "sphinx-copybutton"] +docs-screenshots = ["altair (==5.2.0)", "ipython (==8.16.1)", "ipywidgets (==8.1.1)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.0.post6)", "matplotlib (==3.8.2)", "nbconvert (>=7.0.0)", "pandas (==2.2.0)", "scipy (==1.12.0)", "vega-datasets (==0.9.0)"] +test = ["coverage", "pytest (>=7.0)", "pytest-check-links (>=0.7)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter (>=0.5.3)", "pytest-timeout", "pytest-tornasync", "requests", "requests-cache", "virtualenv"] [[package]] name = "jupyterlab-pygments" -version = "0.1.2" +version = "0.3.0" description = "Pygments theme using JupyterLab CSS variables" -category = "main" optional = false -python-versions = "*" - -[package.dependencies] -pygments = ">=2.4.1,<3" +python-versions = ">=3.8" +files = [ + {file = "jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780"}, + {file = "jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d"}, +] [[package]] name = "jupyterlab-server" -version = "1.2.0" -description = "JupyterLab Server" -category = "main" +version = "2.26.0" +description = "A set of server components for JupyterLab and JupyterLab like applications." optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" +files = [ + {file = "jupyterlab_server-2.26.0-py3-none-any.whl", hash = "sha256:54622cbd330526a385ee0c1fdccdff3a1e7219bf3e864a335284a1270a1973df"}, + {file = "jupyterlab_server-2.26.0.tar.gz", hash = "sha256:9b3ba91cf2837f7f124fca36d63f3ca80ace2bed4898a63dd47e6598c1ab006f"}, +] [package.dependencies] -jinja2 = ">=2.10" -json5 = "*" -jsonschema = ">=3.0.1" -notebook = ">=4.2.0" -requests = "*" +babel = ">=2.10" +jinja2 = ">=3.0.3" +json5 = ">=0.9.0" +jsonschema = ">=4.18.0" +jupyter-server = ">=1.21,<3" +packaging = ">=21.3" +requests = ">=2.31" [package.extras] -test = ["pytest", "requests"] - -[[package]] -name = "lxml" -version = "4.6.1" -description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" - -[package.extras] -cssselect = ["cssselect (>=0.7)"] -html5 = ["html5lib"] -htmlsoup = ["beautifulsoup4"] -source = ["Cython (>=0.29.7)"] +docs = ["autodoc-traits", "jinja2 (<3.2.0)", "mistune (<4)", "myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-copybutton", "sphinxcontrib-openapi (>0.8)"] +openapi = ["openapi-core (>=0.18.0,<0.19.0)", "ruamel-yaml"] +test = ["hatch", "ipykernel", "openapi-core (>=0.18.0,<0.19.0)", "openapi-spec-validator (>=0.6.0,<0.8.0)", "pytest (>=7.0,<8)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter[server] (>=0.6.2)", "pytest-timeout", "requests-mock", "ruamel-yaml", "sphinxcontrib-spelling", "strict-rfc3339", "werkzeug"] [[package]] name = "markupsafe" -version = "1.1.1" +version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." -category = "main" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "matplotlib-inline" +version = "0.1.6" +description = "Inline Matplotlib backend for Jupyter" +optional = false +python-versions = ">=3.5" +files = [ + {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, + {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, +] + +[package.dependencies] +traitlets = "*" [[package]] name = "mistune" -version = "0.8.4" -description = "The fastest markdown parser in pure Python" -category = "main" +version = "3.0.2" +description = "A sane and fast Markdown parser with useful plugins and renderers" optional = false -python-versions = "*" +python-versions = ">=3.7" +files = [ + {file = "mistune-3.0.2-py3-none-any.whl", hash = "sha256:71481854c30fdbc938963d3605b72501f5c10a9320ecd412c121c163a1c7d205"}, + {file = "mistune-3.0.2.tar.gz", hash = "sha256:fc7f93ded930c92394ef2cb6f04a8aabab4117a91449e72dcc8dfa646a508be8"}, +] [[package]] name = "nbclient" -version = "0.5.1" +version = "0.10.0" description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." -category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8.0" +files = [ + {file = "nbclient-0.10.0-py3-none-any.whl", hash = "sha256:f13e3529332a1f1f81d82a53210322476a168bb7090a0289c795fe9cc11c9d3f"}, + {file = "nbclient-0.10.0.tar.gz", hash = "sha256:4b3f1b7dba531e498449c4db4f53da339c91d449dc11e9af3a43b4eb5c5abb09"}, +] [package.dependencies] -async-generator = "*" -jupyter-client = ">=6.1.5" -nbformat = ">=5.0" -nest-asyncio = "*" -traitlets = ">=4.2" +jupyter-client = ">=6.1.12" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +nbformat = ">=5.1" +traitlets = ">=5.4" [package.extras] -dev = ["codecov", "coverage", "ipython", "ipykernel", "ipywidgets", "pytest (>=4.1)", "pytest-cov (>=2.6.1)", "check-manifest", "flake8", "mypy", "tox", "bumpversion", "xmltodict", "pip (>=18.1)", "wheel (>=0.31.0)", "setuptools (>=38.6.0)", "twine (>=1.11.0)", "black"] -sphinx = ["Sphinx (>=1.7)", "sphinx-book-theme", "mock", "moto", "myst-parser"] -test = ["codecov", "coverage", "ipython", "ipykernel", "ipywidgets", "pytest (>=4.1)", "pytest-cov (>=2.6.1)", "check-manifest", "flake8", "mypy", "tox", "bumpversion", "xmltodict", "pip (>=18.1)", "wheel (>=0.31.0)", "setuptools (>=38.6.0)", "twine (>=1.11.0)", "black"] +dev = ["pre-commit"] +docs = ["autodoc-traits", "mock", "moto", "myst-parser", "nbclient[test]", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling"] +test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"] [[package]] name = "nbconvert" -version = "6.0.7" -description = "Converting Jupyter Notebooks" -category = "main" +version = "7.16.3" +description = "Converting Jupyter Notebooks (.ipynb files) to other formats. Output formats include asciidoc, html, latex, markdown, pdf, py, rst, script. nbconvert can be used both as a Python library (`import nbconvert`) or as a command line tool (invoked as `jupyter nbconvert ...`)." optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" +files = [ + {file = "nbconvert-7.16.3-py3-none-any.whl", hash = "sha256:ddeff14beeeedf3dd0bc506623e41e4507e551736de59df69a91f86700292b3b"}, + {file = "nbconvert-7.16.3.tar.gz", hash = "sha256:a6733b78ce3d47c3f85e504998495b07e6ea9cf9bf6ec1c98dda63ec6ad19142"}, +] [package.dependencies] -bleach = "*" +beautifulsoup4 = "*" +bleach = "!=5.0.0" defusedxml = "*" -entrypoints = ">=0.2.2" -jinja2 = ">=2.4" -jupyter-core = "*" +jinja2 = ">=3.0" +jupyter-core = ">=4.7" jupyterlab-pygments = "*" -mistune = ">=0.8.1,<2" -nbclient = ">=0.5.0,<0.6.0" -nbformat = ">=4.4" +markupsafe = ">=2.0" +mistune = ">=2.0.3,<4" +nbclient = ">=0.5.0" +nbformat = ">=5.7" +packaging = "*" pandocfilters = ">=1.4.1" pygments = ">=2.4.1" -testpath = "*" -traitlets = ">=4.2" +tinycss2 = "*" +traitlets = ">=5.1" [package.extras] -all = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (==0.2.2)", "tornado (>=4.0)", "sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"] -docs = ["sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"] -serve = ["tornado (>=4.0)"] -test = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (==0.2.2)"] -webpdf = ["pyppeteer (==0.2.2)"] +all = ["nbconvert[docs,qtpdf,serve,test,webpdf]"] +docs = ["ipykernel", "ipython", "myst-parser", "nbsphinx (>=0.2.12)", "pydata-sphinx-theme", "sphinx (==5.0.2)", "sphinxcontrib-spelling"] +qtpdf = ["nbconvert[qtpng]"] +qtpng = ["pyqtwebengine (>=5.15)"] +serve = ["tornado (>=6.1)"] +test = ["flaky", "ipykernel", "ipywidgets (>=7.5)", "pytest (>=7)"] +webpdf = ["playwright"] [[package]] name = "nbformat" -version = "5.0.8" +version = "5.10.4" description = "The Jupyter Notebook format" -category = "main" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" +files = [ + {file = "nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b"}, + {file = "nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a"}, +] [package.dependencies] -ipython-genutils = "*" -jsonschema = ">=2.4,<2.5.0 || >2.5.0" -jupyter-core = "*" -traitlets = ">=4.1" +fastjsonschema = ">=2.15" +jsonschema = ">=2.6" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +traitlets = ">=5.1" [package.extras] -fast = ["fastjsonschema"] -test = ["fastjsonschema", "testpath", "pytest", "pytest-cov"] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] +test = ["pep440", "pre-commit", "pytest", "testpath"] [[package]] name = "nest-asyncio" -version = "1.4.2" +version = "1.6.0" description = "Patch asyncio to allow nested event loops" -category = "main" optional = false python-versions = ">=3.5" +files = [ + {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, + {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, +] [[package]] name = "nodeenv" -version = "1.5.0" +version = "1.8.0" description = "Node.js virtual environment builder" -category = "dev" optional = false -python-versions = "*" - -[[package]] -name = "notebook" -version = "6.1.4" -description = "A web-based notebook environment for interactive computing" -category = "main" -optional = false -python-versions = ">=3.5" +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, + {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, +] [package.dependencies] -argon2-cffi = "*" -ipykernel = "*" -ipython-genutils = "*" -jinja2 = "*" -jupyter-client = ">=5.3.4" -jupyter-core = ">=4.6.1" -nbconvert = "*" -nbformat = "*" -prometheus-client = "*" -pyzmq = ">=17" -Send2Trash = "*" -terminado = ">=0.8.3" -tornado = ">=5.0" -traitlets = ">=4.2.1" +setuptools = "*" + +[[package]] +name = "notebook-shim" +version = "0.2.4" +description = "A shim layer for notebook traits and config" +optional = false +python-versions = ">=3.7" +files = [ + {file = "notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef"}, + {file = "notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb"}, +] + +[package.dependencies] +jupyter-server = ">=1.8,<3" [package.extras] -docs = ["sphinx", "nbsphinx", "sphinxcontrib-github-alt"] -test = ["nose", "coverage", "requests", "nose-warnings-filters", "nbval", "nose-exclude", "selenium", "pytest", "pytest-cov", "requests-unixsocket"] +test = ["pytest", "pytest-console-scripts", "pytest-jupyter", "pytest-tornasync"] [[package]] name = "nox" -version = "2020.8.22" +version = "2024.3.2" description = "Flexible test automation." -category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.7" +files = [ + {file = "nox-2024.3.2-py3-none-any.whl", hash = "sha256:e53514173ac0b98dd47585096a55572fe504fecede58ced708979184d05440be"}, + {file = "nox-2024.3.2.tar.gz", hash = "sha256:f521ae08a15adbf5e11f16cb34e8d0e6ea521e0b92868f684e91677deb974553"}, +] [package.dependencies] -argcomplete = ">=1.9.4,<2.0" -colorlog = ">=2.6.1,<5.0.0" -py = ">=1.4.0,<2.0.0" -virtualenv = ">=14.0.0" +argcomplete = ">=1.9.4,<4.0" +colorlog = ">=2.6.1,<7.0.0" +packaging = ">=20.9" +virtualenv = ">=20.14.1" [package.extras] -tox_to_nox = ["jinja2", "tox"] +tox-to-nox = ["jinja2", "tox"] +uv = ["uv"] [[package]] name = "numpy" -version = "1.19.2" -description = "NumPy is the fundamental package for array computing with Python." -category = "main" +version = "1.26.4" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, +] + +[[package]] +name = "overrides" +version = "7.7.0" +description = "A decorator to automatically detect mismatch when overriding a method." optional = false python-versions = ">=3.6" +files = [ + {file = "overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49"}, + {file = "overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a"}, +] [[package]] name = "packaging" -version = "20.4" +version = "24.0" description = "Core utilities for Python packages" -category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -pyparsing = ">=2.0.2" -six = "*" +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] [[package]] name = "pandocfilters" -version = "1.4.3" +version = "1.5.1" description = "Utilities for writing pandoc filters in python" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc"}, + {file = "pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e"}, +] [[package]] name = "parso" -version = "0.7.1" +version = "0.8.4" description = "A Python Parser" -category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" +files = [ + {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, + {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, +] [package.extras] -testing = ["docopt", "pytest (>=3.0.7)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["docopt", "pytest"] [[package]] name = "pexpect" -version = "4.8.0" +version = "4.9.0" description = "Pexpect allows easy control of interactive console applications." -category = "main" optional = false python-versions = "*" +files = [ + {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, + {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, +] [package.dependencies] ptyprocess = ">=0.5" [[package]] -name = "pickleshare" -version = "0.7.5" -description = "Tiny 'shelve'-like database with concurrency support" -category = "main" +name = "platformdirs" +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false -python-versions = "*" +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] [[package]] name = "pre-commit" -version = "2.7.1" +version = "3.7.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." -category = "dev" optional = false -python-versions = ">=3.6.1" +python-versions = ">=3.9" +files = [ + {file = "pre_commit-3.7.0-py2.py3-none-any.whl", hash = "sha256:5eae9e10c2b5ac51577c3452ec0a490455c45a0533f7960f993a0d01e59decab"}, + {file = "pre_commit-3.7.0.tar.gz", hash = "sha256:e209d61b8acdcf742404408531f0c37d49d2c734fd7cff2d6076083d191cb060"}, +] [package.dependencies] cfgv = ">=2.0.0" identify = ">=1.0.0" nodeenv = ">=0.11.1" pyyaml = ">=5.1" -toml = "*" -virtualenv = ">=20.0.8" +virtualenv = ">=20.10.0" [[package]] name = "prometheus-client" -version = "0.8.0" +version = "0.20.0" description = "Python client for the Prometheus monitoring system." -category = "main" optional = false -python-versions = "*" +python-versions = ">=3.8" +files = [ + {file = "prometheus_client-0.20.0-py3-none-any.whl", hash = "sha256:cde524a85bce83ca359cc837f28b8c0db5cac7aa653a588fd7e84ba061c329e7"}, + {file = "prometheus_client-0.20.0.tar.gz", hash = "sha256:287629d00b147a32dcb2be0b9df905da599b2d82f80377083ec8463309a4bb89"}, +] [package.extras] twisted = ["twisted"] [[package]] name = "prompt-toolkit" -version = "3.0.8" +version = "3.0.43" description = "Library for building powerful interactive command lines in Python" -category = "main" optional = false -python-versions = ">=3.6.1" +python-versions = ">=3.7.0" +files = [ + {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"}, + {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"}, +] [package.dependencies] wcwidth = "*" [[package]] -name = "ptyprocess" -version = "0.6.0" -description = "Run a subprocess in a pseudo terminal" -category = "main" +name = "psutil" +version = "5.9.8" +description = "Cross-platform lib for process and system monitoring in Python." optional = false -python-versions = "*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, + {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, + {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, + {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, + {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, + {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, + {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, + {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, + {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, + {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, + {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, + {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, + {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] [[package]] -name = "py" -version = "1.9.0" -description = "library with cross-python path, ini-parsing, io, code, log facilities" -category = "dev" +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = "*" +files = [ + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, +] + +[[package]] +name = "pure-eval" +version = "0.2.2" +description = "Safely evaluate AST nodes without side effects" +optional = false +python-versions = "*" +files = [ + {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, + {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, +] + +[package.extras] +tests = ["pytest"] [[package]] name = "pycparser" -version = "2.20" +version = "2.22" description = "C parser in Python" -category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.8" +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] [[package]] name = "pygments" -version = "2.7.2" +version = "2.17.2" description = "Pygments is a syntax highlighting package written in Python." -category = "main" optional = false -python-versions = ">=3.5" +python-versions = ">=3.7" +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] -[[package]] -name = "pyparsing" -version = "2.4.7" -description = "Python parsing module" -category = "main" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" - -[[package]] -name = "pyrsistent" -version = "0.17.3" -description = "Persistent/Functional/Immutable data structures" -category = "main" -optional = false -python-versions = ">=3.5" +[package.extras] +plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "python-dateutil" -version = "2.8.1" +version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] [package.dependencies] six = ">=1.5" +[[package]] +name = "python-json-logger" +version = "2.0.7" +description = "A python library adding a json log formatter" +optional = false +python-versions = ">=3.6" +files = [ + {file = "python-json-logger-2.0.7.tar.gz", hash = "sha256:23e7ec02d34237c5aa1e29a070193a4ea87583bb4e7f8fd06d3de8264c4b2e1c"}, + {file = "python_json_logger-2.0.7-py3-none-any.whl", hash = "sha256:f380b826a991ebbe3de4d897aeec42760035ac760345e57b812938dc8b35e2bd"}, +] + [[package]] name = "pywin32" -version = "228" +version = "306" description = "Python for Window Extensions" -category = "main" optional = false python-versions = "*" +files = [ + {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, + {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, + {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, + {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, + {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, + {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, + {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, + {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, + {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, + {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, + {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, + {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, + {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, + {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, +] [[package]] name = "pywinpty" -version = "0.5.7" -description = "Python bindings for the winpty library" -category = "main" +version = "2.0.13" +description = "Pseudo terminal support for Windows from Python." optional = false -python-versions = "*" +python-versions = ">=3.8" +files = [ + {file = "pywinpty-2.0.13-cp310-none-win_amd64.whl", hash = "sha256:697bff211fb5a6508fee2dc6ff174ce03f34a9a233df9d8b5fe9c8ce4d5eaf56"}, + {file = "pywinpty-2.0.13-cp311-none-win_amd64.whl", hash = "sha256:b96fb14698db1284db84ca38c79f15b4cfdc3172065b5137383910567591fa99"}, + {file = "pywinpty-2.0.13-cp312-none-win_amd64.whl", hash = "sha256:2fd876b82ca750bb1333236ce98488c1be96b08f4f7647cfdf4129dfad83c2d4"}, + {file = "pywinpty-2.0.13-cp38-none-win_amd64.whl", hash = "sha256:61d420c2116c0212808d31625611b51caf621fe67f8a6377e2e8b617ea1c1f7d"}, + {file = "pywinpty-2.0.13-cp39-none-win_amd64.whl", hash = "sha256:71cb613a9ee24174730ac7ae439fd179ca34ccb8c5349e8d7b72ab5dea2c6f4b"}, + {file = "pywinpty-2.0.13.tar.gz", hash = "sha256:c34e32351a3313ddd0d7da23d27f835c860d32fe4ac814d372a3ea9594f41dde"}, +] [[package]] name = "pyyaml" -version = "5.3.1" +version = "6.0.1" description = "YAML parser and emitter for Python" -category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] [[package]] name = "pyzmq" -version = "19.0.2" +version = "25.1.2" description = "Python bindings for 0MQ" -category = "main" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*" +python-versions = ">=3.6" +files = [ + {file = "pyzmq-25.1.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:e624c789359f1a16f83f35e2c705d07663ff2b4d4479bad35621178d8f0f6ea4"}, + {file = "pyzmq-25.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:49151b0efece79f6a79d41a461d78535356136ee70084a1c22532fc6383f4ad0"}, + {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9a5f194cf730f2b24d6af1f833c14c10f41023da46a7f736f48b6d35061e76e"}, + {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:faf79a302f834d9e8304fafdc11d0d042266667ac45209afa57e5efc998e3872"}, + {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f51a7b4ead28d3fca8dda53216314a553b0f7a91ee8fc46a72b402a78c3e43d"}, + {file = "pyzmq-25.1.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0ddd6d71d4ef17ba5a87becf7ddf01b371eaba553c603477679ae817a8d84d75"}, + {file = "pyzmq-25.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:246747b88917e4867e2367b005fc8eefbb4a54b7db363d6c92f89d69abfff4b6"}, + {file = "pyzmq-25.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:00c48ae2fd81e2a50c3485de1b9d5c7c57cd85dc8ec55683eac16846e57ac979"}, + {file = "pyzmq-25.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5a68d491fc20762b630e5db2191dd07ff89834086740f70e978bb2ef2668be08"}, + {file = "pyzmq-25.1.2-cp310-cp310-win32.whl", hash = "sha256:09dfe949e83087da88c4a76767df04b22304a682d6154de2c572625c62ad6886"}, + {file = "pyzmq-25.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:fa99973d2ed20417744fca0073390ad65ce225b546febb0580358e36aa90dba6"}, + {file = "pyzmq-25.1.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:82544e0e2d0c1811482d37eef297020a040c32e0687c1f6fc23a75b75db8062c"}, + {file = "pyzmq-25.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:01171fc48542348cd1a360a4b6c3e7d8f46cdcf53a8d40f84db6707a6768acc1"}, + {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc69c96735ab501419c432110016329bf0dea8898ce16fab97c6d9106dc0b348"}, + {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3e124e6b1dd3dfbeb695435dff0e383256655bb18082e094a8dd1f6293114642"}, + {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7598d2ba821caa37a0f9d54c25164a4fa351ce019d64d0b44b45540950458840"}, + {file = "pyzmq-25.1.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d1299d7e964c13607efd148ca1f07dcbf27c3ab9e125d1d0ae1d580a1682399d"}, + {file = "pyzmq-25.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4e6f689880d5ad87918430957297c975203a082d9a036cc426648fcbedae769b"}, + {file = "pyzmq-25.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cc69949484171cc961e6ecd4a8911b9ce7a0d1f738fcae717177c231bf77437b"}, + {file = "pyzmq-25.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9880078f683466b7f567b8624bfc16cad65077be046b6e8abb53bed4eeb82dd3"}, + {file = "pyzmq-25.1.2-cp311-cp311-win32.whl", hash = "sha256:4e5837af3e5aaa99a091302df5ee001149baff06ad22b722d34e30df5f0d9097"}, + {file = "pyzmq-25.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:25c2dbb97d38b5ac9fd15586e048ec5eb1e38f3d47fe7d92167b0c77bb3584e9"}, + {file = "pyzmq-25.1.2-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:11e70516688190e9c2db14fcf93c04192b02d457b582a1f6190b154691b4c93a"}, + {file = "pyzmq-25.1.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:313c3794d650d1fccaaab2df942af9f2c01d6217c846177cfcbc693c7410839e"}, + {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b3cbba2f47062b85fe0ef9de5b987612140a9ba3a9c6d2543c6dec9f7c2ab27"}, + {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc31baa0c32a2ca660784d5af3b9487e13b61b3032cb01a115fce6588e1bed30"}, + {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c9087b109070c5ab0b383079fa1b5f797f8d43e9a66c07a4b8b8bdecfd88ee"}, + {file = "pyzmq-25.1.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f8429b17cbb746c3e043cb986328da023657e79d5ed258b711c06a70c2ea7537"}, + {file = "pyzmq-25.1.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5074adeacede5f810b7ef39607ee59d94e948b4fd954495bdb072f8c54558181"}, + {file = "pyzmq-25.1.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7ae8f354b895cbd85212da245f1a5ad8159e7840e37d78b476bb4f4c3f32a9fe"}, + {file = "pyzmq-25.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b264bf2cc96b5bc43ce0e852be995e400376bd87ceb363822e2cb1964fcdc737"}, + {file = "pyzmq-25.1.2-cp312-cp312-win32.whl", hash = "sha256:02bbc1a87b76e04fd780b45e7f695471ae6de747769e540da909173d50ff8e2d"}, + {file = "pyzmq-25.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:ced111c2e81506abd1dc142e6cd7b68dd53747b3b7ae5edbea4578c5eeff96b7"}, + {file = "pyzmq-25.1.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:7b6d09a8962a91151f0976008eb7b29b433a560fde056ec7a3db9ec8f1075438"}, + {file = "pyzmq-25.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:967668420f36878a3c9ecb5ab33c9d0ff8d054f9c0233d995a6d25b0e95e1b6b"}, + {file = "pyzmq-25.1.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5edac3f57c7ddaacdb4d40f6ef2f9e299471fc38d112f4bc6d60ab9365445fb0"}, + {file = "pyzmq-25.1.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:0dabfb10ef897f3b7e101cacba1437bd3a5032ee667b7ead32bbcdd1a8422fe7"}, + {file = "pyzmq-25.1.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:2c6441e0398c2baacfe5ba30c937d274cfc2dc5b55e82e3749e333aabffde561"}, + {file = "pyzmq-25.1.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:16b726c1f6c2e7625706549f9dbe9b06004dfbec30dbed4bf50cbdfc73e5b32a"}, + {file = "pyzmq-25.1.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:a86c2dd76ef71a773e70551a07318b8e52379f58dafa7ae1e0a4be78efd1ff16"}, + {file = "pyzmq-25.1.2-cp36-cp36m-win32.whl", hash = "sha256:359f7f74b5d3c65dae137f33eb2bcfa7ad9ebefd1cab85c935f063f1dbb245cc"}, + {file = "pyzmq-25.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:55875492f820d0eb3417b51d96fea549cde77893ae3790fd25491c5754ea2f68"}, + {file = "pyzmq-25.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b8c8a419dfb02e91b453615c69568442e897aaf77561ee0064d789705ff37a92"}, + {file = "pyzmq-25.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8807c87fa893527ae8a524c15fc505d9950d5e856f03dae5921b5e9aa3b8783b"}, + {file = "pyzmq-25.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5e319ed7d6b8f5fad9b76daa0a68497bc6f129858ad956331a5835785761e003"}, + {file = "pyzmq-25.1.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3c53687dde4d9d473c587ae80cc328e5b102b517447456184b485587ebd18b62"}, + {file = "pyzmq-25.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9add2e5b33d2cd765ad96d5eb734a5e795a0755f7fc49aa04f76d7ddda73fd70"}, + {file = "pyzmq-25.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e690145a8c0c273c28d3b89d6fb32c45e0d9605b2293c10e650265bf5c11cfec"}, + {file = "pyzmq-25.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:00a06faa7165634f0cac1abb27e54d7a0b3b44eb9994530b8ec73cf52e15353b"}, + {file = "pyzmq-25.1.2-cp37-cp37m-win32.whl", hash = "sha256:0f97bc2f1f13cb16905a5f3e1fbdf100e712d841482b2237484360f8bc4cb3d7"}, + {file = "pyzmq-25.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6cc0020b74b2e410287e5942e1e10886ff81ac77789eb20bec13f7ae681f0fdd"}, + {file = "pyzmq-25.1.2-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:bef02cfcbded83473bdd86dd8d3729cd82b2e569b75844fb4ea08fee3c26ae41"}, + {file = "pyzmq-25.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e10a4b5a4b1192d74853cc71a5e9fd022594573926c2a3a4802020360aa719d8"}, + {file = "pyzmq-25.1.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8c5f80e578427d4695adac6fdf4370c14a2feafdc8cb35549c219b90652536ae"}, + {file = "pyzmq-25.1.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5dde6751e857910c1339890f3524de74007958557593b9e7e8c5f01cd919f8a7"}, + {file = "pyzmq-25.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea1608dd169da230a0ad602d5b1ebd39807ac96cae1845c3ceed39af08a5c6df"}, + {file = "pyzmq-25.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0f513130c4c361201da9bc69df25a086487250e16b5571ead521b31ff6b02220"}, + {file = "pyzmq-25.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:019744b99da30330798bb37df33549d59d380c78e516e3bab9c9b84f87a9592f"}, + {file = "pyzmq-25.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2e2713ef44be5d52dd8b8e2023d706bf66cb22072e97fc71b168e01d25192755"}, + {file = "pyzmq-25.1.2-cp38-cp38-win32.whl", hash = "sha256:07cd61a20a535524906595e09344505a9bd46f1da7a07e504b315d41cd42eb07"}, + {file = "pyzmq-25.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb7e49a17fb8c77d3119d41a4523e432eb0c6932187c37deb6fbb00cc3028088"}, + {file = "pyzmq-25.1.2-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:94504ff66f278ab4b7e03e4cba7e7e400cb73bfa9d3d71f58d8972a8dc67e7a6"}, + {file = "pyzmq-25.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6dd0d50bbf9dca1d0bdea219ae6b40f713a3fb477c06ca3714f208fd69e16fd8"}, + {file = "pyzmq-25.1.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:004ff469d21e86f0ef0369717351073e0e577428e514c47c8480770d5e24a565"}, + {file = "pyzmq-25.1.2-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c0b5ca88a8928147b7b1e2dfa09f3b6c256bc1135a1338536cbc9ea13d3b7add"}, + {file = "pyzmq-25.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c9a79f1d2495b167119d02be7448bfba57fad2a4207c4f68abc0bab4b92925b"}, + {file = "pyzmq-25.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:518efd91c3d8ac9f9b4f7dd0e2b7b8bf1a4fe82a308009016b07eaa48681af82"}, + {file = "pyzmq-25.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1ec23bd7b3a893ae676d0e54ad47d18064e6c5ae1fadc2f195143fb27373f7f6"}, + {file = "pyzmq-25.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db36c27baed588a5a8346b971477b718fdc66cf5b80cbfbd914b4d6d355e44e2"}, + {file = "pyzmq-25.1.2-cp39-cp39-win32.whl", hash = "sha256:39b1067f13aba39d794a24761e385e2eddc26295826530a8c7b6c6c341584289"}, + {file = "pyzmq-25.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:8e9f3fabc445d0ce320ea2c59a75fe3ea591fdbdeebec5db6de530dd4b09412e"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a8c1d566344aee826b74e472e16edae0a02e2a044f14f7c24e123002dcff1c05"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:759cfd391a0996345ba94b6a5110fca9c557ad4166d86a6e81ea526c376a01e8"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c61e346ac34b74028ede1c6b4bcecf649d69b707b3ff9dc0fab453821b04d1e"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cb8fc1f8d69b411b8ec0b5f1ffbcaf14c1db95b6bccea21d83610987435f1a4"}, + {file = "pyzmq-25.1.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3c00c9b7d1ca8165c610437ca0c92e7b5607b2f9076f4eb4b095c85d6e680a1d"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:df0c7a16ebb94452d2909b9a7b3337940e9a87a824c4fc1c7c36bb4404cb0cde"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:45999e7f7ed5c390f2e87ece7f6c56bf979fb213550229e711e45ecc7d42ccb8"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ac170e9e048b40c605358667aca3d94e98f604a18c44bdb4c102e67070f3ac9b"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1b604734bec94f05f81b360a272fc824334267426ae9905ff32dc2be433ab96"}, + {file = "pyzmq-25.1.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:a793ac733e3d895d96f865f1806f160696422554e46d30105807fdc9841b9f7d"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0806175f2ae5ad4b835ecd87f5f85583316b69f17e97786f7443baaf54b9bb98"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ef12e259e7bc317c7597d4f6ef59b97b913e162d83b421dd0db3d6410f17a244"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea253b368eb41116011add00f8d5726762320b1bda892f744c91997b65754d73"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b9b1f2ad6498445a941d9a4fee096d387fee436e45cc660e72e768d3d8ee611"}, + {file = "pyzmq-25.1.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:8b14c75979ce932c53b79976a395cb2a8cd3aaf14aef75e8c2cb55a330b9b49d"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:889370d5174a741a62566c003ee8ddba4b04c3f09a97b8000092b7ca83ec9c49"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a18fff090441a40ffda8a7f4f18f03dc56ae73f148f1832e109f9bffa85df15"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99a6b36f95c98839ad98f8c553d8507644c880cf1e0a57fe5e3a3f3969040882"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4345c9a27f4310afbb9c01750e9461ff33d6fb74cd2456b107525bbeebcb5be3"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3516e0b6224cf6e43e341d56da15fd33bdc37fa0c06af4f029f7d7dfceceabbc"}, + {file = "pyzmq-25.1.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:146b9b1f29ead41255387fb07be56dc29639262c0f7344f570eecdcd8d683314"}, + {file = "pyzmq-25.1.2.tar.gz", hash = "sha256:93f1aa311e8bb912e34f004cf186407a4e90eec4f0ecc0efd26056bf7eda0226"}, +] + +[package.dependencies] +cffi = {version = "*", markers = "implementation_name == \"pypy\""} + +[[package]] +name = "referencing" +version = "0.34.0" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "referencing-0.34.0-py3-none-any.whl", hash = "sha256:d53ae300ceddd3169f1ffa9caf2cb7b769e92657e4fafb23d34b93679116dfd4"}, + {file = "referencing-0.34.0.tar.gz", hash = "sha256:5773bd84ef41799a5a8ca72dc34590c041eb01bf9aa02632b4a973fb0181a844"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" [[package]] name = "requests" -version = "2.24.0" +version = "2.31.0" description = "Python HTTP for Humans." -category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] [package.dependencies] certifi = ">=2017.4.17" -chardet = ">=3.0.2,<4" -idna = ">=2.5,<3" -urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" [package.extras] -security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] -socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] -name = "rise" -version = "5.7.0" -description = "Reveal.js - Jupyter/IPython Slideshow Extension" -category = "dev" +name = "rfc3339-validator" +version = "0.1.4" +description = "A pure python RFC3339 validator" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa"}, + {file = "rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b"}, +] [package.dependencies] -notebook = ">=6.0" +six = "*" + +[[package]] +name = "rfc3986-validator" +version = "0.1.1" +description = "Pure python rfc3986 validator" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9"}, + {file = "rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055"}, +] + +[[package]] +name = "rpds-py" +version = "0.18.0" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rpds_py-0.18.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5b4e7d8d6c9b2e8ee2d55c90b59c707ca59bc30058269b3db7b1f8df5763557e"}, + {file = "rpds_py-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c463ed05f9dfb9baebef68048aed8dcdc94411e4bf3d33a39ba97e271624f8f7"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d62dec4976954a23d7f91f2f4530852b0c7608116c257833922a896101336c51"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd18772815d5f008fa03d2b9a681ae38d5ae9f0e599f7dda233c439fcaa00d40"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:923d39efa3cfb7279a0327e337a7958bff00cc447fd07a25cddb0a1cc9a6d2da"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39514da80f971362f9267c600b6d459bfbbc549cffc2cef8e47474fddc9b45b1"}, + {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a34d557a42aa28bd5c48a023c570219ba2593bcbbb8dc1b98d8cf5d529ab1434"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:93df1de2f7f7239dc9cc5a4a12408ee1598725036bd2dedadc14d94525192fc3"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:34b18ba135c687f4dac449aa5157d36e2cbb7c03cbea4ddbd88604e076aa836e"}, + {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0b5dcf9193625afd8ecc92312d6ed78781c46ecbf39af9ad4681fc9f464af88"}, + {file = "rpds_py-0.18.0-cp310-none-win32.whl", hash = "sha256:c4325ff0442a12113a6379af66978c3fe562f846763287ef66bdc1d57925d337"}, + {file = "rpds_py-0.18.0-cp310-none-win_amd64.whl", hash = "sha256:7223a2a5fe0d217e60a60cdae28d6949140dde9c3bcc714063c5b463065e3d66"}, + {file = "rpds_py-0.18.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3a96e0c6a41dcdba3a0a581bbf6c44bb863f27c541547fb4b9711fd8cf0ffad4"}, + {file = "rpds_py-0.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30f43887bbae0d49113cbaab729a112251a940e9b274536613097ab8b4899cf6"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcb25daa9219b4cf3a0ab24b0eb9a5cc8949ed4dc72acb8fa16b7e1681aa3c58"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d68c93e381010662ab873fea609bf6c0f428b6d0bb00f2c6939782e0818d37bf"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b34b7aa8b261c1dbf7720b5d6f01f38243e9b9daf7e6b8bc1fd4657000062f2c"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e6d75ab12b0bbab7215e5d40f1e5b738aa539598db27ef83b2ec46747df90e1"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8612cd233543a3781bc659c731b9d607de65890085098986dfd573fc2befe5"}, + {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aec493917dd45e3c69d00a8874e7cbed844efd935595ef78a0f25f14312e33c6"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:661d25cbffaf8cc42e971dd570d87cb29a665f49f4abe1f9e76be9a5182c4688"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1df3659d26f539ac74fb3b0c481cdf9d725386e3552c6fa2974f4d33d78e544b"}, + {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1ce3ba137ed54f83e56fb983a5859a27d43a40188ba798993812fed73c70836"}, + {file = "rpds_py-0.18.0-cp311-none-win32.whl", hash = "sha256:69e64831e22a6b377772e7fb337533c365085b31619005802a79242fee620bc1"}, + {file = "rpds_py-0.18.0-cp311-none-win_amd64.whl", hash = "sha256:998e33ad22dc7ec7e030b3df701c43630b5bc0d8fbc2267653577e3fec279afa"}, + {file = "rpds_py-0.18.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7f2facbd386dd60cbbf1a794181e6aa0bd429bd78bfdf775436020172e2a23f0"}, + {file = "rpds_py-0.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d9a5be316c15ffb2b3c405c4ff14448c36b4435be062a7f578ccd8b01f0c4d8"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd5bf1af8efe569654bbef5a3e0a56eca45f87cfcffab31dd8dde70da5982475"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5417558f6887e9b6b65b4527232553c139b57ec42c64570569b155262ac0754f"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:56a737287efecafc16f6d067c2ea0117abadcd078d58721f967952db329a3e5c"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8f03bccbd8586e9dd37219bce4d4e0d3ab492e6b3b533e973fa08a112cb2ffc9"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4457a94da0d5c53dc4b3e4de1158bdab077db23c53232f37a3cb7afdb053a4e3"}, + {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ab39c1ba9023914297dd88ec3b3b3c3f33671baeb6acf82ad7ce883f6e8e157"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d54553c1136b50fd12cc17e5b11ad07374c316df307e4cfd6441bea5fb68496"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0af039631b6de0397ab2ba16eaf2872e9f8fca391b44d3d8cac317860a700a3f"}, + {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84ffab12db93b5f6bad84c712c92060a2d321b35c3c9960b43d08d0f639d60d7"}, + {file = "rpds_py-0.18.0-cp312-none-win32.whl", hash = "sha256:685537e07897f173abcf67258bee3c05c374fa6fff89d4c7e42fb391b0605e98"}, + {file = "rpds_py-0.18.0-cp312-none-win_amd64.whl", hash = "sha256:e003b002ec72c8d5a3e3da2989c7d6065b47d9eaa70cd8808b5384fbb970f4ec"}, + {file = "rpds_py-0.18.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:08f9ad53c3f31dfb4baa00da22f1e862900f45908383c062c27628754af2e88e"}, + {file = "rpds_py-0.18.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0013fe6b46aa496a6749c77e00a3eb07952832ad6166bd481c74bda0dcb6d58"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32a92116d4f2a80b629778280103d2a510a5b3f6314ceccd6e38006b5e92dcb"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e541ec6f2ec456934fd279a3120f856cd0aedd209fc3852eca563f81738f6861"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bed88b9a458e354014d662d47e7a5baafd7ff81c780fd91584a10d6ec842cb73"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2644e47de560eb7bd55c20fc59f6daa04682655c58d08185a9b95c1970fa1e07"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e8916ae4c720529e18afa0b879473049e95949bf97042e938530e072fde061d"}, + {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:465a3eb5659338cf2a9243e50ad9b2296fa15061736d6e26240e713522b6235c"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ea7d4a99f3b38c37eac212dbd6ec42b7a5ec51e2c74b5d3223e43c811609e65f"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:67071a6171e92b6da534b8ae326505f7c18022c6f19072a81dcf40db2638767c"}, + {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:41ef53e7c58aa4ef281da975f62c258950f54b76ec8e45941e93a3d1d8580594"}, + {file = "rpds_py-0.18.0-cp38-none-win32.whl", hash = "sha256:fdea4952db2793c4ad0bdccd27c1d8fdd1423a92f04598bc39425bcc2b8ee46e"}, + {file = "rpds_py-0.18.0-cp38-none-win_amd64.whl", hash = "sha256:7cd863afe7336c62ec78d7d1349a2f34c007a3cc6c2369d667c65aeec412a5b1"}, + {file = "rpds_py-0.18.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5307def11a35f5ae4581a0b658b0af8178c65c530e94893345bebf41cc139d33"}, + {file = "rpds_py-0.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77f195baa60a54ef9d2de16fbbfd3ff8b04edc0c0140a761b56c267ac11aa467"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39f5441553f1c2aed4de4377178ad8ff8f9d733723d6c66d983d75341de265ab"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a00312dea9310d4cb7dbd7787e722d2e86a95c2db92fbd7d0155f97127bcb40"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f2fc11e8fe034ee3c34d316d0ad8808f45bc3b9ce5857ff29d513f3ff2923a1"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:586f8204935b9ec884500498ccc91aa869fc652c40c093bd9e1471fbcc25c022"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddc2f4dfd396c7bfa18e6ce371cba60e4cf9d2e5cdb71376aa2da264605b60b9"}, + {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ddcba87675b6d509139d1b521e0c8250e967e63b5909a7e8f8944d0f90ff36f"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7bd339195d84439cbe5771546fe8a4e8a7a045417d8f9de9a368c434e42a721e"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d7c36232a90d4755b720fbd76739d8891732b18cf240a9c645d75f00639a9024"}, + {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6b0817e34942b2ca527b0e9298373e7cc75f429e8da2055607f4931fded23e20"}, + {file = "rpds_py-0.18.0-cp39-none-win32.whl", hash = "sha256:99f70b740dc04d09e6b2699b675874367885217a2e9f782bdf5395632ac663b7"}, + {file = "rpds_py-0.18.0-cp39-none-win_amd64.whl", hash = "sha256:6ef687afab047554a2d366e112dd187b62d261d49eb79b77e386f94644363294"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad36cfb355e24f1bd37cac88c112cd7730873f20fb0bdaf8ba59eedf8216079f"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:36b3ee798c58ace201289024b52788161e1ea133e4ac93fba7d49da5fec0ef9e"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8a2f084546cc59ea99fda8e070be2fd140c3092dc11524a71aa8f0f3d5a55ca"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e4461d0f003a0aa9be2bdd1b798a041f177189c1a0f7619fe8c95ad08d9a45d7"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8db715ebe3bb7d86d77ac1826f7d67ec11a70dbd2376b7cc214199360517b641"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793968759cd0d96cac1e367afd70c235867831983f876a53389ad869b043c948"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66e6a3af5a75363d2c9a48b07cb27c4ea542938b1a2e93b15a503cdfa8490795"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ef0befbb5d79cf32d0266f5cff01545602344eda89480e1dd88aca964260b18"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d4acf42190d449d5e89654d5c1ed3a4f17925eec71f05e2a41414689cda02d1"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a5f446dd5055667aabaee78487f2b5ab72e244f9bc0b2ffebfeec79051679984"}, + {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9dbbeb27f4e70bfd9eec1be5477517365afe05a9b2c441a0b21929ee61048124"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:22806714311a69fd0af9b35b7be97c18a0fc2826e6827dbb3a8c94eac6cf7eeb"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b34ae4636dfc4e76a438ab826a0d1eed2589ca7d9a1b2d5bb546978ac6485461"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c8370641f1a7f0e0669ddccca22f1da893cef7628396431eb445d46d893e5cd"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c8362467a0fdeccd47935f22c256bec5e6abe543bf0d66e3d3d57a8fb5731863"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11a8c85ef4a07a7638180bf04fe189d12757c696eb41f310d2426895356dcf05"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b316144e85316da2723f9d8dc75bada12fa58489a527091fa1d5a612643d1a0e"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf1ea2e34868f6fbf070e1af291c8180480310173de0b0c43fc38a02929fc0e3"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e546e768d08ad55b20b11dbb78a745151acbd938f8f00d0cfbabe8b0199b9880"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4901165d170a5fde6f589acb90a6b33629ad1ec976d4529e769c6f3d885e3e80"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:618a3d6cae6ef8ec88bb76dd80b83cfe415ad4f1d942ca2a903bf6b6ff97a2da"}, + {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ed4eb745efbff0a8e9587d22a84be94a5eb7d2d99c02dacf7bd0911713ed14dd"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c81e5f372cd0dc5dc4809553d34f832f60a46034a5f187756d9b90586c2c307"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:43fbac5f22e25bee1d482c97474f930a353542855f05c1161fd804c9dc74a09d"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d7faa6f14017c0b1e69f5e2c357b998731ea75a442ab3841c0dbbbfe902d2c4"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:08231ac30a842bd04daabc4d71fddd7e6d26189406d5a69535638e4dcb88fe76"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f26b5bd1079acdb0c7a5645e350fe54d16b17bfc5e71f371c449383d3342e17"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:482103aed1dfe2f3b71a58eff35ba105289b8d862551ea576bd15479aba01f66"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1374f4129f9bcca53a1bba0bb86bf78325a0374577cf7e9e4cd046b1e6f20e24"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:635dc434ff724b178cb192c70016cc0ad25a275228f749ee0daf0eddbc8183b1"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:bc362ee4e314870a70f4ae88772d72d877246537d9f8cb8f7eacf10884862432"}, + {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:4832d7d380477521a8c1644bbab6588dfedea5e30a7d967b5fb75977c45fd77f"}, + {file = "rpds_py-0.18.0.tar.gz", hash = "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d"}, +] [[package]] name = "send2trash" -version = "1.5.0" -description = "Send file to trash natively under Mac OS X, Windows and Linux." -category = "main" +version = "1.8.3" +description = "Send file to trash natively under Mac OS X, Windows and Linux" optional = false -python-versions = "*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "Send2Trash-1.8.3-py3-none-any.whl", hash = "sha256:0c31227e0bd08961c7665474a3d1ef7193929fedda4233843689baa056be46c9"}, + {file = "Send2Trash-1.8.3.tar.gz", hash = "sha256:b18e7a3966d99871aefeb00cfbcfdced55ce4871194810fc71f4aa484b953abf"}, +] + +[package.extras] +nativelib = ["pyobjc-framework-Cocoa", "pywin32"] +objc = ["pyobjc-framework-Cocoa"] +win32 = ["pywin32"] + +[[package]] +name = "setuptools" +version = "69.2.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, + {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" -version = "1.15.0" +version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "soupsieve" +version = "2.5" +description = "A modern CSS selector implementation for Beautiful Soup." +optional = false +python-versions = ">=3.8" +files = [ + {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, + {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +description = "Extract data from python stack frames and tracebacks for informative displays" +optional = false +python-versions = "*" +files = [ + {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, + {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, +] + +[package.dependencies] +asttokens = ">=2.1.0" +executing = ">=1.2.0" +pure-eval = "*" + +[package.extras] +tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] [[package]] name = "terminado" -version = "0.9.1" +version = "0.18.1" description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library." -category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" +files = [ + {file = "terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0"}, + {file = "terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e"}, +] [package.dependencies] ptyprocess = {version = "*", markers = "os_name != \"nt\""} -pywinpty = {version = ">=0.5", markers = "os_name == \"nt\""} -tornado = ">=4" - -[[package]] -name = "testpath" -version = "0.4.4" -description = "Test utilities for code working with files and commands" -category = "main" -optional = false -python-versions = "*" +pywinpty = {version = ">=1.1.0", markers = "os_name == \"nt\""} +tornado = ">=6.1.0" [package.extras] -test = ["pathlib2"] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["pre-commit", "pytest (>=7.0)", "pytest-timeout"] +typing = ["mypy (>=1.6,<2.0)", "traitlets (>=5.11.1)"] [[package]] -name = "toml" -version = "0.10.1" -description = "Python Library for Tom's Obvious, Minimal Language" -category = "dev" +name = "tinycss2" +version = "1.2.1" +description = "A tiny CSS parser" optional = false -python-versions = "*" +python-versions = ">=3.7" +files = [ + {file = "tinycss2-1.2.1-py3-none-any.whl", hash = "sha256:2b80a96d41e7c3914b8cda8bc7f705a4d9c49275616e886103dd839dfc847847"}, + {file = "tinycss2-1.2.1.tar.gz", hash = "sha256:8cff3a8f066c2ec677c06dbc7b45619804a6938478d9d73c284b29d14ecb0627"}, +] + +[package.dependencies] +webencodings = ">=0.4" + +[package.extras] +doc = ["sphinx", "sphinx_rtd_theme"] +test = ["flake8", "isort", "pytest"] [[package]] name = "tornado" -version = "6.0.4" +version = "6.4" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." -category = "main" optional = false -python-versions = ">= 3.5" +python-versions = ">= 3.8" +files = [ + {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, + {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, + {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, + {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, + {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, + {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, + {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, +] [[package]] name = "traitlets" -version = "5.0.5" +version = "5.14.2" description = "Traitlets Python configuration system" -category = "main" optional = false -python-versions = ">=3.7" - -[package.dependencies] -ipython-genutils = "*" +python-versions = ">=3.8" +files = [ + {file = "traitlets-5.14.2-py3-none-any.whl", hash = "sha256:fcdf85684a772ddeba87db2f398ce00b40ff550d1528c03c14dbf6a02003cd80"}, + {file = "traitlets-5.14.2.tar.gz", hash = "sha256:8cdd83c040dab7d1dee822678e5f5d100b514f7b72b01615b26fc5718916fdf9"}, +] [package.extras] -test = ["pytest"] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.1)", "pytest-mock", "pytest-mypy-testing"] + +[[package]] +name = "types-python-dateutil" +version = "2.9.0.20240316" +description = "Typing stubs for python-dateutil" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-python-dateutil-2.9.0.20240316.tar.gz", hash = "sha256:5d2f2e240b86905e40944dd787db6da9263f0deabef1076ddaed797351ec0202"}, + {file = "types_python_dateutil-2.9.0.20240316-py3-none-any.whl", hash = "sha256:6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b"}, +] + +[[package]] +name = "typing-extensions" +version = "4.11.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, +] + +[[package]] +name = "uri-template" +version = "1.3.0" +description = "RFC 6570 URI Template Processor" +optional = false +python-versions = ">=3.7" +files = [ + {file = "uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7"}, + {file = "uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363"}, +] + +[package.extras] +dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake8-commas", "flake8-comprehensions", "flake8-continuation", "flake8-datetimez", "flake8-docstrings", "flake8-import-order", "flake8-literal", "flake8-modern-annotations", "flake8-noqa", "flake8-pyproject", "flake8-requirements", "flake8-typechecking-import", "flake8-use-fstring", "mypy", "pep8-naming", "types-PyYAML"] [[package]] name = "urllib3" -version = "1.25.11" +version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] [package.extras] -brotli = ["brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.1.0" +version = "20.25.1" description = "Virtual Python Environment builder" -category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"}, + {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"}, +] [package.dependencies] -appdirs = ">=1.4.3,<2" -distlib = ">=0.3.1,<1" -filelock = ">=3.0.0,<4" -six = ">=1.9.0,<2" +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" [package.extras] -docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"] -testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "pytest-xdist (>=1.31.0)", "packaging (>=20.0)", "xonsh (>=0.9.16)"] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [[package]] name = "wcwidth" -version = "0.2.5" +version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" -category = "main" optional = false python-versions = "*" +files = [ + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, +] + +[[package]] +name = "webcolors" +version = "1.13" +description = "A library for working with the color formats defined by HTML and CSS." +optional = false +python-versions = ">=3.7" +files = [ + {file = "webcolors-1.13-py3-none-any.whl", hash = "sha256:29bc7e8752c0a1bd4a1f03c14d6e6a72e93d82193738fa860cbff59d0fcc11bf"}, + {file = "webcolors-1.13.tar.gz", hash = "sha256:c225b674c83fa923be93d235330ce0300373d02885cef23238813b0d5668304a"}, +] + +[package.extras] +docs = ["furo", "sphinx", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-notfound-page", "sphinxext-opengraph"] +tests = ["pytest", "pytest-cov"] [[package]] name = "webencodings" version = "0.5.1" description = "Character encoding aliases for legacy web content" -category = "main" optional = false python-versions = "*" - -[[package]] -name = "xdoctest" -version = "0.15.0" -description = "A rewrite of the builtin doctest module" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -six = "*" - -[package.extras] -all = ["six", "pytest", "pytest-cov", "codecov", "scikit-build", "cmake", "ninja", "pybind11", "pygments", "colorama", "nbformat", "nbconvert", "jupyter-client", "ipython", "ipykernel"] -colors = ["pygments", "colorama"] -jupyter = ["nbformat", "nbconvert", "jupyter-client", "ipython", "ipykernel"] -optional = ["pygments", "colorama", "nbformat", "nbconvert", "jupyter-client", "ipython", "ipykernel"] -tests = ["pytest", "pytest-cov", "codecov", "scikit-build", "cmake", "ninja", "pybind11", "nbformat", "nbconvert", "jupyter-client", "ipython", "ipykernel"] - -[metadata] -lock-version = "1.1" -python-versions = "^3.8" -content-hash = "0483228a1bbf92c52f5045db05de80b951dbd7302db756ca6f65cd56b482b814" - -[metadata.files] -appdirs = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, -] -appnope = [ - {file = "appnope-0.1.0-py2.py3-none-any.whl", hash = "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0"}, - {file = "appnope-0.1.0.tar.gz", hash = "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"}, -] -argcomplete = [ - {file = "argcomplete-1.12.1-py2.py3-none-any.whl", hash = "sha256:5cd1ac4fc49c29d6016fc2cc4b19a3c08c3624544503495bf25989834c443898"}, - {file = "argcomplete-1.12.1.tar.gz", hash = "sha256:849c2444c35bb2175aea74100ca5f644c29bf716429399c0f2203bb5d9a8e4e6"}, -] -argon2-cffi = [ - {file = "argon2-cffi-20.1.0.tar.gz", hash = "sha256:d8029b2d3e4b4cea770e9e5a0104dd8fa185c1724a0f01528ae4826a6d25f97d"}, - {file = "argon2_cffi-20.1.0-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:6ea92c980586931a816d61e4faf6c192b4abce89aa767ff6581e6ddc985ed003"}, - {file = "argon2_cffi-20.1.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:05a8ac07c7026542377e38389638a8a1e9b78f1cd8439cd7493b39f08dd75fbf"}, - {file = "argon2_cffi-20.1.0-cp27-cp27m-win32.whl", hash = "sha256:0bf066bc049332489bb2d75f69216416329d9dc65deee127152caeb16e5ce7d5"}, - {file = "argon2_cffi-20.1.0-cp27-cp27m-win_amd64.whl", hash = "sha256:57358570592c46c420300ec94f2ff3b32cbccd10d38bdc12dc6979c4a8484fbc"}, - {file = "argon2_cffi-20.1.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7d455c802727710e9dfa69b74ccaab04568386ca17b0ad36350b622cd34606fe"}, - {file = "argon2_cffi-20.1.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:b160416adc0f012fb1f12588a5e6954889510f82f698e23ed4f4fa57f12a0647"}, - {file = "argon2_cffi-20.1.0-cp35-cp35m-win32.whl", hash = "sha256:9bee3212ba4f560af397b6d7146848c32a800652301843df06b9e8f68f0f7361"}, - {file = "argon2_cffi-20.1.0-cp35-cp35m-win_amd64.whl", hash = "sha256:392c3c2ef91d12da510cfb6f9bae52512a4552573a9e27600bdb800e05905d2b"}, - {file = "argon2_cffi-20.1.0-cp36-cp36m-win32.whl", hash = "sha256:ba7209b608945b889457f949cc04c8e762bed4fe3fec88ae9a6b7765ae82e496"}, - {file = "argon2_cffi-20.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:da7f0445b71db6d3a72462e04f36544b0de871289b0bc8a7cc87c0f5ec7079fa"}, - {file = "argon2_cffi-20.1.0-cp37-abi3-macosx_10_6_intel.whl", hash = "sha256:cc0e028b209a5483b6846053d5fd7165f460a1f14774d79e632e75e7ae64b82b"}, - {file = "argon2_cffi-20.1.0-cp37-cp37m-win32.whl", hash = "sha256:18dee20e25e4be86680b178b35ccfc5d495ebd5792cd00781548d50880fee5c5"}, - {file = "argon2_cffi-20.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:6678bb047373f52bcff02db8afab0d2a77d83bde61cfecea7c5c62e2335cb203"}, - {file = "argon2_cffi-20.1.0-cp38-cp38-win32.whl", hash = "sha256:77e909cc756ef81d6abb60524d259d959bab384832f0c651ed7dcb6e5ccdbb78"}, - {file = "argon2_cffi-20.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:9dfd5197852530294ecb5795c97a823839258dfd5eb9420233c7cfedec2058f2"}, -] -async-generator = [ - {file = "async_generator-1.10-py3-none-any.whl", hash = "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b"}, - {file = "async_generator-1.10.tar.gz", hash = "sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144"}, -] -attrs = [ - {file = "attrs-20.2.0-py2.py3-none-any.whl", hash = "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"}, - {file = "attrs-20.2.0.tar.gz", hash = "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594"}, -] -backcall = [ - {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, - {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, -] -bleach = [ - {file = "bleach-3.2.1-py2.py3-none-any.whl", hash = "sha256:9f8ccbeb6183c6e6cddea37592dfb0167485c1e3b13b3363bc325aa8bda3adbd"}, - {file = "bleach-3.2.1.tar.gz", hash = "sha256:52b5919b81842b1854196eaae5ca29679a2f2e378905c346d3ca8227c2c66080"}, -] -certifi = [ - {file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"}, - {file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"}, -] -cffi = [ - {file = "cffi-1.14.3-2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3eeeb0405fd145e714f7633a5173318bd88d8bbfc3dd0a5751f8c4f70ae629bc"}, - {file = "cffi-1.14.3-2-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:cb763ceceae04803adcc4e2d80d611ef201c73da32d8f2722e9d0ab0c7f10768"}, - {file = "cffi-1.14.3-2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:44f60519595eaca110f248e5017363d751b12782a6f2bd6a7041cba275215f5d"}, - {file = "cffi-1.14.3-2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c53af463f4a40de78c58b8b2710ade243c81cbca641e34debf3396a9640d6ec1"}, - {file = "cffi-1.14.3-2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:33c6cdc071ba5cd6d96769c8969a0531be2d08c2628a0143a10a7dcffa9719ca"}, - {file = "cffi-1.14.3-2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c11579638288e53fc94ad60022ff1b67865363e730ee41ad5e6f0a17188b327a"}, - {file = "cffi-1.14.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:3cb3e1b9ec43256c4e0f8d2837267a70b0e1ca8c4f456685508ae6106b1f504c"}, - {file = "cffi-1.14.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f0620511387790860b249b9241c2f13c3a80e21a73e0b861a2df24e9d6f56730"}, - {file = "cffi-1.14.3-cp27-cp27m-win32.whl", hash = "sha256:005f2bfe11b6745d726dbb07ace4d53f057de66e336ff92d61b8c7e9c8f4777d"}, - {file = "cffi-1.14.3-cp27-cp27m-win_amd64.whl", hash = "sha256:2f9674623ca39c9ebe38afa3da402e9326c245f0f5ceff0623dccdac15023e05"}, - {file = "cffi-1.14.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:09e96138280241bd355cd585148dec04dbbedb4f46128f340d696eaafc82dd7b"}, - {file = "cffi-1.14.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:3363e77a6176afb8823b6e06db78c46dbc4c7813b00a41300a4873b6ba63b171"}, - {file = "cffi-1.14.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0ef488305fdce2580c8b2708f22d7785ae222d9825d3094ab073e22e93dfe51f"}, - {file = "cffi-1.14.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:0b1ad452cc824665ddc682400b62c9e4f5b64736a2ba99110712fdee5f2505c4"}, - {file = "cffi-1.14.3-cp35-cp35m-win32.whl", hash = "sha256:85ba797e1de5b48aa5a8427b6ba62cf69607c18c5d4eb747604b7302f1ec382d"}, - {file = "cffi-1.14.3-cp35-cp35m-win_amd64.whl", hash = "sha256:e66399cf0fc07de4dce4f588fc25bfe84a6d1285cc544e67987d22663393926d"}, - {file = "cffi-1.14.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:15f351bed09897fbda218e4db5a3d5c06328862f6198d4fb385f3e14e19decb3"}, - {file = "cffi-1.14.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4d7c26bfc1ea9f92084a1d75e11999e97b62d63128bcc90c3624d07813c52808"}, - {file = "cffi-1.14.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:23e5d2040367322824605bc29ae8ee9175200b92cb5483ac7d466927a9b3d537"}, - {file = "cffi-1.14.3-cp36-cp36m-win32.whl", hash = "sha256:a624fae282e81ad2e4871bdb767e2c914d0539708c0f078b5b355258293c98b0"}, - {file = "cffi-1.14.3-cp36-cp36m-win_amd64.whl", hash = "sha256:de31b5164d44ef4943db155b3e8e17929707cac1e5bd2f363e67a56e3af4af6e"}, - {file = "cffi-1.14.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f92cdecb618e5fa4658aeb97d5eb3d2f47aa94ac6477c6daf0f306c5a3b9e6b1"}, - {file = "cffi-1.14.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:22399ff4870fb4c7ef19fff6eeb20a8bbf15571913c181c78cb361024d574579"}, - {file = "cffi-1.14.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:f4eae045e6ab2bb54ca279733fe4eb85f1effda392666308250714e01907f394"}, - {file = "cffi-1.14.3-cp37-cp37m-win32.whl", hash = "sha256:b0358e6fefc74a16f745afa366acc89f979040e0cbc4eec55ab26ad1f6a9bfbc"}, - {file = "cffi-1.14.3-cp37-cp37m-win_amd64.whl", hash = "sha256:6642f15ad963b5092d65aed022d033c77763515fdc07095208f15d3563003869"}, - {file = "cffi-1.14.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:2791f68edc5749024b4722500e86303a10d342527e1e3bcac47f35fbd25b764e"}, - {file = "cffi-1.14.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:529c4ed2e10437c205f38f3691a68be66c39197d01062618c55f74294a4a4828"}, - {file = "cffi-1.14.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f0f1e499e4000c4c347a124fa6a27d37608ced4fe9f7d45070563b7c4c370c9"}, - {file = "cffi-1.14.3-cp38-cp38-win32.whl", hash = "sha256:3b8eaf915ddc0709779889c472e553f0d3e8b7bdf62dab764c8921b09bf94522"}, - {file = "cffi-1.14.3-cp38-cp38-win_amd64.whl", hash = "sha256:bbd2f4dfee1079f76943767fce837ade3087b578aeb9f69aec7857d5bf25db15"}, - {file = "cffi-1.14.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:cc75f58cdaf043fe6a7a6c04b3b5a0e694c6a9e24050967747251fb80d7bce0d"}, - {file = "cffi-1.14.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:bf39a9e19ce7298f1bd6a9758fa99707e9e5b1ebe5e90f2c3913a47bc548747c"}, - {file = "cffi-1.14.3-cp39-cp39-win32.whl", hash = "sha256:d80998ed59176e8cba74028762fbd9b9153b9afc71ea118e63bbf5d4d0f9552b"}, - {file = "cffi-1.14.3-cp39-cp39-win_amd64.whl", hash = "sha256:c150eaa3dadbb2b5339675b88d4573c1be3cb6f2c33a6c83387e10cc0bf05bd3"}, - {file = "cffi-1.14.3.tar.gz", hash = "sha256:f92f789e4f9241cd262ad7a555ca2c648a98178a953af117ef7fad46aa1d5591"}, -] -cfgv = [ - {file = "cfgv-3.2.0-py2.py3-none-any.whl", hash = "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d"}, - {file = "cfgv-3.2.0.tar.gz", hash = "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"}, -] -chardet = [ - {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, - {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, -] -colorama = [ - {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, -] -colorlog = [ - {file = "colorlog-4.4.0-py2.py3-none-any.whl", hash = "sha256:f14f30f58e2ce6ef40b0088307cac7efb9ecff5605fa2267a2d29955f26aff23"}, - {file = "colorlog-4.4.0.tar.gz", hash = "sha256:0272c537469ab1e63b9915535874d15b671963c9325db0c4891a2aeff97ce3d1"}, -] -decorator = [ - {file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"}, - {file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"}, -] -defusedxml = [ - {file = "defusedxml-0.6.0-py2.py3-none-any.whl", hash = "sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93"}, - {file = "defusedxml-0.6.0.tar.gz", hash = "sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"}, -] -distlib = [ - {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, - {file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"}, -] -entrypoints = [ - {file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"}, - {file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"}, -] -filelock = [ - {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, - {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, -] -identify = [ - {file = "identify-1.5.6-py2.py3-none-any.whl", hash = "sha256:3139bf72d81dfd785b0a464e2776bd59bdc725b4cc10e6cf46b56a0db931c82e"}, - {file = "identify-1.5.6.tar.gz", hash = "sha256:969d844b7a85d32a5f9ac4e163df6e846d73c87c8b75847494ee8f4bd2186421"}, -] -idna = [ - {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, - {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, -] -ipykernel = [ - {file = "ipykernel-5.3.4-py3-none-any.whl", hash = "sha256:d6fbba26dba3cebd411382bc484f7bc2caa98427ae0ddb4ab37fe8bfeb5c7dd3"}, - {file = "ipykernel-5.3.4.tar.gz", hash = "sha256:9b2652af1607986a1b231c62302d070bc0534f564c393a5d9d130db9abbbe89d"}, -] -ipython = [ - {file = "ipython-7.18.1-py3-none-any.whl", hash = "sha256:2e22c1f74477b5106a6fb301c342ab8c64bb75d702e350f05a649e8cb40a0fb8"}, - {file = "ipython-7.18.1.tar.gz", hash = "sha256:a331e78086001931de9424940699691ad49dfb457cea31f5471eae7b78222d5e"}, -] -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.17.2-py2.py3-none-any.whl", hash = "sha256:98cc583fa0f2f8304968199b01b6b4b94f469a1f4a74c1560506ca2a211378b5"}, - {file = "jedi-0.17.2.tar.gz", hash = "sha256:86ed7d9b750603e4ba582ea8edc678657fb4007894a12bcf6f4bb97892f31d20"}, -] -jinja2 = [ - {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"}, - {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"}, -] -json5 = [ - {file = "json5-0.9.5-py2.py3-none-any.whl", hash = "sha256:af1a1b9a2850c7f62c23fde18be4749b3599fd302f494eebf957e2ada6b9e42c"}, - {file = "json5-0.9.5.tar.gz", hash = "sha256:703cfee540790576b56a92e1c6aaa6c4b0d98971dc358ead83812aa4d06bdb96"}, -] -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-6.1.7-py3-none-any.whl", hash = "sha256:c958d24d6eacb975c1acebb68ac9077da61b5f5c040f22f6849928ad7393b950"}, - {file = "jupyter_client-6.1.7.tar.gz", hash = "sha256:49e390b36fe4b4226724704ea28d9fb903f1a3601b6882ce3105221cd09377a1"}, -] -jupyter-contrib-core = [ - {file = "jupyter_contrib_core-0.3.3-py2.py3-none-any.whl", hash = "sha256:1ec81e275a8f5858d56b0c4c6cd85335aa8e915001b8657fe51c620c3cdde50f"}, - {file = "jupyter_contrib_core-0.3.3.tar.gz", hash = "sha256:e65bc0e932ff31801003cef160a4665f2812efe26a53801925a634735e9a5794"}, -] -jupyter-contrib-nbextensions = [ - {file = "jupyter_contrib_nbextensions-0.5.1-py2.py3-none-any.whl", hash = "sha256:2c071f0aa208c569666f656bdc0f66906ca493cf9f06f46db6350db11030ff40"}, - {file = "jupyter_contrib_nbextensions-0.5.1.tar.gz", hash = "sha256:eecd28ecc2fc410226c0a3d4932ed2fac4860ccf8d9e9b1b29548835a35b22ab"}, -] -jupyter-core = [ - {file = "jupyter_core-4.6.3-py2.py3-none-any.whl", hash = "sha256:a4ee613c060fe5697d913416fc9d553599c05e4492d58fac1192c9a6844abb21"}, - {file = "jupyter_core-4.6.3.tar.gz", hash = "sha256:394fd5dd787e7c8861741880bdf8a00ce39f95de5d18e579c74b882522219e7e"}, -] -jupyter-highlight-selected-word = [ - {file = "jupyter_highlight_selected_word-0.2.0-py2.py3-none-any.whl", hash = "sha256:9545dfa9cb057eebe3a5795604dcd3a5294ea18637e553f61a0b67c1b5903c58"}, - {file = "jupyter_highlight_selected_word-0.2.0.tar.gz", hash = "sha256:9fa740424859a807950ca08d2bfd28a35154cd32dd6d50ac4e0950022adc0e7b"}, -] -jupyter-latex-envs = [ - {file = "jupyter_latex_envs-1.4.6.tar.gz", hash = "sha256:070a31eb2dc488bba983915879a7c2939247bf5c3b669b398bdb36a9b5343872"}, -] -jupyter-nbextensions-configurator = [ - {file = "jupyter_nbextensions_configurator-0.4.1.tar.gz", hash = "sha256:e5e86b5d9d898e1ffb30ebb08e4ad8696999f798fef3ff3262d7b999076e4e83"}, -] -jupyterlab = [ - {file = "jupyterlab-2.2.9-py3-none-any.whl", hash = "sha256:59af02c26a15ec2d2862a15bc72e41ae304b406a0b0d3f4f705eeb7caf91902b"}, - {file = "jupyterlab-2.2.9.tar.gz", hash = "sha256:3be8f8edea173753dd838c1b6d3bbcb6f5c801121f824a477025c1b6a1d33dc6"}, -] -jupyterlab-pygments = [ - {file = "jupyterlab_pygments-0.1.2-py2.py3-none-any.whl", hash = "sha256:abfb880fd1561987efaefcb2d2ac75145d2a5d0139b1876d5be806e32f630008"}, - {file = "jupyterlab_pygments-0.1.2.tar.gz", hash = "sha256:cfcda0873626150932f438eccf0f8bf22bfa92345b814890ab360d666b254146"}, -] -jupyterlab-server = [ - {file = "jupyterlab_server-1.2.0-py3-none-any.whl", hash = "sha256:55d256077bf13e5bc9e8fbd5aac51bef82f6315111cec6b712b9a5ededbba924"}, - {file = "jupyterlab_server-1.2.0.tar.gz", hash = "sha256:5431d9dde96659364b7cc877693d5d21e7b80cea7ae3959ecc2b87518e5f5d8c"}, -] -lxml = [ - {file = "lxml-4.6.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:4b7572145054330c8e324a72d808c8c8fbe12be33368db28c39a255ad5f7fb51"}, - {file = "lxml-4.6.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:302160eb6e9764168e01d8c9ec6becddeb87776e81d3fcb0d97954dd51d48e0a"}, - {file = "lxml-4.6.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:d4ad7fd3269281cb471ad6c7bafca372e69789540d16e3755dd717e9e5c9d82f"}, - {file = "lxml-4.6.1-cp27-cp27m-win32.whl", hash = "sha256:189ad47203e846a7a4951c17694d845b6ade7917c47c64b29b86526eefc3adf5"}, - {file = "lxml-4.6.1-cp27-cp27m-win_amd64.whl", hash = "sha256:56eff8c6fb7bc4bcca395fdff494c52712b7a57486e4fbde34c31bb9da4c6cc4"}, - {file = "lxml-4.6.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:23c83112b4dada0b75789d73f949dbb4e8f29a0a3511647024a398ebd023347b"}, - {file = "lxml-4.6.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:0e89f5d422988c65e6936e4ec0fe54d6f73f3128c80eb7ecc3b87f595523607b"}, - {file = "lxml-4.6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2358809cc64394617f2719147a58ae26dac9e21bae772b45cfb80baa26bfca5d"}, - {file = "lxml-4.6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:be1ebf9cc25ab5399501c9046a7dcdaa9e911802ed0e12b7d620cd4bbf0518b3"}, - {file = "lxml-4.6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:4fff34721b628cce9eb4538cf9a73d02e0f3da4f35a515773cce6f5fe413b360"}, - {file = "lxml-4.6.1-cp35-cp35m-win32.whl", hash = "sha256:475325e037fdf068e0c2140b818518cf6bc4aa72435c407a798b2db9f8e90810"}, - {file = "lxml-4.6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:f98b6f256be6cec8dd308a8563976ddaff0bdc18b730720f6f4bee927ffe926f"}, - {file = "lxml-4.6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:be7c65e34d1b50ab7093b90427cbc488260e4b3a38ef2435d65b62e9fa3d798a"}, - {file = "lxml-4.6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:d18331ea905a41ae71596502bd4c9a2998902328bbabd29e3d0f5f8569fabad1"}, - {file = "lxml-4.6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3d9b2b72eb0dbbdb0e276403873ecfae870599c83ba22cadff2db58541e72856"}, - {file = "lxml-4.6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d20d32cbb31d731def4b1502294ca2ee99f9249b63bc80e03e67e8f8e126dea8"}, - {file = "lxml-4.6.1-cp36-cp36m-win32.whl", hash = "sha256:d182eada8ea0de61a45a526aa0ae4bcd222f9673424e65315c35820291ff299c"}, - {file = "lxml-4.6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:c0dac835c1a22621ffa5e5f999d57359c790c52bbd1c687fe514ae6924f65ef5"}, - {file = "lxml-4.6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d84d741c6e35c9f3e7406cb7c4c2e08474c2a6441d59322a00dcae65aac6315d"}, - {file = "lxml-4.6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:8862d1c2c020cb7a03b421a9a7b4fe046a208db30994fc8ff68c627a7915987f"}, - {file = "lxml-4.6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3a7a380bfecc551cfd67d6e8ad9faa91289173bdf12e9cfafbd2bdec0d7b1ec1"}, - {file = "lxml-4.6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:2d6571c48328be4304aee031d2d5046cbc8aed5740c654575613c5a4f5a11311"}, - {file = "lxml-4.6.1-cp37-cp37m-win32.whl", hash = "sha256:803a80d72d1f693aa448566be46ffd70882d1ad8fc689a2e22afe63035eb998a"}, - {file = "lxml-4.6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:24e811118aab6abe3ce23ff0d7d38932329c513f9cef849d3ee88b0f848f2aa9"}, - {file = "lxml-4.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2e311a10f3e85250910a615fe194839a04a0f6bc4e8e5bb5cac221344e3a7891"}, - {file = "lxml-4.6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a71400b90b3599eb7bf241f947932e18a066907bf84617d80817998cee81e4bf"}, - {file = "lxml-4.6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:211b3bcf5da70c2d4b84d09232534ad1d78320762e2c59dedc73bf01cb1fc45b"}, - {file = "lxml-4.6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e65c221b2115a91035b55a593b6eb94aa1206fa3ab374f47c6dc10d364583ff9"}, - {file = "lxml-4.6.1-cp38-cp38-win32.whl", hash = "sha256:d6f8c23f65a4bfe4300b85f1f40f6c32569822d08901db3b6454ab785d9117cc"}, - {file = "lxml-4.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:573b2f5496c7e9f4985de70b9bbb4719ffd293d5565513e04ac20e42e6e5583f"}, - {file = "lxml-4.6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:1d87936cb5801c557f3e981c9c193861264c01209cb3ad0964a16310ca1b3301"}, - {file = "lxml-4.6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:2d5896ddf5389560257bbe89317ca7bcb4e54a02b53a3e572e1ce4226512b51b"}, - {file = "lxml-4.6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9b06690224258db5cd39a84e993882a6874676f5de582da57f3df3a82ead9174"}, - {file = "lxml-4.6.1-cp39-cp39-win32.whl", hash = "sha256:bb252f802f91f59767dcc559744e91efa9df532240a502befd874b54571417bd"}, - {file = "lxml-4.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ecaef52fd9b9535ae5f01a1dd2651f6608e4ec9dc136fc4dfe7ebe3c3ddb230"}, - {file = "lxml-4.6.1.tar.gz", hash = "sha256:c152b2e93b639d1f36ec5a8ca24cde4a8eefb2b6b83668fcd8e83a67badcb367"}, -] -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-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, - {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, - {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"}, -] -nbclient = [ - {file = "nbclient-0.5.1-py3-none-any.whl", hash = "sha256:4d6b116187c795c99b9dba13d46e764d596574b14c296d60670c8dfe454db364"}, - {file = "nbclient-0.5.1.tar.gz", hash = "sha256:01e2d726d16eaf2cde6db74a87e2451453547e8832d142f73f72fddcd4fe0250"}, -] -nbconvert = [ - {file = "nbconvert-6.0.7-py3-none-any.whl", hash = "sha256:39e9f977920b203baea0be67eea59f7b37a761caa542abe80f5897ce3cf6311d"}, - {file = "nbconvert-6.0.7.tar.gz", hash = "sha256:cbbc13a86dfbd4d1b5dee106539de0795b4db156c894c2c5dc382062bbc29002"}, -] -nbformat = [ - {file = "nbformat-5.0.8-py3-none-any.whl", hash = "sha256:aa9450c16d29286dc69b92ea4913c1bffe86488f90184445996ccc03a2f60382"}, - {file = "nbformat-5.0.8.tar.gz", hash = "sha256:f545b22138865bfbcc6b1ffe89ed5a2b8e2dc5d4fe876f2ca60d8e6f702a30f8"}, -] -nest-asyncio = [ - {file = "nest_asyncio-1.4.2-py3-none-any.whl", hash = "sha256:c2d3bdc76ba235a7ad215128afe31d74a320d25790c50cd94685ec5ea221b94d"}, - {file = "nest_asyncio-1.4.2.tar.gz", hash = "sha256:c614fcfaca72b1f04778bc0e73f49c84500b3d045c49d149fc46f1566643c175"}, -] -nodeenv = [ - {file = "nodeenv-1.5.0-py2.py3-none-any.whl", hash = "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9"}, - {file = "nodeenv-1.5.0.tar.gz", hash = "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c"}, -] -notebook = [ - {file = "notebook-6.1.4-py3-none-any.whl", hash = "sha256:07b6e8b8a61aa2f780fe9a97430470485bc71262bc5cae8521f1441b910d2c88"}, - {file = "notebook-6.1.4.tar.gz", hash = "sha256:687d01f963ea20360c0b904ee7a37c3d8cda553858c8d6e33fd0afd13e89de32"}, -] -nox = [ - {file = "nox-2020.8.22-py3-none-any.whl", hash = "sha256:55f8cab16bcfaaea08b141c83bf2b7c779e943518d0de6cd9c38cd8da95d11ea"}, - {file = "nox-2020.8.22.tar.gz", hash = "sha256:efa5adcf1134012f96bcd0a496ccebd4c9e9da53a831888a2a779462440eebcf"}, -] -numpy = [ - {file = "numpy-1.19.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b594f76771bc7fc8a044c5ba303427ee67c17a09b36e1fa32bde82f5c419d17a"}, - {file = "numpy-1.19.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:e6ddbdc5113628f15de7e4911c02aed74a4ccff531842c583e5032f6e5a179bd"}, - {file = "numpy-1.19.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3733640466733441295b0d6d3dcbf8e1ffa7e897d4d82903169529fd3386919a"}, - {file = "numpy-1.19.2-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:4339741994c775396e1a274dba3609c69ab0f16056c1077f18979bec2a2c2e6e"}, - {file = "numpy-1.19.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7c6646314291d8f5ea900a7ea9c4261f834b5b62159ba2abe3836f4fa6705526"}, - {file = "numpy-1.19.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:7118f0a9f2f617f921ec7d278d981244ba83c85eea197be7c5a4f84af80a9c3c"}, - {file = "numpy-1.19.2-cp36-cp36m-win32.whl", hash = "sha256:9a3001248b9231ed73894c773142658bab914645261275f675d86c290c37f66d"}, - {file = "numpy-1.19.2-cp36-cp36m-win_amd64.whl", hash = "sha256:967c92435f0b3ba37a4257c48b8715b76741410467e2bdb1097e8391fccfae15"}, - {file = "numpy-1.19.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d526fa58ae4aead839161535d59ea9565863bb0b0bdb3cc63214613fb16aced4"}, - {file = "numpy-1.19.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:eb25c381d168daf351147713f49c626030dcff7a393d5caa62515d415a6071d8"}, - {file = "numpy-1.19.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:62139af94728d22350a571b7c82795b9d59be77fc162414ada6c8b6a10ef5d02"}, - {file = "numpy-1.19.2-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:0c66da1d202c52051625e55a249da35b31f65a81cb56e4c69af0dfb8fb0125bf"}, - {file = "numpy-1.19.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:2117536e968abb7357d34d754e3733b0d7113d4c9f1d921f21a3d96dec5ff716"}, - {file = "numpy-1.19.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:54045b198aebf41bf6bf4088012777c1d11703bf74461d70cd350c0af2182e45"}, - {file = "numpy-1.19.2-cp37-cp37m-win32.whl", hash = "sha256:aba1d5daf1144b956bc87ffb87966791f5e9f3e1f6fab3d7f581db1f5b598f7a"}, - {file = "numpy-1.19.2-cp37-cp37m-win_amd64.whl", hash = "sha256:addaa551b298052c16885fc70408d3848d4e2e7352de4e7a1e13e691abc734c1"}, - {file = "numpy-1.19.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:58d66a6b3b55178a1f8a5fe98df26ace76260a70de694d99577ddeab7eaa9a9d"}, - {file = "numpy-1.19.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:59f3d687faea7a4f7f93bd9665e5b102f32f3fa28514f15b126f099b7997203d"}, - {file = "numpy-1.19.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:cebd4f4e64cfe87f2039e4725781f6326a61f095bc77b3716502bed812b385a9"}, - {file = "numpy-1.19.2-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:c35a01777f81e7333bcf276b605f39c872e28295441c265cd0c860f4b40148c1"}, - {file = "numpy-1.19.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d7ac33585e1f09e7345aa902c281bd777fdb792432d27fca857f39b70e5dd31c"}, - {file = "numpy-1.19.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:04c7d4ebc5ff93d9822075ddb1751ff392a4375e5885299445fcebf877f179d5"}, - {file = "numpy-1.19.2-cp38-cp38-win32.whl", hash = "sha256:51ee93e1fac3fe08ef54ff1c7f329db64d8a9c5557e6c8e908be9497ac76374b"}, - {file = "numpy-1.19.2-cp38-cp38-win_amd64.whl", hash = "sha256:1669ec8e42f169ff715a904c9b2105b6640f3f2a4c4c2cb4920ae8b2785dac65"}, - {file = "numpy-1.19.2-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:0bfd85053d1e9f60234f28f63d4a5147ada7f432943c113a11afcf3e65d9d4c8"}, - {file = "numpy-1.19.2.zip", hash = "sha256:0d310730e1e793527065ad7dde736197b705d0e4c9999775f212b03c44a8484c"}, -] -packaging = [ - {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, - {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, -] -pandocfilters = [ - {file = "pandocfilters-1.4.3.tar.gz", hash = "sha256:bc63fbb50534b4b1f8ebe1860889289e8af94a23bff7445259592df25a3906eb"}, -] -parso = [ - {file = "parso-0.7.1-py2.py3-none-any.whl", hash = "sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea"}, - {file = "parso-0.7.1.tar.gz", hash = "sha256:caba44724b994a8a5e086460bb212abc5a8bc46951bf4a9a1210745953622eb9"}, -] -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"}, -] -pre-commit = [ - {file = "pre_commit-2.7.1-py2.py3-none-any.whl", hash = "sha256:810aef2a2ba4f31eed1941fc270e72696a1ad5590b9751839c90807d0fff6b9a"}, - {file = "pre_commit-2.7.1.tar.gz", hash = "sha256:c54fd3e574565fe128ecc5e7d2f91279772ddb03f8729645fa812fe809084a70"}, -] -prometheus-client = [ - {file = "prometheus_client-0.8.0-py2.py3-none-any.whl", hash = "sha256:983c7ac4b47478720db338f1491ef67a100b474e3bc7dafcbaefb7d0b8f9b01c"}, - {file = "prometheus_client-0.8.0.tar.gz", hash = "sha256:c6e6b706833a6bd1fd51711299edee907857be10ece535126a158f911ee80915"}, -] -prompt-toolkit = [ - {file = "prompt_toolkit-3.0.8-py3-none-any.whl", hash = "sha256:7debb9a521e0b1ee7d2fe96ee4bd60ef03c6492784de0547337ca4433e46aa63"}, - {file = "prompt_toolkit-3.0.8.tar.gz", hash = "sha256:25c95d2ac813909f813c93fde734b6e44406d1477a9faef7c915ff37d39c0a8c"}, -] -ptyprocess = [ - {file = "ptyprocess-0.6.0-py2.py3-none-any.whl", hash = "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"}, - {file = "ptyprocess-0.6.0.tar.gz", hash = "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0"}, -] -py = [ - {file = "py-1.9.0-py2.py3-none-any.whl", hash = "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2"}, - {file = "py-1.9.0.tar.gz", hash = "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"}, -] -pycparser = [ - {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, - {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, -] -pygments = [ - {file = "Pygments-2.7.2-py3-none-any.whl", hash = "sha256:88a0bbcd659fcb9573703957c6b9cff9fab7295e6e76db54c9d00ae42df32773"}, - {file = "Pygments-2.7.2.tar.gz", hash = "sha256:381985fcc551eb9d37c52088a32914e00517e57f4a21609f48141ba08e193fa0"}, -] -pyparsing = [ - {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, - {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, -] -pyrsistent = [ - {file = "pyrsistent-0.17.3.tar.gz", hash = "sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e"}, -] -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-228-cp27-cp27m-win32.whl", hash = "sha256:37dc9935f6a383cc744315ae0c2882ba1768d9b06700a70f35dc1ce73cd4ba9c"}, - {file = "pywin32-228-cp27-cp27m-win_amd64.whl", hash = "sha256:11cb6610efc2f078c9e6d8f5d0f957620c333f4b23466931a247fb945ed35e89"}, - {file = "pywin32-228-cp35-cp35m-win32.whl", hash = "sha256:1f45db18af5d36195447b2cffacd182fe2d296849ba0aecdab24d3852fbf3f80"}, - {file = "pywin32-228-cp35-cp35m-win_amd64.whl", hash = "sha256:6e38c44097a834a4707c1b63efa9c2435f5a42afabff634a17f563bc478dfcc8"}, - {file = "pywin32-228-cp36-cp36m-win32.whl", hash = "sha256:ec16d44b49b5f34e99eb97cf270806fdc560dff6f84d281eb2fcb89a014a56a9"}, - {file = "pywin32-228-cp36-cp36m-win_amd64.whl", hash = "sha256:a60d795c6590a5b6baeacd16c583d91cce8038f959bd80c53bd9a68f40130f2d"}, - {file = "pywin32-228-cp37-cp37m-win32.whl", hash = "sha256:af40887b6fc200eafe4d7742c48417529a8702dcc1a60bf89eee152d1d11209f"}, - {file = "pywin32-228-cp37-cp37m-win_amd64.whl", hash = "sha256:00eaf43dbd05ba6a9b0080c77e161e0b7a601f9a3f660727a952e40140537de7"}, - {file = "pywin32-228-cp38-cp38-win32.whl", hash = "sha256:fa6ba028909cfc64ce9e24bcf22f588b14871980d9787f1e2002c99af8f1850c"}, - {file = "pywin32-228-cp38-cp38-win_amd64.whl", hash = "sha256:9b3466083f8271e1a5eb0329f4e0d61925d46b40b195a33413e0905dccb285e8"}, - {file = "pywin32-228-cp39-cp39-win32.whl", hash = "sha256:ed74b72d8059a6606f64842e7917aeee99159ebd6b8d6261c518d002837be298"}, - {file = "pywin32-228-cp39-cp39-win_amd64.whl", hash = "sha256:8319bafdcd90b7202c50d6014efdfe4fde9311b3ff15fd6f893a45c0868de203"}, -] -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"}, -] -pyyaml = [ - {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, - {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"}, - {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"}, - {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"}, - {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"}, - {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"}, - {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"}, - {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, - {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, - {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, - {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, -] -pyzmq = [ - {file = "pyzmq-19.0.2-cp27-cp27m-macosx_10_9_intel.whl", hash = "sha256:59f1e54627483dcf61c663941d94c4af9bf4163aec334171686cdaee67974fe5"}, - {file = "pyzmq-19.0.2-cp27-cp27m-win32.whl", hash = "sha256:c36ffe1e5aa35a1af6a96640d723d0d211c5f48841735c2aa8d034204e87eb87"}, - {file = "pyzmq-19.0.2-cp27-cp27m-win_amd64.whl", hash = "sha256:0a422fc290d03958899743db091f8154958410fc76ce7ee0ceb66150f72c2c97"}, - {file = "pyzmq-19.0.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:c20dd60b9428f532bc59f2ef6d3b1029a28fc790d408af82f871a7db03e722ff"}, - {file = "pyzmq-19.0.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d46fb17f5693244de83e434648b3dbb4f4b0fec88415d6cbab1c1452b6f2ae17"}, - {file = "pyzmq-19.0.2-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:f1a25a61495b6f7bb986accc5b597a3541d9bd3ef0016f50be16dbb32025b302"}, - {file = "pyzmq-19.0.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:ab0d01148d13854de716786ca73701012e07dff4dfbbd68c4e06d8888743526e"}, - {file = "pyzmq-19.0.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:720d2b6083498a9281eaee3f2927486e9fe02cd16d13a844f2e95217f243efea"}, - {file = "pyzmq-19.0.2-cp35-cp35m-win32.whl", hash = "sha256:29d51279060d0a70f551663bc592418bcad7f4be4eea7b324f6dd81de05cb4c1"}, - {file = "pyzmq-19.0.2-cp35-cp35m-win_amd64.whl", hash = "sha256:5120c64646e75f6db20cc16b9a94203926ead5d633de9feba4f137004241221d"}, - {file = "pyzmq-19.0.2-cp36-cp36m-macosx_10_9_intel.whl", hash = "sha256:8a6ada5a3f719bf46a04ba38595073df8d6b067316c011180102ba2a1925f5b5"}, - {file = "pyzmq-19.0.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:fa411b1d8f371d3a49d31b0789eb6da2537dadbb2aef74a43aa99a78195c3f76"}, - {file = "pyzmq-19.0.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:00dca814469436455399660247d74045172955459c0bd49b54a540ce4d652185"}, - {file = "pyzmq-19.0.2-cp36-cp36m-win32.whl", hash = "sha256:046b92e860914e39612e84fa760fc3f16054d268c11e0e25dcb011fb1bc6a075"}, - {file = "pyzmq-19.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99cc0e339a731c6a34109e5c4072aaa06d8e32c0b93dc2c2d90345dd45fa196c"}, - {file = "pyzmq-19.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e36f12f503511d72d9bdfae11cadbadca22ff632ff67c1b5459f69756a029c19"}, - {file = "pyzmq-19.0.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c40fbb2b9933369e994b837ee72193d6a4c35dfb9a7c573257ef7ff28961272c"}, - {file = "pyzmq-19.0.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5d9fc809aa8d636e757e4ced2302569d6e60e9b9c26114a83f0d9d6519c40493"}, - {file = "pyzmq-19.0.2-cp37-cp37m-win32.whl", hash = "sha256:3fa6debf4bf9412e59353defad1f8035a1e68b66095a94ead8f7a61ae90b2675"}, - {file = "pyzmq-19.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:73483a2caaa0264ac717af33d6fb3f143d8379e60a422730ee8d010526ce1913"}, - {file = "pyzmq-19.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:36ab114021c0cab1a423fe6689355e8f813979f2c750968833b318c1fa10a0fd"}, - {file = "pyzmq-19.0.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:8b66b94fe6243d2d1d89bca336b2424399aac57932858b9a30309803ffc28112"}, - {file = "pyzmq-19.0.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:654d3e06a4edc566b416c10293064732516cf8871a4522e0a2ba00cc2a2e600c"}, - {file = "pyzmq-19.0.2-cp38-cp38-win32.whl", hash = "sha256:276ad604bffd70992a386a84bea34883e696a6b22e7378053e5d3227321d9702"}, - {file = "pyzmq-19.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:09d24a80ccb8cbda1af6ed8eb26b005b6743e58e9290566d2a6841f4e31fa8e0"}, - {file = "pyzmq-19.0.2-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:c1a31cd42905b405530e92bdb70a8a56f048c8a371728b8acf9d746ecd4482c0"}, - {file = "pyzmq-19.0.2-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a7e7f930039ee0c4c26e4dfee015f20bd6919cd8b97c9cd7afbde2923a5167b6"}, - {file = "pyzmq-19.0.2.tar.gz", hash = "sha256:296540a065c8c21b26d63e3cea2d1d57902373b16e4256afe46422691903a438"}, -] -requests = [ - {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"}, - {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"}, -] -rise = [ - {file = "rise-5.7.0-py2.py3-none-any.whl", hash = "sha256:b500c7c3f7b09c8194a66feeffd60ead6da0132d9fa18a2bb7352587778ef482"}, - {file = "rise-5.7.0.tar.gz", hash = "sha256:6c00721189e0b457ca40ab4eb0abef8edbba6c71bc04d7f04ad813a214ddea74"}, -] -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.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, - {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, -] -terminado = [ - {file = "terminado-0.9.1-py3-none-any.whl", hash = "sha256:c55f025beb06c2e2669f7ba5a04f47bb3304c30c05842d4981d8f0fc9ab3b4e3"}, - {file = "terminado-0.9.1.tar.gz", hash = "sha256:3da72a155b807b01c9e8a5babd214e052a0a45a975751da3521a1c3381ce6d76"}, -] -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.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"}, - {file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"}, -] -tornado = [ - {file = "tornado-6.0.4-cp35-cp35m-win32.whl", hash = "sha256:5217e601700f24e966ddab689f90b7ea4bd91ff3357c3600fa1045e26d68e55d"}, - {file = "tornado-6.0.4-cp35-cp35m-win_amd64.whl", hash = "sha256:c98232a3ac391f5faea6821b53db8db461157baa788f5d6222a193e9456e1740"}, - {file = "tornado-6.0.4-cp36-cp36m-win32.whl", hash = "sha256:5f6a07e62e799be5d2330e68d808c8ac41d4a259b9cea61da4101b83cb5dc673"}, - {file = "tornado-6.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:c952975c8ba74f546ae6de2e226ab3cc3cc11ae47baf607459a6728585bb542a"}, - {file = "tornado-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:2c027eb2a393d964b22b5c154d1a23a5f8727db6fda837118a776b29e2b8ebc6"}, - {file = "tornado-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:5618f72e947533832cbc3dec54e1dffc1747a5cb17d1fd91577ed14fa0dc081b"}, - {file = "tornado-6.0.4-cp38-cp38-win32.whl", hash = "sha256:22aed82c2ea340c3771e3babc5ef220272f6fd06b5108a53b4976d0d722bcd52"}, - {file = "tornado-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:c58d56003daf1b616336781b26d184023ea4af13ae143d9dda65e31e534940b9"}, - {file = "tornado-6.0.4.tar.gz", hash = "sha256:0fe2d45ba43b00a41cd73f8be321a44936dc1aba233dee979f17a042b83eb6dc"}, -] -traitlets = [ - {file = "traitlets-5.0.5-py3-none-any.whl", hash = "sha256:69ff3f9d5351f31a7ad80443c2674b7099df13cc41fc5fa6e2f6d3b0330b0426"}, - {file = "traitlets-5.0.5.tar.gz", hash = "sha256:178f4ce988f69189f7e523337a3e11d91c786ded9360174a3d9ca83e79bc5396"}, -] -urllib3 = [ - {file = "urllib3-1.25.11-py2.py3-none-any.whl", hash = "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e"}, - {file = "urllib3-1.25.11.tar.gz", hash = "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2"}, -] -virtualenv = [ - {file = "virtualenv-20.1.0-py2.py3-none-any.whl", hash = "sha256:b0011228208944ce71052987437d3843e05690b2f23d1c7da4263fde104c97a2"}, - {file = "virtualenv-20.1.0.tar.gz", hash = "sha256:b8d6110f493af256a40d65e29846c69340a947669eec8ce784fcf3dd3af28380"}, -] -wcwidth = [ - {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, - {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, -] -webencodings = [ +files = [ {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, ] -xdoctest = [ - {file = "xdoctest-0.15.0-py2.py3-none-any.whl", hash = "sha256:695ea04303a48cbb319709270d43f7bae7f3de3701aec73f09d90a216499992e"}, - {file = "xdoctest-0.15.0.tar.gz", hash = "sha256:7f0a184d403b69b166ebec1aadb13c98c96c59101e974ae2e4db4c3a803ec371"}, + +[[package]] +name = "websocket-client" +version = "1.7.0" +description = "WebSocket client for Python with low level API options" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websocket-client-1.7.0.tar.gz", hash = "sha256:10e511ea3a8c744631d3bd77e61eb17ed09304c413ad42cf6ddfa4c7787e8fe6"}, + {file = "websocket_client-1.7.0-py3-none-any.whl", hash = "sha256:f4c3d22fec12a2461427a29957ff07d35098ee2d976d3ba244e688b8b4057588"}, ] + +[package.extras] +docs = ["Sphinx (>=6.0)", "sphinx-rtd-theme (>=1.1.0)"] +optional = ["python-socks", "wsaccel"] +test = ["websockets"] + +[[package]] +name = "xdoctest" +version = "1.1.3" +description = "A rewrite of the builtin doctest module" +optional = false +python-versions = ">=3.6" +files = [ + {file = "xdoctest-1.1.3-py3-none-any.whl", hash = "sha256:9360535bd1a971ffc216d9613898cedceb81d0fd024587cc3c03c74d14c00a31"}, + {file = "xdoctest-1.1.3.tar.gz", hash = "sha256:84e76a42a11a5926ff66d9d84c616bc101821099672550481ad96549cbdd02ae"}, +] + +[package.extras] +all = ["IPython (>=7.10.0)", "IPython (>=7.23.1)", "Pygments (>=2.0.0)", "Pygments (>=2.4.1)", "attrs (>=19.2.0)", "colorama (>=0.4.1)", "debugpy (>=1.0.0)", "debugpy (>=1.0.0)", "debugpy (>=1.0.0)", "debugpy (>=1.3.0)", "debugpy (>=1.6.0)", "ipykernel (>=5.2.0)", "ipykernel (>=6.0.0)", "ipykernel (>=6.11.0)", "ipython-genutils (>=0.2.0)", "jedi (>=0.16)", "jinja2 (>=3.0.0)", "jupyter-client (>=6.1.5)", "jupyter-client (>=7.0.0)", "jupyter-core (>=4.7.0)", "nbconvert (>=6.0.0)", "nbconvert (>=6.1.0)", "pyflakes (>=2.2.0)", "pytest (>=4.6.0)", "pytest (>=4.6.0)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)", "tomli (>=0.2.0)", "typing (>=3.7.4)"] +all-strict = ["IPython (==7.10.0)", "IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "colorama (==0.4.1)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==5.2.0)", "ipykernel (==6.0.0)", "ipykernel (==6.11.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==6.1.5)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "nbconvert (==6.1.0)", "pyflakes (==2.2.0)", "pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==3.0.0)", "tomli (==0.2.0)", "typing (==3.7.4)"] +colors = ["Pygments", "Pygments", "colorama"] +jupyter = ["IPython", "IPython", "attrs", "debugpy", "debugpy", "debugpy", "debugpy", "debugpy", "ipykernel", "ipykernel", "ipykernel", "ipython-genutils", "jedi", "jinja2", "jupyter-client", "jupyter-client", "jupyter-core", "nbconvert", "nbconvert"] +optional = ["IPython (>=7.10.0)", "IPython (>=7.23.1)", "Pygments (>=2.0.0)", "Pygments (>=2.4.1)", "attrs (>=19.2.0)", "colorama (>=0.4.1)", "debugpy (>=1.0.0)", "debugpy (>=1.0.0)", "debugpy (>=1.0.0)", "debugpy (>=1.3.0)", "debugpy (>=1.6.0)", "ipykernel (>=5.2.0)", "ipykernel (>=6.0.0)", "ipykernel (>=6.11.0)", "ipython-genutils (>=0.2.0)", "jedi (>=0.16)", "jinja2 (>=3.0.0)", "jupyter-client (>=6.1.5)", "jupyter-client (>=7.0.0)", "jupyter-core (>=4.7.0)", "nbconvert (>=6.0.0)", "nbconvert (>=6.1.0)", "pyflakes (>=2.2.0)", "tomli (>=0.2.0)"] +optional-strict = ["IPython (==7.10.0)", "IPython (==7.23.1)", "Pygments (==2.0.0)", "Pygments (==2.4.1)", "attrs (==19.2.0)", "colorama (==0.4.1)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.0.0)", "debugpy (==1.3.0)", "debugpy (==1.6.0)", "ipykernel (==5.2.0)", "ipykernel (==6.0.0)", "ipykernel (==6.11.0)", "ipython-genutils (==0.2.0)", "jedi (==0.16)", "jinja2 (==3.0.0)", "jupyter-client (==6.1.5)", "jupyter-client (==7.0.0)", "jupyter-core (==4.7.0)", "nbconvert (==6.0.0)", "nbconvert (==6.1.0)", "pyflakes (==2.2.0)", "tomli (==0.2.0)"] +tests = ["pytest (>=4.6.0)", "pytest (>=4.6.0)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)", "typing (>=3.7.4)"] +tests-binary = ["cmake", "cmake", "ninja", "ninja", "pybind11", "pybind11", "scikit-build", "scikit-build"] +tests-binary-strict = ["cmake (==3.21.2)", "cmake (==3.25.0)", "ninja (==1.10.2)", "ninja (==1.11.1)", "pybind11 (==2.10.3)", "pybind11 (==2.7.1)", "scikit-build (==0.11.1)", "scikit-build (==0.16.1)"] +tests-strict = ["pytest (==4.6.0)", "pytest (==4.6.0)", "pytest (==6.2.5)", "pytest-cov (==3.0.0)", "typing (==3.7.4)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.11" +content-hash = "c6c80c1e03b497a4533d3c622b77740b172fa4818921210d88759b224a3654b4" diff --git a/pyproject.toml b/pyproject.toml index aa92bb6..4491fee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,29 +1,26 @@ [build-system] -requires = ["poetry>=0.12"] -build-backend = "poetry.masonry.api" +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" [tool.poetry] name = "intro-to-python" version = "0.1.0.dev0" +package-mode = false authors = ["Alexander Hess "] description = "An intro to Python & programming for wanna-be data scientists" license = "MIT" [tool.poetry.dependencies] -python = "^3.8" +python = "^3.11" -jupyterlab = "^2.2.8" -numpy = "^1.19.2" +jupyterlab = "^4.1.0" +numpy = "^1.26.0" [tool.poetry.dev-dependencies] # Task runners -nox = "^2020.8.22" -pre-commit = "^2.7.1" +nox = "^2024.3.2" +pre-commit = "^3.7.0" # Testing -xdoctest = "^0.15.0" - -# Live coding during presentation mode -jupyter-contrib-nbextensions = "^0.5.1" -rise = "^5.6.1" +xdoctest = "^1.1.0" From d6c8a5b8ac927a33ac1f9412a2ecddd1b845e793 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 8 Apr 2024 15:54:29 +0200 Subject: [PATCH 125/142] Re-run Chapter 00 files with Python 3.12 --- 00_intro/00_content.ipynb | 4 ++-- 00_intro/01_exercises.ipynb | 2 +- 00_intro/02_review.ipynb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/00_intro/00_content.ipynb b/00_intro/00_content.ipynb index 2bc24f2..8409944 100644 --- a/00_intro/00_content.ipynb +++ b/00_intro/00_content.ipynb @@ -291,7 +291,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Python 3.8.6\n" + "Python 3.12.2\n" ] } ], @@ -841,7 +841,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/00_intro/01_exercises.ipynb b/00_intro/01_exercises.ipynb index c5082eb..272d5f9 100644 --- a/00_intro/01_exercises.ipynb +++ b/00_intro/01_exercises.ipynb @@ -94,7 +94,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/00_intro/02_review.ipynb b/00_intro/02_review.ipynb index 9a8d7c2..0fa77c5 100644 --- a/00_intro/02_review.ipynb +++ b/00_intro/02_review.ipynb @@ -194,7 +194,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, From 8da18b86773b2aebd908df9084fbd79ca2d5a6a1 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 8 Apr 2024 16:03:12 +0200 Subject: [PATCH 126/142] Re-run Chapter 01 files with Python 3.12 --- 01_elements/00_content.ipynb | 16 ++++++++-------- 01_elements/01_exercises.ipynb | 2 +- 01_elements/02_exercises.ipynb | 2 +- 01_elements/03_content.ipynb | 20 ++++++++++++++------ 01_elements/04_exercises.ipynb | 2 +- 01_elements/05_summary.ipynb | 2 +- 01_elements/06_review.ipynb | 2 +- 01_elements/07_resources.ipynb | 2 +- 8 files changed, 28 insertions(+), 20 deletions(-) diff --git a/01_elements/00_content.ipynb b/01_elements/00_content.ipynb index 4d6c792..3335486 100644 --- a/01_elements/00_content.ipynb +++ b/01_elements/00_content.ipynb @@ -1654,10 +1654,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "invalid syntax (73631267.py, 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 Cell \u001b[0;32mIn[47], line 1\u001b[0;36m\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" ] } ], @@ -1687,10 +1687,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "expected ':' (2545637715.py, 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 Cell \u001b[0;32mIn[48], line 1\u001b[0;36m\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 expected ':'\n" ] } ], @@ -1721,10 +1721,10 @@ "outputs": [ { "ename": "IndentationError", - "evalue": "expected an indented block (, line 2)", + "evalue": "expected an indented block after 'for' statement on line 1 (1544118965.py, 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 Cell \u001b[0;32mIn[49], line 2\u001b[0;36m\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 after 'for' statement on line 1\n" ] } ], @@ -1775,7 +1775,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", + "Cell \u001b[0;32mIn[50], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;241;43m1\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m/\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\n", "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" ] } @@ -2156,7 +2156,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/01_elements/01_exercises.ipynb b/01_elements/01_exercises.ipynb index d073a26..592d48a 100644 --- a/01_elements/01_exercises.ipynb +++ b/01_elements/01_exercises.ipynb @@ -151,7 +151,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/01_elements/02_exercises.ipynb b/01_elements/02_exercises.ipynb index 5ae269c..2ff9c45 100644 --- a/01_elements/02_exercises.ipynb +++ b/01_elements/02_exercises.ipynb @@ -197,7 +197,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/01_elements/03_content.ipynb b/01_elements/03_content.ipynb index 74c3477..e7886f8 100644 --- a/01_elements/03_content.ipynb +++ b/01_elements/03_content.ipynb @@ -350,7 +350,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[0mvariable\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[13], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mvariable\u001b[49m\n", "\u001b[0;31mNameError\u001b[0m: name 'variable' is not defined" ] } @@ -419,6 +419,7 @@ "text/plain": [ "['In',\n", " 'Out',\n", + " 'Path',\n", " '_',\n", " '_10',\n", " '_11',\n", @@ -435,6 +436,7 @@ " '__loader__',\n", " '__name__',\n", " '__package__',\n", + " '__session__',\n", " '__spec__',\n", " '_dh',\n", " '_i',\n", @@ -457,11 +459,17 @@ " '_ii',\n", " '_iii',\n", " '_oh',\n", + " 'atexit',\n", " 'exit',\n", " 'get_ipython',\n", + " 'history',\n", + " 'history_path',\n", + " 'open',\n", + " 'os',\n", " 'quit',\n", - " 'register_readline_completion',\n", - " 'sys']" + " 'readline',\n", + " 'state_home',\n", + " 'write_history']" ] }, "execution_count": 15, @@ -636,10 +644,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "cannot assign to operator (, line 1)", + "evalue": "cannot assign to expression here. Maybe you meant '==' instead of '='? (202417111.py, 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 = \"WHU, Burgplatz 2, Vallendar\"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m cannot assign to operator\n" + "\u001b[0;36m Cell \u001b[0;32mIn[23], line 1\u001b[0;36m\u001b[0m\n\u001b[0;31m address@work = \"WHU, Burgplatz 2, Vallendar\"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m cannot assign to expression here. Maybe you meant '==' instead of '='?\n" ] } ], @@ -1685,7 +1693,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/01_elements/04_exercises.ipynb b/01_elements/04_exercises.ipynb index f1cca6d..3fa9301 100644 --- a/01_elements/04_exercises.ipynb +++ b/01_elements/04_exercises.ipynb @@ -191,7 +191,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/01_elements/05_summary.ipynb b/01_elements/05_summary.ipynb index c3b6339..f57bb09 100644 --- a/01_elements/05_summary.ipynb +++ b/01_elements/05_summary.ipynb @@ -110,7 +110,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/01_elements/06_review.ipynb b/01_elements/06_review.ipynb index d975603..33ff535 100644 --- a/01_elements/06_review.ipynb +++ b/01_elements/06_review.ipynb @@ -194,7 +194,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/01_elements/07_resources.ipynb b/01_elements/07_resources.ipynb index 9d46189..0ac2b87 100644 --- a/01_elements/07_resources.ipynb +++ b/01_elements/07_resources.ipynb @@ -83,7 +83,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", From 7eed0841f19761f05153f2b5a8d529d37092f241 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 8 Apr 2024 16:09:30 +0200 Subject: [PATCH 127/142] Re-run Chapter 02 files with Python 3.12 --- 02_functions/00_content.ipynb | 48 ++++++++++++++++----------------- 02_functions/01_exercises.ipynb | 2 +- 02_functions/02_content.ipynb | 36 +++++++++++++++++-------- 02_functions/03_summary.ipynb | 2 +- 02_functions/04_review.ipynb | 2 +- 5 files changed, 52 insertions(+), 38 deletions(-) diff --git a/02_functions/00_content.ipynb b/02_functions/00_content.ipynb index 964ecaa..cfc47d3 100644 --- a/02_functions/00_content.ipynb +++ b/02_functions/00_content.ipynb @@ -592,7 +592,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", + "Cell \u001b[0;32mIn[18], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;43mint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mseven\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mValueError\u001b[0m: invalid literal for int() with base 10: 'seven'" ] } @@ -1026,10 +1026,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "invalid syntax (2246690741.py, 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 invalid syntax\n" + "\u001b[0;36m Cell \u001b[0;32mIn[31], line 1\u001b[0;36m\u001b[0m\n\u001b[0;31m \u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], @@ -1102,10 +1102,10 @@ "\n", "average_evens(integers)\n", " Calculate the average of all even numbers in a list.\n", - " \n", + "\n", " Args:\n", " integers (list of int's): whole numbers to be averaged\n", - " \n", + "\n", " Returns:\n", " average (float)\n", "\n" @@ -1148,8 +1148,8 @@ "\n", "Returns:\n", " average (float)\n", - "\u001b[0;31mFile:\u001b[0m ~/repos/intro-to-python/02_functions/\n", - "\u001b[0;31mType:\u001b[0m function\n" + "\u001b[0;31mFile:\u001b[0m /tmp/ipykernel_152540/3598721284.py\n", + "\u001b[0;31mType:\u001b[0m function" ] }, "metadata": {}, @@ -1197,8 +1197,8 @@ "\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[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\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/02_functions/\n", - "\u001b[0;31mType:\u001b[0m function\n" + "\u001b[0;31mFile:\u001b[0m /tmp/ipykernel_152540/3598721284.py\n", + "\u001b[0;31mType:\u001b[0m function" ] }, "metadata": {}, @@ -1237,7 +1237,7 @@ "\n", "sum(iterable, /, start=0)\n", " Return the sum of a 'start' value (default: 0) plus an iterable of numbers\n", - " \n", + "\n", " When the iterable is empty, return the start value.\n", " This function is intended specifically for use with numeric values and may\n", " reject non-numeric types.\n", @@ -1416,7 +1416,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[0mintegers\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[41], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mintegers\u001b[49m\n", "\u001b[0;31mNameError\u001b[0m: name 'integers' is not defined" ] } @@ -1441,7 +1441,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", + "Cell \u001b[0;32mIn[42], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mevens\u001b[49m\n", "\u001b[0;31mNameError\u001b[0m: name 'evens' is not defined" ] } @@ -1466,7 +1466,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", + "Cell \u001b[0;32mIn[43], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43maverage\u001b[49m\n", "\u001b[0;31mNameError\u001b[0m: name 'average' is not defined" ] } @@ -1483,7 +1483,7 @@ } }, "source": [ - "[PythonTutor ](http://pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%5D%0A%0Adef%20average_evens%28integers%29%3A%0A%20%20%20%20evens%20%3D%20%5Bn%20for%20n%20in%20integers%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%0Aresult%20%3D%20average_evens%28numbers%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 `numbers` passed in as the `integers` 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 integers 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=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%5D%0A%0Adef%20average_evens%28integers%29%3A%0A%20%20%20%20evens%20%3D%20%5Bn%20for%20n%20in%20integers%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%0Aresult%20%3D%20average_evens%28numbers%29&cumulative=false&curstr=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 `numbers` passed in as the `integers` 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 integers 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)." ] }, { @@ -1645,7 +1645,7 @@ } }, "source": [ - "[PythonTutor ](http://pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%5D%0A%0Adef%20average_wrong%28integers%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%0Aresult%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 `numbers` in the global scope (i.e., that is the error), and one to make `evens` reference it (cf., steps 6-21).\n", + "[PythonTutor ](http://pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%5D%0A%0Adef%20average_wrong%28integers%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%0Aresult%20%3D%20average_wrong%28%5B123,%20456,%20789%5D%29&cumulative=false&curstr=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 `numbers` 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 with 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/develop/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." ] @@ -1786,7 +1786,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mAssertionError\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;32massert\u001b[0m \u001b[0maverage_evens\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m40.0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m41.1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m42.2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m43.3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m44.4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m87.0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[51], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m average_evens([\u001b[38;5;241m40.0\u001b[39m, \u001b[38;5;241m41.1\u001b[39m, \u001b[38;5;241m42.2\u001b[39m, \u001b[38;5;241m43.3\u001b[39m, \u001b[38;5;241m44.4\u001b[39m]) \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m87.0\u001b[39m\n", "\u001b[0;31mAssertionError\u001b[0m: " ] } @@ -1910,7 +1910,7 @@ "source": [ "The reason why everything works is that *every* time we (re-)assign an object to a variable *inside* a function's body 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.\n", "\n", - "[PythonTutor ](http://pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%5D%0A%0Adef%20average_evens%28integers%29%3A%0A%20%20%20%20numbers%20%3D%20%5Bround%28n%29%20for%20n%20in%20integers%5D%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%0Aresult%20%3D%20average_evens%28%5B40.0,%2041.1,%2042.2,%2043.3,%2044.4%5D%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how *two* `numbers` variables exist in *different* scopes referencing *different* objects (cf., steps 14-25) when we execute `average_evens([40.0, 41.1, 42.2, 43.3, 44.4])`.\n", + "[PythonTutor ](http://pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%5D%0A%0Adef%20average_evens%28integers%29%3A%0A%20%20%20%20numbers%20%3D%20%5Bround%28n%29%20for%20n%20in%20integers%5D%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%0Aresult%20%3D%20average_evens%28%5B40.0,%2041.1,%2042.2,%2043.3,%2044.4%5D%29&cumulative=false&curstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how *two* `numbers` variables exist in *different* scopes referencing *different* objects (cf., steps 14-25) when we execute `average_evens([40.0, 41.1, 42.2, 43.3, 44.4])`.\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", @@ -1999,7 +1999,7 @@ } }, "source": [ - "[PythonTutor ](http://pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%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%0Aresult%20%3D%20average_evens%28numbers%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) reveals that in this example there are *two* `numbers` variables in *different* scope referencing the *same* `list` object in memory (cf., steps 4-23)." + "[PythonTutor ](http://pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%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%0Aresult%20%3D%20average_evens%28numbers%29&cumulative=false&curstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) reveals that in this example there are *two* `numbers` variables in *different* scope referencing the *same* `list` object in memory (cf., steps 4-23)." ] }, { @@ -2274,10 +2274,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "positional argument follows keyword argument (, line 1)", + "evalue": "positional argument follows keyword argument (159253642.py, 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=numbers, 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 Cell \u001b[0;32mIn[65], line 1\u001b[0;36m\u001b[0m\n\u001b[0;31m scaled_average_evens(numbers=numbers, 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" ] } ], @@ -2312,7 +2312,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[0mscaled_average_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[0m", + "Cell \u001b[0;32mIn[66], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mscaled_average_evens\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnumbers\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mTypeError\u001b[0m: scaled_average_evens() missing 1 required positional argument: 'scalar'" ] } @@ -2337,7 +2337,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[0mscaled_average_evens\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnumbers\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\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", + "Cell \u001b[0;32mIn[67], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mscaled_average_evens\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnumbers\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m3\u001b[39;49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mTypeError\u001b[0m: scaled_average_evens() takes 2 positional arguments but 3 were given" ] } @@ -2782,7 +2782,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[0mnumbers\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", + "Cell \u001b[0;32mIn[81], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43maverage_evens\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnumbers\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mTypeError\u001b[0m: average_evens() takes 1 positional argument but 2 were given" ] } @@ -3032,7 +3032,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/02_functions/01_exercises.ipynb b/02_functions/01_exercises.ipynb index 795be04..e4173c4 100644 --- a/02_functions/01_exercises.ipynb +++ b/02_functions/01_exercises.ipynb @@ -220,7 +220,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/02_functions/02_content.ipynb b/02_functions/02_content.ipynb index 5162e9c..f0631a0 100644 --- a/02_functions/02_content.ipynb +++ b/02_functions/02_content.ipynb @@ -127,7 +127,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 2, @@ -227,6 +227,7 @@ " 'atan',\n", " 'atan2',\n", " 'atanh',\n", + " 'cbrt',\n", " 'ceil',\n", " 'comb',\n", " 'copysign',\n", @@ -238,6 +239,7 @@ " 'erf',\n", " 'erfc',\n", " 'exp',\n", + " 'exp2',\n", " 'expm1',\n", " 'fabs',\n", " 'factorial',\n", @@ -254,6 +256,7 @@ " 'isinf',\n", " 'isnan',\n", " 'isqrt',\n", + " 'lcm',\n", " 'ldexp',\n", " 'lgamma',\n", " 'log',\n", @@ -262,6 +265,7 @@ " 'log2',\n", " 'modf',\n", " 'nan',\n", + " 'nextafter',\n", " 'perm',\n", " 'pi',\n", " 'pow',\n", @@ -271,10 +275,12 @@ " 'sin',\n", " 'sinh',\n", " 'sqrt',\n", + " 'sumprod',\n", " 'tan',\n", " 'tanh',\n", " 'tau',\n", - " 'trunc']" + " 'trunc',\n", + " 'ulp']" ] }, "execution_count": 5, @@ -589,7 +595,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 16, @@ -632,8 +638,8 @@ " 'SG_MAGICCONST',\n", " 'SystemRandom',\n", " 'TWOPI',\n", + " '_ONE',\n", " '_Sequence',\n", - " '_Set',\n", " '__all__',\n", " '__builtins__',\n", " '__cached__',\n", @@ -650,8 +656,14 @@ " '_cos',\n", " '_e',\n", " '_exp',\n", + " '_fabs',\n", + " '_floor',\n", + " '_index',\n", " '_inst',\n", + " '_isfinite',\n", + " '_lgamma',\n", " '_log',\n", + " '_log2',\n", " '_os',\n", " '_pi',\n", " '_random',\n", @@ -664,6 +676,7 @@ " '_urandom',\n", " '_warn',\n", " 'betavariate',\n", + " 'binomialvariate',\n", " 'choice',\n", " 'choices',\n", " 'expovariate',\n", @@ -674,6 +687,7 @@ " 'lognormvariate',\n", " 'normalvariate',\n", " 'paretovariate',\n", + " 'randbytes',\n", " 'randint',\n", " 'random',\n", " 'randrange',\n", @@ -1031,7 +1045,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Requirement already satisfied: numpy in /home/webartifex/repos/intro-to-python/.venv/lib/python3.8/site-packages (1.19.2)\n" + "Requirement already satisfied: numpy in /home/alexander/Repositories/intro-to-python/.venv/lib64/python3.12/site-packages (1.26.4)\n" ] } ], @@ -1086,7 +1100,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 31, @@ -1338,7 +1352,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "/home/webartifex/repos/intro-to-python/02_functions\n" + "/home/alexander/Repositories/intro-to-python/02_functions\n" ] } ], @@ -1384,7 +1398,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 41, @@ -1499,12 +1513,12 @@ "\n", "average_evens(numbers, *, scalar=1)\n", " Calculate the average of all even numbers in a list.\n", - " \n", + "\n", " Args:\n", " numbers (list of int's/float's): numbers to be averaged;\n", " if non-whole numbers are provided, they are rounded\n", " scalar (float, optional): multiplies the average; defaults to 1\n", - " \n", + "\n", " Returns:\n", " scaled_average (float)\n", "\n" @@ -1593,7 +1607,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/02_functions/03_summary.ipynb b/02_functions/03_summary.ipynb index 2161f17..f5efdad 100644 --- a/02_functions/03_summary.ipynb +++ b/02_functions/03_summary.ipynb @@ -58,7 +58,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/02_functions/04_review.ipynb b/02_functions/04_review.ipynb index a85dade..7ebc8fd 100644 --- a/02_functions/04_review.ipynb +++ b/02_functions/04_review.ipynb @@ -208,7 +208,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, From c0b09454bdcd920e98d417c5df28d1e1b4449d06 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 8 Apr 2024 16:12:09 +0200 Subject: [PATCH 128/142] Re-run Chapter 03 files with Python 3.12 --- 03_conditionals/00_content.ipynb | 4 ++-- 03_conditionals/01_exercises.ipynb | 2 +- 03_conditionals/02_exercises.ipynb | 2 +- 03_conditionals/03_summary.ipynb | 2 +- 03_conditionals/04_review.ipynb | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/03_conditionals/00_content.ipynb b/03_conditionals/00_content.ipynb index 5a6bc8e..62bf469 100644 --- a/03_conditionals/00_content.ipynb +++ b/03_conditionals/00_content.ipynb @@ -2606,7 +2606,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[1;32m 1\u001b[0m \u001b[0muser_input\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrandom\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mchoice\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;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m4\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[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0;36m1\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0muser_input\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[78], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m user_input \u001b[38;5;241m=\u001b[39m random\u001b[38;5;241m.\u001b[39mchoice([\u001b[38;5;241m0\u001b[39m, \u001b[38;5;241m1\u001b[39m, \u001b[38;5;241m2\u001b[39m, \u001b[38;5;241m3\u001b[39m, \u001b[38;5;241m4\u001b[39m, \u001b[38;5;241m5\u001b[39m])\n\u001b[0;32m----> 3\u001b[0m \u001b[38;5;241;43m1\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m/\u001b[39;49m\u001b[43m \u001b[49m\u001b[43muser_input\u001b[49m\n", "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" ] } @@ -2799,7 +2799,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/03_conditionals/01_exercises.ipynb b/03_conditionals/01_exercises.ipynb index 513c67e..6bd0657 100644 --- a/03_conditionals/01_exercises.ipynb +++ b/03_conditionals/01_exercises.ipynb @@ -184,7 +184,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/03_conditionals/02_exercises.ipynb b/03_conditionals/02_exercises.ipynb index 432ca73..4728719 100644 --- a/03_conditionals/02_exercises.ipynb +++ b/03_conditionals/02_exercises.ipynb @@ -119,7 +119,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/03_conditionals/03_summary.ipynb b/03_conditionals/03_summary.ipynb index 86c8468..7d7b12d 100644 --- a/03_conditionals/03_summary.ipynb +++ b/03_conditionals/03_summary.ipynb @@ -44,7 +44,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/03_conditionals/04_review.ipynb b/03_conditionals/04_review.ipynb index 78d9061..e9890be 100644 --- a/03_conditionals/04_review.ipynb +++ b/03_conditionals/04_review.ipynb @@ -180,7 +180,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, From 2974f8322e0562606c9453a1bccd128f60492cf4 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 8 Apr 2024 16:17:09 +0200 Subject: [PATCH 129/142] Re-run Chapter 04 files with Python 3.12 --- 04_iteration/00_content.ipynb | 105 +++++++++++++++++++------------- 04_iteration/01_exercises.ipynb | 2 +- 04_iteration/02_content.ipynb | 4 +- 04_iteration/03_content.ipynb | 2 +- 04_iteration/04_exercises.ipynb | 2 +- 04_iteration/05_summary.ipynb | 2 +- 04_iteration/06_review.ipynb | 2 +- 7 files changed, 71 insertions(+), 48 deletions(-) diff --git a/04_iteration/00_content.ipynb b/04_iteration/00_content.ipynb index 0171c6c..cc17d58 100644 --- a/04_iteration/00_content.ipynb +++ b/04_iteration/00_content.ipynb @@ -163,7 +163,7 @@ } }, "source": [ - "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." + "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&curstr=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." ] }, { @@ -273,7 +273,7 @@ "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 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 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`." + "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&curstr=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`." ] }, { @@ -334,7 +334,7 @@ "source": [ "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! " + "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&curstr=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! " ] }, { @@ -448,9 +448,9 @@ "text": [ "Help on built-in function factorial in module math:\n", "\n", - "factorial(x, /)\n", - " Find x!.\n", - " \n", + "factorial(n, /)\n", + " Find n!.\n", + "\n", " Raise a ValueError if x is negative or non-integral.\n", "\n" ] @@ -675,8 +675,8 @@ "text": [ "Help on built-in function gcd in module math:\n", "\n", - "gcd(x, y, /)\n", - " greatest common divisor of x and y\n", + "gcd(*integers)\n", + " Greatest Common Divisor.\n", "\n" ] } @@ -870,7 +870,7 @@ } }, "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", + "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&curstr=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 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", @@ -1022,10 +1022,11 @@ "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", + "Cell \u001b[0;32mIn[28], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mrun_forever\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[27], line 3\u001b[0m, in \u001b[0;36mrun_forever\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mrun_forever\u001b[39m():\n\u001b[1;32m 2\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Also a pointless function should have a docstring.\"\"\"\u001b[39;00m\n\u001b[0;32m----> 3\u001b[0m \u001b[43mrun_forever\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[27], line 3\u001b[0m, in \u001b[0;36mrun_forever\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mrun_forever\u001b[39m():\n\u001b[1;32m 2\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Also a pointless function should have a docstring.\"\"\"\u001b[39;00m\n\u001b[0;32m----> 3\u001b[0m \u001b[43mrun_forever\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + " \u001b[0;31m[... skipping similar frames: run_forever at line 3 (2974 times)]\u001b[0m\n", + "Cell \u001b[0;32mIn[27], line 3\u001b[0m, in \u001b[0;36mrun_forever\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mrun_forever\u001b[39m():\n\u001b[1;32m 2\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Also a pointless function should have a docstring.\"\"\"\u001b[39;00m\n\u001b[0;32m----> 3\u001b[0m \u001b[43mrun_forever\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mRecursionError\u001b[0m: maximum recursion depth exceeded" ] } @@ -4014,21 +4015,42 @@ "-2949.9\n", "-2950.9\n", "-2951.9\n", - "-2952.9\n" + "-2952.9\n", + "-2953.9\n", + "-2954.9\n", + "-2955.9\n", + "-2956.9\n", + "-2957.9\n", + "-2958.9\n", + "-2959.9\n", + "-2960.9\n", + "-2961.9\n", + "-2962.9\n", + "-2963.9\n", + "-2964.9\n", + "-2965.9\n", + "-2966.9\n", + "-2967.9\n", + "-2968.9\n", + "-2969.9\n", + "-2970.9\n" ] }, { "ename": "RecursionError", - "evalue": "maximum recursion depth exceeded while calling a Python object", + "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[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" + "Cell \u001b[0;32mIn[29], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mcountdown\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m3.1\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[1], line 11\u001b[0m, in \u001b[0;36mcountdown\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 10\u001b[0m \u001b[38;5;28mprint\u001b[39m(n)\n\u001b[0;32m---> 11\u001b[0m \u001b[43mcountdown\u001b[49m\u001b[43m(\u001b[49m\u001b[43mn\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[1], line 11\u001b[0m, in \u001b[0;36mcountdown\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 10\u001b[0m \u001b[38;5;28mprint\u001b[39m(n)\n\u001b[0;32m---> 11\u001b[0m \u001b[43mcountdown\u001b[49m\u001b[43m(\u001b[49m\u001b[43mn\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\n", + " \u001b[0;31m[... skipping similar frames: countdown at line 11 (2972 times)]\u001b[0m\n", + "Cell \u001b[0;32mIn[1], line 11\u001b[0m, in \u001b[0;36mcountdown\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 10\u001b[0m \u001b[38;5;28mprint\u001b[39m(n)\n\u001b[0;32m---> 11\u001b[0m \u001b[43mcountdown\u001b[49m\u001b[43m(\u001b[49m\u001b[43mn\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[1], line 10\u001b[0m, in \u001b[0;36mcountdown\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mHappy New Year!\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 9\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m---> 10\u001b[0m \u001b[38;5;28;43mprint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mn\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 11\u001b[0m countdown(n \u001b[38;5;241m-\u001b[39m \u001b[38;5;241m1\u001b[39m)\n", + "File \u001b[0;32m~/Repositories/intro-to-python/.venv/lib/python3.12/site-packages/ipykernel/iostream.py:664\u001b[0m, in \u001b[0;36mOutStream.write\u001b[0;34m(self, string)\u001b[0m\n\u001b[1;32m 655\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mwrite\u001b[39m(\u001b[38;5;28mself\u001b[39m, string: \u001b[38;5;28mstr\u001b[39m) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Optional[\u001b[38;5;28mint\u001b[39m]: \u001b[38;5;66;03m# type:ignore[override]\u001b[39;00m\n\u001b[1;32m 656\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Write to current stream after encoding if necessary\u001b[39;00m\n\u001b[1;32m 657\u001b[0m \n\u001b[1;32m 658\u001b[0m \u001b[38;5;124;03m Returns\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 662\u001b[0m \n\u001b[1;32m 663\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m--> 664\u001b[0m parent \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparent_header\u001b[49m\n\u001b[1;32m 666\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(string, \u001b[38;5;28mstr\u001b[39m):\n\u001b[1;32m 667\u001b[0m msg \u001b[38;5;241m=\u001b[39m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mwrite() argument must be str, not \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mtype\u001b[39m(string)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;66;03m# type:ignore[unreachable]\u001b[39;00m\n", + "\u001b[0;31mRecursionError\u001b[0m: maximum recursion depth exceeded" ] } ], @@ -4058,16 +4080,17 @@ "outputs": [ { "ename": "RecursionError", - "evalue": "maximum recursion depth exceeded in comparison", + "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[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" + "Cell \u001b[0;32mIn[30], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mfactorial\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m3.1\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[6], line 12\u001b[0m, in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m n \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[1;32m 11\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;241m1\u001b[39m\n\u001b[0;32m---> 12\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m n \u001b[38;5;241m*\u001b[39m \u001b[43mfactorial\u001b[49m\u001b[43m(\u001b[49m\u001b[43mn\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[6], line 12\u001b[0m, in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m n \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[1;32m 11\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;241m1\u001b[39m\n\u001b[0;32m---> 12\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m n \u001b[38;5;241m*\u001b[39m \u001b[43mfactorial\u001b[49m\u001b[43m(\u001b[49m\u001b[43mn\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\n", + " \u001b[0;31m[... skipping similar frames: factorial at line 12 (2974 times)]\u001b[0m\n", + "Cell \u001b[0;32mIn[6], line 12\u001b[0m, in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m n \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[1;32m 11\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;241m1\u001b[39m\n\u001b[0;32m---> 12\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m n \u001b[38;5;241m*\u001b[39m \u001b[43mfactorial\u001b[49m\u001b[43m(\u001b[49m\u001b[43mn\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\n", + "\u001b[0;31mRecursionError\u001b[0m: maximum recursion depth exceeded" ] } ], @@ -4382,8 +4405,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)\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", + "Cell \u001b[0;32mIn[38], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mfactorial\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m3.1\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[35], line 15\u001b[0m, in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Calculate the factorial of a number.\u001b[39;00m\n\u001b[1;32m 3\u001b[0m \n\u001b[1;32m 4\u001b[0m \u001b[38;5;124;03mArgs:\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[38;5;124;03m ValueError: if n is negative\u001b[39;00m\n\u001b[1;32m 13\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 14\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(n, \u001b[38;5;28mint\u001b[39m):\n\u001b[0;32m---> 15\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mFactorial is only defined for integers\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 16\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m n \u001b[38;5;241m<\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[1;32m 17\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mFactorial is not defined for negative integers\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", "\u001b[0;31mTypeError\u001b[0m: Factorial is only defined for integers" ] } @@ -4408,8 +4431,8 @@ "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", + "Cell \u001b[0;32mIn[39], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mfactorial\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[38;5;241;43m42\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[35], line 17\u001b[0m, in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mFactorial is only defined for integers\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 16\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m n \u001b[38;5;241m<\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[0;32m---> 17\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mFactorial is not defined for negative integers\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 18\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m n \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[1;32m 19\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;241m1\u001b[39m\n", "\u001b[0;31mValueError\u001b[0m: Factorial is not defined for negative integers" ] } @@ -4445,8 +4468,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.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;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", + "Cell \u001b[0;32mIn[40], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mfactorial\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m3.0\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[35], line 15\u001b[0m, in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Calculate the factorial of a number.\u001b[39;00m\n\u001b[1;32m 3\u001b[0m \n\u001b[1;32m 4\u001b[0m \u001b[38;5;124;03mArgs:\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[38;5;124;03m ValueError: if n is negative\u001b[39;00m\n\u001b[1;32m 13\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 14\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(n, \u001b[38;5;28mint\u001b[39m):\n\u001b[0;32m---> 15\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mFactorial is only defined for integers\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 16\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m n \u001b[38;5;241m<\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[1;32m 17\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mFactorial is not defined for negative integers\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", "\u001b[0;31mTypeError\u001b[0m: Factorial is only defined for integers" ] } @@ -4688,8 +4711,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)\u001b[0m\n\u001b[1;32m 13\u001b[0m \"\"\"\n\u001b[1;32m 14\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;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\"n is not integer-like; it has non-zero 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 16\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "Cell \u001b[0;32mIn[47], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mfactorial\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m3.1\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[45], line 15\u001b[0m, in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Calculate the factorial of a number.\u001b[39;00m\n\u001b[1;32m 3\u001b[0m \n\u001b[1;32m 4\u001b[0m \u001b[38;5;124;03mArgs:\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[38;5;124;03m ValueError: if n is negative\u001b[39;00m\n\u001b[1;32m 13\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 14\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m n \u001b[38;5;241m!=\u001b[39m \u001b[38;5;28mint\u001b[39m(n):\n\u001b[0;32m---> 15\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mn is not integer-like; it has non-zero decimals\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 16\u001b[0m n \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mint\u001b[39m(n)\n\u001b[1;32m 18\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m n \u001b[38;5;241m<\u001b[39m \u001b[38;5;241m0\u001b[39m:\n", "\u001b[0;31mTypeError\u001b[0m: n is not integer-like; it has non-zero decimals" ] } @@ -4725,8 +4748,8 @@ "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\"text\"\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 12\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0mnegative\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 13\u001b[0m \"\"\"\n\u001b[0;32m---> 14\u001b[0;31m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 15\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"n is not integer-like; it has non-zero decimals\"\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[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "Cell \u001b[0;32mIn[48], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mfactorial\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtext\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[45], line 14\u001b[0m, in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mfactorial\u001b[39m(n):\n\u001b[1;32m 2\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Calculate the factorial of a number.\u001b[39;00m\n\u001b[1;32m 3\u001b[0m \n\u001b[1;32m 4\u001b[0m \u001b[38;5;124;03m Args:\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[38;5;124;03m ValueError: if n is negative\u001b[39;00m\n\u001b[1;32m 13\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m---> 14\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m n \u001b[38;5;241m!=\u001b[39m \u001b[38;5;28;43mint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mn\u001b[49m\u001b[43m)\u001b[49m:\n\u001b[1;32m 15\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mn is not integer-like; it has non-zero decimals\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 16\u001b[0m n \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mint\u001b[39m(n)\n", "\u001b[0;31mValueError\u001b[0m: invalid literal for int() with base 10: 'text'" ] } @@ -4801,8 +4824,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;34m\"text\"\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[0mcasted_n\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mValueError\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\"n cannot be casted as an integer\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0;32mNone\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;32melse\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;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mcasted_n\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "Cell \u001b[0;32mIn[50], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mfactorial\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtext\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[49], line 17\u001b[0m, in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 15\u001b[0m casted_n \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mint\u001b[39m(n)\n\u001b[1;32m 16\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m:\n\u001b[0;32m---> 17\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mn cannot be casted as an integer\u001b[39m\u001b[38;5;124m\"\u001b[39m) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 18\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 19\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m n \u001b[38;5;241m!=\u001b[39m casted_n:\n", "\u001b[0;31mTypeError\u001b[0m: n cannot be casted as an integer" ] } @@ -4827,8 +4850,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)\u001b[0m\n\u001b[1;32m 18\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 19\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mcasted_n\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 20\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"n is not integer-like; it has non-zero 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 21\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcasted_n\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 22\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "Cell \u001b[0;32mIn[51], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mfactorial\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m3.1\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[49], line 20\u001b[0m, in \u001b[0;36mfactorial\u001b[0;34m(n)\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 19\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m n \u001b[38;5;241m!=\u001b[39m casted_n:\n\u001b[0;32m---> 20\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mn is not integer-like; it has non-zero decimals\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 21\u001b[0m n \u001b[38;5;241m=\u001b[39m casted_n\n\u001b[1;32m 23\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m n \u001b[38;5;241m<\u001b[39m \u001b[38;5;241m0\u001b[39m:\n", "\u001b[0;31mTypeError\u001b[0m: n is not integer-like; it has non-zero decimals" ] } @@ -4887,7 +4910,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/04_iteration/01_exercises.ipynb b/04_iteration/01_exercises.ipynb index cf4b0ce..28304a9 100644 --- a/04_iteration/01_exercises.ipynb +++ b/04_iteration/01_exercises.ipynb @@ -589,7 +589,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/04_iteration/02_content.ipynb b/04_iteration/02_content.ipynb index 7fb3eb3..35ac2cb 100644 --- a/04_iteration/02_content.ipynb +++ b/04_iteration/02_content.ipynb @@ -137,7 +137,7 @@ } }, "source": [ - "As [PythonTutor ](http://pythontutor.com/visualize.html#code=def%20countdown%28n%29%3A%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." + "As [PythonTutor ](http://pythontutor.com/visualize.html#code=def%20countdown%28n%29%3A%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&curstr=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." ] }, { @@ -1268,7 +1268,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/04_iteration/03_content.ipynb b/04_iteration/03_content.ipynb index dafbe08..d9bb92f 100644 --- a/04_iteration/03_content.ipynb +++ b/04_iteration/03_content.ipynb @@ -959,7 +959,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/04_iteration/04_exercises.ipynb b/04_iteration/04_exercises.ipynb index 04654bc..8bd97ff 100644 --- a/04_iteration/04_exercises.ipynb +++ b/04_iteration/04_exercises.ipynb @@ -382,7 +382,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/04_iteration/05_summary.ipynb b/04_iteration/05_summary.ipynb index 981bfec..18d287d 100644 --- a/04_iteration/05_summary.ipynb +++ b/04_iteration/05_summary.ipynb @@ -47,7 +47,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/04_iteration/06_review.ipynb b/04_iteration/06_review.ipynb index b40647b..2043dcc 100644 --- a/04_iteration/06_review.ipynb +++ b/04_iteration/06_review.ipynb @@ -247,7 +247,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, From 4444d197f1a22ba1aa050b7954e34426093cc9bf Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 8 Apr 2024 16:26:28 +0200 Subject: [PATCH 130/142] Re-run Chapter 05 files with Python 3.12 --- 05_numbers/00_content.ipynb | 8 ++++---- 05_numbers/01_content.ipynb | 18 +++++++++--------- 05_numbers/02_content.ipynb | 12 ++++++------ 05_numbers/03_appendix.ipynb | 10 +++++----- 05_numbers/04_summary.ipynb | 2 +- 05_numbers/05_review.ipynb | 2 +- 05_numbers/06_resources.ipynb | 7 ++++++- 7 files changed, 32 insertions(+), 27 deletions(-) diff --git a/05_numbers/00_content.ipynb b/05_numbers/00_content.ipynb index 0a106c4..d04c106 100644 --- a/05_numbers/00_content.ipynb +++ b/05_numbers/00_content.ipynb @@ -257,10 +257,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "leading zeros in decimal integer literals are not permitted; use an 0o prefix for octal integers (, line 1)", + "evalue": "leading zeros in decimal integer literals are not permitted; use an 0o prefix for octal integers (1481240458.py, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m 042\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m leading zeros in decimal integer literals are not permitted; use an 0o prefix for octal integers\n" + "\u001b[0;36m Cell \u001b[0;32mIn[7], line 1\u001b[0;36m\u001b[0m\n\u001b[0;31m 042\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m leading zeros in decimal integer literals are not permitted; use an 0o prefix for octal integers\n" ] } ], @@ -413,7 +413,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\"42.0\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[12], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;43mint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m42.0\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mValueError\u001b[0m: invalid literal for int() with base 10: '42.0'" ] } @@ -2422,7 +2422,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/05_numbers/01_content.ipynb b/05_numbers/01_content.ipynb index 00157ad..af3b6c0 100644 --- a/05_numbers/01_content.ipynb +++ b/05_numbers/01_content.ipynb @@ -326,7 +326,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[0mfloat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"42. 87\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[10], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;43mfloat\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m42. 87\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mValueError\u001b[0m: could not convert string to float: '42. 87'" ] } @@ -497,10 +497,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "invalid syntax (1434619204.py, 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 Cell \u001b[0;32mIn[15], line 1\u001b[0;36m\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" ] } ], @@ -519,10 +519,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "invalid decimal literal (3971374856.py, 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 Cell \u001b[0;32mIn[16], line 1\u001b[0;36m\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 decimal literal\n" ] } ], @@ -541,10 +541,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", + "evalue": "invalid syntax (2795275998.py, 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 Cell \u001b[0;32mIn[17], line 1\u001b[0;36m\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" ] } ], @@ -579,7 +579,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[0me0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[18], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43me0\u001b[49m\n", "\u001b[0;31mNameError\u001b[0m: name 'e0' is not defined" ] } @@ -2148,7 +2148,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/05_numbers/02_content.ipynb b/05_numbers/02_content.ipynb index ae1b01d..3f24e6a 100644 --- a/05_numbers/02_content.ipynb +++ b/05_numbers/02_content.ipynb @@ -991,7 +991,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;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", + "Cell \u001b[0;32mIn[29], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;43mround\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43mj\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mTypeError\u001b[0m: type complex doesn't define __round__ method" ] } @@ -1388,8 +1388,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 17\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mReal\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mstrict\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 19\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"n is not integer-like; it has non-zero decimals\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 20\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 21\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "Cell \u001b[0;32mIn[41], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mfactorial\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m3.1\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[37], line 19\u001b[0m, in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(n, numbers\u001b[38;5;241m.\u001b[39mReal):\n\u001b[1;32m 18\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m n \u001b[38;5;241m!=\u001b[39m \u001b[38;5;28mint\u001b[39m(n) \u001b[38;5;129;01mand\u001b[39;00m strict:\n\u001b[0;32m---> 19\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mn is not integer-like; it has non-zero decimals\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 20\u001b[0m n \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mint\u001b[39m(n)\n\u001b[1;32m 21\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n", "\u001b[0;31mTypeError\u001b[0m: n is not integer-like; it has non-zero decimals" ] } @@ -1460,8 +1460,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 20\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 21\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 22\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Factorial is only defined for integers\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 23\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 24\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mn\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "Cell \u001b[0;32mIn[43], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mfactorial\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43mj\u001b[49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[37], line 22\u001b[0m, in \u001b[0;36mfactorial\u001b[0;34m(n, strict)\u001b[0m\n\u001b[1;32m 20\u001b[0m n \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mint\u001b[39m(n)\n\u001b[1;32m 21\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m---> 22\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mFactorial is only defined for integers\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 24\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m n \u001b[38;5;241m<\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[1;32m 25\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mFactorial is not defined for negative integers\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", "\u001b[0;31mTypeError\u001b[0m: Factorial is only defined for integers" ] } @@ -1487,7 +1487,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/05_numbers/03_appendix.ipynb b/05_numbers/03_appendix.ipynb index c470e82..9551b70 100644 --- a/05_numbers/03_appendix.ipynb +++ b/05_numbers/03_appendix.ipynb @@ -510,7 +510,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", + "Cell \u001b[0;32mIn[16], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;241;43m1.0\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mDecimal\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m42\u001b[39;49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for *: 'float' and 'decimal.Decimal'" ] } @@ -681,7 +681,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\"1e-28\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[21], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mtwo\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mquantize\u001b[49m\u001b[43m(\u001b[49m\u001b[43mDecimal\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m1e-28\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mInvalidOperation\u001b[0m: []" ] } @@ -964,7 +964,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", + "Cell \u001b[0;32mIn[30], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mDecimal\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43minf\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mDecimal\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m-inf\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mInvalidOperation\u001b[0m: []" ] } @@ -989,7 +989,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", + "Cell \u001b[0;32mIn[31], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mDecimal\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43minf\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mDecimal\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43minf\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mInvalidOperation\u001b[0m: []" ] } @@ -1476,7 +1476,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/05_numbers/04_summary.ipynb b/05_numbers/04_summary.ipynb index 45c7c29..ad1ebde 100644 --- a/05_numbers/04_summary.ipynb +++ b/05_numbers/04_summary.ipynb @@ -53,7 +53,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/05_numbers/05_review.ipynb b/05_numbers/05_review.ipynb index db49191..a911fb0 100644 --- a/05_numbers/05_review.ipynb +++ b/05_numbers/05_review.ipynb @@ -306,7 +306,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/05_numbers/06_resources.ipynb b/05_numbers/06_resources.ipynb index 87343f1..5fa8cfa 100644 --- a/05_numbers/06_resources.ipynb +++ b/05_numbers/06_resources.ipynb @@ -56,6 +56,7 @@ " src=\"https://www.youtube.com/embed/RgklPQ8rbkg\"\n", " frameborder=\"0\"\n", " allowfullscreen\n", + " \n", " >\n", " " ], @@ -93,6 +94,7 @@ " src=\"https://www.youtube.com/embed/xHWKYFhhtJQ\"\n", " frameborder=\"0\"\n", " allowfullscreen\n", + " \n", " >\n", " " ], @@ -140,6 +142,7 @@ " src=\"https://www.youtube.com/embed/4qH4unVtJkE\"\n", " frameborder=\"0\"\n", " allowfullscreen\n", + " \n", " >\n", " " ], @@ -194,6 +197,7 @@ " src=\"https://www.youtube.com/embed/PZRI1IfStY0\"\n", " frameborder=\"0\"\n", " allowfullscreen\n", + " \n", " >\n", " " ], @@ -248,6 +252,7 @@ " src=\"https://www.youtube.com/embed/Jkv-55ndVYY\"\n", " frameborder=\"0\"\n", " allowfullscreen\n", + " \n", " >\n", " " ], @@ -281,7 +286,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", From 7999b808a964f866a393bdd96d7c6a17b739b5d3 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 8 Apr 2024 16:29:22 +0200 Subject: [PATCH 131/142] Re-run Chapter 06 files with Python 3.12 --- 06_text/00_content.ipynb | 14 +++++++------- 06_text/01_exercises.ipynb | 2 +- 06_text/02_content.ipynb | 28 +++++++++++++++++++--------- 06_text/03_summary.ipynb | 2 +- 06_text/04_review.ipynb | 2 +- 06_text/05_resources.ipynb | 14 ++++++++++---- 6 files changed, 39 insertions(+), 23 deletions(-) diff --git a/06_text/00_content.ipynb b/06_text/00_content.ipynb index 236942b..f1f6871 100644 --- a/06_text/00_content.ipynb +++ b/06_text/00_content.ipynb @@ -1678,13 +1678,13 @@ "outputs": [ { "ename": "TypeError", - "evalue": "string indices must be integers", + "evalue": "string indices must be integers, not 'float'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1.0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m: string indices must be integers" + "Cell \u001b[0;32mIn[54], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mtext\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m1.0\u001b[39;49m\u001b[43m]\u001b[49m\n", + "\u001b[0;31mTypeError\u001b[0m: string indices must be integers, not 'float'" ] } ], @@ -1754,7 +1754,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[0mtext\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m27\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;31m# == text[len(text)]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[56], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mtext\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m27\u001b[39;49m\u001b[43m]\u001b[49m \u001b[38;5;66;03m# == text[len(text)]\u001b[39;00m\n", "\u001b[0;31mIndexError\u001b[0m: string index out of range" ] } @@ -2315,7 +2315,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\"X\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[72], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mtext\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m]\u001b[49m \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mX\u001b[39m\u001b[38;5;124m\"\u001b[39m\n", "\u001b[0;31mTypeError\u001b[0m: 'str' object does not support item assignment" ] } @@ -2340,7 +2340,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", + "Cell \u001b[0;32mIn[73], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mtext\u001b[49m\u001b[43m[\u001b[49m\u001b[43m:\u001b[49m\u001b[38;5;241;43m5\u001b[39;49m\u001b[43m]\u001b[49m \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrandom\u001b[39m\u001b[38;5;124m\"\u001b[39m\n", "\u001b[0;31mTypeError\u001b[0m: 'str' object does not support item assignment" ] } @@ -3938,7 +3938,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/06_text/01_exercises.ipynb b/06_text/01_exercises.ipynb index 0781f2b..89dc687 100644 --- a/06_text/01_exercises.ipynb +++ b/06_text/01_exercises.ipynb @@ -974,7 +974,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/06_text/02_content.ipynb b/06_text/02_content.ipynb index 6ff2407..fc5efa3 100644 --- a/06_text/02_content.ipynb +++ b/06_text/02_content.ipynb @@ -315,6 +315,16 @@ "C:\\Programs\n", "ew_application\n" ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "<>:1: SyntaxWarning: invalid escape sequence '\\P'\n", + "<>:1: SyntaxWarning: invalid escape sequence '\\P'\n", + "/tmp/ipykernel_159416/1102122489.py:1: SyntaxWarning: invalid escape sequence '\\P'\n", + " print(\"C:\\Programs\\new_application\")\n" + ] } ], "source": [ @@ -343,10 +353,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 (2296736867.py, line 1)", "output_type": "error", "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print(\"C:\\Users\\Administrator\\Desktop\\Project\")\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape\n" + "\u001b[0;36m Cell \u001b[0;32mIn[10], line 1\u001b[0;36m\u001b[0m\n\u001b[0;31m print(\"C:\\Users\\Administrator\\Desktop\\Project\")\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \\UXXXXXXXX escape\n" ] } ], @@ -1174,10 +1184,10 @@ "outputs": [ { "ename": "SyntaxError", - "evalue": "EOL while scanning string literal (, line 1)", + "evalue": "unterminated string literal (detected at line 1) (2682216481.py, 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 Cell \u001b[0;32mIn[34], line 1\u001b[0;36m\u001b[0m\n\u001b[0;31m \"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m unterminated string literal (detected at line 1)\n" ] } ], @@ -1835,7 +1845,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mUnicodeDecodeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mplace\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"iso-8859-1\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[55], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mplace\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mencode\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43miso-8859-1\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdecode\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mUnicodeDecodeError\u001b[0m: 'utf-8' codec can't decode byte 0xe9 in position 3: invalid continuation byte" ] } @@ -1871,7 +1881,7 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mUnicodeEncodeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;34m\"Dobrý den, přátelé!\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"iso-8859-1\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[56], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mDobrý den, přátelé!\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mencode\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43miso-8859-1\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mUnicodeEncodeError\u001b[0m: 'latin-1' codec can't encode character '\\u0159' in position 12: ordinal not in range(256)" ] } @@ -1918,8 +1928,8 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mUnicodeDecodeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"umlauts.txt\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mfile\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfile\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreadlines\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m~/.pyenv/versions/3.8.6/lib/python3.8/codecs.py\u001b[0m in \u001b[0;36mdecode\u001b[0;34m(self, input, final)\u001b[0m\n\u001b[1;32m 320\u001b[0m \u001b[0;31m# decode input (taking the buffer into account)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 321\u001b[0m \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbuffer\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0minput\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 322\u001b[0;31m \u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconsumed\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_buffer_decode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0merrors\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfinal\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 323\u001b[0m \u001b[0;31m# keep undecoded input until the next call\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 324\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbuffer\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mconsumed\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "Cell \u001b[0;32mIn[57], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mopen\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mumlauts.txt\u001b[39m\u001b[38;5;124m\"\u001b[39m) \u001b[38;5;28;01mas\u001b[39;00m file:\n\u001b[0;32m----> 2\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mjoin(\u001b[43mfile\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mreadlines\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m))\n", + "File \u001b[0;32m:322\u001b[0m, in \u001b[0;36mdecode\u001b[0;34m(self, input, final)\u001b[0m\n", "\u001b[0;31mUnicodeDecodeError\u001b[0m: 'utf-8' codec can't decode byte 0xe4 in position 9: invalid continuation byte" ] } @@ -2068,7 +2078,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/06_text/03_summary.ipynb b/06_text/03_summary.ipynb index bcb6506..f3ac4d7 100644 --- a/06_text/03_summary.ipynb +++ b/06_text/03_summary.ipynb @@ -43,7 +43,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/06_text/04_review.ipynb b/06_text/04_review.ipynb index 9804bfc..b92cb56 100644 --- a/06_text/04_review.ipynb +++ b/06_text/04_review.ipynb @@ -178,7 +178,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/06_text/05_resources.ipynb b/06_text/05_resources.ipynb index a5517f1..24882f9 100644 --- a/06_text/05_resources.ipynb +++ b/06_text/05_resources.ipynb @@ -62,6 +62,7 @@ " src=\"https://www.youtube.com/embed/MijmeoH9LT4\"\n", " frameborder=\"0\"\n", " allowfullscreen\n", + " \n", " >\n", " " ], @@ -87,7 +88,7 @@ } }, "source": [ - "In his [PyCon Australia 2018](https://2018.pycon-au.org/) talk titled \"*Unicode and Python: The absolute minimum you need to know*\" [Raphaël Merx](https://www.linkedin.com/in/raphaelmerx/) explains some caveats and best practices regarding Unicode." + "In his [PyCon Australia 2018](https://2018.pycon-au.org/) talk titled \"*Unicode and Python: The absolute minimum you need to know*\" [Raphaël Merx](https://www.linkedin.com/raphaelmerx/) explains some caveats and best practices regarding Unicode." ] }, { @@ -110,6 +111,7 @@ " src=\"https://www.youtube.com/embed/oXVmZGN6plY\"\n", " frameborder=\"0\"\n", " allowfullscreen\n", + " \n", " >\n", " " ], @@ -134,7 +136,7 @@ } }, "source": [ - "In a similar talk at [PyCon 2017](https://us.pycon.org/2017/) titled \"*Unicode what is the big deal*\" [Åukasz Langa](https://www.linkedin.com/in/llanga/) provides further lessons learned regarding Unicode." + "In a similar talk at [PyCon 2017](https://us.pycon.org/2017/) titled \"*Unicode what is the big deal*\" [Åukasz Langa](https://www.linkedin.com/llanga/) provides further lessons learned regarding Unicode." ] }, { @@ -157,6 +159,7 @@ " src=\"https://www.youtube.com/embed/7m5JA3XaZ4k\"\n", " frameborder=\"0\"\n", " allowfullscreen\n", + " \n", " >\n", " " ], @@ -204,6 +207,7 @@ " src=\"https://www.youtube.com/embed/sgHbC6udIqc\"\n", " frameborder=\"0\"\n", " allowfullscreen\n", + " \n", " >\n", " " ], @@ -235,7 +239,7 @@ } }, "source": [ - "Lastly, in his entertaining talk at [PyCon.DE 2019](https://de.pycon.org/) titled \"*Your Name is Invalid!*\" [Miroslav Å edivý](https://www.linkedin.com/in/%C5%A1ediv%C3%BD/) shows how hard it actually is to write software that can process any name a human can possibly have. Miroslav also gave a lightning talk where he shows how he uses only one keyboard for the 12 (!!!) languages he speaks." + "Lastly, in his entertaining talk at [PyCon.DE 2019](https://de.pycon.org/) titled \"*Your Name is Invalid!*\" [Miroslav Å edivý](https://www.linkedin.com/%C5%A1ediv%C3%BD/) shows how hard it actually is to write software that can process any name a human can possibly have. Miroslav also gave a lightning talk where he shows how he uses only one keyboard for the 12 (!!!) languages he speaks." ] }, { @@ -258,6 +262,7 @@ " src=\"https://www.youtube.com/embed/pBuS7EUPnQA\"\n", " frameborder=\"0\"\n", " allowfullscreen\n", + " \n", " >\n", " " ], @@ -294,6 +299,7 @@ " src=\"https://www.youtube.com/embed/-4QjII981sM\"\n", " frameborder=\"0\"\n", " allowfullscreen\n", + " \n", " >\n", " " ], @@ -327,7 +333,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", From ba1349f796d876b9024d905afa4445b591d09e45 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 8 Apr 2024 16:34:43 +0200 Subject: [PATCH 132/142] Re-run Chapter 07 files with Python 3.12 --- 07_sequences/00_content.ipynb | 8 ++++---- 07_sequences/01_content.ipynb | 20 ++++++++++---------- 07_sequences/02_exercises.ipynb | 2 +- 07_sequences/03_content.ipynb | 20 ++++++++++---------- 07_sequences/04_exercises.ipynb | 2 +- 07_sequences/05_appendix.ipynb | 4 ++-- 07_sequences/06_summary.ipynb | 2 +- 07_sequences/07_review.ipynb | 2 +- 8 files changed, 30 insertions(+), 30 deletions(-) diff --git a/07_sequences/00_content.ipynb b/07_sequences/00_content.ipynb index 43ec596..c37450b 100644 --- a/07_sequences/00_content.ipynb +++ b/07_sequences/00_content.ipynb @@ -307,7 +307,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;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", + "Cell \u001b[0;32mIn[9], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mdigit\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;241;43m999\u001b[39;49m\u001b[43m:\u001b[49m\n\u001b[1;32m 2\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mprint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mdigit\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mTypeError\u001b[0m: 'int' object is not iterable" ] } @@ -491,7 +491,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;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", + "Cell \u001b[0;32mIn[15], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;241;43m9\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;241;43m999\u001b[39;49m\n", "\u001b[0;31mTypeError\u001b[0m: argument of type 'int' is not iterable" ] } @@ -658,7 +658,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[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", + "Cell \u001b[0;32mIn[21], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;43mlen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m999\u001b[39;49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mTypeError\u001b[0m: object of type 'int' has no len()" ] } @@ -926,7 +926,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/07_sequences/01_content.ipynb b/07_sequences/01_content.ipynb index 1dd590a..fb6d54d 100644 --- a/07_sequences/01_content.ipynb +++ b/07_sequences/01_content.ipynb @@ -113,7 +113,7 @@ } }, "source": [ - "[PythonTutor ](http://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." + "[PythonTutor ](http://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&curstr=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." ] }, { @@ -209,7 +209,7 @@ "source": [ "Alternatively, we use the built-in [list() ](https://docs.python.org/3/library/functions.html#func-list) constructor to create a `list` object out of any (finite) *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/develop/04_iteration/02_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://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/develop/04_iteration/02_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://pythontutor.com/visualize.html#code=r%20%3D%20range%281,%2013%29%0Al%20%3D%20list%28range%281,%2013%29%29&cumulative=false&curstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows this difference visually." ] }, { @@ -263,7 +263,7 @@ "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", + "Cell \u001b[0;32mIn[8], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;43mlist\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mrange\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m999_999_999_999\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mMemoryError\u001b[0m: " ] } @@ -614,7 +614,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[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", + "Cell \u001b[0;32mIn[17], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mnested\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m5\u001b[39;49m\u001b[43m]\u001b[49m\n", "\u001b[0;31mIndexError\u001b[0m: list index out of range" ] } @@ -1039,7 +1039,7 @@ } }, "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 *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", + "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&curstr=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* 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." ] @@ -1191,7 +1191,7 @@ } }, "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." + "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&curstr=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." ] }, { @@ -1569,7 +1569,7 @@ } }, "source": [ - "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", + "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&curstr=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." ] @@ -2355,7 +2355,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", + "Cell \u001b[0;32mIn[80], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mnames\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mremove\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mPeter\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mValueError\u001b[0m: list.remove(x): x not in list" ] } @@ -2439,7 +2439,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[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", + "Cell \u001b[0;32mIn[83], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mnames\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mindex\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mKarl\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mValueError\u001b[0m: 'Karl' is not in list" ] } @@ -2967,7 +2967,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/07_sequences/02_exercises.ipynb b/07_sequences/02_exercises.ipynb index 0ec7907..2709373 100644 --- a/07_sequences/02_exercises.ipynb +++ b/07_sequences/02_exercises.ipynb @@ -236,7 +236,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/07_sequences/03_content.ipynb b/07_sequences/03_content.ipynb index 7b63f1e..817951f 100644 --- a/07_sequences/03_content.ipynb +++ b/07_sequences/03_content.ipynb @@ -96,7 +96,7 @@ "source": [ "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, referencing the *same* object. We can also visualize that with [PythonTutor ](http://pythontutor.com/visualize.html#code=letters%20%3D%20%5B%22a%22,%20%22b%22,%20%22c%22%5D%0A%0Adef%20add_xyz%28arg%29%3A%0A%20%20%20%20arg.extend%28%5B%22x%22,%20%22y%22,%20%22z%22%5D%29%0A%20%20%20%20return%20arg%0A%0Aletters_with_xyz%20%3D%20add_xyz%28letters%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false)." + "So, after the function call, `letters_with_xyz` and `letters` are **aliases** as well, referencing the *same* object. We can also visualize that with [PythonTutor ](http://pythontutor.com/visualize.html#code=letters%20%3D%20%5B%22a%22,%20%22b%22,%20%22c%22%5D%0A%0Adef%20add_xyz%28arg%29%3A%0A%20%20%20%20arg.extend%28%5B%22x%22,%20%22y%22,%20%22z%22%5D%29%0A%20%20%20%20return%20arg%0A%0Aletters_with_xyz%20%3D%20add_xyz%28letters%29&cumulative=false&curstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false)." ] }, { @@ -170,7 +170,7 @@ "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. [PythonTutor ](http://pythontutor.com/visualize.html#code=letters%20%3D%20%5B%22a%22,%20%22b%22,%20%22c%22%5D%0A%0Adef%20add_xyz%28arg%29%3A%0A%20%20%20%20new_arg%20%3D%20arg%5B%3A%5D%0A%20%20%20%20new_arg.extend%28%5B%22x%22,%20%22y%22,%20%22z%22%5D%29%0A%20%20%20%20return%20new_arg%0A%0Aletters_with_xyz%20%3D%20add_xyz%28letters%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows that as well. 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", + "The revised `add_xyz()` function below is more natural to reason about as it does *not* modify the passed in `arg` internally. [PythonTutor ](http://pythontutor.com/visualize.html#code=letters%20%3D%20%5B%22a%22,%20%22b%22,%20%22c%22%5D%0A%0Adef%20add_xyz%28arg%29%3A%0A%20%20%20%20new_arg%20%3D%20arg%5B%3A%5D%0A%20%20%20%20new_arg.extend%28%5B%22x%22,%20%22y%22,%20%22z%22%5D%29%0A%20%20%20%20return%20new_arg%0A%0Aletters_with_xyz%20%3D%20add_xyz%28letters%29&cumulative=false&curstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows that as well. 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 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb)." ] @@ -275,7 +275,7 @@ } }, "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`. [PythonTutor ](http://pythontutor.com/visualize.html#code=letters%20%3D%20%5B%22a%22,%20%22b%22,%20%22c%22%5D%0A%0Adef%20add_xyz%28arg%29%3A%0A%20%20%20%20arg.extend%28%5B%22x%22,%20%22y%22,%20%22z%22%5D%29%0A%20%20%20%20return%0A%0Aadd_xyz%28letters%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how there is only *one* reference to `letters` after the function call." + "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`. [PythonTutor ](http://pythontutor.com/visualize.html#code=letters%20%3D%20%5B%22a%22,%20%22b%22,%20%22c%22%5D%0A%0Adef%20add_xyz%28arg%29%3A%0A%20%20%20%20arg.extend%28%5B%22x%22,%20%22y%22,%20%22z%22%5D%29%0A%20%20%20%20return%0A%0Aadd_xyz%28letters%29&cumulative=false&curstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows how there is only *one* reference to `letters` after the function call." ] }, { @@ -1129,7 +1129,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", + "Cell \u001b[0;32mIn[43], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mnumbers\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m]\u001b[49m \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m99\u001b[39m\n", "\u001b[0;31mTypeError\u001b[0m: 'tuple' object does not support item assignment" ] } @@ -1170,7 +1170,7 @@ } ], "source": [ - "numbers + (99,) " + "numbers + (99,)" ] }, { @@ -1586,7 +1586,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", + "Cell \u001b[0;32mIn[60], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11 \u001b[38;5;241m=\u001b[39m numbers\n", "\u001b[0;31mValueError\u001b[0m: too many values to unpack (expected 11)" ] } @@ -1611,7 +1611,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", + "Cell \u001b[0;32mIn[61], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13 \u001b[38;5;241m=\u001b[39m numbers\n", "\u001b[0;31mValueError\u001b[0m: not enough values to unpack (expected 13, got 12)" ] } @@ -2240,8 +2240,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", + "Cell \u001b[0;32mIn[83], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mproduct\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[80], line 3\u001b[0m, in \u001b[0;36mproduct\u001b[0;34m(*args)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mproduct\u001b[39m(\u001b[38;5;241m*\u001b[39margs):\n\u001b[1;32m 2\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Multiply all arguments.\"\"\"\u001b[39;00m\n\u001b[0;32m----> 3\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43margs\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m]\u001b[49m\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m arg \u001b[38;5;129;01min\u001b[39;00m args[\u001b[38;5;241m1\u001b[39m:]:\n\u001b[1;32m 6\u001b[0m result \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m=\u001b[39m arg\n", "\u001b[0;31mIndexError\u001b[0m: tuple index out of range" ] } @@ -2462,7 +2462,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/07_sequences/04_exercises.ipynb b/07_sequences/04_exercises.ipynb index 1d46524..6df6a33 100644 --- a/07_sequences/04_exercises.ipynb +++ b/07_sequences/04_exercises.ipynb @@ -642,7 +642,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/07_sequences/05_appendix.ipynb b/07_sequences/05_appendix.ipynb index 09ebd0c..b7fb1f6 100644 --- a/07_sequences/05_appendix.ipynb +++ b/07_sequences/05_appendix.ipynb @@ -420,7 +420,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", + "Cell \u001b[0;32mIn[13], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mcurrent_position\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mz\u001b[49m\n", "\u001b[0;31mAttributeError\u001b[0m: 'Point' object has no attribute 'z'" ] } @@ -577,7 +577,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/07_sequences/06_summary.ipynb b/07_sequences/06_summary.ipynb index 8115139..043ed03 100644 --- a/07_sequences/06_summary.ipynb +++ b/07_sequences/06_summary.ipynb @@ -53,7 +53,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/07_sequences/07_review.ipynb b/07_sequences/07_review.ipynb index de10156..44b7766 100644 --- a/07_sequences/07_review.ipynb +++ b/07_sequences/07_review.ipynb @@ -231,7 +231,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, From 6a7bf98768ba0eea21d42e69a9dba36f67492d7d Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 8 Apr 2024 16:43:43 +0200 Subject: [PATCH 133/142] Re-run Chapter 08 files with Python 3.12 --- 08_mfr/00_content.ipynb | 4 ++-- 08_mfr/01_content.ipynb | 26 +++++++++++++------------- 08_mfr/02_exercises.ipynb | 2 +- 08_mfr/03_exercises.ipynb | 2 +- 08_mfr/04_content.ipynb | 14 +++++++------- 08_mfr/05_summary.ipynb | 2 +- 08_mfr/06_review.ipynb | 2 +- 7 files changed, 26 insertions(+), 26 deletions(-) diff --git a/08_mfr/00_content.ipynb b/08_mfr/00_content.ipynb index 1884515..bcb7f04 100644 --- a/08_mfr/00_content.ipynb +++ b/08_mfr/00_content.ipynb @@ -1321,7 +1321,7 @@ }, "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 and **3** `list` objects\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&curstr=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 and **3** `list` objects\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 and **no** `list` object\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 and **no** `list` object\n", "\n", @@ -1349,7 +1349,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/08_mfr/01_content.ipynb b/08_mfr/01_content.ipynb index 688d953..a92bd6e 100644 --- a/08_mfr/01_content.ipynb +++ b/08_mfr/01_content.ipynb @@ -156,7 +156,7 @@ "source": [ "A list comprehension may always be used in a place where otherwise a `list` object would work.\n", "\n", - "For example, let's rewrite the \"*A simple Filter*\" example from [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb#Example:-A-simple-Filter) in just one line. As a caveat, the code below *materializes* all elements in memory *before* summing them up, and may, therefore, cause a `MemoryError` when executed with a bigger `numbers` list. We see with [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) how a `list` object exists in memory at step 17 and then \"gets lost\" right after. As the next section shows, this downside may be mitigated." + "For example, let's rewrite the \"*A simple Filter*\" example from [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb#Example:-A-simple-Filter) in just one line. As a caveat, the code below *materializes* all elements in memory *before* summing them up, and may, therefore, cause a `MemoryError` when executed with a bigger `numbers` list. We see with [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&curstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) how a `list` object exists in memory at step 17 and then \"gets lost\" right after. As the next section shows, this downside may be mitigated." ] }, { @@ -932,7 +932,7 @@ } }, "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) as in the original \"*A simple Filter*\" example. [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 do so (cf., step 17)." + "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) as in the original \"*A simple Filter*\" example. [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&curstr=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 do so (cf., step 17)." ] }, { @@ -1224,7 +1224,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", + "Cell \u001b[0;32mIn[36], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;43mnext\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mgen\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mStopIteration\u001b[0m: " ] } @@ -1249,7 +1249,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", + "Cell \u001b[0;32mIn[37], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;43mnext\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mgen\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mStopIteration\u001b[0m: " ] } @@ -1417,7 +1417,7 @@ } }, "source": [ - "Because `nested_numbers` has an internal structure (i.e., the inner `list` objects are series of consecutive `int` objects), we can even provide an effectively **memoryless** implementation 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` objects materialize at any point in time." + "Because `nested_numbers` has an internal structure (i.e., the inner `list` objects are series of consecutive `int` objects), we can even provide an effectively **memoryless** implementation 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&curstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) confirms that no `list` objects materialize at any point in time." ] }, { @@ -1489,7 +1489,7 @@ } }, "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." + "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&curstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) also shows that." ] }, { @@ -1564,7 +1564,7 @@ "source": [ "Now, the first of the two alternative solutions may be more appealing to many readers. In general, many practitioners seem to dislike `lambda` expressions.\n", "\n", - "In the first solution, we *unpack* the elements produced by `(1 + (x / y) for x in first for y in second)` into the `product()` function from the \"*Function Definitions & Calls*\" sub-section in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/03_content.ipynb#Function-Definitions-&-Calls). However, inside `product()`, the elements are *packed* into `args`, a *materialized* `tuple` object! So, all the memory efficiency gained by using a generator expression is lost! [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*%281%20%2B%20%28x%20/%20y%29%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." + "In the first solution, we *unpack* the elements produced by `(1 + (x / y) for x in first for y in second)` into the `product()` function from the \"*Function Definitions & Calls*\" sub-section in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/03_content.ipynb#Function-Definitions-&-Calls). However, inside `product()`, the elements are *packed* into `args`, a *materialized* `tuple` object! So, all the memory efficiency gained by using a generator expression is lost! [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*%281%20%2B%20%28x%20/%20y%29%20for%20x%20in%20first%20for%20y%20in%20second%29%29&cumulative=false&curstr=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." ] }, { @@ -1599,7 +1599,7 @@ } }, "source": [ - "On the contrary, the second 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* the 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%281%20%2B%20%28x%20/%20y%29%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!" + "On the contrary, the second 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* the 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%281%20%2B%20%28x%20/%20y%29%20for%20x%20in%20first%20for%20y%20in%20second%29%0A%29&cumulative=false&curstr=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!" ] }, { @@ -1871,14 +1871,14 @@ "outputs": [ { "ename": "TypeError", - "evalue": "reduce() of empty sequence with no initial value", + "evalue": "reduce() of empty iterable 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, scalar)\u001b[0m\n\u001b[1;32m 11\u001b[0m \"\"\"\n\u001b[1;32m 12\u001b[0m \u001b[0mintegers\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mround\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mn\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mn\u001b[0m \u001b[0;32min\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;32m---> 13\u001b[0;31m total, count = reduce(\n\u001b[0m\u001b[1;32m 14\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[1;32m 15\u001b[0m \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[0;31mTypeError\u001b[0m: reduce() of empty sequence with no initial value" + "Cell \u001b[0;32mIn[56], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43maverage_evens\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mrandom\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrandint\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m49\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43m_\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43mrange\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m10_000_000\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[49], line 13\u001b[0m, in \u001b[0;36maverage_evens\u001b[0;34m(numbers, scalar)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Calculate the average of all even numbers.\u001b[39;00m\n\u001b[1;32m 3\u001b[0m \n\u001b[1;32m 4\u001b[0m \u001b[38;5;124;03mArgs:\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[38;5;124;03m float: (scaled) average\u001b[39;00m\n\u001b[1;32m 11\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 12\u001b[0m integers \u001b[38;5;241m=\u001b[39m (\u001b[38;5;28mround\u001b[39m(n) \u001b[38;5;28;01mfor\u001b[39;00m n \u001b[38;5;129;01min\u001b[39;00m numbers)\n\u001b[0;32m---> 13\u001b[0m total, count \u001b[38;5;241m=\u001b[39m \u001b[43mreduce\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 14\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mlambda\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43my\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[43my\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mx\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[43my\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 15\u001b[0m \u001b[43m \u001b[49m\u001b[43m(\u001b[49m\u001b[43m(\u001b[49m\u001b[43mn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mn\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mintegers\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mn\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m%\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m==\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 16\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 17\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m scalar \u001b[38;5;241m*\u001b[39m total \u001b[38;5;241m/\u001b[39m count\n", + "\u001b[0;31mTypeError\u001b[0m: reduce() of empty iterable with no initial value" ] } ], @@ -2352,7 +2352,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/08_mfr/02_exercises.ipynb b/08_mfr/02_exercises.ipynb index 0911aaf..f6d81f8 100644 --- a/08_mfr/02_exercises.ipynb +++ b/08_mfr/02_exercises.ipynb @@ -739,7 +739,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/08_mfr/03_exercises.ipynb b/08_mfr/03_exercises.ipynb index b93761a..d4b0518 100644 --- a/08_mfr/03_exercises.ipynb +++ b/08_mfr/03_exercises.ipynb @@ -434,7 +434,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/08_mfr/04_content.ipynb b/08_mfr/04_content.ipynb index ff55376..8a3fd51 100644 --- a/08_mfr/04_content.ipynb +++ b/08_mfr/04_content.ipynb @@ -706,7 +706,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", + "Cell \u001b[0;32mIn[25], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;43mnext\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43miterator1\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mStopIteration\u001b[0m: " ] } @@ -926,7 +926,7 @@ "source": [ "Now that we know the concept of an *iterator*, let's compare some of the built-ins introduced in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb) in detail and make sure we understand what is going on in memory. This section also serves as a summary of all the concepts in this chapter.\n", "\n", - "We use two simple examples, `numbers` and `memoryless`: `numbers` creates *thirteen* objects in memory and `memoryless` only *one* (cf., [PythonTutor ](http://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`: `numbers` creates *thirteen* objects in memory and `memoryless` only *one* (cf., [PythonTutor ](http://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&curstr=2&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false))." ] }, { @@ -965,7 +965,7 @@ "source": [ "The [sorted() ](https://docs.python.org/3/library/functions.html#sorted) function takes a *finite* `iterable` 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 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://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://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&curstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false))." ] }, { @@ -1024,7 +1024,7 @@ } }, "source": [ - "By adding a keyword-only argument `reverse=True`, the materialized `list` objects are sorted in *reverse* order (cf., [PythonTutor ](http://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))." + "By adding a keyword-only argument `reverse=True`, the materialized `list` objects are sorted in *reverse* order (cf., [PythonTutor ](http://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&curstr=4&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false))." ] }, { @@ -1142,7 +1142,7 @@ } }, "source": [ - "The [reversed() ](https://docs.python.org/3/library/functions.html#reversed) built-in takes a `sequence` 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://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", + "The [reversed() ](https://docs.python.org/3/library/functions.html#reversed) built-in takes a `sequence` 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://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&curstr=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)." ] @@ -1203,7 +1203,7 @@ } }, "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) constructors. That creates *new* `list` and `tuple` objects (cf., [PythonTutor ](http://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", + "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) constructors. That creates *new* `list` and `tuple` objects (cf., [PythonTutor ](http://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&curstr=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." ] @@ -1640,7 +1640,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/08_mfr/05_summary.ipynb b/08_mfr/05_summary.ipynb index 361fbd0..28859bc 100644 --- a/08_mfr/05_summary.ipynb +++ b/08_mfr/05_summary.ipynb @@ -43,7 +43,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/08_mfr/06_review.ipynb b/08_mfr/06_review.ipynb index 2f3d29f..faf8ab8 100644 --- a/08_mfr/06_review.ipynb +++ b/08_mfr/06_review.ipynb @@ -193,7 +193,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, From 88c67873dd313646a7690067d79148d34d8b9cbf Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 8 Apr 2024 16:51:05 +0200 Subject: [PATCH 134/142] Re-run Chapter 09 files with Python 3.12 --- 09_mappings/00_content.ipynb | 14 +++++++------- 09_mappings/01_exercises.ipynb | 2 +- 09_mappings/02_content.ipynb | 27 +++++++++++++-------------- 09_mappings/03_exercises.ipynb | 2 +- 09_mappings/04_content.ipynb | 12 ++++++------ 09_mappings/05_appendix.ipynb | 32 ++++++++++++++++---------------- 09_mappings/06_summary.ipynb | 2 +- 09_mappings/07_review.ipynb | 2 +- 09_mappings/08_resources.ipynb | 5 ++++- 9 files changed, 50 insertions(+), 48 deletions(-) diff --git a/09_mappings/00_content.ipynb b/09_mappings/00_content.ipynb index b20e7ec..4e97131 100644 --- a/09_mappings/00_content.ipynb +++ b/09_mappings/00_content.ipynb @@ -571,7 +571,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 {\n\u001b[0m\u001b[1;32m 2\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;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[1;32m 3\u001b[0m }\n", + "Cell \u001b[0;32mIn[17], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m {\n\u001b[1;32m 2\u001b[0m [\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mzero\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mone\u001b[39m\u001b[38;5;124m\"\u001b[39m]: [\u001b[38;5;241m0\u001b[39m, \u001b[38;5;241m1\u001b[39m],\n\u001b[1;32m 3\u001b[0m }\n", "\u001b[0;31mTypeError\u001b[0m: unhashable type: 'list'" ] } @@ -837,7 +837,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[0mhash\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", + "Cell \u001b[0;32mIn[24], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;43mhash\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mzero\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mone\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mTypeError\u001b[0m: unhashable type: 'list'" ] } @@ -1272,7 +1272,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Python 3.8.6\n" + "Python 3.12.2\n" ] } ], @@ -1952,7 +1952,7 @@ "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", + "Cell \u001b[0;32mIn[61], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mfrom_words\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mdrei\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\n", "\u001b[0;31mKeyError\u001b[0m: 'drei'" ] } @@ -2840,7 +2840,7 @@ "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", + "Cell \u001b[0;32mIn[91], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mto_words\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m]\u001b[49m\n", "\u001b[0;31mKeyError\u001b[0m: 0" ] } @@ -3099,7 +3099,7 @@ "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", + "Cell \u001b[0;32mIn[101], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mfrom_words\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpop\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mzero\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mKeyError\u001b[0m: 'zero'" ] } @@ -3741,7 +3741,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/09_mappings/01_exercises.ipynb b/09_mappings/01_exercises.ipynb index 72b503d..82eae0d 100644 --- a/09_mappings/01_exercises.ipynb +++ b/09_mappings/01_exercises.ipynb @@ -407,7 +407,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/09_mappings/02_content.ipynb b/09_mappings/02_content.ipynb index 78ac6ff..e0b9083 100644 --- a/09_mappings/02_content.ipynb +++ b/09_mappings/02_content.ipynb @@ -394,7 +394,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[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", + "Cell \u001b[0;32mIn[13], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mprint_args2\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mTypeError\u001b[0m: print_args2() missing 1 required positional argument: 'positional'" ] } @@ -419,7 +419,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[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", + "Cell \u001b[0;32mIn[14], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mprint_args2\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mp\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mTypeError\u001b[0m: print_args2() missing 1 required keyword-only argument: 'keyword'" ] } @@ -1045,17 +1045,16 @@ "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)\\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~/repos/intro-to-python/.venv/lib/python3.8/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 2379\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 2380\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-> 2381\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 2382\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 2383\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~/repos/intro-to-python/.venv/lib/python3.8/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~/repos/intro-to-python/.venv/lib/python3.8/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 1171\u001b[0m \u001b[0;32mbreak\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1172\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1173\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 1174\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 1175\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/3.8.6/lib/python3.8/timeit.py\u001b[0m in \u001b[0;36mrepeat\u001b[0;34m(self, repeat, number)\u001b[0m\n\u001b[1;32m 203\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 204\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--> 205\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 206\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 207\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~/repos/intro-to-python/.venv/lib/python3.8/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 15\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 16\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 17\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 18\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 19\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 15\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 16\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 17\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 18\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 19\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mrecurse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "Cell \u001b[0;32mIn[32], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mget_ipython\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun_cell_magic\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mtimeit\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43m-n 1\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mfibonacci(9999)\u001b[39;49m\u001b[38;5;130;43;01m\\n\u001b[39;49;00m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/Repositories/intro-to-python/.venv/lib/python3.12/site-packages/IPython/core/interactiveshell.py:2541\u001b[0m, in \u001b[0;36mInteractiveShell.run_cell_magic\u001b[0;34m(self, magic_name, line, cell)\u001b[0m\n\u001b[1;32m 2539\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mbuiltin_trap:\n\u001b[1;32m 2540\u001b[0m args \u001b[38;5;241m=\u001b[39m (magic_arg_s, cell)\n\u001b[0;32m-> 2541\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2543\u001b[0m \u001b[38;5;66;03m# The code below prevents the output from being displayed\u001b[39;00m\n\u001b[1;32m 2544\u001b[0m \u001b[38;5;66;03m# when using magics with decorator @output_can_be_silenced\u001b[39;00m\n\u001b[1;32m 2545\u001b[0m \u001b[38;5;66;03m# when the last Python token in the expression is a ';'.\u001b[39;00m\n\u001b[1;32m 2546\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mgetattr\u001b[39m(fn, magic\u001b[38;5;241m.\u001b[39mMAGIC_OUTPUT_CAN_BE_SILENCED, \u001b[38;5;28;01mFalse\u001b[39;00m):\n", + "File \u001b[0;32m~/Repositories/intro-to-python/.venv/lib/python3.12/site-packages/IPython/core/magics/execution.py:1189\u001b[0m, in \u001b[0;36mExecutionMagics.timeit\u001b[0;34m(self, line, cell, local_ns)\u001b[0m\n\u001b[1;32m 1186\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m time_number \u001b[38;5;241m>\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0.2\u001b[39m:\n\u001b[1;32m 1187\u001b[0m \u001b[38;5;28;01mbreak\u001b[39;00m\n\u001b[0;32m-> 1189\u001b[0m all_runs \u001b[38;5;241m=\u001b[39m \u001b[43mtimer\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrepeat\u001b[49m\u001b[43m(\u001b[49m\u001b[43mrepeat\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnumber\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1190\u001b[0m best \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mmin\u001b[39m(all_runs) \u001b[38;5;241m/\u001b[39m number\n\u001b[1;32m 1191\u001b[0m worst \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mmax\u001b[39m(all_runs) \u001b[38;5;241m/\u001b[39m number\n", + "File \u001b[0;32m/usr/lib64/python3.12/timeit.py:208\u001b[0m, in \u001b[0;36mTimer.repeat\u001b[0;34m(self, repeat, number)\u001b[0m\n\u001b[1;32m 206\u001b[0m r \u001b[38;5;241m=\u001b[39m []\n\u001b[1;32m 207\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(repeat):\n\u001b[0;32m--> 208\u001b[0m t \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtimeit\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnumber\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 209\u001b[0m r\u001b[38;5;241m.\u001b[39mappend(t)\n\u001b[1;32m 210\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m r\n", + "File \u001b[0;32m~/Repositories/intro-to-python/.venv/lib/python3.12/site-packages/IPython/core/magics/execution.py:173\u001b[0m, in \u001b[0;36mTimer.timeit\u001b[0;34m(self, number)\u001b[0m\n\u001b[1;32m 171\u001b[0m gc\u001b[38;5;241m.\u001b[39mdisable()\n\u001b[1;32m 172\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 173\u001b[0m timing \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minner\u001b[49m\u001b[43m(\u001b[49m\u001b[43mit\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtimer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 174\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[1;32m 175\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m gcold:\n", + "File \u001b[0;32m:1\u001b[0m, in \u001b[0;36minner\u001b[0;34m(_it, _timer)\u001b[0m\n", + "Cell \u001b[0;32mIn[26], line 17\u001b[0m, in \u001b[0;36mfibonacci\u001b[0;34m(i, debug)\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m debug: \u001b[38;5;66;03m# added for didactical purposes\u001b[39;00m\n\u001b[1;32m 15\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfibonacci(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mi\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) is calculated\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m---> 17\u001b[0m recurse \u001b[38;5;241m=\u001b[39m \u001b[43mfibonacci\u001b[49m\u001b[43m(\u001b[49m\u001b[43mi\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdebug\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdebug\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;241m+\u001b[39m fibonacci(i \u001b[38;5;241m-\u001b[39m \u001b[38;5;241m2\u001b[39m, debug\u001b[38;5;241m=\u001b[39mdebug)\n\u001b[1;32m 18\u001b[0m memo[i] \u001b[38;5;241m=\u001b[39m recurse\n\u001b[1;32m 19\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m recurse\n", + "Cell \u001b[0;32mIn[26], line 17\u001b[0m, in \u001b[0;36mfibonacci\u001b[0;34m(i, debug)\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m debug: \u001b[38;5;66;03m# added for didactical purposes\u001b[39;00m\n\u001b[1;32m 15\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfibonacci(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mi\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) is calculated\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m---> 17\u001b[0m recurse \u001b[38;5;241m=\u001b[39m \u001b[43mfibonacci\u001b[49m\u001b[43m(\u001b[49m\u001b[43mi\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdebug\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdebug\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;241m+\u001b[39m fibonacci(i \u001b[38;5;241m-\u001b[39m \u001b[38;5;241m2\u001b[39m, debug\u001b[38;5;241m=\u001b[39mdebug)\n\u001b[1;32m 18\u001b[0m memo[i] \u001b[38;5;241m=\u001b[39m recurse\n\u001b[1;32m 19\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m recurse\n", + " \u001b[0;31m[... skipping similar frames: fibonacci at line 17 (2969 times)]\u001b[0m\n", + "Cell \u001b[0;32mIn[26], line 17\u001b[0m, in \u001b[0;36mfibonacci\u001b[0;34m(i, debug)\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m debug: \u001b[38;5;66;03m# added for didactical purposes\u001b[39;00m\n\u001b[1;32m 15\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfibonacci(\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mi\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m) is calculated\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m---> 17\u001b[0m recurse \u001b[38;5;241m=\u001b[39m \u001b[43mfibonacci\u001b[49m\u001b[43m(\u001b[49m\u001b[43mi\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m-\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdebug\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mdebug\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;241m+\u001b[39m fibonacci(i \u001b[38;5;241m-\u001b[39m \u001b[38;5;241m2\u001b[39m, debug\u001b[38;5;241m=\u001b[39mdebug)\n\u001b[1;32m 18\u001b[0m memo[i] \u001b[38;5;241m=\u001b[39m recurse\n\u001b[1;32m 19\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m recurse\n", "\u001b[0;31mRecursionError\u001b[0m: maximum recursion depth exceeded" ] } @@ -1205,7 +1204,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/09_mappings/03_exercises.ipynb b/09_mappings/03_exercises.ipynb index ea10e8f..1ef6bf8 100644 --- a/09_mappings/03_exercises.ipynb +++ b/09_mappings/03_exercises.ipynb @@ -227,7 +227,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/09_mappings/04_content.ipynb b/09_mappings/04_content.ipynb index 06e0f7f..6b97bf7 100644 --- a/09_mappings/04_content.ipynb +++ b/09_mappings/04_content.ipynb @@ -315,7 +315,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;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", + "Cell \u001b[0;32mIn[10], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m {\u001b[38;5;241m0\u001b[39m, [\u001b[38;5;241m1\u001b[39m, \u001b[38;5;241m2\u001b[39m], \u001b[38;5;241m3\u001b[39m}\n", "\u001b[0;31mTypeError\u001b[0m: unhashable type: 'list'" ] } @@ -419,7 +419,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;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", + "Cell \u001b[0;32mIn[13], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m number \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28;43mreversed\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mnumbers\u001b[49m\u001b[43m)\u001b[49m:\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28mprint\u001b[39m(number, end\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m \u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", "\u001b[0;31mTypeError\u001b[0m: 'set' object is not reversible" ] } @@ -646,7 +646,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;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[21], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mnumbers\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m]\u001b[49m\n", "\u001b[0;31mTypeError\u001b[0m: 'set' object is not subscriptable" ] } @@ -671,7 +671,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;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[22], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mnumbers\u001b[49m\u001b[43m[\u001b[49m\u001b[43m:\u001b[49m\u001b[43m]\u001b[49m\n", "\u001b[0;31mTypeError\u001b[0m: 'set' object is not subscriptable" ] } @@ -829,7 +829,7 @@ "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;36m999\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[28], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mnumbers\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mremove\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m999\u001b[39;49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mKeyError\u001b[0m: 999" ] } @@ -1573,7 +1573,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/09_mappings/05_appendix.ipynb b/09_mappings/05_appendix.ipynb index fbe47e3..95050b6 100644 --- a/09_mappings/05_appendix.ipynb +++ b/09_mappings/05_appendix.ipynb @@ -463,11 +463,11 @@ { "data": { "text/plain": [ - "Counter({'Müller': 1,\n", - " 'Klose': 1,\n", - " 'Kroos': 2,\n", - " 'Khedira': 1,\n", + "Counter({'Kroos': 2,\n", " 'Schürrle': 2,\n", + " 'Müller': 1,\n", + " 'Klose': 1,\n", + " 'Khedira': 1,\n", " 'Oscar': 1})" ] }, @@ -647,11 +647,11 @@ { "data": { "text/plain": [ - "Counter({'Müller': 1,\n", - " 'Klose': 1,\n", - " 'Kroos': 2,\n", - " 'Khedira': 1,\n", + "Counter({'Kroos': 2,\n", " 'Schürrle': 2,\n", + " 'Müller': 1,\n", + " 'Klose': 1,\n", + " 'Khedira': 1,\n", " 'Oscar': 1,\n", " 'Lahm': 1})" ] @@ -701,11 +701,11 @@ { "data": { "text/plain": [ - "Counter({'Müller': 1,\n", - " 'Klose': 1,\n", - " 'Kroos': 2,\n", - " 'Khedira': 1,\n", + "Counter({'Kroos': 2,\n", " 'Schürrle': 2,\n", + " 'Müller': 1,\n", + " 'Klose': 1,\n", + " 'Khedira': 1,\n", " 'Oscar': 1,\n", " 'Lahm': 1,\n", " 'L': 1,\n", @@ -910,9 +910,9 @@ "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/3.8.6/lib/python3.8/collections/__init__.py\u001b[0m in \u001b[0;36m__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 896\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 897\u001b[0m \u001b[0;32mpass\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 898\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 899\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 900\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/3.8.6/lib/python3.8/collections/__init__.py\u001b[0m in \u001b[0;36m__missing__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 888\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 889\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--> 890\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 891\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 892\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", + "Cell \u001b[0;32mIn[28], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mchain\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m10\u001b[39;49m\u001b[43m]\u001b[49m\n", + "File \u001b[0;32m/usr/lib64/python3.12/collections/__init__.py:1014\u001b[0m, in \u001b[0;36mChainMap.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 1012\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m:\n\u001b[1;32m 1013\u001b[0m \u001b[38;5;28;01mpass\u001b[39;00m\n\u001b[0;32m-> 1014\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__missing__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mkey\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m/usr/lib64/python3.12/collections/__init__.py:1006\u001b[0m, in \u001b[0;36mChainMap.__missing__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 1005\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__missing__\u001b[39m(\u001b[38;5;28mself\u001b[39m, key):\n\u001b[0;32m-> 1006\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mKeyError\u001b[39;00m(key)\n", "\u001b[0;31mKeyError\u001b[0m: 10" ] } @@ -938,7 +938,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/09_mappings/06_summary.ipynb b/09_mappings/06_summary.ipynb index 0b9c814..ef620b7 100644 --- a/09_mappings/06_summary.ipynb +++ b/09_mappings/06_summary.ipynb @@ -45,7 +45,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/09_mappings/07_review.ipynb b/09_mappings/07_review.ipynb index 7e7722d..a484886 100644 --- a/09_mappings/07_review.ipynb +++ b/09_mappings/07_review.ipynb @@ -226,7 +226,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/09_mappings/08_resources.ipynb b/09_mappings/08_resources.ipynb index 9b23588..c68eef1 100644 --- a/09_mappings/08_resources.ipynb +++ b/09_mappings/08_resources.ipynb @@ -67,6 +67,7 @@ " src=\"https://www.youtube.com/embed/C4Kc8xzcA68\"\n", " frameborder=\"0\"\n", " allowfullscreen\n", + " \n", " >\n", " " ], @@ -104,6 +105,7 @@ " src=\"https://www.youtube.com/embed/66P5FMkWoVU\"\n", " frameborder=\"0\"\n", " allowfullscreen\n", + " \n", " >\n", " " ], @@ -163,6 +165,7 @@ " src=\"https://www.youtube.com/embed/npw4s1QTmPg\"\n", " frameborder=\"0\"\n", " allowfullscreen\n", + " \n", " >\n", " " ], @@ -196,7 +199,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", From 1902eb2a503d245862c322f8e0baea888413cdac Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 8 Apr 2024 16:53:41 +0200 Subject: [PATCH 135/142] Add new Chapter 10 on Arrays and DataFrames --- 10_arrays/TODO.md | 1 + CONTENTS.md | 1 + README.md | 1 + 3 files changed, 3 insertions(+) create mode 100644 10_arrays/TODO.md diff --git a/10_arrays/TODO.md b/10_arrays/TODO.md new file mode 100644 index 0000000..9902b8f --- /dev/null +++ b/10_arrays/TODO.md @@ -0,0 +1 @@ +Here, a new section on `numpy` Arrays and, maybe, `pandas` DataFrames will be added. \ No newline at end of file diff --git a/CONTENTS.md b/CONTENTS.md index bdad0d7..46e16ce 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -262,6 +262,7 @@ If this is not possible, - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/07_review.ipynb) - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/08_resources.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/08_resources.ipynb) + - *Chapter 10*: Arrays & Dataframes - *Chapter 11*: Classes & Instances - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/00_content.ipynb) diff --git a/README.md b/README.md index 49ae3d0..f3fffd8 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ For a more *detailed version* with **clickable links** - *Chapter 7*: Sequential Data - *Chapter 8*: Map, Filter, & Reduce - *Chapter 9*: Mappings & Sets + - *Chapter 10*: Arrays & Dataframes - *Chapter 11*: Classes & Instances From c206a75f3cb01f1bdd5fafa7a42354e631d77417 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 8 Apr 2024 17:03:29 +0200 Subject: [PATCH 136/142] Re-run Chapter 11 files with Python 3.12 --- 11_classes/00_content.ipynb | 61 +++--- 11_classes/01_exercises.ipynb | 2 +- 11_classes/02_content.ipynb | 6 +- 11_classes/03_content.ipynb | 41 ++-- 11_classes/04_content.ipynb | 390 +++++++++++++++++----------------- 11_classes/05_summary.ipynb | 2 +- 11_classes/06_review.ipynb | 2 +- 7 files changed, 256 insertions(+), 248 deletions(-) diff --git a/11_classes/00_content.ipynb b/11_classes/00_content.ipynb index 34e3df3..b910095 100644 --- a/11_classes/00_content.ipynb +++ b/11_classes/00_content.ipynb @@ -212,7 +212,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[0mA\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[5], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mA\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mx\u001b[49m\n", "\u001b[0;31mTypeError\u001b[0m: can't multiply sequence by non-int of type 'tuple'" ] } @@ -405,7 +405,7 @@ "\u001b[0;31mInit signature:\u001b[0m \u001b[0mVector\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;31mDocstring:\u001b[0m A one-dimensional vector from linear algebra.\n", "\u001b[0;31mType:\u001b[0m type\n", - "\u001b[0;31mSubclasses:\u001b[0m \n" + "\u001b[0;31mSubclasses:\u001b[0m " ] }, "metadata": {}, @@ -433,24 +433,24 @@ "\n", "class Vector(builtins.object)\n", " | A one-dimensional vector from linear algebra.\n", - " | \n", + " |\n", " | Methods defined here:\n", - " | \n", + " |\n", " | dummy_method(self)\n", " | A dummy method for illustration purposes.\n", - " | \n", + " |\n", " | ----------------------------------------------------------------------\n", " | Data descriptors defined here:\n", - " | \n", + " |\n", " | __dict__\n", - " | dictionary for instance variables (if defined)\n", - " | \n", + " | dictionary for instance variables\n", + " |\n", " | __weakref__\n", - " | list of weak references to the object (if defined)\n", - " | \n", + " | list of weak references to the object\n", + " |\n", " | ----------------------------------------------------------------------\n", " | Data and other attributes defined here:\n", - " | \n", + " |\n", " | dummy_variable = 'I am a vector'\n", "\n" ] @@ -521,6 +521,7 @@ " '__format__',\n", " '__ge__',\n", " '__getattribute__',\n", + " '__getstate__',\n", " '__gt__',\n", " '__hash__',\n", " '__init__',\n", @@ -632,13 +633,13 @@ "outputs": [ { "ename": "TypeError", - "evalue": "dummy_method() missing 1 required positional argument: 'self'", + "evalue": "Vector.dummy_method() missing 1 required positional argument: 'self'", "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[0mVector\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdummy_method\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: dummy_method() missing 1 required positional argument: 'self'" + "Cell \u001b[0;32mIn[16], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mVector\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdummy_method\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[0;31mTypeError\u001b[0m: Vector.dummy_method() missing 1 required positional argument: 'self'" ] } ], @@ -859,13 +860,13 @@ "outputs": [ { "ename": "TypeError", - "evalue": "__init__() missing 1 required positional argument: 'data'", + "evalue": "Vector.__init__() missing 1 required positional argument: 'data'", "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[0mVector\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: __init__() missing 1 required positional argument: 'data'" + "Cell \u001b[0;32mIn[22], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mVector\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[0;31mTypeError\u001b[0m: Vector.__init__() missing 1 required positional argument: 'data'" ] } ], @@ -884,13 +885,13 @@ "outputs": [ { "ename": "TypeError", - "evalue": "__init__() takes 2 positional arguments but 4 were given", + "evalue": "Vector.__init__() takes 2 positional arguments but 4 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[0mVector\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;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: __init__() takes 2 positional arguments but 4 were given" + "Cell \u001b[0;32mIn[23], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mVector\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m3\u001b[39;49m\u001b[43m)\u001b[49m\n", + "\u001b[0;31mTypeError\u001b[0m: Vector.__init__() takes 2 positional arguments but 4 were given" ] } ], @@ -925,8 +926,8 @@ "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[0mVector\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, data)\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfloat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mdata\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[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\u001b[0m\u001b[0;34m)\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---> 18\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"a vector must have at least one entry\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[24], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mVector\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[17], line 18\u001b[0m, in \u001b[0;36mVector.__init__\u001b[0;34m(self, data)\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_entries \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(\u001b[38;5;28mfloat\u001b[39m(x) \u001b[38;5;28;01mfor\u001b[39;00m x \u001b[38;5;129;01min\u001b[39;00m data)\n\u001b[1;32m 17\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_entries) \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[0;32m---> 18\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124ma vector must have at least one entry\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", "\u001b[0;31mValueError\u001b[0m: a vector must have at least one entry" ] } @@ -1589,8 +1590,8 @@ "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[0mMatrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, data)\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"rows must have the same number of entries\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\u001b[0m\u001b[0;34m)\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----> 9\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"a matrix must have at least one entry\"\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 10\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__repr__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\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", + "Cell \u001b[0;32mIn[45], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mMatrix\u001b[49m\u001b[43m(\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[36], line 9\u001b[0m, in \u001b[0;36mMatrix.__init__\u001b[0;34m(self, data)\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrows must have the same number of entries\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 8\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_entries) \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[0;32m----> 9\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124ma matrix must have at least one entry\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", "\u001b[0;31mValueError\u001b[0m: a matrix must have at least one entry" ] } @@ -1615,8 +1616,8 @@ "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[0mMatrix\u001b[0m\u001b[0;34m(\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;36m3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m4\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[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, data)\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mrow\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\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[1;32m 6\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrow\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\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[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"rows must have the same number of entries\"\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 8\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\u001b[0m\u001b[0;34m)\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 9\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"a matrix must have at least one entry\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "Cell \u001b[0;32mIn[46], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mMatrix\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m3\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m4\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m5\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[36], line 7\u001b[0m, in \u001b[0;36mMatrix.__init__\u001b[0;34m(self, data)\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m row \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_entries[\u001b[38;5;241m1\u001b[39m:]:\n\u001b[1;32m 6\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(row) \u001b[38;5;241m!=\u001b[39m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_entries[\u001b[38;5;241m0\u001b[39m]):\n\u001b[0;32m----> 7\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrows must have the same number of entries\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 8\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_entries) \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[1;32m 9\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124ma matrix must have at least one entry\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", "\u001b[0;31mValueError\u001b[0m: rows must have the same number of entries" ] } @@ -2083,13 +2084,13 @@ "outputs": [ { "ename": "AttributeError", - "evalue": "can't set attribute", + "evalue": "property 'n_rows' of 'Matrix' object has no setter", "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[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_rows\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m: can't set attribute" + "Cell \u001b[0;32mIn[61], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mm\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mn_rows\u001b[49m \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m3\u001b[39m\n", + "\u001b[0;31mAttributeError\u001b[0m: property 'n_rows' of 'Matrix' object has no setter" ] } ], @@ -2114,7 +2115,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/11_classes/01_exercises.ipynb b/11_classes/01_exercises.ipynb index 915dbc9..cdc755c 100644 --- a/11_classes/01_exercises.ipynb +++ b/11_classes/01_exercises.ipynb @@ -1476,7 +1476,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, diff --git a/11_classes/02_content.ipynb b/11_classes/02_content.ipynb index 90d650e..fb8a6bd 100644 --- a/11_classes/02_content.ipynb +++ b/11_classes/02_content.ipynb @@ -346,7 +346,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[0mv\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\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", + "Cell \u001b[0;32mIn[10], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mv\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m]\u001b[49m \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m99\u001b[39m\n", "\u001b[0;31mTypeError\u001b[0m: 'Vector' object does not support item assignment" ] } @@ -1200,7 +1200,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[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtranspose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtranspose\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", + "Cell \u001b[0;32mIn[37], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mm\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtranspose\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtranspose\u001b[49m()\n", "\u001b[0;31mAttributeError\u001b[0m: 'NoneType' object has no attribute 'transpose'" ] } @@ -1951,7 +1951,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/11_classes/03_content.ipynb b/11_classes/03_content.ipynb index 32491d8..3d40e39 100644 --- a/11_classes/03_content.ipynb +++ b/11_classes/03_content.ipynb @@ -415,8 +415,8 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mRuntimeError\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[0mm\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_vector\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;36mas_vector\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 22\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mas_vector\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\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 23\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_rows\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m1\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_cols\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[0;32m---> 24\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"one dimension (m or n) must be 1\"\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 25\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mVector\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "Cell \u001b[0;32mIn[14], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mm\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mas_vector\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[2], line 24\u001b[0m, in \u001b[0;36mMatrix.as_vector\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 22\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mas_vector\u001b[39m(\u001b[38;5;28mself\u001b[39m):\n\u001b[1;32m 23\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m (\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mn_rows \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m1\u001b[39m \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mn_cols \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m1\u001b[39m):\n\u001b[0;32m---> 24\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mRuntimeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mone dimension (m or n) must be 1\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 25\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m Vector(x \u001b[38;5;28;01mfor\u001b[39;00m x \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m)\n", "\u001b[0;31mRuntimeError\u001b[0m: one dimension (m or n) must be 1" ] } @@ -889,8 +889,8 @@ "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[0mv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mw\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__mul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 45\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# dot product\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 46\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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---> 47\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"vectors must be of the same length\"\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 48\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0msum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0my\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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 49\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mNumber\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# scalar multiplication\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "Cell \u001b[0;32mIn[27], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mv\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mw\u001b[49m\n", + "Cell \u001b[0;32mIn[21], line 47\u001b[0m, in \u001b[0;36mVector.__mul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 45\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(other, Vector): \u001b[38;5;66;03m# dot product\u001b[39;00m\n\u001b[1;32m 46\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m) \u001b[38;5;241m!=\u001b[39m \u001b[38;5;28mlen\u001b[39m(other):\n\u001b[0;32m---> 47\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mvectors must be of the same length\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 48\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28msum\u001b[39m(x \u001b[38;5;241m*\u001b[39m y \u001b[38;5;28;01mfor\u001b[39;00m (x, y) \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mzip\u001b[39m(\u001b[38;5;28mself\u001b[39m, other))\n\u001b[1;32m 49\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(other, numbers\u001b[38;5;241m.\u001b[39mNumber): \u001b[38;5;66;03m# scalar multiplication\u001b[39;00m\n", "\u001b[0;31mValueError\u001b[0m: vectors must be of the same length" ] } @@ -1009,8 +1009,8 @@ "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[0mv\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mw\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__add__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# vector addition\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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---> 20\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"vectors must be of the same length\"\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 21\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mVector\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0my\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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 22\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnumbers\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mNumber\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# broadcasting addition\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "Cell \u001b[0;32mIn[31], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mv\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mw\u001b[49m\n", + "Cell \u001b[0;32mIn[21], line 20\u001b[0m, in \u001b[0;36mVector.__add__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(other, Vector): \u001b[38;5;66;03m# vector addition\u001b[39;00m\n\u001b[1;32m 19\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m) \u001b[38;5;241m!=\u001b[39m \u001b[38;5;28mlen\u001b[39m(other):\n\u001b[0;32m---> 20\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mvectors must be of the same length\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 21\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m Vector(x \u001b[38;5;241m+\u001b[39m y \u001b[38;5;28;01mfor\u001b[39;00m (x, y) \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mzip\u001b[39m(\u001b[38;5;28mself\u001b[39m, other))\n\u001b[1;32m 22\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(other, numbers\u001b[38;5;241m.\u001b[39mNumber): \u001b[38;5;66;03m# broadcasting addition\u001b[39;00m\n", "\u001b[0;31mValueError\u001b[0m: vectors must be of the same length" ] } @@ -1098,6 +1098,7 @@ " src=\"https://www.youtube.com/embed/OMA2Mwo0aZg\"\n", " frameborder=\"0\"\n", " allowfullscreen\n", + " \n", " >\n", " " ], @@ -1404,9 +1405,9 @@ "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[0mm\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mn\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__mul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 72\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_matrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_vector\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 73\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\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---> 74\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\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 75\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mNotImplemented\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 76\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36m_matrix_multiply\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 63\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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 64\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_cols\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_rows\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 65\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"matrices must have compatible dimensions\"\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 66\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mMatrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcols\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mrv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrows\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 67\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "Cell \u001b[0;32mIn[41], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mm\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mn\u001b[49m\n", + "Cell \u001b[0;32mIn[35], line 74\u001b[0m, in \u001b[0;36mMatrix.__mul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 72\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_matrix_multiply(other\u001b[38;5;241m.\u001b[39mas_matrix())\u001b[38;5;241m.\u001b[39mas_vector()\n\u001b[1;32m 73\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(other, Matrix):\n\u001b[0;32m---> 74\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_matrix_multiply\u001b[49m\u001b[43m(\u001b[49m\u001b[43mother\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 75\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mNotImplemented\u001b[39m\n", + "Cell \u001b[0;32mIn[35], line 65\u001b[0m, in \u001b[0;36mMatrix._matrix_multiply\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 63\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_matrix_multiply\u001b[39m(\u001b[38;5;28mself\u001b[39m, other):\n\u001b[1;32m 64\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mn_cols \u001b[38;5;241m!=\u001b[39m other\u001b[38;5;241m.\u001b[39mn_rows:\n\u001b[0;32m---> 65\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmatrices must have compatible dimensions\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 66\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m Matrix((rv \u001b[38;5;241m*\u001b[39m cv \u001b[38;5;28;01mfor\u001b[39;00m cv \u001b[38;5;129;01min\u001b[39;00m other\u001b[38;5;241m.\u001b[39mcols()) \u001b[38;5;28;01mfor\u001b[39;00m rv \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mrows())\n", "\u001b[0;31mValueError\u001b[0m: matrices must have compatible dimensions" ] } @@ -1514,9 +1515,9 @@ "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[0mv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mn\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__rmul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 79\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 80\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mVector\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---> 81\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_matrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcolumn\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_vector\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 82\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mNotImplemented\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 83\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36m_matrix_multiply\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 63\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_matrix_multiply\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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 64\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_cols\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mn_rows\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 65\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"matrices must have compatible dimensions\"\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 66\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mMatrix\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrv\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mcv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcols\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mrv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrows\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 67\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "Cell \u001b[0;32mIn[45], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mv\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mn\u001b[49m\n", + "Cell \u001b[0;32mIn[35], line 81\u001b[0m, in \u001b[0;36mMatrix.__rmul__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 79\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m \u001b[38;5;241m*\u001b[39m other\n\u001b[1;32m 80\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(other, Vector):\n\u001b[0;32m---> 81\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mother\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mas_matrix\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcolumn\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_matrix_multiply\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m)\u001b[49m\u001b[38;5;241m.\u001b[39mas_vector()\n\u001b[1;32m 82\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mNotImplemented\u001b[39m\n", + "Cell \u001b[0;32mIn[35], line 65\u001b[0m, in \u001b[0;36mMatrix._matrix_multiply\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 63\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_matrix_multiply\u001b[39m(\u001b[38;5;28mself\u001b[39m, other):\n\u001b[1;32m 64\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mn_cols \u001b[38;5;241m!=\u001b[39m other\u001b[38;5;241m.\u001b[39mn_rows:\n\u001b[0;32m---> 65\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmatrices must have compatible dimensions\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 66\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m Matrix((rv \u001b[38;5;241m*\u001b[39m cv \u001b[38;5;28;01mfor\u001b[39;00m cv \u001b[38;5;129;01min\u001b[39;00m other\u001b[38;5;241m.\u001b[39mcols()) \u001b[38;5;28;01mfor\u001b[39;00m rv \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mrows())\n", "\u001b[0;31mValueError\u001b[0m: matrices must have compatible dimensions" ] } @@ -1611,9 +1612,9 @@ "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[0mm\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mv\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__radd__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 25\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 26\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__radd__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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---> 27\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 28\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 29\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__sub__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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__radd__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 40\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__radd__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mother\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 41\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mother\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mVector\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---> 42\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mTypeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"vectors and matrices cannot be added\"\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 43\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mother\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 44\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "Cell \u001b[0;32mIn[48], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mm\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mv\u001b[49m\n", + "Cell \u001b[0;32mIn[21], line 27\u001b[0m, in \u001b[0;36mVector.__radd__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 26\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__radd__\u001b[39m(\u001b[38;5;28mself\u001b[39m, other):\n\u001b[0;32m---> 27\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mother\u001b[49m\n", + "Cell \u001b[0;32mIn[35], line 42\u001b[0m, in \u001b[0;36mMatrix.__radd__\u001b[0;34m(self, other)\u001b[0m\n\u001b[1;32m 40\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__radd__\u001b[39m(\u001b[38;5;28mself\u001b[39m, other):\n\u001b[1;32m 41\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(other, Vector):\n\u001b[0;32m---> 42\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mvectors and matrices cannot be added\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 43\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m \u001b[38;5;241m+\u001b[39m other\n", "\u001b[0;31mTypeError\u001b[0m: vectors and matrices cannot be added" ] } @@ -1933,7 +1934,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;34m-\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[59], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;241;43m-\u001b[39;49m\u001b[43mv\u001b[49m\n", "\u001b[0;31mTypeError\u001b[0m: bad operand type for unary -: 'Vector'" ] } @@ -1969,7 +1970,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[0mabs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[60], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;43mabs\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mv\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mTypeError\u001b[0m: bad operand type for abs(): 'Vector'" ] } @@ -2353,8 +2354,8 @@ "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mRuntimeError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mfloat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv\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__float__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 29\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__float__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\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 30\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\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;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 31\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"vector must have exactly one entry to become a scalar\"\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 32\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\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", + "Cell \u001b[0;32mIn[75], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;43mfloat\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mv\u001b[49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[63], line 31\u001b[0m, in \u001b[0;36mVector.__float__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 29\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__float__\u001b[39m(\u001b[38;5;28mself\u001b[39m):\n\u001b[1;32m 30\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(\u001b[38;5;28mself\u001b[39m) \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m1\u001b[39m:\n\u001b[0;32m---> 31\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mRuntimeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mvector must have exactly one entry to become a scalar\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 32\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_entries[\u001b[38;5;241m0\u001b[39m]\n", "\u001b[0;31mRuntimeError\u001b[0m: vector must have exactly one entry to become a scalar" ] } @@ -2380,7 +2381,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/11_classes/04_content.ipynb b/11_classes/04_content.ipynb index b495a9a..9f563fe 100644 --- a/11_classes/04_content.ipynb +++ b/11_classes/04_content.ipynb @@ -74,7 +74,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "/home/webartifex/repos/intro-to-python/11_classes\n" + "/home/alexander/Repositories/intro-to-python/11_classes\n" ] } ], @@ -106,8 +106,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "00_content.ipynb 02_content.ipynb 04_content.ipynb\n", - "01_exercises.ipynb 03_content.ipynb sample_package\n" + "00_content.ipynb 02_content.ipynb 04_content.ipynb\t06_review.ipynb\n", + "01_exercises.ipynb 03_content.ipynb 05_summary.ipynb\tsample_package\n" ] } ], @@ -202,7 +202,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 5, @@ -324,9 +324,9 @@ " - vector: defines the Vector class\n", " - utils: defines the norm() function that is shared by Matrix and Vector\n", " and package-wide constants\n", - " \n", + "\n", " The classes implement arithmetic operations involving vectors and matrices.\n", - " \n", + "\n", " See the docstrings in the modules and classes for further info.\n", "\n", "PACKAGE CONTENTS\n", @@ -338,14 +338,14 @@ " builtins.object\n", " sample_package.matrix.Matrix\n", " sample_package.vector.Vector\n", - " \n", + "\n", " class Matrix(builtins.object)\n", " | Matrix(data)\n", - " | \n", + " |\n", " | An m-by-n-dimensional matrix from linear algebra.\n", - " | \n", + " |\n", " | All entries are converted to floats, or whatever is set in the typing attribute.\n", - " | \n", + " |\n", " | Attributes:\n", " | storage (callable): data type used to store the entries internally;\n", " | defaults to tuple\n", @@ -354,63 +354,63 @@ " | vector_cls (vector.Vector): a reference to the Vector class to work with\n", " | zero_threshold (float): max. tolerance when comparing an entry to zero;\n", " | defaults to 1e-12\n", - " | \n", + " |\n", " | Methods defined here:\n", - " | \n", + " |\n", " | __abs__(self)\n", " | The Frobenius norm of a Matrix.\n", - " | \n", + " |\n", " | __add__(self, other)\n", " | Handle `self + other` and `other + self`.\n", - " | \n", + " |\n", " | This may be either matrix addition or broadcasting addition.\n", - " | \n", + " |\n", " | Example Usage:\n", " | >>> Matrix([(1, 2), (3, 4)]) + Matrix([(2, 3), (4, 5)])\n", " | Matrix(((3.0, 5.0,), (7.0, 9.0,)))\n", - " | \n", + " |\n", " | >>> Matrix([(1, 2), (3, 4)]) + 5\n", " | Matrix(((6.0, 7.0,), (8.0, 9.0,)))\n", - " | \n", + " |\n", " | >>> 10 + Matrix([(1, 2), (3, 4)])\n", " | Matrix(((11.0, 12.0,), (13.0, 14.0,)))\n", - " | \n", + " |\n", " | __bool__(self)\n", " | A Matrix is truthy if its Frobenius norm is strictly positive.\n", - " | \n", + " |\n", " | __eq__(self, other)\n", " | Handle `self == other`.\n", - " | \n", + " |\n", " | Compare two Matrix instances for equality.\n", - " | \n", + " |\n", " | Example Usage:\n", " | >>> Matrix([(1, 2), (3, 4)]) == Matrix([(1, 2), (3, 4)])\n", " | True\n", - " | \n", + " |\n", " | >>> Matrix([(1, 2), (3, 4)]) == Matrix([(5, 6), (7, 8)])\n", " | False\n", - " | \n", + " |\n", " | __float__(self)\n", " | Cast a Matrix as a scalar.\n", - " | \n", + " |\n", " | Returns:\n", " | scalar (float)\n", - " | \n", + " |\n", " | Raises:\n", " | RuntimeError: if the Matrix has more than one entry\n", - " | \n", + " |\n", " | __getitem__(self, index)\n", " | Obtain an individual entry of a Matrix.\n", - " | \n", + " |\n", " | Args:\n", " | index (int / tuple of int's): if index is an integer,\n", " | the Matrix is viewed as a sequence in row-major order;\n", " | if index is a tuple of integers, the first one refers to\n", " | the row and the second one to the column of the entry\n", - " | \n", + " |\n", " | Returns:\n", " | entry (Matrix.typing)\n", - " | \n", + " |\n", " | Example Usage:\n", " | >>> m = Matrix([(1, 2), (3, 4)])\n", " | >>> m[0]\n", @@ -419,226 +419,228 @@ " | 4.0\n", " | >>> m[0, 1]\n", " | 2.0\n", - " | \n", + " |\n", " | __init__(self, data)\n", " | Create a new matrix.\n", - " | \n", + " |\n", " | Args:\n", " | data (sequence of sequences): the matrix's entries;\n", " | viewed as a sequence of the matrix's rows (i.e., row-major order);\n", " | use the .from_columns() class method if the data come as a sequence\n", " | of the matrix's columns (i.e., column-major order)\n", - " | \n", + " |\n", " | Raises:\n", " | ValueError:\n", " | - if no entries are provided\n", " | - if the number of columns is inconsistent across the rows\n", - " | \n", + " |\n", " | Example Usage:\n", " | >>> Matrix([(1, 2), (3, 4)])\n", " | Matrix(((1.0, 2.0,), (3.0, 4.0,)))\n", - " | \n", + " |\n", " | __iter__(self)\n", " | Loop over a Matrix's entries.\n", - " | \n", + " |\n", " | See .entries() for more customization options.\n", - " | \n", + " |\n", " | __len__(self)\n", " | Number of entries in a Matrix.\n", - " | \n", + " |\n", " | __mul__(self, other)\n", " | Handle `self * other` and `other * self`.\n", - " | \n", + " |\n", " | This may be either scalar multiplication, matrix-vector multiplication,\n", " | vector-matrix multiplication, or matrix-matrix multiplication.\n", - " | \n", + " |\n", " | Example Usage:\n", " | >>> Matrix([(1, 2), (3, 4)]) * Matrix([(1, 2), (3, 4)])\n", " | Matrix(((7.0, 10.0,), (15.0, 22.0,)))\n", - " | \n", + " |\n", " | >>> 2 * Matrix([(1, 2), (3, 4)])\n", " | Matrix(((2.0, 4.0,), (6.0, 8.0,)))\n", - " | \n", + " |\n", " | >>> Matrix([(1, 2), (3, 4)]) * 3\n", " | Matrix(((3.0, 6.0,), (9.0, 12.0,)))\n", - " | \n", + " |\n", " | Matrix-vector and vector-matrix multiplication are not commutative.\n", - " | \n", + " |\n", " | >>> from sample_package import Vector\n", - " | \n", + " |\n", " | >>> Matrix([(1, 2), (3, 4)]) * Vector([5, 6])\n", " | Vector((17.0, 39.0))\n", - " | \n", + " |\n", " | >>> Vector([5, 6]) * Matrix([(1, 2), (3, 4)])\n", " | Vector((23.0, 34.0))\n", - " | \n", + " |\n", " | __neg__(self)\n", " | Handle `-self`.\n", - " | \n", + " |\n", " | Negate all entries of a Matrix.\n", - " | \n", + " |\n", " | __pos__(self)\n", " | Handle `+self`.\n", - " | \n", + " |\n", " | This is simply an identity operator returning the Matrix itself.\n", - " | \n", + " |\n", " | __radd__(self, other)\n", " | See docstring for .__add__().\n", - " | \n", + " |\n", " | __repr__(self)\n", " | Text representation of a Matrix.\n", - " | \n", + " |\n", " | __reversed__(self)\n", " | Loop over a Matrix's entries in reverse order.\n", - " | \n", + " |\n", " | See .entries() for more customization options.\n", - " | \n", + " |\n", " | __rmul__(self, other)\n", " | See docstring for .__mul__().\n", - " | \n", + " |\n", " | __rsub__(self, other)\n", " | See docstring for .__sub__().\n", - " | \n", + " |\n", " | __str__(self)\n", " | Human-readable text representation of a Matrix.\n", - " | \n", + " |\n", " | __sub__(self, other)\n", " | Handle `self - other` and `other - self`.\n", - " | \n", + " |\n", " | This may be either matrix subtraction or broadcasting subtraction.\n", - " | \n", + " |\n", " | Example Usage:\n", " | >>> Matrix([(2, 3), (4, 5)]) - Matrix([(1, 2), (3, 4)])\n", " | Matrix(((1.0, 1.0,), (1.0, 1.0,)))\n", - " | \n", + " |\n", " | >>> Matrix([(1, 2), (3, 4)]) - 1\n", " | Matrix(((0.0, 1.0,), (2.0, 3.0,)))\n", - " | \n", + " |\n", " | >>> 10 - Matrix([(1, 2), (3, 4)])\n", " | Matrix(((9.0, 8.0,), (7.0, 6.0,)))\n", - " | \n", + " |\n", " | __truediv__(self, other)\n", " | Handle `self / other`.\n", - " | \n", + " |\n", " | Divide a Matrix by a scalar.\n", - " | \n", + " |\n", " | Example Usage:\n", " | >>> Matrix([(1, 2), (3, 4)]) / 4\n", " | Matrix(((0.25, 0.5,), (0.75, 1.0,)))\n", - " | \n", + " |\n", " | as_vector(self)\n", " | Get a Vector representation of a Matrix.\n", - " | \n", + " |\n", " | Returns:\n", " | vector (vector.Vector)\n", - " | \n", + " |\n", " | Raises:\n", " | RuntimeError: if one of the two dimensions, .n_rows or .n_cols, is not 1\n", - " | \n", + " |\n", " | Example Usage:\n", " | >>> Matrix([(1, 2, 3)]).as_vector()\n", " | Vector((1.0, 2.0, 3.0))\n", - " | \n", + " |\n", " | cols(self)\n", " | Loop over a Matrix's columns.\n", - " | \n", + " |\n", " | Returns:\n", " | columns (generator): produces a Matrix's columns as Vectors\n", - " | \n", + " |\n", " | entries(self, *, reverse=False, row_major=True)\n", " | Loop over a Matrix's entries.\n", - " | \n", + " |\n", " | Args:\n", " | reverse (bool): flag to loop backwards; defaults to False\n", " | row_major (bool): flag to loop in row-major order; defaults to True\n", - " | \n", + " |\n", " | Returns:\n", " | entries (generator): produces a Matrix's entries\n", - " | \n", + " |\n", " | rows(self)\n", " | Loop over a Matrix's rows.\n", - " | \n", + " |\n", " | Returns:\n", " | rows (generator): produces a Matrix's rows as Vectors\n", - " | \n", + " |\n", " | transpose(self)\n", " | Switch the rows and columns of a Matrix.\n", - " | \n", + " |\n", " | Returns:\n", " | matrix (Matrix)\n", - " | \n", + " |\n", " | Example Usage:\n", " | >>> m = Matrix([(1, 2), (3, 4)])\n", " | >>> m\n", " | Matrix(((1.0, 2.0,), (3.0, 4.0,)))\n", " | >>> m.transpose()\n", " | Matrix(((1.0, 3.0,), (2.0, 4.0,)))\n", - " | \n", + " |\n", " | ----------------------------------------------------------------------\n", " | Class methods defined here:\n", - " | \n", + " |\n", " | from_columns(data) from builtins.type\n", " | Create a new matrix.\n", - " | \n", + " |\n", " | This is an alternative constructor for data provided in column-major order.\n", - " | \n", + " |\n", " | Args:\n", " | data (sequence of sequences): the matrix's entries;\n", " | viewed as a sequence of the matrix's columns (i.e., column-major order);\n", " | use the normal constructor method if the data come as a sequence\n", " | of the matrix's rows (i.e., row-major order)\n", - " | \n", + " |\n", " | Raises:\n", " | ValueError:\n", " | - if no entries are provided\n", " | - if the number of rows is inconsistent across the columns\n", - " | \n", + " |\n", " | Example Usage:\n", " | >>> Matrix.from_columns([(1, 2), (3, 4)])\n", " | Matrix(((1.0, 3.0,), (2.0, 4.0,)))\n", - " | \n", + " |\n", " | from_rows(data) from builtins.type\n", " | See docstring for .__init__().\n", - " | \n", + " |\n", " | ----------------------------------------------------------------------\n", " | Readonly properties defined here:\n", - " | \n", + " |\n", " | n_cols\n", " | Number of columns in a Matrix.\n", - " | \n", + " |\n", " | n_rows\n", " | Number of rows in a Matrix.\n", - " | \n", + " |\n", " | ----------------------------------------------------------------------\n", " | Data descriptors defined here:\n", - " | \n", + " |\n", " | __dict__\n", - " | dictionary for instance variables (if defined)\n", - " | \n", + " | dictionary for instance variables\n", + " |\n", " | __weakref__\n", - " | list of weak references to the object (if defined)\n", - " | \n", + " | list of weak references to the object\n", + " |\n", " | ----------------------------------------------------------------------\n", " | Data and other attributes defined here:\n", - " | \n", + " |\n", " | __hash__ = None\n", - " | \n", + " |\n", " | storage = \n", " | Built-in immutable sequence.\n", - " | \n", + " |\n", " | If no argument is given, the constructor returns an empty tuple.\n", " | If iterable is specified the tuple is initialized from iterable's items.\n", - " | \n", + " |\n", " | If the argument is a tuple, the return value is the same object.\n", - " | \n", + " |\n", + " |\n", " | typing = \n", " | Convert a string or number to a floating point number, if possible.\n", - " | \n", + " |\n", + " |\n", " | vector_cls = \n", " | A one-dimensional vector from linear algebra.\n", - " | \n", + " |\n", " | All entries are converted to floats, or whatever is set in the typing attribute.\n", - " | \n", + " |\n", " | Attributes:\n", " | matrix_cls (matrix.Matrix): a reference to the Matrix class to work with\n", " | storage (callable): data type used to store the entries internally;\n", @@ -647,16 +649,17 @@ " | defaults to float\n", " | zero_threshold (float): max. tolerance when comparing an entry to zero;\n", " | defaults to 1e-12\n", - " | \n", + " |\n", + " |\n", " | zero_threshold = 1e-12\n", - " \n", + "\n", " class Vector(builtins.object)\n", " | Vector(data)\n", - " | \n", + " |\n", " | A one-dimensional vector from linear algebra.\n", - " | \n", + " |\n", " | All entries are converted to floats, or whatever is set in the typing attribute.\n", - " | \n", + " |\n", " | Attributes:\n", " | matrix_cls (matrix.Matrix): a reference to the Matrix class to work with\n", " | storage (callable): data type used to store the entries internally;\n", @@ -665,179 +668,179 @@ " | defaults to float\n", " | zero_threshold (float): max. tolerance when comparing an entry to zero;\n", " | defaults to 1e-12\n", - " | \n", + " |\n", " | Methods defined here:\n", - " | \n", + " |\n", " | __abs__(self)\n", " | The Euclidean norm of a vector.\n", - " | \n", + " |\n", " | __add__(self, other)\n", " | Handle `self + other` and `other + self`.\n", - " | \n", + " |\n", " | This may be either vector addition or broadcasting addition.\n", - " | \n", + " |\n", " | Example Usage:\n", " | >>> Vector([1, 2, 3]) + Vector([2, 3, 4])\n", " | Vector((3.0, 5.0, 7.0))\n", - " | \n", + " |\n", " | >>> Vector([1, 2, 3]) + 4\n", " | Vector((5.0, 6.0, 7.0))\n", - " | \n", + " |\n", " | >>> 10 + Vector([1, 2, 3])\n", " | Vector((11.0, 12.0, 13.0))\n", - " | \n", + " |\n", " | __bool__(self)\n", " | A Vector is truthy if its Euclidean norm is strictly positive.\n", - " | \n", + " |\n", " | __eq__(self, other)\n", " | Handle `self == other`.\n", - " | \n", + " |\n", " | Compare two Vectors for equality.\n", - " | \n", + " |\n", " | Example Usage:\n", " | >>> Vector([1, 2, 3]) == Vector([1, 2, 3])\n", " | True\n", - " | \n", + " |\n", " | >>> Vector([1, 2, 3]) == Vector([4, 5, 6])\n", " | False\n", - " | \n", + " |\n", " | __float__(self)\n", " | Cast a Vector as a scalar.\n", - " | \n", + " |\n", " | Returns:\n", " | scalar (float)\n", - " | \n", + " |\n", " | Raises:\n", " | RuntimeError: if the Vector has more than one entry\n", - " | \n", + " |\n", " | __getitem__(self, index)\n", " | Obtain an individual entry of a Vector.\n", - " | \n", + " |\n", " | __init__(self, data)\n", " | Create a new vector.\n", - " | \n", + " |\n", " | Args:\n", " | data (sequence): the vector's entries\n", - " | \n", + " |\n", " | Raises:\n", " | ValueError: if no entries are provided\n", - " | \n", + " |\n", " | Example Usage:\n", " | >>> Vector([1, 2, 3])\n", " | Vector((1.0, 2.0, 3.0))\n", - " | \n", + " |\n", " | >>> Vector(range(3))\n", " | Vector((0.0, 1.0, 2.0))\n", - " | \n", + " |\n", " | __iter__(self)\n", " | Loop over a Vector's entries.\n", - " | \n", + " |\n", " | __len__(self)\n", " | Number of entries in a Vector.\n", - " | \n", + " |\n", " | __mul__(self, other)\n", " | Handle `self * other` and `other * self`.\n", - " | \n", + " |\n", " | This may be either the dot product of two vectors or scalar multiplication.\n", - " | \n", + " |\n", " | Example Usage:\n", " | >>> Vector([1, 2, 3]) * Vector([2, 3, 4])\n", " | 20.0\n", - " | \n", + " |\n", " | >>> 2 * Vector([1, 2, 3])\n", " | Vector((2.0, 4.0, 6.0))\n", - " | \n", + " |\n", " | >>> Vector([1, 2, 3]) * 3\n", " | Vector((3.0, 6.0, 9.0))\n", - " | \n", + " |\n", " | __neg__(self)\n", " | Handle `-self`.\n", - " | \n", + " |\n", " | Negate all entries of a Vector.\n", - " | \n", + " |\n", " | __pos__(self)\n", " | Handle `+self`.\n", - " | \n", + " |\n", " | This is simply an identity operator returning the Vector itself.\n", - " | \n", + " |\n", " | __radd__(self, other)\n", " | See docstring for .__add__().\n", - " | \n", + " |\n", " | __repr__(self)\n", " | Text representation of a Vector.\n", - " | \n", + " |\n", " | __reversed__(self)\n", " | Loop over a Vector's entries in reverse order.\n", - " | \n", + " |\n", " | __rmul__(self, other)\n", " | See docstring for .__mul__().\n", - " | \n", + " |\n", " | __rsub__(self, other)\n", " | See docstring for .__sub__().\n", - " | \n", + " |\n", " | __str__(self)\n", " | Human-readable text representation of a Vector.\n", - " | \n", + " |\n", " | __sub__(self, other)\n", " | Handle `self - other` and `other - self`.\n", - " | \n", + " |\n", " | This may be either vector subtraction or broadcasting subtraction.\n", - " | \n", + " |\n", " | Example Usage:\n", " | >>> Vector([7, 8, 9]) - Vector([1, 2, 3])\n", " | Vector((6.0, 6.0, 6.0))\n", - " | \n", + " |\n", " | >>> Vector([1, 2, 3]) - 1\n", " | Vector((0.0, 1.0, 2.0))\n", - " | \n", + " |\n", " | >>> 10 - Vector([1, 2, 3])\n", " | Vector((9.0, 8.0, 7.0))\n", - " | \n", + " |\n", " | __truediv__(self, other)\n", " | Handle `self / other`.\n", - " | \n", + " |\n", " | Divide a Vector by a scalar.\n", - " | \n", + " |\n", " | Example Usage:\n", " | >>> Vector([9, 6, 12]) / 3\n", " | Vector((3.0, 2.0, 4.0))\n", - " | \n", + " |\n", " | as_matrix(self, *, column=True)\n", " | Get a Matrix representation of a Vector.\n", - " | \n", + " |\n", " | Args:\n", " | column (bool): if the vector is interpreted as a\n", " | column vector or a row vector; defaults to True\n", - " | \n", + " |\n", " | Returns:\n", " | matrix (matrix.Matrix)\n", - " | \n", + " |\n", " | Example Usage:\n", " | >>> v = Vector([1, 2, 3])\n", " | >>> v.as_matrix()\n", " | Matrix(((1.0,), (2.0,), (3.0,)))\n", " | >>> v.as_matrix(column=False)\n", " | Matrix(((1.0, 2.0, 3.0,)))\n", - " | \n", + " |\n", " | ----------------------------------------------------------------------\n", " | Data descriptors defined here:\n", - " | \n", + " |\n", " | __dict__\n", - " | dictionary for instance variables (if defined)\n", - " | \n", + " | dictionary for instance variables\n", + " |\n", " | __weakref__\n", - " | list of weak references to the object (if defined)\n", - " | \n", + " | list of weak references to the object\n", + " |\n", " | ----------------------------------------------------------------------\n", " | Data and other attributes defined here:\n", - " | \n", + " |\n", " | __hash__ = None\n", - " | \n", + " |\n", " | matrix_cls = \n", " | An m-by-n-dimensional matrix from linear algebra.\n", - " | \n", + " |\n", " | All entries are converted to floats, or whatever is set in the typing attribute.\n", - " | \n", + " |\n", " | Attributes:\n", " | storage (callable): data type used to store the entries internally;\n", " | defaults to tuple\n", @@ -846,18 +849,21 @@ " | vector_cls (vector.Vector): a reference to the Vector class to work with\n", " | zero_threshold (float): max. tolerance when comparing an entry to zero;\n", " | defaults to 1e-12\n", - " | \n", + " |\n", + " |\n", " | storage = \n", " | Built-in immutable sequence.\n", - " | \n", + " |\n", " | If no argument is given, the constructor returns an empty tuple.\n", " | If iterable is specified the tuple is initialized from iterable's items.\n", - " | \n", + " |\n", " | If the argument is a tuple, the return value is the same object.\n", - " | \n", + " |\n", + " |\n", " | typing = \n", " | Convert a string or number to a floating point number, if possible.\n", - " | \n", + " |\n", + " |\n", " | zero_threshold = 1e-12\n", "\n", "DATA\n", @@ -870,7 +876,7 @@ " Alexander Hess\n", "\n", "FILE\n", - " /home/webartifex/repos/intro-to-python/11_classes/sample_package/__init__.py\n", + " /home/alexander/Repositories/intro-to-python/11_classes/sample_package/__init__.py\n", "\n", "\n" ] @@ -1102,15 +1108,15 @@ "\n", "norm(vec_or_mat)\n", " Calculate the Frobenius or Euclidean norm of a matrix or vector.\n", - " \n", + "\n", " Find more infos here: https://en.wikipedia.org/wiki/Matrix_norm#Frobenius_norm\n", - " \n", + "\n", " Args:\n", " vec_or_mat (Vector / Matrix): object whose entries are squared and summed up\n", - " \n", + "\n", " Returns:\n", " norm (float)\n", - " \n", + "\n", " Example Usage:\n", " As Vector and Matrix objects are by design non-empty sequences,\n", " norm() may be called, for example, with `[3, 4]` as the argument:\n", @@ -1453,8 +1459,8 @@ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstorage\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtyping\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\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[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"a vector must have at least one entry\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mFile:\u001b[0m ~/repos/intro-to-python/11_classes/sample_package/vector.py\n", - "\u001b[0;31mType:\u001b[0m function\n" + "\u001b[0;31mFile:\u001b[0m ~/Repositories/intro-to-python/11_classes/sample_package/vector.py\n", + "\u001b[0;31mType:\u001b[0m function" ] }, "metadata": {}, @@ -1635,8 +1641,8 @@ "\u001b[0;34m Matrix(((1.0, 3.0,), (2.0, 4.0,)))\u001b[0m\n", "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\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;31mFile:\u001b[0m ~/repos/intro-to-python/11_classes/sample_package/matrix.py\n", - "\u001b[0;31mType:\u001b[0m function\n" + "\u001b[0;31mFile:\u001b[0m ~/Repositories/intro-to-python/11_classes/sample_package/matrix.py\n", + "\u001b[0;31mType:\u001b[0m function" ] }, "metadata": {}, @@ -1748,9 +1754,9 @@ "\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[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n", "\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[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrepr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mc\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mc\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mr\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\",)\"\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mr\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_entries\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", - "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;34mf\"{name}(({args}))\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mFile:\u001b[0m ~/repos/intro-to-python/11_classes/sample_package/matrix.py\n", - "\u001b[0;31mType:\u001b[0m function\n" + "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;34mf\"\u001b[0m\u001b[0;34m{\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m}\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[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mFile:\u001b[0m ~/Repositories/intro-to-python/11_classes/sample_package/matrix.py\n", + "\u001b[0;31mType:\u001b[0m function" ] }, "metadata": {}, @@ -1865,8 +1871,8 @@ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcolumn\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmatrix_cls\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmatrix_cls\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mx\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\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;31mFile:\u001b[0m ~/repos/intro-to-python/11_classes/sample_package/vector.py\n", - "\u001b[0;31mType:\u001b[0m function\n" + "\u001b[0;31mFile:\u001b[0m ~/Repositories/intro-to-python/11_classes/sample_package/vector.py\n", + "\u001b[0;31mType:\u001b[0m function" ] }, "metadata": {}, @@ -2122,7 +2128,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Vector(11378937.3105893, ..., 13593029.305789862)[100]\n" + "Vector(11378937.310589302, ..., 13593029.305789862)[100]\n" ] } ], @@ -2167,7 +2173,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Matrix((14370711.265265431, ...), ..., (..., 16545418.239505699))[100x100]\n" + "Matrix((14370711.26526542, ...), ..., (..., 16545418.239505697))[100x100]\n" ] } ], @@ -2201,7 +2207,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Matrix((32618511.50703142, ...), ..., (..., 32339164.77803234))[50x50]\n" + "Matrix((32618511.507031415, ...), ..., (..., 32339164.778032355))[50x50]\n" ] } ], @@ -2265,7 +2271,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[0mA\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[55], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mA\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mx\u001b[49m\n", "\u001b[0;31mTypeError\u001b[0m: can't multiply sequence by non-int of type 'tuple'" ] } @@ -2916,7 +2922,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[0mA_arr\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mA_mat\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "Cell \u001b[0;32mIn[79], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mA_arr\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mA_mat\u001b[49m\n", "\u001b[0;31mValueError\u001b[0m: operands could not be broadcast together with shapes (3,3) (9,) " ] } @@ -2953,7 +2959,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/11_classes/05_summary.ipynb b/11_classes/05_summary.ipynb index 47baf9f..163f711 100644 --- a/11_classes/05_summary.ipynb +++ b/11_classes/05_summary.ipynb @@ -68,7 +68,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "livereveal": { "auto_select": "code", diff --git a/11_classes/06_review.ipynb b/11_classes/06_review.ipynb index df7af79..0f54544 100644 --- a/11_classes/06_review.ipynb +++ b/11_classes/06_review.ipynb @@ -250,7 +250,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.12.2" }, "toc": { "base_numbering": 1, From 1ee63daeb9179a5c286a3862724df19b241f1f5a Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 8 Apr 2024 17:27:53 +0200 Subject: [PATCH 137/142] Rename exercise notebooks more clearly Add a short title/description into the filename of a notebook holding exercises. In the past, students often did not find the exercise files right away in class. --- ...ises.ipynb => 01_exercises_markdown.ipynb} | 0 ...ercises.ipynb => 01_exercises_print.ipynb} | 0 ...ses.ipynb => 02_exercises_for-loops.ipynb} | 0 ...es.ipynb => 04_exercises_calculator.ipynb} | 0 ...ipynb => 01_exercises_sphere-volume.ipynb} | 0 ...ses.ipynb => 01_exercises_discounts.ipynb} | 0 ...ses.ipynb => 02_exercises_fizz-buzz.ipynb} | 0 ....ipynb => 01_exercises_hanoi-towers.ipynb} | 0 ...xercises.ipynb => 04_exercises_dice.ipynb} | 0 ...s.ipynb => 01_exercises_palindromes.ipynb} | 0 ...ercises.ipynb => 02_exercises_lists.ipynb} | 0 ...es.ipynb => 04_exercises_un-packing.ipynb} | 0 ...ises.ipynb => 02_exercises_outliers.ipynb} | 0 ...es.ipynb => 03_exercises_un-packing.ipynb} | 0 ...s.ipynb => 01_exercises_nested-data.ipynb} | 0 ...ses.ipynb => 03_exercises_fibonacci.ipynb} | 0 ...exercises.ipynb => 01_exercises_tsp.ipynb} | 0 CONTENTS.md | 68 +++++++++---------- 18 files changed, 34 insertions(+), 34 deletions(-) rename 00_intro/{01_exercises.ipynb => 01_exercises_markdown.ipynb} (100%) rename 01_elements/{01_exercises.ipynb => 01_exercises_print.ipynb} (100%) rename 01_elements/{02_exercises.ipynb => 02_exercises_for-loops.ipynb} (100%) rename 01_elements/{04_exercises.ipynb => 04_exercises_calculator.ipynb} (100%) rename 02_functions/{01_exercises.ipynb => 01_exercises_sphere-volume.ipynb} (100%) rename 03_conditionals/{01_exercises.ipynb => 01_exercises_discounts.ipynb} (100%) rename 03_conditionals/{02_exercises.ipynb => 02_exercises_fizz-buzz.ipynb} (100%) rename 04_iteration/{01_exercises.ipynb => 01_exercises_hanoi-towers.ipynb} (100%) rename 04_iteration/{04_exercises.ipynb => 04_exercises_dice.ipynb} (100%) rename 06_text/{01_exercises.ipynb => 01_exercises_palindromes.ipynb} (100%) rename 07_sequences/{02_exercises.ipynb => 02_exercises_lists.ipynb} (100%) rename 07_sequences/{04_exercises.ipynb => 04_exercises_un-packing.ipynb} (100%) rename 08_mfr/{02_exercises.ipynb => 02_exercises_outliers.ipynb} (100%) rename 08_mfr/{03_exercises.ipynb => 03_exercises_un-packing.ipynb} (100%) rename 09_mappings/{01_exercises.ipynb => 01_exercises_nested-data.ipynb} (100%) rename 09_mappings/{03_exercises.ipynb => 03_exercises_fibonacci.ipynb} (100%) rename 11_classes/{01_exercises.ipynb => 01_exercises_tsp.ipynb} (100%) diff --git a/00_intro/01_exercises.ipynb b/00_intro/01_exercises_markdown.ipynb similarity index 100% rename from 00_intro/01_exercises.ipynb rename to 00_intro/01_exercises_markdown.ipynb diff --git a/01_elements/01_exercises.ipynb b/01_elements/01_exercises_print.ipynb similarity index 100% rename from 01_elements/01_exercises.ipynb rename to 01_elements/01_exercises_print.ipynb diff --git a/01_elements/02_exercises.ipynb b/01_elements/02_exercises_for-loops.ipynb similarity index 100% rename from 01_elements/02_exercises.ipynb rename to 01_elements/02_exercises_for-loops.ipynb diff --git a/01_elements/04_exercises.ipynb b/01_elements/04_exercises_calculator.ipynb similarity index 100% rename from 01_elements/04_exercises.ipynb rename to 01_elements/04_exercises_calculator.ipynb diff --git a/02_functions/01_exercises.ipynb b/02_functions/01_exercises_sphere-volume.ipynb similarity index 100% rename from 02_functions/01_exercises.ipynb rename to 02_functions/01_exercises_sphere-volume.ipynb diff --git a/03_conditionals/01_exercises.ipynb b/03_conditionals/01_exercises_discounts.ipynb similarity index 100% rename from 03_conditionals/01_exercises.ipynb rename to 03_conditionals/01_exercises_discounts.ipynb diff --git a/03_conditionals/02_exercises.ipynb b/03_conditionals/02_exercises_fizz-buzz.ipynb similarity index 100% rename from 03_conditionals/02_exercises.ipynb rename to 03_conditionals/02_exercises_fizz-buzz.ipynb diff --git a/04_iteration/01_exercises.ipynb b/04_iteration/01_exercises_hanoi-towers.ipynb similarity index 100% rename from 04_iteration/01_exercises.ipynb rename to 04_iteration/01_exercises_hanoi-towers.ipynb diff --git a/04_iteration/04_exercises.ipynb b/04_iteration/04_exercises_dice.ipynb similarity index 100% rename from 04_iteration/04_exercises.ipynb rename to 04_iteration/04_exercises_dice.ipynb diff --git a/06_text/01_exercises.ipynb b/06_text/01_exercises_palindromes.ipynb similarity index 100% rename from 06_text/01_exercises.ipynb rename to 06_text/01_exercises_palindromes.ipynb diff --git a/07_sequences/02_exercises.ipynb b/07_sequences/02_exercises_lists.ipynb similarity index 100% rename from 07_sequences/02_exercises.ipynb rename to 07_sequences/02_exercises_lists.ipynb diff --git a/07_sequences/04_exercises.ipynb b/07_sequences/04_exercises_un-packing.ipynb similarity index 100% rename from 07_sequences/04_exercises.ipynb rename to 07_sequences/04_exercises_un-packing.ipynb diff --git a/08_mfr/02_exercises.ipynb b/08_mfr/02_exercises_outliers.ipynb similarity index 100% rename from 08_mfr/02_exercises.ipynb rename to 08_mfr/02_exercises_outliers.ipynb diff --git a/08_mfr/03_exercises.ipynb b/08_mfr/03_exercises_un-packing.ipynb similarity index 100% rename from 08_mfr/03_exercises.ipynb rename to 08_mfr/03_exercises_un-packing.ipynb diff --git a/09_mappings/01_exercises.ipynb b/09_mappings/01_exercises_nested-data.ipynb similarity index 100% rename from 09_mappings/01_exercises.ipynb rename to 09_mappings/01_exercises_nested-data.ipynb diff --git a/09_mappings/03_exercises.ipynb b/09_mappings/03_exercises_fibonacci.ipynb similarity index 100% rename from 09_mappings/03_exercises.ipynb rename to 09_mappings/03_exercises_fibonacci.ipynb diff --git a/11_classes/01_exercises.ipynb b/11_classes/01_exercises_tsp.ipynb similarity index 100% rename from 11_classes/01_exercises.ipynb rename to 11_classes/01_exercises_tsp.ipynb diff --git a/CONTENTS.md b/CONTENTS.md index 46e16ce..cd8228b 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -35,8 +35,8 @@ If this is not possible, JupyterLab; Programming vs. Computer Science; Learning Tips) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/00_intro/01_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/01_exercises_markdown.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/00_intro/01_exercises_markdown.ipynb) (Mastering Markdown) - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/02_review.ipynb) - **Part A: Expressing Logic** @@ -47,11 +47,11 @@ If this is not possible, Operators; Objects & Data Types; Errors) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/01_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/01_exercises_print.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/01_exercises_print.ipynb) (Printing Output) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/02_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/02_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/02_exercises_for-loops.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/02_exercises_for-loops.ipynb) (Simple `for`-loops) - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_content.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/03_content.ipynb) @@ -59,8 +59,8 @@ If this is not possible, Variables & References; Mutability; Expressions & Statements) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/04_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/04_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/04_exercises_calculator.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/04_exercises_calculator.ipynb) (Python as a Calculator) - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/05_summary.ipynb) - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/06_review.ipynb) @@ -74,8 +74,8 @@ If this is not possible, Function Calls & Scoping Rules; Positional vs. Keyword Arguments; Modularization) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/01_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/01_exercises_sphere-volume.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/01_exercises_sphere-volume.ipynb) (Volume of a Sphere) - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/02_content.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/02_content.ipynb) @@ -92,11 +92,11 @@ If this is not possible, Logical Operators; `if` statement; Exception Handling) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/01_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/01_exercises_discounts.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/01_exercises_discounts.ipynb) (Discounting Customer Orders) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/02_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/02_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/02_exercises_fizz-buzz.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/02_exercises_fizz-buzz.ipynb) (Fizz Buzz) - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/03_summary.ipynb) - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/04_review.ipynb) @@ -108,8 +108,8 @@ If this is not possible, Duck Typing; Type Casting & Checking; Input Validation) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/01_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/01_exercises_hanoi-towers.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/01_exercises_hanoi-towers.ipynb) (Towers of Hanoi) - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/02_content.ipynb) @@ -120,8 +120,8 @@ If this is not possible, [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/03_content.ipynb) (Customizing Loops with `break` and `continue`; Indefinite Loops) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/04_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/04_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/04_exercises_dice.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/04_exercises_dice.ipynb) (Throwing Dice) - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/05_summary.ipynb) - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/06_review.ipynb) @@ -162,8 +162,8 @@ If this is not possible, Indexing & Slicing; String Methods & Operations; String Interpolation) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/01_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/01_exercises_palindromes.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/01_exercises_palindromes.ipynb) (Detecting Palindromes) - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/02_content.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/02_content.ipynb) @@ -187,8 +187,8 @@ If this is not possible, Indexing & Slicing; Shallow vs. Deep Copies; List Methods & Operations) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/02_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/02_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/02_exercises_lists.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/02_exercises_lists.ipynb) (Working with Lists) - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/03_content.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/03_content.ipynb) @@ -196,8 +196,8 @@ If this is not possible, `tuple` Type; Packing & Unpacking; `*args` in Function Definitions) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/04_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/04_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/04_exercises_un-packing.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/04_exercises_un-packing.ipynb) (Packing & Unpacking with Functions) - [appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/05_appendix.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/05_appendix.ipynb) @@ -217,11 +217,11 @@ If this is not possible, `generator` Expression; Streams of Data; Boolean Reducers) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/02_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/02_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/02_exercises_outliers.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/02_exercises_outliers.ipynb) (Removing Outliers in Streaming Data) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/03_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/03_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/03_exercises_un-packing.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/03_exercises_un-packing.ipynb) (Packing & Unpacking with Functions, continued) - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/04_content.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/04_content.ipynb) @@ -237,15 +237,15 @@ If this is not possible, Hash Tables; `dict` Methods & Behavior; `dict` Comprehension) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/01_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/01_exercises_nested-data.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/01_exercises_nested-data.ipynb) (Working with Nested Data) - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/02_content.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/02_content.ipynb) (`**kwargs` in Function Definitions; Memoization) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/03_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/03_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/03_exercises_fibonacci.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/03_exercises_fibonacci.ipynb) (Memoization without Side Effects) - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/04_content.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/04_content.ipynb) @@ -271,8 +271,8 @@ If this is not possible, Text Representations; Instance Methods vs. Class Methods; Computed Properties) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/01_exercises.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/01_exercises.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/01_exercises_tsp.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/01_exercises_tsp.ipynb) (A Traveling Salesman Problem) - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/02_content.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/02_content.ipynb) From da43bc3d97f081aeabaab856d93b5f87f6f00d72 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 8 Apr 2024 17:45:13 +0200 Subject: [PATCH 138/142] Update links and versions --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f3fffd8..35aa76e 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ For a more *detailed version* with **clickable links** #### Videos -Presentations of the chapters are available on this [YouTube playlist ](https://www.youtube.com/playlist?list=PL-2JV1G3J10lQ2xokyQowcRJI5jjNfW7f). +Presentations of the chapters are available on this [YouTube playlist ](https://www.youtube.com/playlist?list=PL-2JV1G3J10kRUPgP7EwLhyeN5lOZW2kH). The recordings are about 25 hours long in total and were made in spring 2020 after a corresponding in-class Bachelor course was cancelled due to Corona. @@ -76,21 +76,21 @@ If you are familiar with ### Installation -To follow this course, an installation of **Python 3.8** or higher is expected. +To follow this course, an installation of **Python 3.11** or higher is expected. A popular and beginner friendly way is - to install the [Anaconda Distribution](https://www.anaconda.com/products/individual) + to install the [Anaconda Distribution](https://www.anaconda.com/download) that not only ships Python itself but also comes pre-packaged with a lot of third-party libraries. -Scroll down to the [download](https://www.anaconda.com/products/individual#Downloads) section +Scroll down to the "Anaconda Installers" section and install the latest version for your operating system - (i.e., *2020-07* with Python 3.8 at the time of this writing). + (i.e., *2024-02* with Python 3.11 at the time of this writing). After installation, - you find an entry "[Anaconda Navigator](https://docs.anaconda.com/anaconda/navigator/)" + you find an entry "[Anaconda Navigator](https://docs.anaconda.com/free/navigator/)" in your start menu. Click on it. From 110bb3b38ee2f0741f610ee6f5d2bc362d645768 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 8 Apr 2024 17:45:35 +0200 Subject: [PATCH 139/142] Remove the instuctor's presentation mode Reason: RISE only works in the older Jupyter Notebook app and not in the more commonly used JupyterLab app --- README.md | 27 +-------------------------- noxfile.py | 5 ----- 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/README.md b/README.md index 35aa76e..c1b0db9 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ Then, unpack the ZIP file into a folder of your choice, -### Alternative Installation (for Instructors) +### Alternative Installation (for Instructors using Linux) Python can also be installed in a "pure" way obtained directly from its core development team [here](https://www.python.org/downloads/). @@ -232,31 +232,6 @@ The command-line interface stays open in the background, -#### Interactive Presentation Mode & Live Coding - -`poetry install` also installs the - [RISE ](https://github.com/damianavila/RISE) - extension for Jupyter. -With that, the instructor can execute code in *presentation* mode during a class session. -However, the RISE extension does *not* work in the more recent - [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) app - but only in the older [Jupyter Notebook](https://jupyter-notebook.readthedocs.io/en/stable/) app, - which comes with less features - and a simpler [GUI ](https://en.wikipedia.org/wiki/Graphical_user_interface). -The instructor can start the latter with: - -- `poetry run jupyter notebook` - -This also opens a new tab in the web browser. -After opening a notebook, - clicking on the button highlighted below - starts the presentation mode. - - - -Not all notebooks are designed for this presentation mode. - - ## Contributing Feedback **is highly encouraged** and will be incorporated. diff --git a/noxfile.py b/noxfile.py index 869cdab..03e40cb 100644 --- a/noxfile.py +++ b/noxfile.py @@ -46,11 +46,6 @@ def init_project(session): ): session.run("poetry", "run", "pre-commit", "install", f"--hook-type={type_}") - # Copy the extensions' JavaScript and CSS files into Jupyter's search directory. - session.run( - "poetry", "run", "jupyter", "contrib", "nbextension", "install", "--user" - ) - @nox.session(venv_backend="none") def doctests(session): From e3ef1aec29ebe16db911abd68a318e5a2273b0c6 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 8 Apr 2024 17:54:41 +0200 Subject: [PATCH 140/142] Update copyright timespan --- LICENSE.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.txt b/LICENSE.txt index 0ec444a..19656d3 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2020 Alexander Hess [alexander@webartifex.biz] +Copyright (c) 2018-2024 Alexander Hess [alexander@webartifex.biz] Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From c2a1a8dcc1b00281409d94447074c84e117037bf Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 8 Apr 2024 17:56:40 +0200 Subject: [PATCH 141/142] Format with black --- noxfile.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/noxfile.py b/noxfile.py index 03e40cb..7a54a68 100644 --- a/noxfile.py +++ b/noxfile.py @@ -87,23 +87,23 @@ def fix_branch_references(_session): { "name": "github", "pattern": re.compile( - fr"((((http)|(https))://github\.com/{REPOSITORY}/((blob)|(tree))/)([\w-]+)/)" + rf"((((http)|(https))://github\.com/{REPOSITORY}/((blob)|(tree))/)([\w-]+)/)" ), - "replacement": fr"\2{branch}/", + "replacement": rf"\2{branch}/", }, { "name": "nbviewer", "pattern": re.compile( - fr"((((http)|(https))://nbviewer\.jupyter\.org/github/{REPOSITORY}/((blob)|(tree))/)([\w-]+)/)", + rf"((((http)|(https))://nbviewer\.jupyter\.org/github/{REPOSITORY}/((blob)|(tree))/)([\w-]+)/)", ), - "replacement": fr"\2{branch}/", + "replacement": rf"\2{branch}/", }, { "name": "mybinder", "pattern": re.compile( - fr"((((http)|(https))://mybinder\.org/v2/gh/{REPOSITORY}/)([\w-]+)\?)", + rf"((((http)|(https))://mybinder\.org/v2/gh/{REPOSITORY}/)([\w-]+)\?)", ), - "replacement": fr"\2{branch}?", + "replacement": rf"\2{branch}?", }, ] From 94e5112f10fe85098ced441f2ddd437c6f9f0739 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 8 Apr 2024 22:13:31 +0200 Subject: [PATCH 142/142] Release 0.1.0 After refurbishing the project we prepare a new relaease. There are no changes with respect to the contents as compared to v0.0.0 that are noteworthy release notes. --- 00_intro/00_content.ipynb | 26 +- 00_intro/01_exercises_markdown.ipynb | 4 +- 00_intro/02_review.ipynb | 2 +- 01_elements/00_content.ipynb | 10 +- 01_elements/01_exercises_print.ipynb | 4 +- 01_elements/02_exercises_for-loops.ipynb | 6 +- 01_elements/03_content.ipynb | 12 +- 01_elements/04_exercises_calculator.ipynb | 4 +- 01_elements/05_summary.ipynb | 6 +- 01_elements/06_review.ipynb | 2 +- 01_elements/07_resources.ipynb | 2 +- 02_functions/00_content.ipynb | 20 +- 02_functions/01_exercises_sphere-volume.ipynb | 6 +- 02_functions/02_content.ipynb | 16 +- 02_functions/04_review.ipynb | 2 +- 03_conditionals/00_content.ipynb | 10 +- 03_conditionals/01_exercises_discounts.ipynb | 4 +- 03_conditionals/02_exercises_fizz-buzz.ipynb | 6 +- 03_conditionals/04_review.ipynb | 2 +- 04_iteration/00_content.ipynb | 4 +- 04_iteration/01_exercises_hanoi-towers.ipynb | 4 +- 04_iteration/02_content.ipynb | 10 +- 04_iteration/03_content.ipynb | 10 +- 04_iteration/04_exercises_dice.ipynb | 6 +- 04_iteration/06_review.ipynb | 2 +- 05_numbers/00_content.ipynb | 22 +- 05_numbers/01_content.ipynb | 6 +- 05_numbers/02_content.ipynb | 8 +- 05_numbers/03_appendix.ipynb | 2 +- 05_numbers/05_review.ipynb | 2 +- 05_numbers/06_resources.ipynb | 2 +- 06_text/00_content.ipynb | 34 +-- 06_text/01_exercises_palindromes.ipynb | 6 +- 06_text/02_content.ipynb | 8 +- 06_text/04_review.ipynb | 2 +- 06_text/05_resources.ipynb | 2 +- 07_sequences/00_content.ipynb | 20 +- 07_sequences/01_content.ipynb | 8 +- 07_sequences/02_exercises_lists.ipynb | 4 +- 07_sequences/03_content.ipynb | 10 +- 07_sequences/04_exercises_un-packing.ipynb | 14 +- 07_sequences/05_appendix.ipynb | 6 +- 07_sequences/07_review.ipynb | 4 +- 08_mfr/00_content.ipynb | 10 +- 08_mfr/01_content.ipynb | 22 +- 08_mfr/02_exercises_outliers.ipynb | 8 +- 08_mfr/03_exercises_un-packing.ipynb | 8 +- 08_mfr/04_content.ipynb | 10 +- 08_mfr/06_review.ipynb | 2 +- 09_mappings/00_content.ipynb | 14 +- 09_mappings/01_exercises_nested-data.ipynb | 4 +- 09_mappings/02_content.ipynb | 10 +- 09_mappings/03_exercises_fibonacci.ipynb | 8 +- 09_mappings/04_content.ipynb | 4 +- 09_mappings/05_appendix.ipynb | 2 +- 09_mappings/07_review.ipynb | 4 +- 09_mappings/08_resources.ipynb | 2 +- 11_classes/00_content.ipynb | 12 +- 11_classes/01_exercises_tsp.ipynb | 4 +- 11_classes/02_content.ipynb | 12 +- 11_classes/03_content.ipynb | 10 +- 11_classes/04_content.ipynb | 34 +-- 11_classes/06_review.ipynb | 2 +- CONTENTS.md | 250 +++++++++--------- pyproject.toml | 2 +- 65 files changed, 387 insertions(+), 387 deletions(-) diff --git a/00_intro/00_content.ipynb b/00_intro/00_content.ipynb index 8409944..0b6ba58 100644 --- a/00_intro/00_content.ipynb +++ b/00_intro/00_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/00_intro/00_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/00_intro/00_content.ipynb)." ] }, { @@ -135,7 +135,7 @@ "source": [ "To \"read\" this book in the most meaningful way, a working installation of **Python 3.8** with [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) is needed.\n", "\n", - "For a tutorial on how to install Python on your computer, follow the instructions in the [README.md](https://github.com/webartifex/intro-to-python/blob/develop/README.md#installation) file in the project's [GitHub repository ](https://github.com/webartifex/intro-to-python). If you cannot install Python on your own machine, you may open the book interactively in the cloud with [Binder ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab)." + "For a tutorial on how to install Python on your computer, follow the instructions in the [README.md](https://github.com/webartifex/intro-to-python/blob/main/README.md#installation) file in the project's [GitHub repository ](https://github.com/webartifex/intro-to-python). If you cannot install Python on your own machine, you may open the book interactively in the cloud with [Binder ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab)." ] }, { @@ -161,7 +161,7 @@ "\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.\n", "\n", - "Jupyter notebooks have become a de-facto standard for communicating and exchanging results in the data science community - both in academia and business - and provide an alternative to command-line interface (CLI or \"terminal\") based ways of running Python code. As an example for the latter case, we could start the default [Python interpreter ](https://docs.python.org/3/tutorial/interpreter.html) that comes with every installation by typing the `python` command into a CLI (or `poetry run python` if the project is managed with the [poetry](https://python-poetry.org/docs/) CLI tool as explained in the [README.md](https://github.com/webartifex/intro-to-python/blob/develop/README.md#alternative-installation-for-instructors) file). Then, as the screenshot below shows, we could execute Python code like `1 + 2` or `print(\"Hello World\")` line by line simply by typing it following the `>>>` **prompt** and pressing the **Enter** key. For an introductory course, however, this would be rather tedious and probably scare off many beginners." + "Jupyter notebooks have become a de-facto standard for communicating and exchanging results in the data science community - both in academia and business - and provide an alternative to command-line interface (CLI or \"terminal\") based ways of running Python code. As an example for the latter case, we could start the default [Python interpreter ](https://docs.python.org/3/tutorial/interpreter.html) that comes with every installation by typing the `python` command into a CLI (or `poetry run python` if the project is managed with the [poetry](https://python-poetry.org/docs/) CLI tool as explained in the [README.md](https://github.com/webartifex/intro-to-python/blob/main/README.md#alternative-installation-for-instructors) file). Then, as the screenshot below shows, we could execute Python code like `1 + 2` or `print(\"Hello World\")` line by line simply by typing it following the `>>>` **prompt** and pressing the **Enter** key. For an introductory course, however, this would be rather tedious and probably scare off many beginners." ] }, { @@ -750,11 +750,11 @@ "**Part A: Expressing Logic**\n", "\n", "- What is a programming language? What kind of words exist?\n", - " - *Chapter 1*: [Elements of a Program ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb)\n", - " - *Chapter 2*: [Functions & Modularization ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb)\n", + " - *Chapter 1*: [Elements of a Program ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb)\n", + " - *Chapter 2*: [Functions & Modularization ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/00_content.ipynb)\n", "- What is the flow of execution? How can we form sentences from words?\n", - " - *Chapter 3*: [Conditionals & Exceptions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb)\n", - " - *Chapter 4*: [Recursion & Looping ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb)" + " - *Chapter 3*: [Conditionals & Exceptions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/00_content.ipynb)\n", + " - *Chapter 4*: [Recursion & Looping ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/00_content.ipynb)" ] }, { @@ -768,14 +768,14 @@ "**Part B: Managing Data and Memory**\n", "\n", "- How is data stored in memory?\n", - " - *Chapter 5*: [Numbers & Bits ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb)\n", - " - *Chapter 6*: [Text & Bytes ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/00_content.ipynb)\n", - " - *Chapter 7*: [Sequential Data ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb)\n", - " - *Chapter 8*: [Map, Filter, & Reduce ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb)\n", - " - *Chapter 9*: [Mappings & Sets ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb)\n", + " - *Chapter 5*: [Numbers & Bits ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/00_content.ipynb)\n", + " - *Chapter 6*: [Text & Bytes ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/00_content.ipynb)\n", + " - *Chapter 7*: [Sequential Data ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/00_content.ipynb)\n", + " - *Chapter 8*: [Map, Filter, & Reduce ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb)\n", + " - *Chapter 9*: [Mappings & Sets ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/00_content.ipynb)\n", " - *Chapter 10*: Arrays & Dataframes\n", "- How can we create custom data types?\n", - " - *Chapter 11*: [Classes & Instances ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb)" + " - *Chapter 11*: [Classes & Instances ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb)" ] }, { diff --git a/00_intro/01_exercises_markdown.ipynb b/00_intro/01_exercises_markdown.ipynb index 272d5f9..d80f9d8 100644 --- a/00_intro/01_exercises_markdown.ipynb +++ b/00_intro/01_exercises_markdown.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/00_intro/02_exercises.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/00_intro/02_exercises.ipynb)." ] }, { @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The exercises below assume that you have read [Chapter 0 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/00_content.ipynb)." + "The exercises below assume that you have read [Chapter 0 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/00_intro/00_content.ipynb)." ] }, { diff --git a/00_intro/02_review.ipynb b/00_intro/02_review.ipynb index 0fa77c5..bf3a39a 100644 --- a/00_intro/02_review.ipynb +++ b/00_intro/02_review.ipynb @@ -11,7 +11,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The questions below assume that you have read [Chapter 0 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/00_content.ipynb).\n", + "The questions below assume that you have read [Chapter 0 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/00_intro/00_content.ipynb).\n", "\n", "Be concise in your answers! Most questions can be answered in *one* sentence." ] diff --git a/01_elements/00_content.ipynb b/01_elements/00_content.ipynb index 3335486..af2d022 100644 --- a/01_elements/00_content.ipynb +++ b/01_elements/00_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/00_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/00_content.ipynb)." ] }, { @@ -1165,7 +1165,7 @@ "source": [ "These addresses are *not* meaningful for anything other than checking if two variables reference the *same* object.\n", "\n", - "Obviously, `a` and `b` have the same *value* as revealed by the **equality operator** `==`: We say `a` and `b` \"evaluate equal.\" The resulting `True` - and the `False` further below - is yet another data type, a so-called **boolean**. We look into them in [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb#Boolean-Expressions)." + "Obviously, `a` and `b` have the same *value* as revealed by the **equality operator** `==`: We say `a` and `b` \"evaluate equal.\" The resulting `True` - and the `False` further below - is yet another data type, a so-called **boolean**. We look into them in [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/00_content.ipynb#Boolean-Expressions)." ] }, { @@ -1342,7 +1342,7 @@ "source": [ "Different types imply different behaviors for the objects. The `b` object, for example, may be \"asked\" if it is a whole number 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** (i.e., as opposed to functions) and we look at them in detail in [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb). For now, it suffices to know that we access them with the **dot operator** `.` on the object. Of course, `b` is a whole number, which the boolean object `True` tells us." + "Formally, we call such type-specific functionalities **methods** (i.e., as opposed to functions) and we look at them in detail in [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb). For now, it suffices to know that we access them with the **dot operator** `.` on the object. Of course, `b` is a whole number, which the boolean object `True` tells us." ] }, { @@ -1934,7 +1934,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 built-in [sum() ](https://docs.python.org/3/library/functions.html#sum) and [len() ](https://docs.python.org/3/library/functions.html#len) functions (cf., [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb#Built-in-Functions)) as well as a so-called **list comprehension** (cf., [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.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 built-in [sum() ](https://docs.python.org/3/library/functions.html#sum) and [len() ](https://docs.python.org/3/library/functions.html#len) functions (cf., [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/00_content.ipynb#Built-in-Functions)) as well as a so-called **list comprehension** (cf., [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb#List-Comprehensions)). Pythonic code runs faster in many cases and is less error-prone." ] }, { @@ -2136,7 +2136,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/develop/02_functions/00_content.ipynb#Local-Modules-and-Packages)) and then use Jupyter notebooks to build up a linear report or storyline for an analysis." + "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/main/02_functions/00_content.ipynb#Local-Modules-and-Packages)) and then use Jupyter notebooks to build up a linear report or storyline for an analysis." ] } ], diff --git a/01_elements/01_exercises_print.ipynb b/01_elements/01_exercises_print.ipynb index 592d48a..68432ee 100644 --- a/01_elements/01_exercises_print.ipynb +++ b/01_elements/01_exercises_print.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/01_exercises.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/01_exercises.ipynb)." ] }, { @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb) of Chapter 1.\n", + "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb) of Chapter 1.\n", "\n", "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." ] diff --git a/01_elements/02_exercises_for-loops.ipynb b/01_elements/02_exercises_for-loops.ipynb index 2ff9c45..fab3299 100644 --- a/01_elements/02_exercises_for-loops.ipynb +++ b/01_elements/02_exercises_for-loops.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/02_exercises.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/02_exercises.ipynb)." ] }, { @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb) of Chapter 1.\n", + "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb) of Chapter 1.\n", "\n", "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." ] @@ -36,7 +36,7 @@ "source": [ "`for`-loops are extremely versatile in Python. That is different from many other programming languages.\n", "\n", - "As shown in the first example in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List), we can create a `list` like `numbers` and loop over the numbers in it on a one-by-one basis." + "As shown in the first example in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List), we can create a `list` like `numbers` and loop over the numbers in it on a one-by-one basis." ] }, { diff --git a/01_elements/03_content.ipynb b/01_elements/03_content.ipynb index e7886f8..a438ab7 100644 --- a/01_elements/03_content.ipynb +++ b/01_elements/03_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/03_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/03_content.ipynb)." ] }, { @@ -367,7 +367,7 @@ } }, "source": [ - "Some variables magically exist when a Python process is started or are added by Jupyter. We may safely ignore the former until [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb) and the latter for good." + "Some variables magically exist when a Python process is started or are added by Jupyter. We may safely ignore the former until [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb) and the latter for good." ] }, { @@ -687,7 +687,7 @@ } }, "source": [ - "Variables with leading and trailing double underscores, referred to as **dunder** in Python jargon, are used for built-in functionalities and to implement object-oriented features as we see in [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb). We must *not* use this style for variables!" + "Variables with leading and trailing double underscores, referred to as **dunder** in Python jargon, are used for built-in functionalities and to implement object-oriented features as we see in [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb). We must *not* use this style for variables!" ] }, { @@ -968,7 +968,7 @@ "source": [ "Let's change the first element of `x`.\n", "\n", - "[Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/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 yet another operator, namely the **indexing operator**. So, `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 we put in the list. The indexing operator must be an operator as we merely *read* the first element and do not change anything in memory permanently.\n", + "[Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/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 yet another operator, namely the **indexing operator**. So, `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 we put in the list. The indexing operator must be an operator as we merely *read* the first element and do not change anything in memory permanently.\n", "\n", "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)." ] @@ -1144,7 +1144,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 have said about *individual* operators before, namely that they have *no* permanent side effects in memory, actually belongs here, to begin with: The absence of any *permanent* side effects is the characteristic property of expressions, and all the code cells in the \"*(Arithmetic) Operators*\" section in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#%28Arithmetic%29-Operators) of this chapter are examples of expressions.\n", + "What we have said about *individual* operators before, namely that they have *no* permanent side effects in memory, actually belongs here, to begin with: The absence of any *permanent* side effects is the characteristic property of expressions, and all the code cells in the \"*(Arithmetic) Operators*\" section in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb#%28Arithmetic%29-Operators) of this chapter are examples of expressions.\n", "\n", "The simplest possible expressions contain only one variable or literal. The output below a code cell is Jupyter's way of returning the reference to the object to us!\n", "\n", @@ -1414,7 +1414,7 @@ } }, "source": [ - "... calling the built-in [print() ](https://docs.python.org/3/library/functions.html#print) function does *neither* change the memory *nor* evaluate to an object (disregarding the `None` object explained in [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb#Function-Definitions)). We could view changing the computer's screen as a side effect but this is outside of Python's memory!\n", + "... calling the built-in [print() ](https://docs.python.org/3/library/functions.html#print) function does *neither* change the memory *nor* evaluate to an object (disregarding the `None` object explained in [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/00_content.ipynb#Function-Definitions)). We could view changing the computer's screen as a side effect but this is outside of Python's memory!\n", "\n", "Also, the cell below has *no* output! It only looks like it does as Jupyter redirects whatever [print() ](https://docs.python.org/3/library/functions.html#print) writes to the \"screen\" to below a cell. We see a difference to the expressions above in that there are no brackets `[...]` next to the output showing the execution count number." ] diff --git a/01_elements/04_exercises_calculator.ipynb b/01_elements/04_exercises_calculator.ipynb index 3fa9301..5b66384 100644 --- a/01_elements/04_exercises_calculator.ipynb +++ b/01_elements/04_exercises_calculator.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/04_exercises.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/04_exercises.ipynb)." ] }, { @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The exercises below assume that you have read the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_content.ipynb) Chapter 1.\n", + "The exercises below assume that you have read the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/03_content.ipynb) Chapter 1.\n", "\n", "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." ] diff --git a/01_elements/05_summary.ipynb b/01_elements/05_summary.ipynb index f57bb09..e5a7dd0 100644 --- a/01_elements/05_summary.ipynb +++ b/01_elements/05_summary.ipynb @@ -45,7 +45,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 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb))\n", + " - built-in objects (incl. **literals**) vs. user-defined objects (cf., [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb))\n", " - e.g., `1`, `1.0`, and `\"one\"` are three different objects of distinct types that are also literals (i.e., by the way we type them into the command line Python knows what the value and type are)\n", "\n", "\n", @@ -80,14 +80,14 @@ " - ignored by Python\n", "\n", "\n", - "- functions (cf., [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb))\n", + "- functions (cf., [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/00_content.ipynb))\n", " - named sequences of instructions\n", " - the smaller parts in a larger program\n", " - make a program more modular and thus easier to understand\n", " - include [built-in functions ](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/develop/03_conditionals/00_content.ipynb) and [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb))\n", + "- flow control (cf., [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/00_content.ipynb) and [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/00_content.ipynb))\n", " - expression of **business logic** or an **algorithm**\n", " - conditional execution of parts of a program (e.g., `if` statements)\n", " - repetitive execution of parts of a program (e.g., `for`-loops)" diff --git a/01_elements/06_review.ipynb b/01_elements/06_review.ipynb index 33ff535..60c9360 100644 --- a/01_elements/06_review.ipynb +++ b/01_elements/06_review.ipynb @@ -11,7 +11,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb) and the [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_content.ipynb) part of Chapter 1.\n", + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb) and the [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/03_content.ipynb) part of Chapter 1.\n", "\n", "Be concise in your answers! Most questions can be answered in *one* sentence." ] diff --git a/01_elements/07_resources.ipynb b/01_elements/07_resources.ipynb index 0ac2b87..0bf7edb 100644 --- a/01_elements/07_resources.ipynb +++ b/01_elements/07_resources.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/07_resources.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/07_resources.ipynb)." ] }, { diff --git a/02_functions/00_content.ipynb b/02_functions/00_content.ipynb index cfc47d3..a40b1e6 100644 --- a/02_functions/00_content.ipynb +++ b/02_functions/00_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/00_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/02_functions/00_content.ipynb)." ] }, { @@ -30,7 +30,7 @@ } }, "source": [ - "In [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List), we simply typed the code to calculate the average of the even numbers in a list of whole numbers 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. 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/main/01_elements/00_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List), we simply typed the code to calculate the average of the even numbers in a list of whole numbers 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. 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", "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. Also, we look at how we can extend our Python installation with functionalities written by other people." ] @@ -307,7 +307,7 @@ } }, "source": [ - "To execute a function, we **call** it with the **call operator** `()` as shown many times in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb) and above.\n", + "To execute a function, we **call** it with the **call operator** `()` as shown many times in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb) and above.\n", "\n", "If we are unsure whether a variable references a function or not, we can verify that with the built-in [callable() ](https://docs.python.org/3/library/functions.html#callable) function.\n", "\n", @@ -538,7 +538,7 @@ } }, "source": [ - "Notice the subtle difference compared to the behavior of the `//` operator in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb##%28Arithmetic#%29-Operators) that \"rounds\" towards minus infinity: [int() ](https://docs.python.org/3/library/functions.html#int) always \"rounds\" towards `0`." + "Notice the subtle difference compared to the behavior of the `//` operator in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb##%28Arithmetic#%29-Operators) that \"rounds\" towards minus infinity: [int() ](https://docs.python.org/3/library/functions.html#int) always \"rounds\" towards `0`." ] }, { @@ -869,7 +869,7 @@ } }, "source": [ - "We may create so-called *user-defined* **functions** with the `def` statement (cf., [reference ](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)). To extend an already familiar example, we reuse the introductory example from [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Best-Practices) in its final Pythonic version and transform it into the function `average_evens()` below. We replace the variable name `numbers` with `integers` for didactical purposes in the first couple of examples.\n", + "We may create so-called *user-defined* **functions** with the `def` statement (cf., [reference ](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)). To extend an already familiar example, we reuse the introductory example from [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb#Best-Practices) in its final Pythonic version and transform it into the function `average_evens()` below. We replace the variable name `numbers` with `integers` for didactical purposes in the first couple of examples.\n", "\n", "A function's **name** must be chosen according to the same naming rules as ordinary variables since 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", @@ -881,7 +881,7 @@ "\n", "A function may specify an *explicit* **return value** (i.e., \"result\" or \"output\") 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**. For example, the built-in [print() ](https://docs.python.org/3/library/functions.html#print) function changes what we see on the screen. Strictly speaking, [print() ](https://docs.python.org/3/library/functions.html#print) and other void functions also have an *implicit* return value, namely the `None` object.\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/develop/06_text/00_content.ipynb#The-str-Type). Widely adopted standards for docstrings are [PEP 257 ](https://www.python.org/dev/peps/pep-0257/) and section 3.8 of [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/main/06_text/00_content.ipynb#The-str-Type). Widely adopted standards for docstrings are [PEP 257 ](https://www.python.org/dev/peps/pep-0257/) and section 3.8 of [Google's Python Style Guide ](https://github.com/google/styleguide/blob/gh-pages/pyguide.md)." ] }, { @@ -1012,7 +1012,7 @@ "source": [ "Its value may seem awkward at first: It consists of a location showing where the function is defined (i.e., `__main__` here, which is Python's way of saying \"in this notebook\") and the signature wrapped inside angle brackets `<` and `>`.\n", " \n", - "The angle brackets are a convention to indicate that the value may *not* be used as a *literal* (i.e., typed back into another code cell). [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb) introduces the concept of a **text representation** of an object, which is related to the *semantic* meaning of an object's value as discussed in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Value-/-\"Meaning\"), and the angle brackets convention is one such way to represent an object as text. When executed, the angle brackets cause a `SyntaxError` because Python expects the `<` operator to come with an operand on both sides (cf., [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb#Relational-Operators))." + "The angle brackets are a convention to indicate that the value may *not* be used as a *literal* (i.e., typed back into another code cell). [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb) introduces the concept of a **text representation** of an object, which is related to the *semantic* meaning of an object's value as discussed in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb#Value-/-\"Meaning\"), and the angle brackets convention is one such way to represent an object as text. When executed, the angle brackets cause a `SyntaxError` because Python expects the `<` operator to come with an operand on both sides (cf., [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/00_content.ipynb#Relational-Operators))." ] }, { @@ -1647,7 +1647,7 @@ "source": [ "[PythonTutor ](http://pythontutor.com/visualize.html#code=numbers%20%3D%20%5B7,%2011,%208,%205,%203,%2012,%202,%206,%209,%2010,%201,%204%5D%0A%0Adef%20average_wrong%28integers%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%0Aresult%20%3D%20average_wrong%28%5B123,%20456,%20789%5D%29&cumulative=false&curstr=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 `numbers` 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 with 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/develop/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." + "The frames logic shown by PythonTutor is the mechanism with 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/main/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." ] }, { @@ -2021,7 +2021,7 @@ } }, "source": [ - "So far, we have specified only one parameter in each of our user-defined functions. In [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#%28Arithmetic%29-Operators), however, we saw the built-in [divmod() ](https://docs.python.org/3/library/functions.html#divmod) function take two arguments. And, the order in which they are passed in matters! 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 specified only one parameter in each of our user-defined functions. In [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb#%28Arithmetic%29-Operators), however, we saw the built-in [divmod() ](https://docs.python.org/3/library/functions.html#divmod) function take two arguments. And, the order in which they are passed in matters! 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**." ] }, { @@ -3012,7 +3012,7 @@ "source": [ "The main point of having functions without a reference to them 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 (cf., [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb#Lambda-Expressions))." + "Popular applications of lambda expressions occur in combination with the **map-filter-reduce** paradigm (cf., [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb#Lambda-Expressions))." ] } ], diff --git a/02_functions/01_exercises_sphere-volume.ipynb b/02_functions/01_exercises_sphere-volume.ipynb index e4173c4..49f957e 100644 --- a/02_functions/01_exercises_sphere-volume.ipynb +++ b/02_functions/01_exercises_sphere-volume.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/01_exercises.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/02_functions/01_exercises.ipynb)." ] }, { @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb) of Chapter 2.\n", + "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/00_content.ipynb) of Chapter 2.\n", "\n", "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." ] @@ -38,7 +38,7 @@ "\n", "Hints:\n", "- use an appropriate approximation for $\\pi$\n", - "- you may use the [standard library ](https://docs.python.org/3/library/index.html) to do so if you have already looked at the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/02_content.ipynb) of Chapter 2." + "- you may use the [standard library ](https://docs.python.org/3/library/index.html) to do so if you have already looked at the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/02_content.ipynb) of Chapter 2." ] }, { diff --git a/02_functions/02_content.ipynb b/02_functions/02_content.ipynb index f0631a0..5e663cc 100644 --- a/02_functions/02_content.ipynb +++ b/02_functions/02_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/02_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/02_functions/02_content.ipynb)." ] }, { @@ -434,7 +434,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/develop/01_elements/03_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", + "So just as a reminder from the expression vs. statement discussion in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/03_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)." ] @@ -615,7 +615,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 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb), 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 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb), while the latter is explained further below." ] }, { @@ -1027,7 +1027,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.\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/develop/00_intro/00_content.ipynb#Markdown-Cells-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/main/00_intro/00_content.ipynb#Markdown-Cells-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." ] @@ -1332,7 +1332,7 @@ } }, "source": [ - "For sure, we can create local modules and packages. In the Chapter 2 directory, there is a [*sample_module.py* ](https://github.com/webartifex/intro-to-python/blob/develop/02_functions/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 Chapter 2 directory, there is a [*sample_module.py* ](https://github.com/webartifex/intro-to-python/blob/main/02_functions/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", @@ -1418,7 +1418,7 @@ } }, "source": [ - "Disregarding the dunder-style attributes, `mod` defines the attributes `_round_all`, `_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/develop/02_functions/sample_module.py) file.\n", + "Disregarding the dunder-style attributes, `mod` defines the attributes `_round_all`, `_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/main/02_functions/sample_module.py) file.\n", "\n", "A convention when working with imported code is to *disregard* any attributes starting with a single 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", @@ -1469,7 +1469,7 @@ } }, "source": [ - "We use the imported `mod.average_evens()` just like `average_evens()` defined in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb) of this chapter. 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." + "We use the imported `mod.average_evens()` just like `average_evens()` defined in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/00_content.ipynb) of this chapter. 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." ] }, { @@ -1585,7 +1585,7 @@ } }, "source": [ - "Packages are a generalization of modules, and we look at one in detail in [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/04_content.ipynb#Packages-vs.-Modules).\n", + "Packages are a generalization of modules, and we look at one in detail in [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/04_content.ipynb#Packages-vs.-Modules).\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/02_functions/04_review.ipynb b/02_functions/04_review.ipynb index 7ebc8fd..f3b7ab9 100644 --- a/02_functions/04_review.ipynb +++ b/02_functions/04_review.ipynb @@ -11,7 +11,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb) and the [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/02_content.ipynb) part of Chapter 2.\n", + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/00_content.ipynb) and the [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/02_content.ipynb) part of Chapter 2.\n", "\n", "Be concise in your answers! Most questions can be answered in *one* sentence." ] diff --git a/03_conditionals/00_content.ipynb b/03_conditionals/00_content.ipynb index 62bf469..8797d0a 100644 --- a/03_conditionals/00_content.ipynb +++ b/03_conditionals/00_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/00_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/03_conditionals/00_content.ipynb)." ] }, { @@ -30,7 +30,7 @@ } }, "source": [ - "We analyzed every aspect of the `average_evens()` function in [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb) except for the `if`-related parts. While it does what we expect it to, there is a whole lot more to learn by taking it apart. In particular, the `if` may occur within both a **statement** or an **expression**, analogous as to how a noun in a natural language can be the subject of *or* an object in a sentence. What is common to both usages is that it leads to code being executed for *parts* of the input only. It is a way of controlling the **flow of execution** in a program.\n", + "We analyzed every aspect of the `average_evens()` function in [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/00_content.ipynb) except for the `if`-related parts. While it does what we expect it to, there is a whole lot more to learn by taking it apart. In particular, the `if` may occur within both a **statement** or an **expression**, analogous as to how a noun in a natural language can be the subject of *or* an object in a sentence. What is common to both usages is that it leads to code being executed for *parts* of the input only. It is a way of controlling the **flow of execution** in a program.\n", "\n", "After deconstructing `if` in the first part of this chapter, we take a close look at a similar concept, namely handling **exceptions**." ] @@ -150,7 +150,7 @@ } }, "source": [ - "There are, however, cases where the `==` operator seems to not work intuitively. [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb#Imprecision) provides more insights into this \"bug.\"" + "There are, however, cases where the `==` operator seems to not work intuitively. [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/00_content.ipynb#Imprecision) provides more insights into this \"bug.\"" ] }, { @@ -351,7 +351,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/develop/02_functions/00_content.ipynb#Function-Definitions) 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/main/02_functions/00_content.ipynb#Function-Definitions) as the *implicit* return value of a function without a `return` statement.\n", "\n", "We might think of `None` indicating a \"maybe\" or even an \"unknown\" answer; however, for Python, there are no \"maybe\" or \"unknown\" objects, as we see further below!\n", "\n", @@ -438,7 +438,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/develop/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", + "`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/main/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." ] diff --git a/03_conditionals/01_exercises_discounts.ipynb b/03_conditionals/01_exercises_discounts.ipynb index 6bd0657..9764fd6 100644 --- a/03_conditionals/01_exercises_discounts.ipynb +++ b/03_conditionals/01_exercises_discounts.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/01_exercises.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/03_conditionals/01_exercises.ipynb)." ] }, { @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The exercises below assume that you have read [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb).\n", + "The exercises below assume that you have read [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/00_content.ipynb).\n", "\n", "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." ] diff --git a/03_conditionals/02_exercises_fizz-buzz.ipynb b/03_conditionals/02_exercises_fizz-buzz.ipynb index 4728719..5b0ce28 100644 --- a/03_conditionals/02_exercises_fizz-buzz.ipynb +++ b/03_conditionals/02_exercises_fizz-buzz.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/02_exercises.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/03_conditionals/02_exercises.ipynb)." ] }, { @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The exercises below assume that you have read [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb).\n", + "The exercises below assume that you have read [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/00_content.ipynb).\n", "\n", "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." ] @@ -61,7 +61,7 @@ "source": [ "**Q2**: Loop over the `numbers` list and *replace* numbers for which one of the two (or both) conditions apply with text strings `\"Fizz\"`, `\"Buzz\"`, or `\"FizzBuzz\"` using the indexing operator `[]` and the assignment statement `=`.\n", "\n", - "In [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_content.ipynb#Who-am-I?-And-how-many?), we saw that Python starts indexing with `0` as the first element. Keep that in mind.\n", + "In [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/03_content.ipynb#Who-am-I?-And-how-many?), we saw that Python starts indexing with `0` as the first element. Keep that in mind.\n", "\n", "So in each iteration of the `for`-loop, you have to determine an `index` variable as well as check the actual `number` for its divisors.\n", "\n", diff --git a/03_conditionals/04_review.ipynb b/03_conditionals/04_review.ipynb index e9890be..67e9dd3 100644 --- a/03_conditionals/04_review.ipynb +++ b/03_conditionals/04_review.ipynb @@ -11,7 +11,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The questions below assume that you have read [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb).\n", + "The questions below assume that you have read [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/00_content.ipynb).\n", "\n", "Be concise in your answers! Most questions can be answered in *one* sentence." ] diff --git a/04_iteration/00_content.ipynb b/04_iteration/00_content.ipynb index cc17d58..362583b 100644 --- a/04_iteration/00_content.ipynb +++ b/04_iteration/00_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/00_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/04_iteration/00_content.ipynb)." ] }, { @@ -874,7 +874,7 @@ "\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 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/02_content.ipynb#Memoization), after introducing the `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 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/02_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." ] diff --git a/04_iteration/01_exercises_hanoi-towers.ipynb b/04_iteration/01_exercises_hanoi-towers.ipynb index 28304a9..b29212e 100644 --- a/04_iteration/01_exercises_hanoi-towers.ipynb +++ b/04_iteration/01_exercises_hanoi-towers.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/01_exercises.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/04_iteration/01_exercises.ipynb)." ] }, { @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb) of Chapter 4.\n", + "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/00_content.ipynb) of Chapter 4.\n", "\n", "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." ] diff --git a/04_iteration/02_content.ipynb b/04_iteration/02_content.ipynb index 35ac2cb..1d41ed7 100644 --- a/04_iteration/02_content.ipynb +++ b/04_iteration/02_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/02_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/04_iteration/02_content.ipynb)." ] }, { @@ -30,7 +30,7 @@ } }, "source": [ - "After learning about the concept of **recursion** in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb) of this chapter, we look at other ways of running code repeatedly, namely **looping** with the `for` and `while` statements. We start with the latter as it is more generic. Throughout this second part of the chapter, we revisit the same examples from the first part to show how recursion and looping are really two sides of the same coin." + "After learning about the concept of **recursion** in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/00_content.ipynb) of this chapter, we look at other ways of running code repeatedly, namely **looping** with the `for` and `while` statements. We start with the latter as it is more generic. Throughout this second part of the chapter, we revisit the same examples from the first part to show how recursion and looping are really two sides of the same coin." ] }, { @@ -582,7 +582,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 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.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 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb#Mapping)." ] }, { @@ -713,11 +713,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/develop/01_elements/03_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", + "We did this already in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/03_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/develop/07_sequences/00_content.ipynb#Collections-vs.-Sequences), we formalize these two concepts and introduce many more. Finally, [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb) 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/main/07_sequences/00_content.ipynb#Collections-vs.-Sequences), we formalize these two concepts and introduce many more. Finally, [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb) 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." ] diff --git a/04_iteration/03_content.ipynb b/04_iteration/03_content.ipynb index d9bb92f..0eb9d25 100644 --- a/04_iteration/03_content.ipynb +++ b/04_iteration/03_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/03_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/04_iteration/03_content.ipynb)." ] }, { @@ -30,7 +30,7 @@ } }, "source": [ - "While what we learned about the `for` and `while` statements in the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb) of this chapter suffices to translate any iterative algorithm into code, both come with some syntactic sugar to make life easier for the developer. This last part of the chapter shows how we can further customize the looping logic and introduces as \"trick\" for situations where we cannot come up with a stopping criterion in a `while`-loop." + "While what we learned about the `for` and `while` statements in the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/02_content.ipynb) of this chapter suffices to translate any iterative algorithm into code, both come with some syntactic sugar to make life easier for the developer. This last part of the chapter shows how we can further customize the looping logic and introduces as \"trick\" for situations where we cannot come up with a stopping criterion in a `while`-loop." ] }, { @@ -352,7 +352,7 @@ } }, "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/develop/01_elements/00_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List) or, more realistically, data from a CSV file with many rows and columns.\n", + "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/main/01_elements/00_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List) or, more realistically, data from a CSV file with many rows and columns.\n", "\n", "Processing numeric data usually comes down to operations that may be grouped into one of the following three categories:\n", "\n", @@ -360,7 +360,7 @@ "- **filtering**: throw away individual numbers (e.g., statistical outliers in a sample)\n", "- **reducing**: collect individual numbers into summary statistics\n", "\n", - "We study this **map-filter-reduce** paradigm extensively in [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb) 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 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb) after introducing more advanced data types that are needed to work with \"big\" data.\n", "\n", "Here, we focus on *filtering out* some numbers in a `for`-loop." ] @@ -535,7 +535,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/develop/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." + "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/main/03_conditionals/00_content.ipynb#The-if-Statement). Then, however, we trade off horizontal space against a more \"complex\" `if` logic, and this is *not* a real improvement." ] }, { diff --git a/04_iteration/04_exercises_dice.ipynb b/04_iteration/04_exercises_dice.ipynb index 8bd97ff..9c3feae 100644 --- a/04_iteration/04_exercises_dice.ipynb +++ b/04_iteration/04_exercises_dice.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/04_exercises.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/04_iteration/04_exercises.ipynb)." ] }, { @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The exercises below assume that you have read the [third part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb) of Chapter 4.\n", + "The exercises below assume that you have read the [third part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/03_content.ipynb) of Chapter 4.\n", "\n", "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." ] @@ -34,7 +34,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In this exercise, you will model the throwing of dice within the context of a guessing game similar to the one shown in the \"*Example: Guessing a Coin Toss*\" section in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb#Example:-Guessing-a-Coin-Toss).\n", + "In this exercise, you will model the throwing of dice within the context of a guessing game similar to the one shown in the \"*Example: Guessing a Coin Toss*\" section in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/03_content.ipynb#Example:-Guessing-a-Coin-Toss).\n", "\n", "As the game involves randomness, we import the [random ](https://docs.python.org/3/library/random.html) module from the [standard library ](https://docs.python.org/3/library/index.html). To follow best practices, we set the random seed as well." ] diff --git a/04_iteration/06_review.ipynb b/04_iteration/06_review.ipynb index 2043dcc..eeb19a5 100644 --- a/04_iteration/06_review.ipynb +++ b/04_iteration/06_review.ipynb @@ -11,7 +11,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb), [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb), and the [third ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb) part of Chapter 4.\n", + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/00_content.ipynb), [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/02_content.ipynb), and the [third ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/03_content.ipynb) part of Chapter 4.\n", "\n", "Be concise in your answers! Most questions can be answered in *one* sentence." ] diff --git a/05_numbers/00_content.ipynb b/05_numbers/00_content.ipynb index d04c106..405c540 100644 --- a/05_numbers/00_content.ipynb +++ b/05_numbers/00_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/00_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/05_numbers/00_content.ipynb)." ] }, { @@ -32,17 +32,17 @@ "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/develop/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/develop/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", + "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/main/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/main/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/develop/07_sequences/00_content.ipynb), [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb), [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb), [Chapter 10 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/10_arrarys/00_content.ipynb) then cover the more \"complex\" data types, including, for example, the `list` type. Finally, [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb) completes the picture by introducing language constructs to create custom types.\n", + "[Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/00_content.ipynb), [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb), [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/00_content.ipynb), [Chapter 10 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/10_arrarys/00_content.ipynb) then cover the more \"complex\" data types, including, for example, the `list` type. Finally, [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb) 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/develop/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/develop/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/develop/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", + "- [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/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/main/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/main/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 add an [Appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/03_appendix.ipynb) where we 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 add an [Appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/03_appendix.ipynb) where we 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." ] }, { @@ -64,7 +64,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/develop/01_elements/00_content.ipynb#%28Arithmetic%29-Operators).\n", + "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/main/01_elements/00_content.ipynb#%28Arithmetic%29-Operators).\n", "\n", "One way to create `int` objects is by simply writing its value as a literal with the digits `0` to `9`." ] @@ -335,7 +335,7 @@ } }, "source": [ - "Whereas the integer division operator `//` \"rounds\" towards negative infinity (cf., the \"*(Arithmetic) Operators*\" section in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#%28Arithmetic%29-Operators)), the [int() ](https://docs.python.org/3/library/functions.html#int) built-in rounds towards `0`." + "Whereas the integer division operator `//` \"rounds\" towards negative infinity (cf., the \"*(Arithmetic) Operators*\" section in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb#%28Arithmetic%29-Operators)), the [int() ](https://docs.python.org/3/library/functions.html#int) built-in rounds towards `0`." ] }, { @@ -1104,7 +1104,7 @@ } }, "source": [ - "The [Further Resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/06_resources.ipynb) section at the end of this chapter provides video tutorials on addition and multiplication in binary. Subtraction and division are a bit more involved but essentially also easy to understand." + "The [Further Resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/06_resources.ipynb) section at the end of this chapter provides video tutorials on addition and multiplication in binary. Subtraction and division are a bit more involved but essentially also easy to understand." ] }, { @@ -1575,7 +1575,7 @@ } }, "source": [ - "While there are conventions that model negative integers with $0$s and $1$s in memory (cf., [Two's Complement ](https://en.wikipedia.org/wiki/Two%27s_complement)), Python manages that for us, and we do not look into the theory here for brevity. We have learned all that a practitioner needs to know about how integers are modeled in a computer. The [Further Resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/06_resources.ipynb) section at the end of this chapter provides a video tutorial on how the [Two's Complement ](https://en.wikipedia.org/wiki/Two%27s_complement) idea works.\n", + "While there are conventions that model negative integers with $0$s and $1$s in memory (cf., [Two's Complement ](https://en.wikipedia.org/wiki/Two%27s_complement)), Python manages that for us, and we do not look into the theory here for brevity. We have learned all that a practitioner needs to know about how integers are modeled in a computer. The [Further Resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/06_resources.ipynb) section at the end of this chapter provides a video tutorial on how the [Two's Complement ](https://en.wikipedia.org/wiki/Two%27s_complement) idea works.\n", "\n", "The binary and hexadecimal representations of negative integers are identical to their positive counterparts except that they start with a minus sign `-`. However, as the video tutorial at the end of the chapter reveals, that is *not* how the bits are organized in memory." ] diff --git a/05_numbers/01_content.ipynb b/05_numbers/01_content.ipynb index af3b6c0..265ae0b 100644 --- a/05_numbers/01_content.ipynb +++ b/05_numbers/01_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/01_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/05_numbers/01_content.ipynb)." ] }, { @@ -1496,7 +1496,7 @@ "source": [ "The built-in [format() ](https://docs.python.org/3/library/functions.html#format) function allows us to show the **significant digits** of a `float` number as they exist in memory to arbitrary precision. To exemplify it, let's view a couple of `float` objects with `50` digits. This analysis reveals that almost no `float` number is precise! After 14 or 15 digits \"weird\" things happen. As we see further below, the \"random\" digits ending the `float` numbers do *not* \"physically\" exist in memory! Rather, they are \"calculated\" by the [format() ](https://docs.python.org/3/library/functions.html#format) function that is forced to show `50` digits.\n", "\n", - "The [format() ](https://docs.python.org/3/library/functions.html#format) function is different from the [format() ](https://docs.python.org/3/library/stdtypes.html#str.format) method on `str` objects introduced in the next chapter (cf., [Chapter 6 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/00_content.ipynb#format%28%29-Method)): Yet, both work with the so-called [format specification mini-language ](https://docs.python.org/3/library/string.html#format-specification-mini-language): `\".50f\"` is the instruction to show `50` digits of a `float` number." + "The [format() ](https://docs.python.org/3/library/functions.html#format) function is different from the [format() ](https://docs.python.org/3/library/stdtypes.html#str.format) method on `str` objects introduced in the next chapter (cf., [Chapter 6 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/00_content.ipynb#format%28%29-Method)): Yet, both work with the so-called [format specification mini-language ](https://docs.python.org/3/library/string.html#format-specification-mini-language): `\".50f\"` is the instruction to show `50` digits of a `float` number." ] }, { @@ -2030,7 +2030,7 @@ } }, "source": [ - "As seen in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/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." + "As seen in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/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." ] }, { diff --git a/05_numbers/02_content.ipynb b/05_numbers/02_content.ipynb index 3f24e6a..701a414 100644 --- a/05_numbers/02_content.ipynb +++ b/05_numbers/02_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/02_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/05_numbers/02_content.ipynb)." ] }, { @@ -697,7 +697,7 @@ } }, "source": [ - "Analogous to the discussion of *containers* and *iterables* in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_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", + "Analogous to the discussion of *containers* and *iterables* in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/02_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", @@ -819,7 +819,7 @@ "\n", "For, example, as all numeric data types are `Complex` numbers in the abstract sense, they all work with the built-in [abs() ](https://docs.python.org/3/library/functions.html#abs) function (cf., [documentation ](https://docs.python.org/3/library/numbers.html#numbers.Complex)). While it is intuitively clear what the [absolute value ](https://en.wikipedia.org/wiki/Absolute_value) (i.e., \"distance\" from $0$) of an integer, a fraction, or any real number is, [abs() ](https://docs.python.org/3/library/functions.html#abs) calculates the equivalent of that for complex numbers. That concept is called the [magnitude ](https://en.wikipedia.org/wiki/Magnitude_%28mathematics%29) of a number, and is really a *generalization* of the absolute value.\n", "\n", - "Relating back to the concept of **duck typing** mentioned in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb#Duck-Typing), `int`, `float`, and `complex` objects \"walk\" and \"quack\" alike in context of the [abs() ](https://docs.python.org/3/library/functions.html#abs) function." + "Relating back to the concept of **duck typing** mentioned in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/00_content.ipynb#Duck-Typing), `int`, `float`, and `complex` objects \"walk\" and \"quack\" alike in context of the [abs() ](https://docs.python.org/3/library/functions.html#abs) function." ] }, { @@ -1174,7 +1174,7 @@ } }, "source": [ - "However, if we model `1 / 10` as a `Fraction` object (cf., [Appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/03_appendix.ipynb#The-Fraction-Type)), it is recognized as a `Rational` number." + "However, if we model `1 / 10` as a `Fraction` object (cf., [Appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/03_appendix.ipynb#The-Fraction-Type)), it is recognized as a `Rational` number." ] }, { diff --git a/05_numbers/03_appendix.ipynb b/05_numbers/03_appendix.ipynb index 9551b70..4ab003b 100644 --- a/05_numbers/03_appendix.ipynb +++ b/05_numbers/03_appendix.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/03_appendix.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/05_numbers/03_appendix.ipynb)." ] }, { diff --git a/05_numbers/05_review.ipynb b/05_numbers/05_review.ipynb index a911fb0..9c0a424 100644 --- a/05_numbers/05_review.ipynb +++ b/05_numbers/05_review.ipynb @@ -11,7 +11,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb), [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/01_content.ipynb), and the [third ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/02_content.ipynb) part of Chapter 5. Some questions regard the [Appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/03_appendix.ipynb); that is indicated with a **\\***.\n", + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/00_content.ipynb), [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/01_content.ipynb), and the [third ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/02_content.ipynb) part of Chapter 5. Some questions regard the [Appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/03_appendix.ipynb); that is indicated with a **\\***.\n", "\n", "Be concise in your answers! Most questions can be answered in *one* sentence." ] diff --git a/05_numbers/06_resources.ipynb b/05_numbers/06_resources.ipynb index 5fa8cfa..76db27c 100644 --- a/05_numbers/06_resources.ipynb +++ b/05_numbers/06_resources.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/06_resources.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/05_numbers/06_resources.ipynb)." ] }, { diff --git a/06_text/00_content.ipynb b/06_text/00_content.ipynb index f1f6871..57c34fa 100644 --- a/06_text/00_content.ipynb +++ b/06_text/00_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/00_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/06_text/00_content.ipynb)." ] }, { @@ -30,7 +30,7 @@ } }, "source": [ - "In this chapter, we continue the study of the built-in data types. The next layer on top of numbers consists of **textual data** that are modeled primarily with the `str` type in Python. `str` objects are more complex than the numeric objects in [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb) as they *consist* of an *arbitrary* and possibly large number of *individual* characters that may be chosen from *any* alphabet in the history of humankind. Luckily, Python abstracts away most of this complexity from us. However, after looking at the `str` type in great detail, we briefly introduce the `bytes` type at the end of this chapter to understand how characters are modeled in memory." + "In this chapter, we continue the study of the built-in data types. The next layer on top of numbers consists of **textual data** that are modeled primarily with the `str` type in Python. `str` objects are more complex than the numeric objects in [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/00_content.ipynb) as they *consist* of an *arbitrary* and possibly large number of *individual* characters that may be chosen from *any* alphabet in the history of humankind. Luckily, Python abstracts away most of this complexity from us. However, after looking at the `str` type in great detail, we briefly introduce the `bytes` type at the end of this chapter to understand how characters are modeled in memory." ] }, { @@ -137,7 +137,7 @@ "source": [ "As seen before, a `str` object evaluates to itself in a literal notation with enclosing **single quotes** `'`.\n", "\n", - "In [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Value-/-(Semantic)-\"Meaning\"), we specify the double quotes `\"` convention this book follows. Yet, single quotes `'` and double quotes `\"` are *perfect* substitutes. We could use the reverse convention, as well. As [this discussion ](https://stackoverflow.com/questions/56011/single-quotes-vs-double-quotes-in-python) shows, many programmers have *strong* opinions about such conventions. Consequently, the discussion was \"closed as not constructive\" by the moderators." + "In [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb#Value-/-(Semantic)-\"Meaning\"), we specify the double quotes `\"` convention this book follows. Yet, single quotes `'` and double quotes `\"` are *perfect* substitutes. We could use the reverse convention, as well. As [this discussion ](https://stackoverflow.com/questions/56011/single-quotes-vs-double-quotes-in-python) shows, many programmers have *strong* opinions about such conventions. Consequently, the discussion was \"closed as not constructive\" by the moderators." ] }, { @@ -290,7 +290,7 @@ } }, "source": [ - "As an alternative to the literal notation, we may use the built-in [str() ](https://docs.python.org/3/library/stdtypes.html#str) constructor to cast non-`str` objects as `str` ones. As [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb) reveals, basically any object in Python has a **text representation**. Because of that we may also pass `list` objects, the boolean `True` and `False`, or `None` to [str() ](https://docs.python.org/3/library/stdtypes.html#str)." + "As an alternative to the literal notation, we may use the built-in [str() ](https://docs.python.org/3/library/stdtypes.html#str) constructor to cast non-`str` objects as `str` ones. As [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb) reveals, basically any object in Python has a **text representation**. Because of that we may also pass `list` objects, the boolean `True` and `False`, or `None` to [str() ](https://docs.python.org/3/library/stdtypes.html#str)." ] }, { @@ -456,7 +456,7 @@ } }, "source": [ - "As shown in the \"*Guessing a Coin Toss*\" example in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb#Example:-Guessing-a-Coin-Toss), the built-in [input() ](https://docs.python.org/3/library/functions.html#input) function displays a prompt to the user and returns whatever is entered as a `str` object. [input() ](https://docs.python.org/3/library/functions.html#input) is in particular valuable when writing command-line tools." + "As shown in the \"*Guessing a Coin Toss*\" example in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/03_content.ipynb#Example:-Guessing-a-Coin-Toss), the built-in [input() ](https://docs.python.org/3/library/functions.html#input) function displays a prompt to the user and returns whatever is entered as a `str` object. [input() ](https://docs.python.org/3/library/functions.html#input) is in particular valuable when writing command-line tools." ] }, { @@ -571,7 +571,7 @@ } }, "source": [ - "[open() ](https://docs.python.org/3/library/functions.html#open) returns a **[proxy ](https://en.wikipedia.org/wiki/Proxy_pattern)** object of type `TextIOWrapper` that allows us to interact with the file on disk. `mode='r'` shows that we opened the file in read-only mode and `encoding='UTF-8'` is explained in detail in the [The `bytes` Type ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/01_content.ipynb#The-bytes-Type) section at the end of this chapter." + "[open() ](https://docs.python.org/3/library/functions.html#open) returns a **[proxy ](https://en.wikipedia.org/wiki/Proxy_pattern)** object of type `TextIOWrapper` that allows us to interact with the file on disk. `mode='r'` shows that we opened the file in read-only mode and `encoding='UTF-8'` is explained in detail in the [The `bytes` Type ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/01_content.ipynb#The-bytes-Type) section at the end of this chapter." ] }, { @@ -1010,7 +1010,7 @@ } }, "source": [ - "Using syntax familiar from [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb#The-try-Statement) to explain what the `with open(...) as file:` does above, we provide an alternative formulation with a `try` statement below: The `finally`-branch is *always* executed, even if an exception is raised inside the `for`-loop. Therefore, `file` is sure to be closed too. However, this formulation is somewhat less expressive." + "Using syntax familiar from [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/00_content.ipynb#The-try-Statement) to explain what the `with open(...) as file:` does above, we provide an alternative formulation with a `try` statement below: The `finally`-branch is *always* executed, even if an exception is raised inside the `for`-loop. Therefore, `file` is sure to be closed too. However, this formulation is somewhat less expressive." ] }, { @@ -1373,7 +1373,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/develop/04_iteration/02_content.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/main/04_iteration/02_content.ipynb#Containers-vs.-Iterables)).\n", "\n", "It unifies *four* [orthogonal ](https://en.wikipedia.org/wiki/Orthogonality) (i.e., \"independent\") concepts into one bigger idea: Any data type, such as `str`, is considered a sequence if it\n", "\n", @@ -1382,11 +1382,11 @@ "3. can be **iterated** over\n", "4. in a *predictable* **order**.\n", "\n", - "[Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb#Collections-vs.-Sequences) formalizes these concepts in great detail. Here, we keep our focus on the `str` type that historically received its name as it models a **[string of characters ](https://en.wikipedia.org/wiki/String_%28computer_science%29)**. *String* is simply another term for *sequence* in the computer science literature.\n", + "[Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/00_content.ipynb#Collections-vs.-Sequences) formalizes these concepts in great detail. Here, we keep our focus on the `str` type that historically received its name as it models a **[string of characters ](https://en.wikipedia.org/wiki/String_%28computer_science%29)**. *String* is simply another term for *sequence* in the computer science literature.\n", "\n", "Another example of a sequence is the `list` type. Because of that, `str` objects may be treated like `list` objects in many situations.\n", "\n", - "Below, the built-in [len() ](https://docs.python.org/3/library/functions.html#len) function tells us how many characters make up `text`. [len() ](https://docs.python.org/3/library/functions.html#len) would not work with an \"infinite\" object. As anything modeled in a program must fit into a computer's finite memory, there cannot exist truly infinite objects; however, [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb#Iterators-vs.-Iterables) introduces specialized iterable data types that can be used to model an *infinite* series of \"things\" and that, consequently, have no concept of \"length.\"" + "Below, the built-in [len() ](https://docs.python.org/3/library/functions.html#len) function tells us how many characters make up `text`. [len() ](https://docs.python.org/3/library/functions.html#len) would not work with an \"infinite\" object. As anything modeled in a program must fit into a computer's finite memory, there cannot exist truly infinite objects; however, [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb#Iterators-vs.-Iterables) introduces specialized iterable data types that can be used to model an *infinite* series of \"things\" and that, consequently, have no concept of \"length.\"" ] }, { @@ -1605,7 +1605,7 @@ } }, "source": [ - "As `str` objects are *ordered* and *finite*, we may **index** into them to obtain individual characters with the **indexing operator** `[]`. This is analogous to how we obtained individual elements of a `list` object in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_content.ipynb#Who-am-I?-And-how-many?)." + "As `str` objects are *ordered* and *finite*, we may **index** into them to obtain individual characters with the **indexing operator** `[]`. This is analogous to how we obtained individual elements of a `list` object in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/03_content.ipynb#Who-am-I?-And-how-many?)." ] }, { @@ -2292,9 +2292,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/develop/01_elements/03_content.ipynb#Who-am-I?-And-how-many?), this is *not* allowed for the individual characters of `str` objects. Once created, they can *not* be changed. Formally, we say that `str` objects are **immutable**. In that regard, they are like the numeric types in [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb).\n", + "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/main/01_elements/03_content.ipynb#Who-am-I?-And-how-many?), this is *not* allowed for the individual characters of `str` objects. Once created, they can *not* be changed. Formally, we say that `str` objects are **immutable**. In that regard, they are like the numeric types in [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/00_content.ipynb).\n", "\n", - "On the contrary, objects that may be changed after creation, are called **mutable**. We already saw in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_content.ipynb#Who-am-I?-And-how-many?) how mutable objects are more difficult to reason about for a beginner, in particular, if more than one variable references it. Yet, mutability does have its place in a programmer's toolbox, and we revisit this idea in the next chapters.\n", + "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/main/01_elements/03_content.ipynb#Who-am-I?-And-how-many?) how mutable objects are more difficult to reason about for a beginner, in particular, if more than one variable references it. Yet, mutability does have its place in a programmer's toolbox, and we revisit this idea in the next chapters.\n", "\n", "The `TypeError` indicates that `str` objects are *immutable*: Assignment to an index or a slice are *not* supported." ] @@ -3377,7 +3377,7 @@ } }, "source": [ - "As mentioned in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Operator-Overloading), the `+` and `*` operators are *overloaded* and used for **string concatenation**. They always create *new* `str` objects. That has nothing to do with the `str` type's immutability, but is the default behavior of operators." + "As mentioned in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/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." ] }, { @@ -3447,7 +3447,7 @@ } }, "source": [ - "The *relational* operators also work with `str` objects, another example of operator overloading. Comparison is done one character at a time in a pairwise fashion until the first pair differs or one operand ends. However, `str` objects are sorted in a \"weird\" way. For example, all upper case characters come before all lower case characters. The reason for that is given in the \"*Characters are Numbers with a Convention*\" sub-section in the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/02_content.ipynb#Characters-are-Numbers-with-a-Convention) of this chapter." + "The *relational* operators also work with `str` objects, another example of operator overloading. Comparison is done one character at a time in a pairwise fashion until the first pair differs or one operand ends. However, `str` objects are sorted in a \"weird\" way. For example, all upper case characters come before all lower case characters. The reason for that is given in the \"*Characters are Numbers with a Convention*\" sub-section in the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/02_content.ipynb#Characters-are-Numbers-with-a-Convention) of this chapter." ] }, { @@ -3857,7 +3857,7 @@ } }, "source": [ - "The `%` operator that we saw in the context of modulo division in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#%28Arithmetic%29-Operators) is overloaded with string interpolation when its first operand is a `str` object. The second operand consists of all expressions to be filled in. Format specifiers work with a `%` instead of curly braces and according to a different set of rules referred to as **[printf-style string formatting ](https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting)**. So, `{:.2f}` becomes `%.2f`.\n", + "The `%` operator that we saw in the context of modulo division in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/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." ] @@ -3894,7 +3894,7 @@ } }, "source": [ - "To insert more than one expression, we must list them in order and between parenthesis `(` and `)`. As [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb#The-tuple-Type) reveals, this literal syntax creates an object of type `tuple`. Also, to format an expression as text, we use the format specifier `%s`." + "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/main/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`." ] }, { diff --git a/06_text/01_exercises_palindromes.ipynb b/06_text/01_exercises_palindromes.ipynb index 89dc687..b8a8c33 100644 --- a/06_text/01_exercises_palindromes.ipynb +++ b/06_text/01_exercises_palindromes.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/01_exercises.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/06_text/01_exercises.ipynb)." ] }, { @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/00_content.ipynb) of Chapter 6.\n", + "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/00_content.ipynb) of Chapter 6.\n", "\n", "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." ] @@ -38,7 +38,7 @@ "\n", "In this exercise, you implement various functions that check if the given arguments are palindromes or not. We start with an iterative implementation and end with a recursive one.\n", "\n", - "Conceptually, the first function, `unpythonic_palindrome()`, is similar to the \"*Is the square of a number in `[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]` greater than `100`?*\" example in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb#Example:-Is-the-square-of-a-number-in-[7,-11,-8,-5,-3,-12,-2,-6,-9,-10,-1,-4]-greater-than-100?): It assumes that the `text` argument is a palindrome (i.e., it initializes `is_palindrom` to `True`) and then checks in a `for`-loop if a pair of corresponding characters, `forward` and `backward`, contradicts that.\n", + "Conceptually, the first function, `unpythonic_palindrome()`, is similar to the \"*Is the square of a number in `[7, 11, 8, 5, 3, 12, 2, 6, 9, 10, 1, 4]` greater than `100`?*\" example in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/03_content.ipynb#Example:-Is-the-square-of-a-number-in-[7,-11,-8,-5,-3,-12,-2,-6,-9,-10,-1,-4]-greater-than-100?): It assumes that the `text` argument is a palindrome (i.e., it initializes `is_palindrom` to `True`) and then checks in a `for`-loop if a pair of corresponding characters, `forward` and `backward`, contradicts that.\n", "\n", "**Q1**: How many iterations are needed in the `for`-loop? Take into account that `text` may contain an even or odd number of characters! Inside `unpythonic_palindrome()` below, write an expression whose result is assigned to `chars_to_check`!" ] diff --git a/06_text/02_content.ipynb b/06_text/02_content.ipynb index fc5efa3..887deb5 100644 --- a/06_text/02_content.ipynb +++ b/06_text/02_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/01_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/06_text/01_content.ipynb)." ] }, { @@ -491,7 +491,7 @@ "source": [ "So far, we used the term **character** without any further consideration. In this section, we briefly look into what characters are and how they are modeled in software.\n", "\n", - "[Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb) gives us an idea on how individual **bits** are used to express all types of numbers, from \"simple\" `int` objects to \"complex\" `float` ones. To model characters, another **layer of abstraction** is put on top of whole numbers. So, just as bits are used to express integers, they themselves are used to express characters." + "[Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/00_content.ipynb) gives us an idea on how individual **bits** are used to express all types of numbers, from \"simple\" `int` objects to \"complex\" `float` ones. To model characters, another **layer of abstraction** is put on top of whole numbers. So, just as bits are used to express integers, they themselves are used to express characters." ] }, { @@ -637,7 +637,7 @@ "\n", "For example, the digit `5` is mapped to the number `53` in ASCII. The binary representation of `53` is `0b_11_0101` and the least significant four bits, `0101`, mean $5$. Similarly, the letter `\"E\"` is the fifth letter in the alphabet. It is encoded with the number `69` in ASCII, which is `0b_100_0101` in binary. And, the least significant bits, `0_0101`, mean $5$. Analogously, `\"e\"` is encoded with `101` in ASCII, which is `0b_110_0101` in binary. And, the least significant bits, `0_0101`, mean $5$ again. This encoding was chosen mainly because programmers \"in the old days\" needed to implement these encodings \"by hand.\" Python abstracts that logic away from its users.\n", "\n", - "This encoding scheme is also the cause for the \"weird\" sorting in the \"*String Comparison*\" section in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/01_content.ipynb#String-Comparison) of this chapter, where `\"apple\"` comes *after* `\"Banana\"`. As `\"a\"` is encoded with `97` and `\"B\"` with `66`, `\"Banana\"` must of course be \"smaller\" than `\"apple\"` when comparison is done in a pairwise fashion of the individual characters." + "This encoding scheme is also the cause for the \"weird\" sorting in the \"*String Comparison*\" section in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/01_content.ipynb#String-Comparison) of this chapter, where `\"apple\"` comes *after* `\"Banana\"`. As `\"a\"` is encoded with `97` and `\"B\"` with `66`, `\"Banana\"` must of course be \"smaller\" than `\"apple\"` when comparison is done in a pairwise fashion of the individual characters." ] }, { @@ -1991,7 +1991,7 @@ } }, "source": [ - "A best practice is to *always* specify the `encoding`, especially on computers running on Windows (cf., the talk by Åukasz Langa in the [Further Resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/05_resources.ipynb#Unicode)) section at the end of this chapter.\n", + "A best practice is to *always* specify the `encoding`, especially on computers running on Windows (cf., the talk by Åukasz Langa in the [Further Resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/05_resources.ipynb#Unicode)) section at the end of this chapter.\n", "\n", "Below is the first example involving [open() ](https://docs.python.org/3/library/functions.html#open) one last time: It shows how *all* the contents of a text file should be read into one `str` object." ] diff --git a/06_text/04_review.ipynb b/06_text/04_review.ipynb index b92cb56..17dd0a5 100644 --- a/06_text/04_review.ipynb +++ b/06_text/04_review.ipynb @@ -11,7 +11,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/00_content.ipynb) and [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/01_content.ipynb) part of Chapter 6.\n", + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/00_content.ipynb) and [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/01_content.ipynb) part of Chapter 6.\n", "\n", "Be concise in your answers! Most questions can be answered in *one* sentence." ] diff --git a/06_text/05_resources.ipynb b/06_text/05_resources.ipynb index 24882f9..425c6cf 100644 --- a/06_text/05_resources.ipynb +++ b/06_text/05_resources.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/05_resources.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/06_text/05_resources.ipynb)." ] }, { diff --git a/07_sequences/00_content.ipynb b/07_sequences/00_content.ipynb index c37450b..8f8984f 100644 --- a/07_sequences/00_content.ipynb +++ b/07_sequences/00_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/00_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/00_content.ipynb)." ] }, { @@ -30,11 +30,11 @@ } }, "source": [ - "We studied numbers (cf., [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb)) and textual data (cf., [Chapter 6 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/00_content.ipynb)) first mainly because objects of the presented data types are \"simple.\" That is so for two reasons: First, they are *immutable*, and, as we saw in the \"*Who am I? And how many?*\" section in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_content.ipynb#Who-am-I?-And-how-many?), mutable objects can quickly become hard to reason about. Second, they are \"flat\" in the sense that they are *not* composed of other objects.\n", + "We studied numbers (cf., [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/00_content.ipynb)) and textual data (cf., [Chapter 6 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/00_content.ipynb)) first mainly because objects of the presented data types are \"simple.\" That is so for two reasons: First, they are *immutable*, and, as we saw in the \"*Who am I? And how many?*\" section in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/03_content.ipynb#Who-am-I?-And-how-many?), mutable objects can quickly become hard to reason about. Second, they are \"flat\" in the sense that they are *not* composed of other objects.\n", "\n", "The `str` type is a bit of a corner case in this regard. While one could argue that a longer `str` object, for example, `\"text\"`, is composed of individual characters, this is *not* the case in memory as the literal `\"text\"` only creates *one* object (i.e., one \"bag\" of $0$s and $1$s modeling all characters).\n", "\n", - "This chapter, [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb), [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb), and [Chapter 10 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/10_arrays/00_content.ipynb) introduce various \"complex\" data types. While some are mutable and others are not, they all share that they are primarily used to \"manage,\" or structure, the memory in a program (i.e., they provide references to other objects). Unsurprisingly, computer scientists refer to the ideas behind these data types as **[data structures ](https://en.wikipedia.org/wiki/Data_structure)**.\n", + "This chapter, [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb), [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/00_content.ipynb), and [Chapter 10 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/10_arrays/00_content.ipynb) introduce various \"complex\" data types. While some are mutable and others are not, they all share that they are primarily used to \"manage,\" or structure, the memory in a program (i.e., they provide references to other objects). Unsurprisingly, computer scientists refer to the ideas behind these data types as **[data structures ](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) and [vectors ](https://en.wikipedia.org/wiki/Vector_%28mathematics_and_physics%29). These formats share the property that they are composed of smaller units that come in a sequence of, for example, rows/columns/cells or elements/entries." ] @@ -58,9 +58,9 @@ } }, "source": [ - "[Chapter 6 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/00_content.ipynb#A-\"String\"-of-Characters) already describes the **sequence** properties of `str` objects. In this section, we take a step back and study these properties one by one.\n", + "[Chapter 6 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/00_content.ipynb#A-\"String\"-of-Characters) already describes the **sequence** properties of `str` objects. In this section, we take a step back and study these properties one by one.\n", "\n", - "The [collections.abc ](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/develop/05_numbers/02_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 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/main/05_numbers/02_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 according to their behavior in various contexts.\n", "\n", "As an illustration, consider `numbers` and `text` below, two objects of *different* types." ] @@ -142,7 +142,7 @@ } }, "source": [ - "In [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb#Containers-vs.-Iterables), we referred to such types as *iterables*. That is *not* a proper [English](https://dictionary.cambridge.org/spellcheck/english-german/?q=iterable) word, even if it may sound like one at first sight. Yet, it is an official term in the Python world formalized with the `Iterable` ABC in the [collections.abc ](https://docs.python.org/3/library/collections.abc.html) module.\n", + "In [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/02_content.ipynb#Containers-vs.-Iterables), we referred to such types as *iterables*. That is *not* a proper [English](https://dictionary.cambridge.org/spellcheck/english-german/?q=iterable) word, even if it may sound like one at first sight. Yet, it is an official term in the Python world formalized with the `Iterable` ABC in the [collections.abc ](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." ] @@ -192,7 +192,7 @@ } }, "source": [ - "As seen in [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/02_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", + "As seen in [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/02_content.ipynb#Goose-Typing), we can use ABCs with the built-in [isinstance() ](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`." ] @@ -325,7 +325,7 @@ } }, "source": [ - "Most of the data types in this chapter and [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb) and [Chapter 10 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/10_arrays/00_content.ipynb) 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", + "Most of the data types in this chapter and [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/00_content.ipynb) and [Chapter 10 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/10_arrays/00_content.ipynb) 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 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", @@ -902,11 +902,11 @@ } }, "source": [ - "The data types introduced in this chapter are sequences. Nevertheless, we also look at some data types that are neither collections nor sequences but are still useful to model sequential data in practice in [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb).\n", + "The data types introduced in this chapter are sequences. Nevertheless, we also look at some data types that are neither collections nor sequences but are still useful to model sequential data in practice in [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb).\n", "\n", "In Python-related documentations, the terms collection and sequence are heavily used, and the data science practitioner should always think of them in terms of the three or four behaviors they exhibit.\n", "\n", - "Data types that are collections but not sequences are covered in [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb)." + "Data types that are collections but not sequences are covered in [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/00_content.ipynb)." ] } ], diff --git a/07_sequences/01_content.ipynb b/07_sequences/01_content.ipynb index fb6d54d..d267d48 100644 --- a/07_sequences/01_content.ipynb +++ b/07_sequences/01_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/01_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/01_content.ipynb)." ] }, { @@ -209,7 +209,7 @@ "source": [ "Alternatively, we use the built-in [list() ](https://docs.python.org/3/library/functions.html#func-list) constructor to create a `list` object out of any (finite) *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/develop/04_iteration/02_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://pythontutor.com/visualize.html#code=r%20%3D%20range%281,%2013%29%0Al%20%3D%20list%28range%281,%2013%29%29&cumulative=false&curstr=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/main/04_iteration/02_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://pythontutor.com/visualize.html#code=r%20%3D%20range%281,%2013%29%0Al%20%3D%20list%28range%281,%2013%29%29&cumulative=false&curstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows this difference visually." ] }, { @@ -1691,7 +1691,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/develop/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", + "In contrast to the `str` type's methods in [Chapter 6 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/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." ] @@ -2029,7 +2029,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/develop/06_text/00_content.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/main/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", diff --git a/07_sequences/02_exercises_lists.ipynb b/07_sequences/02_exercises_lists.ipynb index 2709373..7d5fab5 100644 --- a/07_sequences/02_exercises_lists.ipynb +++ b/07_sequences/02_exercises_lists.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/02_exercises.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/02_exercises.ipynb)." ] }, { @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The exercises below assume that you have read the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/01_content.ipynb) of Chapter 7.\n", + "The exercises below assume that you have read the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/01_content.ipynb) of Chapter 7.\n", "\n", "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." ] diff --git a/07_sequences/03_content.ipynb b/07_sequences/03_content.ipynb index 817951f..bf7e929 100644 --- a/07_sequences/03_content.ipynb +++ b/07_sequences/03_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/03_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/03_content.ipynb)." ] }, { @@ -172,7 +172,7 @@ "\n", "The revised `add_xyz()` function below is more natural to reason about as it does *not* modify the passed in `arg` internally. [PythonTutor ](http://pythontutor.com/visualize.html#code=letters%20%3D%20%5B%22a%22,%20%22b%22,%20%22c%22%5D%0A%0Adef%20add_xyz%28arg%29%3A%0A%20%20%20%20new_arg%20%3D%20arg%5B%3A%5D%0A%20%20%20%20new_arg.extend%28%5B%22x%22,%20%22y%22,%20%22z%22%5D%29%0A%20%20%20%20return%20new_arg%0A%0Aletters_with_xyz%20%3D%20add_xyz%28letters%29&cumulative=false&curstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) shows that as well. 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 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb)." + "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 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb)." ] }, { @@ -1469,7 +1469,7 @@ } }, "source": [ - "In the \"*List Operations*\" section in the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/01_content.ipynb#List-Operations) of this chapter, 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", + "In the \"*List Operations*\" section in the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/01_content.ipynb#List-Operations) of this chapter, 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 literal `tuple` style. Then, any *finite* iterable on the right-hand side is unpacked. So, `numbers` is unpacked into *twelve* variables below." ] @@ -2041,7 +2041,7 @@ } }, "source": [ - "Unpacking allows us to rewrite the iterative `fibonacci()` function from [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb#\"Hard-at-first-Glance\"-Example:-Fibonacci-Numbers-%28revisited%29) in a concise way." + "Unpacking allows us to rewrite the iterative `fibonacci()` function from [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/02_content.ipynb#\"Hard-at-first-Glance\"-Example:-Fibonacci-Numbers-%28revisited%29) in a concise way." ] }, { @@ -2376,7 +2376,7 @@ } }, "source": [ - "In the \"*Packing & Unpacking with Functions*\" [exercise ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/04_exercises.ipynb), 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/main/07_sequences/04_exercises.ipynb), 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. 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." ] diff --git a/07_sequences/04_exercises_un-packing.ipynb b/07_sequences/04_exercises_un-packing.ipynb index 6df6a33..3f783a2 100644 --- a/07_sequences/04_exercises_un-packing.ipynb +++ b/07_sequences/04_exercises_un-packing.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/04_exercises.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/04_exercises.ipynb)." ] }, { @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The exercises below assume that you have read the [third part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/03_content.ipynb) of Chapter 7.\n", + "The exercises below assume that you have read the [third part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/03_content.ipynb) of Chapter 7.\n", "\n", "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." ] @@ -34,7 +34,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/develop/07_sequences/03_content.ipynb#Function-Definitions-&-Calls), we define the following function `product()`. In this exercise, you will improve it by making it more \"user-friendly.\"" + "In the \"*Function Definitions & Calls*\" section in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/03_content.ipynb#Function-Definitions-&-Calls), we define the following function `product()`. In this exercise, you will improve it by making it more \"user-friendly.\"" ] }, { @@ -128,7 +128,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.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/main/07_sequences/00_content.ipynb#Function-Definitions-&-Calls), we also pass a `list` object, like `one_hundred`, to `product()`, and *no* exception is raised." ] }, { @@ -153,7 +153,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q3**: What is wrong with that? What *kind* of error (cf., [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/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()`!" + "**Q3**: What is wrong with that? What *kind* of error (cf., [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb#Formal-vs.-Natural-Languages)) is that conceptually? Describe precisely what happens to the passed in `one_hundred` in every line within `product()`!" ] }, { @@ -331,7 +331,7 @@ "source": [ "**Q7**: Rewrite `product()` so that it takes a *keyword-only* argument `start`, defaulting to the above *default* or *start* value, and use `start` internally instead of `result`!\n", "\n", - "Hint: Remember that a *keyword-only* argument is any parameter specified in a function's header line after the first and only `*` (cf., [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.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/main/02_functions/00_content.ipynb#Keyword-only-Arguments))." ] }, { @@ -544,7 +544,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 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb#The-set-Type), and one essential difference to the `list` type is that objects of type `set` have *no* order regarding their elements. So, even though `[2, 5, 10]` and `{2, 5, 10}` look almost the same, the order implied in the literal notation gets lost in memory!" + "**Side Note**: Above, we make `product()` work with a single *collection* type argument instead of a *sequence* type to keep it more generic: For example, we can pass in a `set` object, like `{2, 5, 10}` below, and `product()` continues to work correctly. The `set` type is introducted in [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/00_content.ipynb#The-set-Type), and one essential difference to the `list` type is that objects of type `set` have *no* order regarding their elements. So, even though `[2, 5, 10]` and `{2, 5, 10}` look almost the same, the order implied in the literal notation gets lost in memory!" ] }, { diff --git a/07_sequences/05_appendix.ipynb b/07_sequences/05_appendix.ipynb index b7fb1f6..f34771d 100644 --- a/07_sequences/05_appendix.ipynb +++ b/07_sequences/05_appendix.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/05_appendix.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/05_appendix.ipynb)." ] }, { @@ -30,7 +30,7 @@ } }, "source": [ - "In the [third part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/03_content.ipynb#Tuples-are-like-\"Immutable-Lists\") of the chapter, we proposed the idea that `tuple` objects are like \"immutable lists.\" Often, however, we use `tuple` objects to represent a **record** of related **fields**. Then, each element has a *semantic* meaning (i.e., a descriptive name).\n", + "In the [third part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/03_content.ipynb#Tuples-are-like-\"Immutable-Lists\") of the chapter, we proposed the idea that `tuple` objects are like \"immutable lists.\" Often, however, we use `tuple` objects to represent a **record** of related **fields**. Then, each element has a *semantic* meaning (i.e., a descriptive name).\n", "\n", "As an example, think of a spreadsheet with information on students in a course. Each row represents a record and holds all the data associated with an individual student. The columns (e.g., matriculation number, first name, last name) are the fields that may come as *different* data types (e.g., `int` for the matriculation number, `str` for the names).\n", "\n", @@ -82,7 +82,7 @@ } }, "source": [ - "A better way is to create a *custom* data type. While that is covered in depth in [Chapter 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb), 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 11 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb), 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." ] }, { diff --git a/07_sequences/07_review.ipynb b/07_sequences/07_review.ipynb index 44b7766..57927bd 100644 --- a/07_sequences/07_review.ipynb +++ b/07_sequences/07_review.ipynb @@ -11,7 +11,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb), [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/01_content.ipynb), and the [third ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/03_content.ipynb) part of Chapter 7. Some questions regard the [Appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/05_appendix.ipynb); that is indicated with a **\\***.\n", + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/00_content.ipynb), [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/01_content.ipynb), and the [third ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/03_content.ipynb) part of Chapter 7. Some questions regard the [Appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/05_appendix.ipynb); that is indicated with a **\\***.\n", "\n", "Be concise in your answers! Most questions can be answered in *one* sentence." ] @@ -34,7 +34,7 @@ "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/develop/04_iteration/02_content.ipynb#Containers-vs.-Iterables). How do they relate to **sequences**? " + "**Q1**: We have seen **containers** and **iterables** before in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/02_content.ipynb#Containers-vs.-Iterables). How do they relate to **sequences**? " ] }, { diff --git a/08_mfr/00_content.ipynb b/08_mfr/00_content.ipynb index bcb7f04..4a1ae86 100644 --- a/08_mfr/00_content.ipynb +++ b/08_mfr/00_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/00_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/08_mfr/00_content.ipynb)." ] }, { @@ -32,9 +32,9 @@ "source": [ "In this chapter, we continue the study of sequential data by looking at memory efficient ways to process the elements in a sequence. That is an important topic for the data science practitioner who must be able to work with data that does *not* fit into a single computer's memory.\n", "\n", - "As shown in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb#Containers-vs.-Iterables), both the `list` objects `[0, 1, 2, 3, 4]` and `[1, 3, 5, 7, 9]` on the one side and the `range` objects `range(5)` and `range(1, 10, 2)` on the other side allow us to loop over the same numbers. However, the latter two only create *one* `int` object in every iteration while the former two create *all* `int` objects before the loop even starts. In this aspect, we consider `range` objects to be \"rules\" in memory that know how to calculate the numbers *without* calculating them.\n", + "As shown in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/02_content.ipynb#Containers-vs.-Iterables), both the `list` objects `[0, 1, 2, 3, 4]` and `[1, 3, 5, 7, 9]` on the one side and the `range` objects `range(5)` and `range(1, 10, 2)` on the other side allow us to loop over the same numbers. However, the latter two only create *one* `int` object in every iteration while the former two create *all* `int` objects before the loop even starts. In this aspect, we consider `range` objects to be \"rules\" in memory that know how to calculate the numbers *without* calculating them.\n", "\n", - "In [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/01_content.ipynb#The-list-Type), we see how the built-in [list() ](https://docs.python.org/3/library/functions.html#func-list) constructor **materializes** the `range(1, 13)` object into the `list` object `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]`. In other words, we make `range(1, 13)` calculate *all* numbers at once and store them in a `list` object for further processing.\n", + "In [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/01_content.ipynb#The-list-Type), we see how the built-in [list() ](https://docs.python.org/3/library/functions.html#func-list) constructor **materializes** the `range(1, 13)` object into the `list` object `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]`. In other words, we make `range(1, 13)` calculate *all* numbers at once and store them in a `list` object for further processing.\n", "\n", "In many cases, however, it is not necessary to do that, and, in this chapter, we look at other types of \"rules\" in memory and how we can compose different \"rules\" together to implement bigger computations.\n", "\n", @@ -779,7 +779,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 in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_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 loop \"at the C level\" and are a lot faster because of that. Because of that, experienced Pythonistas tend to *not* use explicit `for`-loops so often." + "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 in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/03_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 loop \"at the C level\" and are a lot faster because of that. Because of that, experienced Pythonistas tend to *not* use explicit `for`-loops so often." ] }, { @@ -1129,7 +1129,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/develop/02_functions/00_content.ipynb#Anonymous-Functions), we use `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/main/02_functions/00_content.ipynb#Anonymous-Functions), we use `lambda` expressions to create `function` objects *without* a name referencing them.\n", "\n", "So, the above `sum_alt()` function could be rewritten as a `lambda` expression like so ..." ] diff --git a/08_mfr/01_content.ipynb b/08_mfr/01_content.ipynb index a92bd6e..38300c0 100644 --- a/08_mfr/01_content.ipynb +++ b/08_mfr/01_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/01_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/08_mfr/01_content.ipynb)." ] }, { @@ -30,7 +30,7 @@ } }, "source": [ - "After introducing the Map-Filter-Reduce paradigm in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb) of this chapter, we first see how `list` comprehensions can replace the [map() ](https://docs.python.org/3/library/functions.html#map) and [filter() ](https://docs.python.org/3/library/functions.html#filter) built-ins in many cases. Then, we learn how `generator` expressions are like `list` comprehensions *without* using the memory. We end this part with a short discussion of 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." + "After introducing the Map-Filter-Reduce paradigm in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb) of this chapter, we first see how `list` comprehensions can replace the [map() ](https://docs.python.org/3/library/functions.html#map) and [filter() ](https://docs.python.org/3/library/functions.html#filter) built-ins in many cases. Then, we learn how `generator` expressions are like `list` comprehensions *without* using the memory. We end this part with a short discussion of 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." ] }, { @@ -52,7 +52,7 @@ } }, "source": [ - "Consider again the original \"*A simple Filter*\" example from [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb#Example:-A-simple-Filter), re-written such that both the mapping and the filtering are done in *one* `for`-loop instead of the *two* above." + "Consider again the original \"*A simple Filter*\" example from [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/03_content.ipynb#Example:-A-simple-Filter), re-written such that both the mapping and the filtering are done in *one* `for`-loop instead of the *two* above." ] }, { @@ -156,7 +156,7 @@ "source": [ "A list comprehension may always be used in a place where otherwise a `list` object would work.\n", "\n", - "For example, let's rewrite the \"*A simple Filter*\" example from [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb#Example:-A-simple-Filter) in just one line. As a caveat, the code below *materializes* all elements in memory *before* summing them up, and may, therefore, cause a `MemoryError` when executed with a bigger `numbers` list. We see with [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&curstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) how a `list` object exists in memory at step 17 and then \"gets lost\" right after. As the next section shows, this downside may be mitigated." + "For example, let's rewrite the \"*A simple Filter*\" example from [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/03_content.ipynb#Example:-A-simple-Filter) in just one line. As a caveat, the code below *materializes* all elements in memory *before* summing them up, and may, therefore, cause a `MemoryError` when executed with a bigger `numbers` list. We see with [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&curstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false) how a `list` object exists in memory at step 17 and then \"gets lost\" right after. As the next section shows, this downside may be mitigated." ] }, { @@ -639,7 +639,7 @@ } }, "source": [ - "To find the overall product, we *unpack* the list comprehension into the `product()` function from the \"*Function Definitions & Calls*\" sub-section in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/03_content.ipynb#Function-Definitions-&-Calls)." + "To find the overall product, we *unpack* the list comprehension into the `product()` function from the \"*Function Definitions & Calls*\" sub-section in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/03_content.ipynb#Function-Definitions-&-Calls)." ] }, { @@ -766,7 +766,7 @@ "source": [ "Because of the high memory consumption, Pythonistas avoid materialized `list` objects, and, thus, also `list` comprehensions, whenever possible. Instead, they prefer to work with **[`generator` expressions ](https://docs.python.org/3/reference/expressions.html#generator-expressions)**. Syntactically, they work like list comprehensions except that parentheses, `(` and `)`, replace brackets, `[` and `]`.\n", "\n", - "Let's go back to the original \"*A simple Filter*\" example from [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb#Example:-A-simple-Filter) one more time, apply the transformation $y := x^2 + 1$ to all even `numbers`, and sum them up." + "Let's go back to the original \"*A simple Filter*\" example from [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/03_content.ipynb#Example:-A-simple-Filter) one more time, apply the transformation $y := x^2 + 1$ to all even `numbers`, and sum them up." ] }, { @@ -1050,7 +1050,7 @@ } }, "source": [ - "`generator` objects work just like the `map` and `filter` objects in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb) of this chapter. So, with the [next() ](https://docs.python.org/3/library/functions.html#next) function, we can generate elements one by one." + "`generator` objects work just like the `map` and `filter` objects in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb) of this chapter. So, with the [next() ](https://docs.python.org/3/library/functions.html#next) function, we can generate elements one by one." ] }, { @@ -1564,7 +1564,7 @@ "source": [ "Now, the first of the two alternative solutions may be more appealing to many readers. In general, many practitioners seem to dislike `lambda` expressions.\n", "\n", - "In the first solution, we *unpack* the elements produced by `(1 + (x / y) for x in first for y in second)` into the `product()` function from the \"*Function Definitions & Calls*\" sub-section in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/03_content.ipynb#Function-Definitions-&-Calls). However, inside `product()`, the elements are *packed* into `args`, a *materialized* `tuple` object! So, all the memory efficiency gained by using a generator expression is lost! [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*%281%20%2B%20%28x%20/%20y%29%20for%20x%20in%20first%20for%20y%20in%20second%29%29&cumulative=false&curstr=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." + "In the first solution, we *unpack* the elements produced by `(1 + (x / y) for x in first for y in second)` into the `product()` function from the \"*Function Definitions & Calls*\" sub-section in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/03_content.ipynb#Function-Definitions-&-Calls). However, inside `product()`, the elements are *packed* into `args`, a *materialized* `tuple` object! So, all the memory efficiency gained by using a generator expression is lost! [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*%281%20%2B%20%28x%20/%20y%29%20for%20x%20in%20first%20for%20y%20in%20second%29%29&cumulative=false&curstr=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." ] }, { @@ -1656,13 +1656,13 @@ } }, "source": [ - "With the new concepts in this chapter, let's rewrite the book's introductory \"*Averaging all even Numbers in a List*\" example from [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List) such that it efficiently handles a large sequence of numbers. We continue from its latest implementation, the `average_evens()` function in the \"*Keyword-only Arguments*\" section in [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb#Keyword-only-Arguments).\n", + "With the new concepts in this chapter, let's rewrite the book's introductory \"*Averaging all even Numbers in a List*\" example from [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List) such that it efficiently handles a large sequence of numbers. We continue from its latest implementation, the `average_evens()` function in the \"*Keyword-only Arguments*\" section in [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/00_content.ipynb#Keyword-only-Arguments).\n", "\n", "We assume that `average_evens()` is called with a *finite* and *iterable* object that generates a **stream** of numeric objects that can be cast as `int` objects. After all, the idea of even and odd numbers makes sense only in the context of whole numbers.\n", "\n", "The `generator` expression `(round(n) for n in numbers)` implements the type casting, and, when it is evaluated during a function call, *nothing* happens except that a `generator` object is assigned to `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 with the `generator` object to which the inner `generator` expression `((n, 1) for n in integers if n % 2 == 0)` evaluates to. That `generator` object takes the `integers` generator as its source and produces `tuple` objects consisting of the next *even* number in line and `1`. Two such `tuple` objects are then iteratively passed to the `function` object to which the `lambda` expression evaluates to. `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 calling [reduce() ](https://docs.python.org/3/library/functools.html#functools.reduce) is again a `tuple` object, namely the final `total` and `count`. Lastly, we calculate the simple average and scale it.\n", "\n", - "In summary, this implementation of `average_evens()` does *not* keep materialized `list` objects internally like its predecessors in [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb) but processes the elements of the `numbers` argument on a one-by-one basis." + "In summary, this implementation of `average_evens()` does *not* keep materialized `list` objects internally like its predecessors in [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/00_content.ipynb) but processes the elements of the `numbers` argument on a one-by-one basis." ] }, { @@ -2040,7 +2040,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 explained in [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb#Short-Circuiting), and we mimic that by testing for the *opposite* condition in the `if` statement and stopping 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` argument, 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 explained in [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/00_content.ipynb#Short-Circuiting), and we mimic that by testing for the *opposite* condition in the `if` statement and stopping 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` argument, 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." ] }, { diff --git a/08_mfr/02_exercises_outliers.ipynb b/08_mfr/02_exercises_outliers.ipynb index f6d81f8..1ad6876 100644 --- a/08_mfr/02_exercises_outliers.ipynb +++ b/08_mfr/02_exercises_outliers.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/02_exercises.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/08_mfr/02_exercises.ipynb)." ] }, { @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The exercises below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb) and [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/01_content.ipynb) part of Chapter 8.\n", + "The exercises below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb) and [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/01_content.ipynb) part of Chapter 8.\n", "\n", "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." ] @@ -335,7 +335,7 @@ "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/develop/08_mfr/stream.py) file in the repository)." + "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/main/08_mfr/stream.py) file in the repository)." ] }, { @@ -574,7 +574,7 @@ "source": [ "**Q10.2**: Just as in `standardized()`, write a `generator` expression that produces z-scores one by one! However, instead of just generating a z-score, the resulting `generator` object should produce `tuple` objects consisting of a \"raw\" number from `data` and its z-score.\n", "\n", - "Hint: Look at the revisited \"*Averaging Even Numbers*\" example in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/01_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List-%28revisited%29) for some inspiration, which also contains a `generator` expression producing `tuple` objects." + "Hint: Look at the revisited \"*Averaging Even Numbers*\" example in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/01_content.ipynb#Example:-Averaging-all-even-Numbers-in-a-List-%28revisited%29) for some inspiration, which also contains a `generator` expression producing `tuple` objects." ] }, { diff --git a/08_mfr/03_exercises_un-packing.ipynb b/08_mfr/03_exercises_un-packing.ipynb index d4b0518..5723ec6 100644 --- a/08_mfr/03_exercises_un-packing.ipynb +++ b/08_mfr/03_exercises_un-packing.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/03_exercises.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/08_mfr/03_exercises.ipynb)." ] }, { @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The exercises below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb) and [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/01_content.ipynb) part of Chapter 8.\n", + "The exercises below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb) and [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/01_content.ipynb) part of Chapter 8.\n", "\n", "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." ] @@ -34,7 +34,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Q1**: Copy your solution to **Q10** from the \"*Packing & Unpacking with Functions*\" exercise in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/04_exercises.ipynb) into the code cell below!" + "**Q1**: Copy your solution to **Q10** from the \"*Packing & Unpacking with Functions*\" exercise in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/04_exercises.ipynb) into the code cell below!" ] }, { @@ -152,7 +152,7 @@ "\n", "**Q4**: Click through the following code cells and observe what they do!\n", "\n", - "The [*stream.py* ](https://github.com/webartifex/intro-to-python/blob/develop/08_mfr/stream.py) module in the book's repository provides a `make_finite_stream()` function. It is a *factory* function creating objects of type `generator` that we use to model *streaming* data." + "The [*stream.py* ](https://github.com/webartifex/intro-to-python/blob/main/08_mfr/stream.py) module in the book's repository provides a `make_finite_stream()` function. It is a *factory* function creating objects of type `generator` that we use to model *streaming* data." ] }, { diff --git a/08_mfr/04_content.ipynb b/08_mfr/04_content.ipynb index 8a3fd51..a04f016 100644 --- a/08_mfr/04_content.ipynb +++ b/08_mfr/04_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/04_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/08_mfr/04_content.ipynb)." ] }, { @@ -30,7 +30,7 @@ } }, "source": [ - "Similarly to how we classify different *concrete* data types like `list` or `str` by how they behave *abstractly* in a given context in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb#Collections-vs.-Sequences), we also do so for the data types we have introduced in this chapter." + "Similarly to how we classify different *concrete* data types like `list` or `str` by how they behave *abstractly* in a given context in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/00_content.ipynb#Collections-vs.-Sequences), we also do so for the data types we have introduced in this chapter." ] }, { @@ -174,7 +174,7 @@ } }, "source": [ - "Furthermore, we sharpen our definition of an *iterable* from [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb#Collections-vs.-Sequences): Just as we define an *iterator* to be any object that supports the [next() ](https://docs.python.org/3/library/functions.html#next) function, we define an *iterable* to be any object that supports the built-in [iter() ](https://docs.python.org/3/library/functions.html#iter) function.\n", + "Furthermore, we sharpen our definition of an *iterable* from [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/00_content.ipynb#Collections-vs.-Sequences): Just as we define an *iterator* to be any object that supports the [next() ](https://docs.python.org/3/library/functions.html#next) function, we define an *iterable* to be any 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", @@ -780,7 +780,7 @@ } }, "source": [ - "In [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb#The-for-Statement), we argue that the `for` statement is syntactic sugar, replacing the `while` statement in many common 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/main/04_iteration/02_content.ipynb#The-for-Statement), we argue that the `for` statement is syntactic sugar, replacing the `while` statement in many common 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* being looped over." ] @@ -924,7 +924,7 @@ } }, "source": [ - "Now that we know the concept of an *iterator*, let's compare some of the built-ins introduced in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb) in detail and make sure we understand what is going on in memory. This section also serves as a summary of all the concepts in this chapter.\n", + "Now that we know the concept of an *iterator*, let's compare some of the built-ins introduced in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/00_content.ipynb) in detail and make sure we understand what is going on in memory. This section also serves as a summary of all the concepts in this chapter.\n", "\n", "We use two simple examples, `numbers` and `memoryless`: `numbers` creates *thirteen* objects in memory and `memoryless` only *one* (cf., [PythonTutor ](http://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&curstr=2&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false))." ] diff --git a/08_mfr/06_review.ipynb b/08_mfr/06_review.ipynb index faf8ab8..0ebf0b5 100644 --- a/08_mfr/06_review.ipynb +++ b/08_mfr/06_review.ipynb @@ -11,7 +11,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb), [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/01_content.ipynb), and [third ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/04_content.ipynb) part in Chapter 8.\n", + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb), [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/01_content.ipynb), and [third ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/04_content.ipynb) part in Chapter 8.\n", "\n", "Be concise in your answers! Most questions can be answered in *one* sentence." ] diff --git a/09_mappings/00_content.ipynb b/09_mappings/00_content.ipynb index 4e97131..9926f40 100644 --- a/09_mappings/00_content.ipynb +++ b/09_mappings/00_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/00_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/00_content.ipynb)." ] }, { @@ -30,7 +30,7 @@ } }, "source": [ - "While [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb) focuses on one special kind of *collection* types, namely *sequences*, this chapter introduces two more kinds: **Mappings** and **sets**. Both are presented in this chapter as they share the *same* underlying implementation.\n", + "While [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/00_content.ipynb) focuses on one special kind of *collection* types, namely *sequences*, this chapter introduces two more kinds: **Mappings** and **sets**. Both are presented in this chapter as they share the *same* underlying implementation.\n", "\n", "The `dict` type (cf, [documentation ](https://docs.python.org/3/library/stdtypes.html#dict)) introduced in the next section is an essential part in a data scientist's toolbox for two reasons: First, Python employs `dict` objects basically everywhere internally. Second, after the many concepts involving *sequential* data, *mappings* provide a different perspective on data and enhance our general problem solving skills." ] @@ -56,7 +56,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 *value* every object has: In the \"bag\" analogy from [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Value-/-%28Semantic%29-\"Meaning\"), we describe an object's value to be the semantic meaning of the $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 *objects* on their own with distinct *values*.\n", + "In the context of mappings, the term *value* has a meaning different from the *value* every object has: In the \"bag\" analogy from [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb#Value-/-%28Semantic%29-\"Meaning\"), we describe an object's value to be the semantic meaning of the $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 *objects* on their own with distinct *values*.\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. Also, the commas `,` after the respective *last* items, `2: \"two\"` and `\"two\": 2`, are *not* a mistake although they *may* be left out. Besides easier reading, such a style has technical advantages that we do not go into detail about here (cf., [source ](https://www.python.org/dev/peps/pep-0008/#when-to-use-trailing-commas))." ] @@ -550,7 +550,7 @@ } }, "source": [ - "In [Chapter 0 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/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 build some intuition, we describe the underlying implementation details on a conceptual level.\n", + "In [Chapter 0 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/00_intro/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 build some intuition, we describe the underlying implementation details on a conceptual level.\n", "\n", "The first unintuitive behavior is that we may *not* use a *mutable* object as a key. That results in a `TypeError`." ] @@ -1176,7 +1176,7 @@ } }, "source": [ - "In [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb#Collections-vs.-Sequences), we see how a *sequence* is a special kind of a *collection*, and that collections can be described as\n", + "In [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/00_content.ipynb#Collections-vs.-Sequences), we see how a *sequence* is a special kind of a *collection*, and that collections can be described as\n", "- *iterable*\n", "- *containers*\n", "- with a *finite* number of elements.\n", @@ -1245,7 +1245,7 @@ "Also, `dict` objects may be looped over, for example, with the `for` statement. So, in the terminology of the [collections.abc ](https://docs.python.org/3/library/collections.abc.html) module, they are `Iterable` objects.\n", "\n", "Regarding the *iteration order* things are not that easy, and programmers seem to often be confused about this (e.g., this [discussion](https://stackoverflow.com/questions/58413076/why-are-python-dictionaries-not-reversible-for-python3-7)). The confusion usually comes from one of two reasons:\n", - "1. The internal implementation of the `dict` type has been changed over the last couple of minor release versions, and the communication thereof in the official release notes was done only in a later version. In a nutshell, before Python 3.6, the core developers did not care about the iteration order at all as the goal was to optimize `dict` objects for computational speed, primarily regarding key look-up (cf., the \"Indexing -> Key Look-up\" section below). That meant that looping over the *same* `dict` object several times during its lifetime could have resulted in *different* iteration orders. In Python 3.6, it was discovered that it is possible to make `dict` objects remember the order in that items have been inserted *without* giving up any computational speed or memory (cf., [Raymond Hettinger](https://github.com/rhettinger)'s talk in the [Further Resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/08_resources.ipynb#History-of-the-dict-Type) section at the end of the chapter. However, that change was kept an *implementation detail* and *not* made official in the release notes. That was then done in Python 3.7's release notes (cf., [Python 3.7 release notes ](https://www.python.org/downloads/release/python-370/)).\n", + "1. The internal implementation of the `dict` type has been changed over the last couple of minor release versions, and the communication thereof in the official release notes was done only in a later version. In a nutshell, before Python 3.6, the core developers did not care about the iteration order at all as the goal was to optimize `dict` objects for computational speed, primarily regarding key look-up (cf., the \"Indexing -> Key Look-up\" section below). That meant that looping over the *same* `dict` object several times during its lifetime could have resulted in *different* iteration orders. In Python 3.6, it was discovered that it is possible to make `dict` objects remember the order in that items have been inserted *without* giving up any computational speed or memory (cf., [Raymond Hettinger](https://github.com/rhettinger)'s talk in the [Further Resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/08_resources.ipynb#History-of-the-dict-Type) section at the end of the chapter. However, that change was kept an *implementation detail* and *not* made official in the release notes. That was then done in Python 3.7's release notes (cf., [Python 3.7 release notes ](https://www.python.org/downloads/release/python-370/)).\n", "2. To make order an official part of a data type, it must adhere to the `Reversible` ABC in the [collections.abc ](https://docs.python.org/3/library/collections.abc.html) module and support the [reversed() ](https://docs.python.org/3/library/functions.html#reversed) built-in. Even though the items' order inside a `dict` is remembered for Python 3.6 and 3.7, `dict` objects are *not* `Reversible`. That was then changed in Python 3.8, but again *not* officially communicated (cf., [Python 3.8 release notes](https://www.python.org/downloads/release/python-380/)).\n", "\n", "In summary, we can say that depending on the exact Python version a `dict` object *may* remember the **insertion order** of its items.\n", @@ -3541,7 +3541,7 @@ } }, "source": [ - "Analogous to `list` comprehensions in [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/01_content.ipynb#list-Comprehensions), `dict` comprehensions are a concise literal notation to derive new `dict` objects out of existing ones.\n", + "Analogous to `list` comprehensions in [Chapter 8 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/01_content.ipynb#list-Comprehensions), `dict` comprehensions are a concise literal notation to derive new `dict` objects out of existing ones.\n", "\n", "For example, let's derive `from_words` out of `to_words` below by swapping the keys and values." ] diff --git a/09_mappings/01_exercises_nested-data.ipynb b/09_mappings/01_exercises_nested-data.ipynb index 82eae0d..fe7ae76 100644 --- a/09_mappings/01_exercises_nested-data.ipynb +++ b/09_mappings/01_exercises_nested-data.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/01_exercises.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/01_exercises.ipynb)." ] }, { @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb) of Chapter 9.\n", + "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/00_content.ipynb) of Chapter 9.\n", "\n", "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." ] diff --git a/09_mappings/02_content.ipynb b/09_mappings/02_content.ipynb index e0b9083..b2fe6a0 100644 --- a/09_mappings/02_content.ipynb +++ b/09_mappings/02_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/02_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/02_content.ipynb)." ] }, { @@ -30,7 +30,7 @@ } }, "source": [ - "After introducing the `dict` type in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb) of this chapter, we first look at an extension of the packing and unpacking syntax that involves `dict` objects. Then, we see how mappings can help us write computationally more efficient implementations to recursive solutions of problems as introduced in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb#Recursion). In a way, this second part of the chapter \"finishes\" Chapter 4." + "After introducing the `dict` type in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/00_content.ipynb) of this chapter, we first look at an extension of the packing and unpacking syntax that involves `dict` objects. Then, we see how mappings can help us write computationally more efficient implementations to recursive solutions of problems as introduced in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/00_content.ipynb#Recursion). In a way, this second part of the chapter \"finishes\" Chapter 4." ] }, { @@ -52,7 +52,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/develop/07_sequences/03_content.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/main/07_sequences/03_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." ] @@ -608,7 +608,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/develop/04_iteration/00_content.ipynb#\"Easy-at-first-Glance\"-Example:-Fibonacci-Numbers) takes long to compute for large Fibonacci numbers. For easier comparison, we show the old `fibonacci()` version here again." + "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/main/04_iteration/00_content.ipynb#\"Easy-at-first-Glance\"-Example:-Fibonacci-Numbers) takes long to compute for large Fibonacci numbers. For easier comparison, we show the old `fibonacci()` version here again." ] }, { @@ -831,7 +831,7 @@ } }, "source": [ - "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)`, the left-most leaf node $F(1)$, 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. Effectively, this mirrors the *iterative* implementation in that the order of all computational steps are *identical* (cf., the \"*Hard at first Glance*\" example in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb#\"Hard-at-first-Glance\"-Example:-Fibonacci-Numbers--(revisited))).\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)`, the left-most leaf node $F(1)$, 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. Effectively, this mirrors the *iterative* implementation in that the order of all computational steps are *identical* (cf., the \"*Hard at first Glance*\" example in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/02_content.ipynb#\"Hard-at-first-Glance\"-Example:-Fibonacci-Numbers--(revisited))).\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`." ] diff --git a/09_mappings/03_exercises_fibonacci.ipynb b/09_mappings/03_exercises_fibonacci.ipynb index 1ef6bf8..c9738fe 100644 --- a/09_mappings/03_exercises_fibonacci.ipynb +++ b/09_mappings/03_exercises_fibonacci.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/03_exercises.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/03_exercises.ipynb)." ] }, { @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The exercises below assume that you have read the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/02_content.ipynb) of Chapter 9.\n", + "The exercises below assume that you have read the [second part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/02_content.ipynb) of Chapter 9.\n", "\n", "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." ] @@ -38,7 +38,7 @@ } }, "source": [ - "It is considered *bad practice* to make a function and thereby its correctness dependent on a program's *global state*: For example, in the \"*Easy at second Glance: Fibonacci Numbers*\" section in [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/02_content.ipynb#\"Easy-at-second-Glance\"-Example:-Fibonacci-Numbers--%28revisited%29), we use a global `memo` to store the Fibonacci numbers that have already been calculated.\n", + "It is considered *bad practice* to make a function and thereby its correctness dependent on a program's *global state*: For example, in the \"*Easy at second Glance: Fibonacci Numbers*\" section in [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/02_content.ipynb#\"Easy-at-second-Glance\"-Example:-Fibonacci-Numbers--%28revisited%29), we use a global `memo` to store the Fibonacci numbers that have already been calculated.\n", "\n", "That `memo` dictionary could be \"manipulated.\" More often than not, such things happen by accident: Imagine we wrote two independent recursive functions that both rely on memoization to solve different problems, and, unintentionally, we made both work with the *same* global `memo`. As a result, we would observe \"random\" bugs depending on the order in which we executed these functions. Such bugs are hard to track down in practice.\n", "\n", @@ -185,7 +185,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The runtime of `fibonacci()` is now stable: There is no message that \"an intermediate result is being cached\" as in [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/02_content.ipynb#\"Easy-at-second-Glance\"-Example:-Fibonacci-Numbers--%28revisited%29).\n", + "The runtime of `fibonacci()` is now stable: There is no message that \"an intermediate result is being cached\" as in [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/02_content.ipynb#\"Easy-at-second-Glance\"-Example:-Fibonacci-Numbers--%28revisited%29).\n", "\n", "**Q7**: Execute the following code cells a couple of times to observe that!" ] diff --git a/09_mappings/04_content.ipynb b/09_mappings/04_content.ipynb index 6b97bf7..3f1f1d5 100644 --- a/09_mappings/04_content.ipynb +++ b/09_mappings/04_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/04_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/04_content.ipynb)." ] }, { @@ -1407,7 +1407,7 @@ } }, "source": [ - "Python provides a literal notation for `set` comprehensions that works exactly like the one for `dict` comprehensions described in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb#dict-Comprehensions) of this chapter except that they use a single loop variable instead of a key-value pair.\n", + "Python provides a literal notation for `set` comprehensions that works exactly like the one for `dict` comprehensions described in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/00_content.ipynb#dict-Comprehensions) of this chapter 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`." ] diff --git a/09_mappings/05_appendix.ipynb b/09_mappings/05_appendix.ipynb index 95050b6..0b774af 100644 --- a/09_mappings/05_appendix.ipynb +++ b/09_mappings/05_appendix.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/05_appendix.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/05_appendix.ipynb)." ] }, { diff --git a/09_mappings/07_review.ipynb b/09_mappings/07_review.ipynb index a484886..7aed88f 100644 --- a/09_mappings/07_review.ipynb +++ b/09_mappings/07_review.ipynb @@ -11,7 +11,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb), [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/02_content.ipynb), and [third ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/04_content.ipynb) part of Chapter 9.\n", + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/00_content.ipynb), [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/02_content.ipynb), and [third ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/04_content.ipynb) part of Chapter 9.\n", "\n", "Be concise in your answers! Most questions can be answered in *one* sentence." ] @@ -99,7 +99,7 @@ "source": [ "**Q6**: **Memoization** is an essential concept to know to solve problems in the real world. Together with the idea of **recursion**, it enables us to solve problems in a \"backwards\" fashion *effectively*.\n", "\n", - "Compare the **recursive** formulation of `fibonacci()` in [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/02_content.ipynb#\"Easy-at-second-Glance\"-Example:-Fibonacci-Numbers--(revisited)), the \"*Easy at second Glance*\" example, with the **iterative** version in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb#\"Hard-at-first-Glance\"-Example:-Fibonacci-Numbers-%28revisited%29), the \"*Hard at first Glance*\" example!\n", + "Compare the **recursive** formulation of `fibonacci()` in [Chapter 9 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/02_content.ipynb#\"Easy-at-second-Glance\"-Example:-Fibonacci-Numbers--(revisited)), the \"*Easy at second Glance*\" example, with the **iterative** version in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/02_content.ipynb#\"Hard-at-first-Glance\"-Example:-Fibonacci-Numbers-%28revisited%29), the \"*Hard at first Glance*\" example!\n", "\n", "How are they similar and how do they differ? Also consider how the flow of execution behaves when the functions are being executed." ] diff --git a/09_mappings/08_resources.ipynb b/09_mappings/08_resources.ipynb index c68eef1..b9dd3da 100644 --- a/09_mappings/08_resources.ipynb +++ b/09_mappings/08_resources.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/08_resources.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/08_resources.ipynb)." ] }, { diff --git a/11_classes/00_content.ipynb b/11_classes/00_content.ipynb index b910095..696eed2 100644 --- a/11_classes/00_content.ipynb +++ b/11_classes/00_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/00_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/11_classes/00_content.ipynb)." ] }, { @@ -34,7 +34,7 @@ "\n", "Classes and instances follow the **[object-oriented programming ](https://en.wikipedia.org/wiki/Object-oriented_programming)** (OOP) paradigm where a *large program* is broken down into many *small components* (i.e., the objects) that *reuse* code. This way, a program that is too big for a programmer to fully comprehend as a whole becomes maintainable via its easier to understand individual pieces.\n", "\n", - "Often, we see the terminology \"classes & objects\" used instead of \"classes & instances\" in Python related texts. In this book, we are more precise as *both* classes and instances are objects as specified already in the \"*Objects vs. Types vs. Values*\" section in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Objects-vs.-Types-vs.-Values)." + "Often, we see the terminology \"classes & objects\" used instead of \"classes & instances\" in Python related texts. In this book, we are more precise as *both* classes and instances are objects as specified already in the \"*Objects vs. Types vs. Values*\" section in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb#Objects-vs.-Types-vs.-Values)." ] }, { @@ -56,7 +56,7 @@ } }, "source": [ - "Neither core Python nor the standard library offer an implementation of common [linear algebra ](https://en.wikipedia.org/wiki/Linear_algebra) functionalities. While we introduce the popular third-party library [numpy](http://www.numpy.org/) in [Chapter 10 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/10_arrarys/00_content.ipynb) as the de-facto standard for that and recommend to use it in real-life projects, we show how one could use Python's object-oriented language features to implement common matrix and vector operations throughout this chapter. Once we have achieved that, we compare our own library with [numpy](http://www.numpy.org/).\n", + "Neither core Python nor the standard library offer an implementation of common [linear algebra ](https://en.wikipedia.org/wiki/Linear_algebra) functionalities. While we introduce the popular third-party library [numpy](http://www.numpy.org/) in [Chapter 10 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/10_arrarys/00_content.ipynb) as the de-facto standard for that and recommend to use it in real-life projects, we show how one could use Python's object-oriented language features to implement common matrix and vector operations throughout this chapter. Once we have achieved that, we compare our own library with [numpy](http://www.numpy.org/).\n", "\n", "Without classes, we could model a vector, for example, with a `tuple` or a `list` object, depending on if we want it to be mutable or not.\n", "\n", @@ -326,7 +326,7 @@ "source": [ "Its type is `type` indicating that it represents a user-defined data type and it evaluates to its fully qualified name (i.e., `__main__` as it is defined in this Jupyter notebook).\n", "\n", - "We have seen the type `type` before in the \"*Constructors*\" section in [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb#Constructors) and also in the \"*The `namedtuple` Type*\" section in [Chapter 7's Appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/05_appendix.ipynb#The-namedtuple-Type). In the latter case, we could also use a `Point` class but the [namedtuple() ](https://docs.python.org/3/library/collections.html#collections.namedtuple) function from the [collections ](https://docs.python.org/3/library/collections.html) module in the [standard library ](https://docs.python.org/3/library/index.html) is a convenient shortcut to create custom data types that can be derived out of a plain `tuple`.\n", + "We have seen the type `type` before in the \"*Constructors*\" section in [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/00_content.ipynb#Constructors) and also in the \"*The `namedtuple` Type*\" section in [Chapter 7's Appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/05_appendix.ipynb#The-namedtuple-Type). In the latter case, we could also use a `Point` class but the [namedtuple() ](https://docs.python.org/3/library/collections.html#collections.namedtuple) function from the [collections ](https://docs.python.org/3/library/collections.html) module in the [standard library ](https://docs.python.org/3/library/index.html) is a convenient shortcut to create custom data types that can be derived out of a plain `tuple`.\n", "\n", "In all examples, if an object's type is `type`, we can simply view it as a blueprint for a \"family\" of objects." ] @@ -1294,7 +1294,7 @@ } }, "source": [ - "From a theoretical point of view, the text representation provided by `.__repr__()` contains all the information (i.e., the $0$s and $1$s in memory) that is needed to model something in a computer. In a way, it is a natural extension from the binary (cf., [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb#Binary-Representations)), hexadecimal (cf., [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb#Hexadecimal-Representations)), and `bytes` (cf., [Chapter 6 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/02_content.ipynb#The-bytes-Type)) representations of information. After all, just like Unicode characters are encoded in `bytes`, the more \"complex\" objects in this chapter are encoded in Unicode characters via their text representations." + "From a theoretical point of view, the text representation provided by `.__repr__()` contains all the information (i.e., the $0$s and $1$s in memory) that is needed to model something in a computer. In a way, it is a natural extension from the binary (cf., [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/00_content.ipynb#Binary-Representations)), hexadecimal (cf., [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/00_content.ipynb#Hexadecimal-Representations)), and `bytes` (cf., [Chapter 6 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/02_content.ipynb#The-bytes-Type)) representations of information. After all, just like Unicode characters are encoded in `bytes`, the more \"complex\" objects in this chapter are encoded in Unicode characters via their text representations." ] }, { @@ -1649,7 +1649,7 @@ "\n", "An example of an instance method from linear algebra is the `.transpose()` method below that switches the rows and columns of an *existing* `Matrix` instance and returns a *new* `Matrix` instance based off that. It is implemented by passing the *iterator* created with the [zip() ](https://docs.python.org/3/library/functions.html#zip) built-in as the `data` argument to the `Matrix` constructor: The expression `zip(*self._entries)` may be a bit hard to understand because of the involved unpacking but simply flips a `Matrix`'s rows and columns. The built-in [list() ](https://docs.python.org/3/library/functions.html#func-list) constructor within the `.__init__()` method then materializes the iterator into the `._entries` attribute. Without a concrete `Matrix`'s rows and columns, `.transpose()` does not make sense, conceptually speaking.\n", "\n", - "Also, we see that it is ok to reference a class from within one of its methods. While this seems trivial to some readers, others may find this confusing. The final versions of the `Vector` and `Matrix` classes in the [fourth part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/04_content.ipynb#The-final-Vector-and-Matrix-Classes) of this chapter show how this \"hard coded\" redundancy can be avoided." + "Also, we see that it is ok to reference a class from within one of its methods. While this seems trivial to some readers, others may find this confusing. The final versions of the `Vector` and `Matrix` classes in the [fourth part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/04_content.ipynb#The-final-Vector-and-Matrix-Classes) of this chapter show how this \"hard coded\" redundancy can be avoided." ] }, { diff --git a/11_classes/01_exercises_tsp.ipynb b/11_classes/01_exercises_tsp.ipynb index cdc755c..fbdbc1f 100644 --- a/11_classes/01_exercises_tsp.ipynb +++ b/11_classes/01_exercises_tsp.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/01_exercises.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/11_classes/01_exercises.ipynb)." ] }, { @@ -18,7 +18,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb) of Chapter 11.\n", + "The exercises below assume that you have read the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb) of Chapter 11.\n", "\n", "The `...`'s in the code cells indicate where you need to fill in code snippets. The number of `...`'s within a code cell give you a rough idea of how many lines of code are needed to solve the task. You should not need to create any additional code cells for your final solution. However, you may want to use temporary code cells to try out some ideas." ] diff --git a/11_classes/02_content.ipynb b/11_classes/02_content.ipynb index fb8a6bd..5ab6d81 100644 --- a/11_classes/02_content.ipynb +++ b/11_classes/02_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/02_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/11_classes/02_content.ipynb)." ] }, { @@ -52,7 +52,7 @@ } }, "source": [ - "As discussed in detail in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb#Collections-vs.-Sequences), a sequence is any finite and iterable container type with a *predictable* order of its elements such that we can label each element with an index in the range `0 <= index < len(sequence)`.\n", + "As discussed in detail in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/00_content.ipynb#Collections-vs.-Sequences), a sequence is any finite and iterable container type with a *predictable* order of its elements such that we can label each element with an index in the range `0 <= index < len(sequence)`.\n", "\n", "To make `Vector` and `Matrix` instances emulate sequences, we implement the `.__len__()` (cf., [reference ](https://docs.python.org/3/reference/datamodel.html#object.__len__)) and `.__getitem__()` (cf., [reference ](https://docs.python.org/3/reference/datamodel.html#object.__getitem__)) methods. While the former returns the total number of elements in a container and is automatically invoked on any object passed to the built-in [len() ](https://docs.python.org/3/library/functions.html#len) function, the latter is invoked by the interpreter behind the scenes when we use the indexing operator `[]`.\n", "\n", @@ -714,7 +714,7 @@ "\n", "The collection of all such behaviors a programming language offers is commonly referred to as its **object model**. In Python, the term **data model** is used instead and all possible behaviors are documented in the [language reference ](https://docs.python.org/3/reference/datamodel.html), in particular, in the section on special methods. We can think of the data model as the collection of all the behaviors we can make our user-defined data types follow. Pythonistas also use the term **protocol** instead of behavior, for example, we may say that \"the `Vector` and `Matrix` classes follow the sequence protocol.\"\n", "\n", - "So, merely defining the *two* `.__len__()` and `.__getitem__()` methods is enough to make instances of any user-defined type behave like the built-in sequences in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb). Yet, there we defined sequences as all objects having the *four* properties of being finite, iterable, and ordered container types. And, these properties correspond to special methods by the names of `.__len__()`, `.__iter__()`, `.__reversed__()`, and `.__contains__()` as we see in the next section. Thus, Python \"magically\" knows how to derive the logic for the `.__iter__()`, `.__reversed__()`, and `.__contains__()` methods from the combination of the `.__len__()` and `.__getitem__()` methods. In general, while some special methods are related, others are not. Understanding these relationships means understanding the Python data model and vice versa. That is what every aspiring data scientist should aim for.\n", + "So, merely defining the *two* `.__len__()` and `.__getitem__()` methods is enough to make instances of any user-defined type behave like the built-in sequences in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/00_content.ipynb). Yet, there we defined sequences as all objects having the *four* properties of being finite, iterable, and ordered container types. And, these properties correspond to special methods by the names of `.__len__()`, `.__iter__()`, `.__reversed__()`, and `.__contains__()` as we see in the next section. Thus, Python \"magically\" knows how to derive the logic for the `.__iter__()`, `.__reversed__()`, and `.__contains__()` methods from the combination of the `.__len__()` and `.__getitem__()` methods. In general, while some special methods are related, others are not. Understanding these relationships means understanding the Python data model and vice versa. That is what every aspiring data scientist should aim for.\n", "\n", "On the contrary, we could also look at special methods individually. Whereas `.__len__()` is invoked on the object passed to [len() ](https://docs.python.org/3/library/functions.html#len), Python \"translates\" the indexing operator applied on any name like `a[i]`, for example, into the method invocation `a.__getitem__(i)`. So, in both cases, the special methods are executed according to a deterministic rule of the language. In that sense, they act as some sort of syntactic sugar. Thus, they even work if only one of them is defined. For example, without `.__len__()`, iteration with a `for`-loop still works but only in forward direction." ] @@ -1047,7 +1047,7 @@ "source": [ "In the above implementations, the instance attribute `._entries` on a `Vector` or `Matrix` instance references either a `list` or a `list` of row `list`s , which is by the convention of the leading underscore `_` an implementation detail. If users of our classes adhere to this convention, `Vector` and `Matrix` instances can be regarded as *immutable*.\n", "\n", - "In line with the implied immutability, we implemented the `.transpose()` method such that it returns a *new* `Matrix` instance. Instead, we could make the method change the internal `self._entries` attribute *in place* as we do in the next example. To indicate this mutation to the user of the `Matrix` class clearly, the revised `.transpose()` method returns `None`. That mirrors, for example, how the mutating methods of the built-in `list` type behave (cf., [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/01_content.ipynb#List-Methods)).\n", + "In line with the implied immutability, we implemented the `.transpose()` method such that it returns a *new* `Matrix` instance. Instead, we could make the method change the internal `self._entries` attribute *in place* as we do in the next example. To indicate this mutation to the user of the `Matrix` class clearly, the revised `.transpose()` method returns `None`. That mirrors, for example, how the mutating methods of the built-in `list` type behave (cf., [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/01_content.ipynb#List-Methods)).\n", "\n", "Such decisions are better made consciously when designing a custom data type. The main trade-off is that immutable data types are typically easier to reason about when reading code whereas mutable data types tend to be more memory efficient and make programs faster as less copying operations take place in memory. However, this trade-off only becomes critical when we deal with big amounts of data." ] @@ -1577,7 +1577,7 @@ } }, "source": [ - "After this discussion of mutable `Vector` and `Matrix` classes, we continue with immutable implementations in the rest of this chapter. To lower the chance that we accidently design parts of our classes to be mutable, we replace the built-in [list() ](https://docs.python.org/3/library/functions.html#func-list) constructor with [tuple() ](https://docs.python.org/3/library/functions.html#func-tuple) in the `.__init__()` methods. As we learn in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/03_content.ipynb#Tuples-are-like-%22Immutable-Lists%22), `tuple`s are like immutable `list`s." + "After this discussion of mutable `Vector` and `Matrix` classes, we continue with immutable implementations in the rest of this chapter. To lower the chance that we accidently design parts of our classes to be mutable, we replace the built-in [list() ](https://docs.python.org/3/library/functions.html#func-list) constructor with [tuple() ](https://docs.python.org/3/library/functions.html#func-tuple) in the `.__init__()` methods. As we learn in [Chapter 7 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/03_content.ipynb#Tuples-are-like-%22Immutable-Lists%22), `tuple`s are like immutable `list`s." ] }, { @@ -1599,7 +1599,7 @@ } }, "source": [ - "A function is considered **polymorphic** if it can work with *different* data types. The main advantage is reuse of the function's code. Polymorphism goes hand in hand with the concept of [duck typing ](https://en.wikipedia.org/wiki/Duck_typing), first mentioned in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb#Duck-Typing) in the context of input validation.\n", + "A function is considered **polymorphic** if it can work with *different* data types. The main advantage is reuse of the function's code. Polymorphism goes hand in hand with the concept of [duck typing ](https://en.wikipedia.org/wiki/Duck_typing), first mentioned in [Chapter 4 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/00_content.ipynb#Duck-Typing) in the context of input validation.\n", "\n", "We know polymorphic functions already: The built-in [sum() ](https://docs.python.org/3/library/functions.html#sum) function is a trivial example that works with all kinds of `iterable` arguments." ] diff --git a/11_classes/03_content.ipynb b/11_classes/03_content.ipynb index 3d40e39..8689e63 100644 --- a/11_classes/03_content.ipynb +++ b/11_classes/03_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/03_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/11_classes/03_content.ipynb)." ] }, { @@ -446,7 +446,7 @@ "source": [ "By implementing special methods such as `.__add__()`, `.__sub__()`, `.__mul__()`, and some others, we can make user-defined data types emulate how numeric types operate with each other (cf., [reference ](https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types)): Then, `Vector` and `Matrix` instances can be added together, subtracted from one another, or be multiplied together. We use them to implement the arithmetic rules from linear algebra.\n", "\n", - "The OOP concept behind this is **[operator overloading ](https://en.wikipedia.org/wiki/Operator_overloading)** as first mentioned in the context of `str` concatenation in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb#Operator-Overloading)." + "The OOP concept behind this is **[operator overloading ](https://en.wikipedia.org/wiki/Operator_overloading)** as first mentioned in the context of `str` concatenation in [Chapter 1 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb#Operator-Overloading)." ] }, { @@ -599,7 +599,7 @@ } }, "source": [ - "To check if `other` is a scalar, we need to specify what data type constitutes a scalar. We use a goose typing strategy as explained in [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/02_content.ipynb#Goose-Typing): Any object that behaves like a `numbers.Number` from the [numbers ](https://docs.python.org/3/library/numbers.html) module in the [standard library ](https://docs.python.org/3/library/index.html) is considered a scalar.\n", + "To check if `other` is a scalar, we need to specify what data type constitutes a scalar. We use a goose typing strategy as explained in [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/02_content.ipynb#Goose-Typing): Any object that behaves like a `numbers.Number` from the [numbers ](https://docs.python.org/3/library/numbers.html) module in the [standard library ](https://docs.python.org/3/library/index.html) is considered a scalar.\n", "\n", "For example, the integer `1` is an instance of the built-in `int` type. At the same time, [isinstance() ](https://docs.python.org/3/library/functions.html#isinstance) also confirms that it is a `numbers.Number` in the abstract sense." ] @@ -1715,7 +1715,7 @@ } }, "source": [ - "We implement the `.__eq__()` (cf., [reference ](https://docs.python.org/3/reference/datamodel.html#object.__eq__)) method to control how the comparison operator `==` is carried out. For brevity, we show this only for the `Vector` class. The `.__eq__()` method exits early as soon as the first pair of entries does not match. Also, for reasons discussed in [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/01_content.ipynb#Imprecision), we compare the absolute difference of two corresponding entries to a very small `zero_threshold` that is stored as a class attribute shared among all `Vector` instances. If the `Vector`s differ in their numbers of entries, we fail loudly and raise a `ValueError`." + "We implement the `.__eq__()` (cf., [reference ](https://docs.python.org/3/reference/datamodel.html#object.__eq__)) method to control how the comparison operator `==` is carried out. For brevity, we show this only for the `Vector` class. The `.__eq__()` method exits early as soon as the first pair of entries does not match. Also, for reasons discussed in [Chapter 5 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/01_content.ipynb#Imprecision), we compare the absolute difference of two corresponding entries to a very small `zero_threshold` that is stored as a class attribute shared among all `Vector` instances. If the `Vector`s differ in their numbers of entries, we fail loudly and raise a `ValueError`." ] }, { @@ -2239,7 +2239,7 @@ } }, "source": [ - "Only an all `0`s `Vector` is `False` in a boolean context. As mentioned in [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb#Truthy-vs.-Falsy), commonly we view an *empty* sequence as falsy; however, as we do not allow `Vector`s without any entries, we choose the all `0`s alternative. In that regard, the `Vector` class does not behave like the built-in sequence types." + "Only an all `0`s `Vector` is `False` in a boolean context. As mentioned in [Chapter 3 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/00_content.ipynb#Truthy-vs.-Falsy), commonly we view an *empty* sequence as falsy; however, as we do not allow `Vector`s without any entries, we choose the all `0`s alternative. In that regard, the `Vector` class does not behave like the built-in sequence types." ] }, { diff --git a/11_classes/04_content.ipynb b/11_classes/04_content.ipynb index 9f563fe..684d227 100644 --- a/11_classes/04_content.ipynb +++ b/11_classes/04_content.ipynb @@ -8,7 +8,7 @@ } }, "source": [ - "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/04_content.ipynb)." + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Clear All Outputs*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *before* reading this notebook to reset its output. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/11_classes/04_content.ipynb)." ] }, { @@ -52,13 +52,13 @@ } }, "source": [ - "In [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/02_content.ipynb#Local-Modules-and-Packages), we introduce the concept of a Python module that is imported with the `import` statement. Essentially, a **module** is a single plain text \\*.py file on disk that contains Python code (e.g., [*sample_module.py* ](https://github.com/webartifex/intro-to-python/blob/develop/02_functions/sample_module.py) in [Chapter 2's folder ](https://github.com/webartifex/intro-to-python/tree/develop/02_functions)).\n", + "In [Chapter 2 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/02_content.ipynb#Local-Modules-and-Packages), we introduce the concept of a Python module that is imported with the `import` statement. Essentially, a **module** is a single plain text \\*.py file on disk that contains Python code (e.g., [*sample_module.py* ](https://github.com/webartifex/intro-to-python/blob/main/02_functions/sample_module.py) in [Chapter 2's folder ](https://github.com/webartifex/intro-to-python/tree/main/02_functions)).\n", "\n", - "Conceptually, a **package** is a generalization of a module whose code is split across several \\*.py to achieve a better organization of the individual parts. The \\*.py files are stored within a folder (e.g., [*sample_package* ](https://github.com/webartifex/intro-to-python/tree/develop/11_classes/sample_package) in [Chapter 11's folder ](https://github.com/webartifex/intro-to-python/tree/develop/11_classes)). In addition to that, a \"*\\_\\_init\\_\\_.py*\" file that may be empty must be put inside the folder. The latter is what the Python interpreter looks for to decide if a folder is a package or not.\n", + "Conceptually, a **package** is a generalization of a module whose code is split across several \\*.py to achieve a better organization of the individual parts. The \\*.py files are stored within a folder (e.g., [*sample_package* ](https://github.com/webartifex/intro-to-python/tree/main/11_classes/sample_package) in [Chapter 11's folder ](https://github.com/webartifex/intro-to-python/tree/main/11_classes)). In addition to that, a \"*\\_\\_init\\_\\_.py*\" file that may be empty must be put inside the folder. The latter is what the Python interpreter looks for to decide if a folder is a package or not.\n", "\n", "Let's look at an example with the final version of our `Vector` and `Matrix` classes.\n", "\n", - "`!pwd` shows the location of this Jupyter notebook on the computer you are running [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) on: It is the local equivalent of [Chapter 11's folder ](https://github.com/webartifex/intro-to-python/tree/develop/11_classes) in this book's [GitHub repository ](https://github.com/webartifex/intro-to-python)." + "`!pwd` shows the location of this Jupyter notebook on the computer you are running [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) on: It is the local equivalent of [Chapter 11's folder ](https://github.com/webartifex/intro-to-python/tree/main/11_classes) in this book's [GitHub repository ](https://github.com/webartifex/intro-to-python)." ] }, { @@ -90,7 +90,7 @@ } }, "source": [ - "`!ls` lists all the files and folders in the current location: These are Chapter 11's Jupyter notebooks (i.e., the \\*.ipynb files) and the [*sample_package* ](https://github.com/webartifex/intro-to-python/tree/develop/11_classes/sample_package) folder. " + "`!ls` lists all the files and folders in the current location: These are Chapter 11's Jupyter notebooks (i.e., the \\*.ipynb files) and the [*sample_package* ](https://github.com/webartifex/intro-to-python/tree/main/11_classes/sample_package) folder. " ] }, { @@ -155,11 +155,11 @@ } }, "source": [ - "The package is organized such that the [*matrix.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/matrix.py) and [*vector.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/vector.py) modules each define just one class, `Matrix` and `Vector`. That is intentional as both classes consist of several hundred lines of code and comments.\n", + "The package is organized such that the [*matrix.py* ](https://github.com/webartifex/intro-to-python/blob/main/11_classes/sample_package/matrix.py) and [*vector.py* ](https://github.com/webartifex/intro-to-python/blob/main/11_classes/sample_package/vector.py) modules each define just one class, `Matrix` and `Vector`. That is intentional as both classes consist of several hundred lines of code and comments.\n", "\n", - "The [*utils.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/utils.py) module contains code that is shared by both classes. Such code snippets are commonly called \"utilities\" or \"helpers,\" which explains the module's name.\n", + "The [*utils.py* ](https://github.com/webartifex/intro-to-python/blob/main/11_classes/sample_package/utils.py) module contains code that is shared by both classes. Such code snippets are commonly called \"utilities\" or \"helpers,\" which explains the module's name.\n", "\n", - "Finally, the [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/__init__.py) file contains mostly meta information and defines what objects should be importable from the package's top level.\n", + "Finally, the [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/main/11_classes/sample_package/__init__.py) file contains mostly meta information and defines what objects should be importable from the package's top level.\n", "\n", "With the `import` statement, we can import the entire package just as we would import a module from the [standard library ](https://docs.python.org/3/library/index.html)." ] @@ -185,7 +185,7 @@ } }, "source": [ - "The above cell runs the code in the [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/__init__.py) file from top to bottom, which in turn runs the [*matrix.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/matrix.py), [*utils.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/utils.py), and [*vector.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/vector.py) modules (cf., look at the `import` statements in the four \\*.py files to get the idea). As both [*matrix.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/matrix.py) and [*vector.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/vector.py) depend on each other (i.e., the `Matrix` class needs the `Vector` class to work and vice versa), understanding the order in that the modules are executed is not trivial. Without going into detail, we mention that Python guarantees that each \\*.py file is run only once and figures out the order on its own. If Python is unable to do that, for example, due to unresolvable cirular imports, it aborts with an `ImportError`.\n", + "The above cell runs the code in the [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/main/11_classes/sample_package/__init__.py) file from top to bottom, which in turn runs the [*matrix.py* ](https://github.com/webartifex/intro-to-python/blob/main/11_classes/sample_package/matrix.py), [*utils.py* ](https://github.com/webartifex/intro-to-python/blob/main/11_classes/sample_package/utils.py), and [*vector.py* ](https://github.com/webartifex/intro-to-python/blob/main/11_classes/sample_package/vector.py) modules (cf., look at the `import` statements in the four \\*.py files to get the idea). As both [*matrix.py* ](https://github.com/webartifex/intro-to-python/blob/main/11_classes/sample_package/matrix.py) and [*vector.py* ](https://github.com/webartifex/intro-to-python/blob/main/11_classes/sample_package/vector.py) depend on each other (i.e., the `Matrix` class needs the `Vector` class to work and vice versa), understanding the order in that the modules are executed is not trivial. Without going into detail, we mention that Python guarantees that each \\*.py file is run only once and figures out the order on its own. If Python is unable to do that, for example, due to unresolvable cirular imports, it aborts with an `ImportError`.\n", "\n", "Below, `pkg` is an object of type `module` ..." ] @@ -297,7 +297,7 @@ } }, "source": [ - "The package's meta information and documentation are automatically parsed from the [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/__init__.py) file." + "The package's meta information and documentation are automatically parsed from the [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/main/11_classes/sample_package/__init__.py) file." ] }, { @@ -1012,7 +1012,7 @@ } }, "source": [ - "A common practice by package authors is to put all the objects on the package's top level that they want the package users to work with directly. That is achieved via the `import` statements in the [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/__init__.py) file.\n", + "A common practice by package authors is to put all the objects on the package's top level that they want the package users to work with directly. That is achieved via the `import` statements in the [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/main/11_classes/sample_package/__init__.py) file.\n", "\n", "However, users can always reach into a package and work with its internals.\n", "\n", @@ -1075,7 +1075,7 @@ } }, "source": [ - "Also, let's import the [*utils.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/utils.py) module with the `norm()` function into the global scope. As this function is integrated into the `Vector.__abs__()` and `Matrix.__abs__()` methods, there is actually no need to work with it explicitly." + "Also, let's import the [*utils.py* ](https://github.com/webartifex/intro-to-python/blob/main/11_classes/sample_package/utils.py) module with the `norm()` function into the global scope. As this function is integrated into the `Vector.__abs__()` and `Matrix.__abs__()` methods, there is actually no need to work with it explicitly." ] }, { @@ -1140,7 +1140,7 @@ "source": [ "Many tutorials on the internet begin by importing \"everything\" from a package into the global scope with `from ... import *`.\n", "\n", - "That is commonly considered a *bad* practice as it may overwrite already existing variables. However, if the package's [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/__init__.py) file defines an `__all__` attribute, a `list` with all the names to be \"exported,\" the **star import** is safe to be used, in particular, in *interactive* sessions like Jupyter notebooks. We emphasize that the star import should *not* be used *within* packages and modules as then it is not directly evident from a name where the corresponding object is defined.\n", + "That is commonly considered a *bad* practice as it may overwrite already existing variables. However, if the package's [*\\_\\_init\\_\\_.py* ](https://github.com/webartifex/intro-to-python/blob/main/11_classes/sample_package/__init__.py) file defines an `__all__` attribute, a `list` with all the names to be \"exported,\" the **star import** is safe to be used, in particular, in *interactive* sessions like Jupyter notebooks. We emphasize that the star import should *not* be used *within* packages and modules as then it is not directly evident from a name where the corresponding object is defined.\n", "\n", "For more best practices regarding importing we refer to, among others, [Google's Python Style Guide](https://google.github.io/styleguide/pyguide.html#22-imports).\n", "\n", @@ -1238,9 +1238,9 @@ } }, "source": [ - "The final implementations of the `Vector` and `Matrix` classes are in the [*matrix.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/matrix.py) and [*vector.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/vector.py) files: They integrate all of the functionalities introduced in this chapter. In addition, the code is cleaned up and fully documented, including examples of common usages.\n", + "The final implementations of the `Vector` and `Matrix` classes are in the [*matrix.py* ](https://github.com/webartifex/intro-to-python/blob/main/11_classes/sample_package/matrix.py) and [*vector.py* ](https://github.com/webartifex/intro-to-python/blob/main/11_classes/sample_package/vector.py) files: They integrate all of the functionalities introduced in this chapter. In addition, the code is cleaned up and fully documented, including examples of common usages.\n", "\n", - "We strongly suggest the eager student go over the files in the [*sample_package* ](https://github.com/webartifex/intro-to-python/tree/develop/11_classes/sample_package) in detail at some point to understand what well-written and (re-)usable code looks like." + "We strongly suggest the eager student go over the files in the [*sample_package* ](https://github.com/webartifex/intro-to-python/tree/main/11_classes/sample_package) in detail at some point to understand what well-written and (re-)usable code looks like." ] }, { @@ -1479,7 +1479,7 @@ } }, "source": [ - "Both `Matrix/Vector.storage` and `Matrix/Vector.typing` themselves reference the `DEFAULT_ENTRIES_STORAGE` and `DEFAULT_ENTRY_TYPE` constants in the [*utils.py* ](https://github.com/webartifex/intro-to-python/blob/develop/11_classes/sample_package/utils.py) module. This way, we could, for example, change only the constants and thereby also change how the `._entries` are stored internally in both classes. Also, this single **[single source of truth ](https://en.wikipedia.org/wiki/Single_source_of_truth)** ensures that both classes are consistent with each other at all times." + "Both `Matrix/Vector.storage` and `Matrix/Vector.typing` themselves reference the `DEFAULT_ENTRIES_STORAGE` and `DEFAULT_ENTRY_TYPE` constants in the [*utils.py* ](https://github.com/webartifex/intro-to-python/blob/main/11_classes/sample_package/utils.py) module. This way, we could, for example, change only the constants and thereby also change how the `._entries` are stored internally in both classes. Also, this single **[single source of truth ](https://en.wikipedia.org/wiki/Single_source_of_truth)** ensures that both classes are consistent with each other at all times." ] }, { @@ -2238,7 +2238,7 @@ "\n", "If we feel like sharing our linear algebra library with the world, we could easily do so on either [GitHub ](https://github.com) or [PyPI](https://pypi.org). However, for the domain of linear algebra this would be rather pointless as there is already a widely adopted library with [numpy](https://www.numpy.org/) that not only has a lot more features than ours but also is implemented in C, which makes it a lot faster with big data.\n", "\n", - "Let's model the example in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb#Example:-Vectors-&-Matrices) with both [numpy](https://www.numpy.org/) and our own DSL and compare them." + "Let's model the example in the [first part ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb#Example:-Vectors-&-Matrices) with both [numpy](https://www.numpy.org/) and our own DSL and compare them." ] }, { diff --git a/11_classes/06_review.ipynb b/11_classes/06_review.ipynb index 0f54544..53cb6bb 100644 --- a/11_classes/06_review.ipynb +++ b/11_classes/06_review.ipynb @@ -11,7 +11,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb), [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/02_content.ipynb), [third ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/03_content.ipynb), and [fourth ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/04_content.ipynb) part of Chapter 11.\n", + "The questions below assume that you have read the [first ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb), [second ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/02_content.ipynb), [third ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/03_content.ipynb), and [fourth ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/04_content.ipynb) part of Chapter 11.\n", "\n", "Be concise in your answers! Most questions can be answered in *one* sentence." ] diff --git a/CONTENTS.md b/CONTENTS.md index cd8228b..c958a72 100644 --- a/CONTENTS.md +++ b/CONTENTS.md @@ -24,271 +24,271 @@ It is recommended If this is not possible, the files can be viewed in a web browser - either statically (i.e., read-only) on [nbviewer ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/tree/develop/) - or interactively (i.e., code can be executed) on [Binder ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab). + either statically (i.e., read-only) on [nbviewer ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/tree/main/) + or interactively (i.e., code can be executed) on [Binder ](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab). - *Chapter 0*: Introduction - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/00_intro/00_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/00_intro/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/00_intro/00_content.ipynb) (Python's History & Background; Open-source & Communities; JupyterLab; Programming vs. Computer Science; Learning Tips) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/01_exercises_markdown.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/00_intro/01_exercises_markdown.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/00_intro/01_exercises_markdown.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/00_intro/01_exercises_markdown.ipynb) (Mastering Markdown) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/00_intro/02_review.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/00_intro/02_review.ipynb) - **Part A: Expressing Logic** - *Chapter 1*: Elements of a Program - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/00_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/00_content.ipynb) (A first Example: Averaging Even Numbers; Operators; Objects & Data Types; Errors) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/01_exercises_print.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/01_exercises_print.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/01_exercises_print.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/01_exercises_print.ipynb) (Printing Output) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/02_exercises_for-loops.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/02_exercises_for-loops.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/02_exercises_for-loops.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/02_exercises_for-loops.ipynb) (Simple `for`-loops) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/03_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/03_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/03_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/03_content.ipynb) (Memory in Detail; Variables & References; Mutability; Expressions & Statements) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/04_exercises_calculator.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/04_exercises_calculator.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/04_exercises_calculator.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/04_exercises_calculator.ipynb) (Python as a Calculator) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/05_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/06_review.ipynb) - - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/01_elements/07_resources.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/01_elements/07_resources.ipynb) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/05_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/06_review.ipynb) + - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/01_elements/07_resources.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/01_elements/07_resources.ipynb) - *Chapter 2*: Functions & Modularization - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/00_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/02_functions/00_content.ipynb) (Built-in Functions & Constructors; Function Definitions; Function Calls & Scoping Rules; Positional vs. Keyword Arguments; Modularization) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/01_exercises_sphere-volume.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/01_exercises_sphere-volume.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/01_exercises_sphere-volume.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/02_functions/01_exercises_sphere-volume.ipynb) (Volume of a Sphere) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/02_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/02_functions/02_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/02_functions/02_content.ipynb) (Standard Library: `math` & `random` Modules; Third-party Packages: `numpy` Library; Writing one's own Modules) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/03_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/02_functions/04_review.ipynb) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/03_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/02_functions/04_review.ipynb) - *Chapter 3*: Conditionals & Exceptions - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/00_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/03_conditionals/00_content.ipynb) (Boolean Expressions; Relational Operators; Logical Operators; `if` statement; Exception Handling) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/01_exercises_discounts.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/01_exercises_discounts.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/01_exercises_discounts.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/03_conditionals/01_exercises_discounts.ipynb) (Discounting Customer Orders) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/02_exercises_fizz-buzz.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/03_conditionals/02_exercises_fizz-buzz.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/02_exercises_fizz-buzz.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/03_conditionals/02_exercises_fizz-buzz.ipynb) (Fizz Buzz) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/03_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/03_conditionals/04_review.ipynb) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/03_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/03_conditionals/04_review.ipynb) - *Chapter 4*: Recursion & Looping - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/00_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/04_iteration/00_content.ipynb) (Recursion; Examples: Factorial, Euclid's Algorithm, & Fibonacci; Duck Typing; Type Casting & Checking; Input Validation) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/01_exercises_hanoi-towers.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/01_exercises_hanoi-towers.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/01_exercises_hanoi-towers.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/04_iteration/01_exercises_hanoi-towers.ipynb) (Towers of Hanoi) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/02_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/02_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/04_iteration/02_content.ipynb) (Looping with `while` & `for`; Examples: Collatz Conjecture, Factorial, Euclid's Algorithm, & Fibonacci; Containers vs. Iterables) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/03_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/03_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/03_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/04_iteration/03_content.ipynb) (Customizing Loops with `break` and `continue`; Indefinite Loops) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/04_exercises_dice.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/04_iteration/04_exercises_dice.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/04_exercises_dice.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/04_iteration/04_exercises_dice.ipynb) (Throwing Dice) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/05_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/04_iteration/06_review.ipynb) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/05_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/04_iteration/06_review.ipynb) - **Part B: Managing Data and Memory** - *Chapter 5*: Numbers & Bits - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/00_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/05_numbers/00_content.ipynb) (`int` Type; Binary & Hexadecimal Representations; Bit Arithmetic; Bitwise Operators) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/01_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/01_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/01_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/05_numbers/01_content.ipynb) (`float` Type; Floating-point Standard; Special Values; Imprecision; Binary & Hexadecimal Representations) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/02_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/02_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/05_numbers/02_content.ipynb) (`complex` Type; Numerical Tower; Duck vs. Goose Typing) - - [appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/03_appendix.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/03_appendix.ipynb) + - [appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/03_appendix.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/05_numbers/03_appendix.ipynb) (`Decimal` Type; `Fraction` Type) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/04_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/05_review.ipynb) - - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/05_numbers/06_resources.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/05_numbers/06_resources.ipynb) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/04_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/05_review.ipynb) + - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/05_numbers/06_resources.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/05_numbers/06_resources.ipynb) - *Chapter 6*: Text & Bytes - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/00_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/06_text/00_content.ipynb) (`str` Type; Reading Files; Sequences; Indexing & Slicing; String Methods & Operations; String Interpolation) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/01_exercises_palindromes.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/01_exercises_palindromes.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/01_exercises_palindromes.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/06_text/01_exercises_palindromes.ipynb) (Detecting Palindromes) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/02_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/02_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/06_text/02_content.ipynb) (Special Characters; ASCII & Unicode; Multi-line Strings; `bytes` Type; Character Encodings) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/03_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/04_review.ipynb) - - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/06_text/05_resources.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/06_text/05_resources.ipynb) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/03_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/04_review.ipynb) + - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/06_text/05_resources.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/06_text/05_resources.ipynb) - *Chapter 7*: Sequential Data - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/00_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/00_content.ipynb) (Collections vs. Sequences; ABCs: `Container`, `Iterable`, `Sized`, & `Reversible`) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/01_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/01_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/01_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/01_content.ipynb) (`list` Type; Indexing & Slicing; Shallow vs. Deep Copies; List Methods & Operations) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/02_exercises_lists.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/02_exercises_lists.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/02_exercises_lists.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/02_exercises_lists.ipynb) (Working with Lists) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/03_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/03_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/03_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/03_content.ipynb) (Modifiers vs. Pure Functions; `tuple` Type; Packing & Unpacking; `*args` in Function Definitions) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/04_exercises_un-packing.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/04_exercises_un-packing.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/04_exercises_un-packing.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/04_exercises_un-packing.ipynb) (Packing & Unpacking with Functions) - - [appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/05_appendix.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/07_sequences/05_appendix.ipynb) + - [appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/05_appendix.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/07_sequences/05_appendix.ipynb) (`namedtuple` Type) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/06_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/07_sequences/07_review.ipynb) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/06_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/07_sequences/07_review.ipynb) - *Chapter 8*: Map, Filter, & Reduce - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/00_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/08_mfr/00_content.ipynb) (Mapping; Filtering; Reducing; `lambda` Expression) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/01_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/01_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/01_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/08_mfr/01_content.ipynb) (`list` Comprehension; `generator` Expression; Streams of Data; Boolean Reducers) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/02_exercises_outliers.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/02_exercises_outliers.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/02_exercises_outliers.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/08_mfr/02_exercises_outliers.ipynb) (Removing Outliers in Streaming Data) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/03_exercises_un-packing.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/03_exercises_un-packing.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/03_exercises_un-packing.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/08_mfr/03_exercises_un-packing.ipynb) (Packing & Unpacking with Functions, continued) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/04_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/08_mfr/04_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/04_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/08_mfr/04_content.ipynb) (Iterators vs. Iterables; Example: `sorted()` vs. `reversed()`) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/05_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/08_mfr/06_review.ipynb) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/05_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/08_mfr/06_review.ipynb) - *Chapter 9*: Mappings & Sets - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/00_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/00_content.ipynb) (`dict` Type; Nested Data; Hash Tables; `dict` Methods & Behavior; `dict` Comprehension) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/01_exercises_nested-data.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/01_exercises_nested-data.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/01_exercises_nested-data.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/01_exercises_nested-data.ipynb) (Working with Nested Data) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/02_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/02_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/02_content.ipynb) (`**kwargs` in Function Definitions; Memoization) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/03_exercises_fibonacci.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/03_exercises_fibonacci.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/03_exercises_fibonacci.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/03_exercises_fibonacci.ipynb) (Memoization without Side Effects) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/04_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/04_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/04_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/04_content.ipynb) (`set` Type; `set` Methods & Operations; `set` Comprehension; `frozenset` Type) - - [appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/05_appendix.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/05_appendix.ipynb) + - [appendix ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/05_appendix.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/05_appendix.ipynb) (`defaultdict` Type; `Counter` Type; `ChainMap` Type) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/06_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/07_review.ipynb) - - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/09_mappings/08_resources.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/09_mappings/08_resources.ipynb) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/06_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/07_review.ipynb) + - [further resources ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/09_mappings/08_resources.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/09_mappings/08_resources.ipynb) - *Chapter 10*: Arrays & Dataframes - *Chapter 11*: Classes & Instances - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/00_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/00_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/00_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/11_classes/00_content.ipynb) (`class` Statement; Instantiation; Text Representations; Instance Methods vs. Class Methods; Computed Properties) - - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/01_exercises_tsp.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/01_exercises_tsp.ipynb) + - [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/01_exercises_tsp.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/11_classes/01_exercises_tsp.ipynb) (A Traveling Salesman Problem) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/02_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/02_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/02_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/11_classes/02_content.ipynb) (Sequence Emulation & Iteration; Python's Data Model; (Im)mutable Data Types; Method Chaining; Polymorphism) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/03_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/03_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/03_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/11_classes/03_content.ipynb) (Operator Overloading: Arithmetic & Relational Operators; Number Emulation) - - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/04_content.ipynb) - [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/11_classes/04_content.ipynb) + - [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/04_content.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/main?urlpath=lab/tree/11_classes/04_content.ipynb) (Writing one's own Packages; The final `Vector` & `Matrix` Classes; Comparison with `numpy`) - - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/05_summary.ipynb) - - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/11_classes/06_review.ipynb) + - [summary ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/05_summary.ipynb) + - [review questions ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/main/11_classes/06_review.ipynb) diff --git a/pyproject.toml b/pyproject.toml index 4491fee..8d26fc8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "intro-to-python" -version = "0.1.0.dev0" +version = "0.1.0" package-mode = false authors = ["Alexander Hess "]

r8M@(i6>G`_3 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 literal 0 HcmV?d00001 diff --git a/chapter_00_intro/static/logo.png b/chapter_00_intro/static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b3b5b2220a609b23acf42fcbba221cafea22f043 GIT binary patch literal 7821 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 z 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 literal 0 HcmV?d00001 From 570d6cec1364a49a7bba925fc0306dcb2d24ae4e Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 12 Oct 2020 22:02:58 +0200 Subject: [PATCH 018/142] Add initial version of chapter 00's review --- README.md | 3 + chapter_00_intro/01_review.ipynb | 222 +++++++++++++++++++++++++++++++ 2 files changed, 225 insertions(+) create mode 100644 chapter_00_intro/01_review.ipynb diff --git a/README.md b/README.md index 458d934..2ed69f7 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,9 @@ They can be viewed in a web browser [content ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/00_content.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/00_content.ipynb) [](https://www.youtube.com/watch?v=YTU8jaG27Xk&list=PL-2JV1G3J10lQ2xokyQowcRJI5jjNfW7f) + | + [review ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/01_review.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/01_review.ipynb) ) diff --git a/chapter_00_intro/01_review.ipynb b/chapter_00_intro/01_review.ipynb new file mode 100644 index 0000000..2ea6b98 --- /dev/null +++ b/chapter_00_intro/01_review.ipynb @@ -0,0 +1,222 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 0: Introduction" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Review" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The questions below assume that you have read [Chapter 0 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/00_content.ipynb) in the book.\n", + "\n", + "Be concise in your answers! Most questions can be answered in *one* sentence." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Essay Questions " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: Describe the difference between the terms **programming** and **computer science**!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: Explain what is a **pull request** and elaborate on how this concept fits a *distributed* organization of work!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: In what sense are **open-source** communities democracies? How are they near-perfect [meritocracies ](https://en.wikipedia.org/wiki/Meritocracy)? How is open-source software development similar to academia?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q4**: What is a *significant* advantage of a \"slow\" programming language like Python over a faster one like C?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### True / False Questions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Motivate your answer with *one short* sentence!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q5**: Python has been the fastest-growing *major* programming language in recent years." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q6**: Python is named after a snake to emphasize its agility and fast development speed right in its name." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q7**: Python was initially designed for highly intensive numerical computing, in particular for use cases from physics and astronomy." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q8**: JavaScript is a subset of the Java language." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q9**: Python is **free software**. That means it does not cost anything." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q10**: The primary purpose of PEPs is to regulate how code should be documented and styled." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 0c754f1c7bd8519c0e6c8ebb702ea5f968b47f00 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 12 Oct 2020 22:09:38 +0200 Subject: [PATCH 019/142] Add initial version of chapter 00's exercises --- README.md | 3 + chapter_00_intro/02_exercises.ipynb | 122 ++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 chapter_00_intro/02_exercises.ipynb diff --git a/README.md b/README.md index 2ed69f7..66698ad 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,9 @@ They can be viewed in a web browser | [review ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/01_review.ipynb) [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/01_review.ipynb) + | + [exercises ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/02_exercises.ipynb) + [](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/02_exercises.ipynb) ) diff --git a/chapter_00_intro/02_exercises.ipynb b/chapter_00_intro/02_exercises.ipynb new file mode 100644 index 0000000..28a2cbf --- /dev/null +++ b/chapter_00_intro/02_exercises.ipynb @@ -0,0 +1,122 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note**: Click on \"*Kernel*\" > \"*Restart Kernel and Run All*\" in [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) *after* finishing the exercises to ensure that your solution runs top to bottom *without* any errors. If you cannot run this file on your machine, you may want to open it [in the cloud ](https://mybinder.org/v2/gh/webartifex/intro-to-python/develop?urlpath=lab/tree/chapter_00_intro/02_exercises.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Chapter 0: Introduction" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## \"Coding\" Exercises" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The exercises below assume that you have read [Chapter 0 ](https://nbviewer.jupyter.org/github/webartifex/intro-to-python/blob/develop/chapter_00_intro/00_content.ipynb) in the book." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Mastering Markdown" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Briefly review GitHub's guide on [Mastering Markdown](https://guides.github.com/features/mastering-markdown/) and create nicely formatted \"text\" cells below!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q1**: Check the latest [Bundesliga standings](https://www.bundesliga.com/en/bundesliga/table) and provide a table of the top three teams with the following four columns: rank, team name, games played, and points scored. Render the rank in **bold**, make the team name a clickable link (to the team's website), and put both the games played and points scored in *italics*. The header row should be visually different from the three rows with the teams' information." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q2**: The quote \"Education is what remains after one has forgotten what one has learned in school\" is attributed to Albert Einstein. Display the author and his quote appropriately!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Q3**: Integrate the image of the delicious dessert milk rice at this [URL](https://i.ytimg.com/vi/-BoSRlzy9c4/maxresdefault.jpg) into this notebook." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " < your answer >" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": false, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": false, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 955fb74673f2b4fa217227f293177f7531623758 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 12 Oct 2020 22:47:50 +0200 Subject: [PATCH 020/142] Mention nox and init task --- README.md | 11 ++++++++++- noxfile.py | 5 +++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 66698ad..85f55ee 100644 --- a/README.md +++ b/README.md @@ -205,7 +205,16 @@ The following *one* command not only [poetry](https://python-poetry.org/docs/) is also used to execute commands in the project's (virtual) environment. The command is then prefixed with `poetry run ...`. -For example, to do the equivalent of clicking "Launch" in the Anaconda Navigator: + +The project uses [nox](https://nox.thea.codes/en/stable/) + to manage various maintenance tasks. +After cloning the repository and setting up the virual environment, + it is recommended to run the initialization task. +That needs to be done only once. + +- `poetry run nox -s init-project` + +To do the equivalent of clicking "Launch" in the Anaconda Navigator: - `poetry run jupyter lab` diff --git a/noxfile.py b/noxfile.py index 184e1b5..e77cc26 100644 --- a/noxfile.py +++ b/noxfile.py @@ -39,6 +39,11 @@ def init_project(session): ): session.run("poetry", "run", "pre-commit", "install", f"--hook-type={type_}") + # Copy the extensions' JavaScript and CSS files into Jupyter's search directory. + session.run( + "poetry", "run", "jupyter", "contrib", "nbextension", "install", "--user" + ) + @nox.session(name="fix-branch-references", venv_backend="none") def fix_branch_references(_session): From 35d20aae40a24dfdc3ceeaae7be9b117b913c73d Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 12 Oct 2020 22:49:53 +0200 Subject: [PATCH 021/142] Add dependencies for presentation mode - RISE extension for Jupyter - jupyter-contrib-nbextensions --- poetry.lock | 178 +++++++++++++++++++++++++++++++++++++++++++++++-- pyproject.toml | 4 ++ 2 files changed, 175 insertions(+), 7 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6b2b916..bc75853 100644 --- a/poetry.lock +++ b/poetry.lock @@ -330,6 +330,48 @@ traitlets = "*" [package.extras] test = ["ipykernel", "ipython", "mock", "pytest", "pytest-asyncio", "async-generator", "pytest-timeout"] +[[package]] +name = "jupyter-contrib-core" +version = "0.3.3" +description = "Common utilities for jupyter-contrib projects." +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +jupyter-core = "*" +notebook = ">=4.0" +tornado = "*" +traitlets = "*" + +[package.extras] +testing_utils = ["nose", "mock"] + +[[package]] +name = "jupyter-contrib-nbextensions" +version = "0.5.1" +description = "A collection of Jupyter nbextensions." +category = "dev" +optional = false +python-versions = "*" + +[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 = ["nbformat", "nose", "pip", "requests", "mock"] + [[package]] name = "jupyter-core" version = "4.6.3" @@ -342,6 +384,48 @@ python-versions = "!=3.0,!=3.1,!=3.2,!=3.3,!=3.4,>=2.7" pywin32 = {version = ">=1.0", markers = "sys_platform == \"win32\""} traitlets = "*" +[[package]] +name = "jupyter-highlight-selected-word" +version = "0.2.0" +description = "Jupyter notebook extension that enables highlighting every instance of the current word in the notebook." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "jupyter-latex-envs" +version = "1.4.6" +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..." +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +ipython = "*" +jupyter_core = "*" +nbconvert = "*" +notebook = ">=4.0" +traitlets = ">=4.1" + +[[package]] +name = "jupyter-nbextensions-configurator" +version = "0.4.1" +description = "jupyter serverextension providing configuration interfaces for nbextensions." +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +jupyter_contrib_core = ">=0.3.3" +jupyter_core = "*" +notebook = ">=4.0" +pyyaml = "*" +tornado = "*" +traitlets = "*" + +[package.extras] +test = ["jupyter-contrib-core", "nose", "requests", "selenium", "mock"] + [[package]] name = "jupyterlab" version = "2.2.8" @@ -389,6 +473,20 @@ requests = "*" [package.extras] test = ["pytest", "requests"] +[[package]] +name = "lxml" +version = "4.5.2" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html5 = ["html5lib"] +htmlsoup = ["beautifulsoup4"] +source = ["Cython (>=0.29.7)"] + [[package]] name = "markupsafe" version = "1.1.1" @@ -612,7 +710,7 @@ twisted = ["twisted"] [[package]] name = "prompt-toolkit" -version = "3.0.7" +version = "3.0.8" description = "Library for building powerful interactive command lines in Python" category = "main" optional = false @@ -730,6 +828,17 @@ urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] +[[package]] +name = "rise" +version = "5.6.1" +description = "Reveal.js - Jupyter/IPython Slideshow Extension" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4" + +[package.dependencies] +notebook = ">=5.5.0" + [[package]] name = "send2trash" version = "1.5.0" @@ -815,7 +924,7 @@ socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] [[package]] name = "virtualenv" -version = "20.0.33" +version = "20.0.34" description = "Virtual Python Environment builder" category = "dev" optional = false @@ -850,7 +959,7 @@ python-versions = "*" [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "cbabc76963596b4cb08138b7b42feef3ba509e9b10ea821a1a5d02675fd6c761" +content-hash = "1fd17e55acd76edd6126d5ee9223c0dd291bc2acacaaa242e80a3a2aaa5decce" [metadata.files] appdirs = [ @@ -1017,10 +1126,28 @@ jupyter-client = [ {file = "jupyter_client-6.1.7-py3-none-any.whl", hash = "sha256:c958d24d6eacb975c1acebb68ac9077da61b5f5c040f22f6849928ad7393b950"}, {file = "jupyter_client-6.1.7.tar.gz", hash = "sha256:49e390b36fe4b4226724704ea28d9fb903f1a3601b6882ce3105221cd09377a1"}, ] +jupyter-contrib-core = [ + {file = "jupyter_contrib_core-0.3.3-py2.py3-none-any.whl", hash = "sha256:1ec81e275a8f5858d56b0c4c6cd85335aa8e915001b8657fe51c620c3cdde50f"}, + {file = "jupyter_contrib_core-0.3.3.tar.gz", hash = "sha256:e65bc0e932ff31801003cef160a4665f2812efe26a53801925a634735e9a5794"}, +] +jupyter-contrib-nbextensions = [ + {file = "jupyter_contrib_nbextensions-0.5.1-py2.py3-none-any.whl", hash = "sha256:2c071f0aa208c569666f656bdc0f66906ca493cf9f06f46db6350db11030ff40"}, + {file = "jupyter_contrib_nbextensions-0.5.1.tar.gz", hash = "sha256:eecd28ecc2fc410226c0a3d4932ed2fac4860ccf8d9e9b1b29548835a35b22ab"}, +] jupyter-core = [ {file = "jupyter_core-4.6.3-py2.py3-none-any.whl", hash = "sha256:a4ee613c060fe5697d913416fc9d553599c05e4492d58fac1192c9a6844abb21"}, {file = "jupyter_core-4.6.3.tar.gz", hash = "sha256:394fd5dd787e7c8861741880bdf8a00ce39f95de5d18e579c74b882522219e7e"}, ] +jupyter-highlight-selected-word = [ + {file = "jupyter_highlight_selected_word-0.2.0-py2.py3-none-any.whl", hash = "sha256:9545dfa9cb057eebe3a5795604dcd3a5294ea18637e553f61a0b67c1b5903c58"}, + {file = "jupyter_highlight_selected_word-0.2.0.tar.gz", hash = "sha256:9fa740424859a807950ca08d2bfd28a35154cd32dd6d50ac4e0950022adc0e7b"}, +] +jupyter-latex-envs = [ + {file = "jupyter_latex_envs-1.4.6.tar.gz", hash = "sha256:070a31eb2dc488bba983915879a7c2939247bf5c3b669b398bdb36a9b5343872"}, +] +jupyter-nbextensions-configurator = [ + {file = "jupyter_nbextensions_configurator-0.4.1.tar.gz", hash = "sha256:e5e86b5d9d898e1ffb30ebb08e4ad8696999f798fef3ff3262d7b999076e4e83"}, +] jupyterlab = [ {file = "jupyterlab-2.2.8-py3-none-any.whl", hash = "sha256:95d0509557881cfa8a5fcdf225f2fca46faf1bc52fc56a28e0b72fcc594c90ab"}, {file = "jupyterlab-2.2.8.tar.gz", hash = "sha256:c8377bee30504919c1e79949f9fe35443ab7f5c4be622c95307e8108410c8b8c"}, @@ -1033,6 +1160,39 @@ jupyterlab-server = [ {file = "jupyterlab_server-1.2.0-py3-none-any.whl", hash = "sha256:55d256077bf13e5bc9e8fbd5aac51bef82f6315111cec6b712b9a5ededbba924"}, {file = "jupyterlab_server-1.2.0.tar.gz", hash = "sha256:5431d9dde96659364b7cc877693d5d21e7b80cea7ae3959ecc2b87518e5f5d8c"}, ] +lxml = [ + {file = "lxml-4.5.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:74f48ec98430e06c1fa8949b49ebdd8d27ceb9df8d3d1c92e1fdc2773f003f20"}, + {file = "lxml-4.5.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e70d4e467e243455492f5de463b72151cc400710ac03a0678206a5f27e79ddef"}, + {file = "lxml-4.5.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:7ad7906e098ccd30d8f7068030a0b16668ab8aa5cda6fcd5146d8d20cbaa71b5"}, + {file = "lxml-4.5.2-cp27-cp27m-win32.whl", hash = "sha256:92282c83547a9add85ad658143c76a64a8d339028926d7dc1998ca029c88ea6a"}, + {file = "lxml-4.5.2-cp27-cp27m-win_amd64.whl", hash = "sha256:05a444b207901a68a6526948c7cc8f9fe6d6f24c70781488e32fd74ff5996e3f"}, + {file = "lxml-4.5.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:94150231f1e90c9595ccc80d7d2006c61f90a5995db82bccbca7944fd457f0f6"}, + {file = "lxml-4.5.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bea760a63ce9bba566c23f726d72b3c0250e2fa2569909e2d83cda1534c79443"}, + {file = "lxml-4.5.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c3f511a3c58676147c277eff0224c061dd5a6a8e1373572ac817ac6324f1b1e0"}, + {file = "lxml-4.5.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:59daa84aef650b11bccd18f99f64bfe44b9f14a08a28259959d33676554065a1"}, + {file = "lxml-4.5.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:c9d317efde4bafbc1561509bfa8a23c5cab66c44d49ab5b63ff690f5159b2304"}, + {file = "lxml-4.5.2-cp35-cp35m-win32.whl", hash = "sha256:9dc9006dcc47e00a8a6a029eb035c8f696ad38e40a27d073a003d7d1443f5d88"}, + {file = "lxml-4.5.2-cp35-cp35m-win_amd64.whl", hash = "sha256:08fc93257dcfe9542c0a6883a25ba4971d78297f63d7a5a26ffa34861ca78730"}, + {file = "lxml-4.5.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:121b665b04083a1e85ff1f5243d4a93aa1aaba281bc12ea334d5a187278ceaf1"}, + {file = "lxml-4.5.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5591c4164755778e29e69b86e425880f852464a21c7bb53c7ea453bbe2633bbe"}, + {file = "lxml-4.5.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:cc411ad324a4486b142c41d9b2b6a722c534096963688d879ea6fa8a35028258"}, + {file = "lxml-4.5.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:1fa21263c3aba2b76fd7c45713d4428dbcc7644d73dcf0650e9d344e433741b3"}, + {file = "lxml-4.5.2-cp36-cp36m-win32.whl", hash = "sha256:786aad2aa20de3dbff21aab86b2fb6a7be68064cbbc0219bde414d3a30aa47ae"}, + {file = "lxml-4.5.2-cp36-cp36m-win_amd64.whl", hash = "sha256:e1cacf4796b20865789083252186ce9dc6cc59eca0c2e79cca332bdff24ac481"}, + {file = "lxml-4.5.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:80a38b188d20c0524fe8959c8ce770a8fdf0e617c6912d23fc97c68301bb9aba"}, + {file = "lxml-4.5.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ecc930ae559ea8a43377e8b60ca6f8d61ac532fc57efb915d899de4a67928efd"}, + {file = "lxml-4.5.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a76979f728dd845655026ab991df25d26379a1a8fc1e9e68e25c7eda43004bed"}, + {file = "lxml-4.5.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cfd7c5dd3c35c19cec59c63df9571c67c6d6e5c92e0fe63517920e97f61106d1"}, + {file = "lxml-4.5.2-cp37-cp37m-win32.whl", hash = "sha256:5a9c8d11aa2c8f8b6043d845927a51eb9102eb558e3f936df494e96393f5fd3e"}, + {file = "lxml-4.5.2-cp37-cp37m-win_amd64.whl", hash = "sha256:4b4a111bcf4b9c948e020fd207f915c24a6de3f1adc7682a2d92660eb4e84f1a"}, + {file = "lxml-4.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5dd20538a60c4cc9a077d3b715bb42307239fcd25ef1ca7286775f95e9e9a46d"}, + {file = "lxml-4.5.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:2b30aa2bcff8e958cd85d907d5109820b01ac511eae5b460803430a7404e34d7"}, + {file = "lxml-4.5.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:aa8eba3db3d8761db161003e2d0586608092e217151d7458206e243be5a43843"}, + {file = "lxml-4.5.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f0ec6b9b3832e0bd1d57af41f9238ea7709bbd7271f639024f2fc9d3bb01293"}, + {file = "lxml-4.5.2-cp38-cp38-win32.whl", hash = "sha256:107781b213cf7201ec3806555657ccda67b1fccc4261fb889ef7fc56976db81f"}, + {file = "lxml-4.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:f161af26f596131b63b236372e4ce40f3167c1b5b5d459b29d2514bd8c9dc9ee"}, + {file = "lxml-4.5.2.tar.gz", hash = "sha256:cdc13a1682b2a6241080745b1953719e7fe0850b40a5c71ca574f090a1391df6"}, +] 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"}, @@ -1128,8 +1288,8 @@ prometheus-client = [ {file = "prometheus_client-0.8.0.tar.gz", hash = "sha256:c6e6b706833a6bd1fd51711299edee907857be10ece535126a158f911ee80915"}, ] prompt-toolkit = [ - {file = "prompt_toolkit-3.0.7-py3-none-any.whl", hash = "sha256:83074ee28ad4ba6af190593d4d4c607ff525272a504eb159199b6dd9f950c950"}, - {file = "prompt_toolkit-3.0.7.tar.gz", hash = "sha256:822f4605f28f7d2ba6b0b09a31e25e140871e96364d1d377667b547bb3bf4489"}, + {file = "prompt_toolkit-3.0.8-py3-none-any.whl", hash = "sha256:7debb9a521e0b1ee7d2fe96ee4bd60ef03c6492784de0547337ca4433e46aa63"}, + {file = "prompt_toolkit-3.0.8.tar.gz", hash = "sha256:25c95d2ac813909f813c93fde734b6e44406d1477a9faef7c915ff37d39c0a8c"}, ] ptyprocess = [ {file = "ptyprocess-0.6.0-py2.py3-none-any.whl", hash = "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"}, @@ -1231,6 +1391,10 @@ requests = [ {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"}, {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"}, ] +rise = [ + {file = "rise-5.6.1-py2.py3-none-any.whl", hash = "sha256:e9637ee5499ad7801474da53a2c830350a44b2192c2f113594e4426190e55ad4"}, + {file = "rise-5.6.1.tar.gz", hash = "sha256:1343f068d01adc4dd0226d9b278ce93fc92f365d827431a57e8d5679eb39f4d6"}, +] send2trash = [ {file = "Send2Trash-1.5.0-py3-none-any.whl", hash = "sha256:f1691922577b6fa12821234aeb57599d887c4900b9ca537948d2dac34aea888b"}, {file = "Send2Trash-1.5.0.tar.gz", hash = "sha256:60001cc07d707fe247c94f74ca6ac0d3255aabcb930529690897ca2a39db28b2"}, @@ -1271,8 +1435,8 @@ urllib3 = [ {file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"}, ] virtualenv = [ - {file = "virtualenv-20.0.33-py2.py3-none-any.whl", hash = "sha256:35ecdeb58cfc2147bb0706f7cdef69a8f34f1b81b6d49568174e277932908b8f"}, - {file = "virtualenv-20.0.33.tar.gz", hash = "sha256:a5e0d253fe138097c6559c906c528647254f437d1019af9d5a477b09bfa7300f"}, + {file = "virtualenv-20.0.34-py2.py3-none-any.whl", hash = "sha256:4ecd607e7809bd7384039065639a8babc9fa562fe1b73b24095ce85b83d55fcf"}, + {file = "virtualenv-20.0.34.tar.gz", hash = "sha256:4bf0e2bf99d33b123a895a5a244f0d7adb2a92171c6bbb31c3e2db235624abf1"}, ] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, diff --git a/pyproject.toml b/pyproject.toml index c06108c..e3b89bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,3 +19,7 @@ jupyterlab = "^2.2.8" # Task runners nox = "^2020.8.22" pre-commit = "^2.7.1" + +# Live coding during presentation mode +jupyter-contrib-nbextensions = "^0.5.1" +rise = "^5.6.1" From f4c4eace8de81bc81aba6919f04a8e30ba607827 Mon Sep 17 00:00:00 2001 From: Alexander Hess Date: Mon, 12 Oct 2020 22:51:13 +0200 Subject: [PATCH 022/142] Add section on presentation mode --- README.md | 24 ++++++++++++++++++++++++ static/presentation_mode.png | Bin 0 -> 102326 bytes 2 files changed, 24 insertions(+) create mode 100644 static/presentation_mode.png diff --git a/README.md b/README.md index 85f55ee..208a5bd 100644 --- a/README.md +++ b/README.md @@ -226,6 +226,30 @@ The command-line interface stays open in the background, +#### Interactive Presentation Mode & Live Coding + +`poetry install` also installs the + [RISE ](https://github.com/damianavila/RISE) + extension for Jupyter. +With that, the instructor can execute code in *presentation* mode during a class session. +However, the RISE extension does *not* work in the more recent + [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) app + but only in the older [Jupyter Notebook](https://jupyter-notebook.readthedocs.io/en/stable/) app, + which comes with less features and a simpler [GUI](https://en.wikipedia.org/wiki/Graphical_user_interface). +The instructor can start the latter with: + +- `poetry run jupyter notebook` + +This also opens a new tab in the web browser. +After opening a notebook, + clicking on the button highlighted below + starts the presentation mode. + + + +Not all notebooks are designed for this presentation mode. + + ## Contributing Feedback **is highly encouraged** and will be incorporated. diff --git a/static/presentation_mode.png b/static/presentation_mode.png new file mode 100644 index 0000000000000000000000000000000000000000..898d8e142b374ebca7074f90efa6971437e21764 GIT binary patch literal 102326 zcmbTecR1Gl8$WzmB`ZlXGb=lkjD#d36xlN~vqU7>krLT5i^?V;GDFGM5=v$ng|cVX z^K#$!_c@;Dzu)oesQWH1*Yz3i_j$h7`3ccHr%X-3OhF(Js8v-IFAxZ%DFg!1DmgK} zqNeep0sq@|RmJccfk5+i`wvl^01XR)z(G(|Jf(9Z<#)e_0e#b!;aD=KoL!QS0O=7b;u5l7HO>GdrS&Tkvh|Gwf$c9Mm^hmsS|K%Sbty}e~o z`8khODJDk7W0$`zkm#)s+UHbx&X2wFTK@g}H!(3WDe);E{X75t?4}2YziT4b9~;%N zypA8Ph88&`>FSxBbiW*P#_h=OyLBe@BruL=e{p&C*0QLq%!6%uVLl`@bggh7i>9XX zH!9Ias`)6WuElxL4PUDFK?6{5U)M> z_%_sjf|~ID{rm86nwE$IEG#T*fBzEKZCyCZwxOc=@11quZDr0FtFPm-;qpkH!$h+1 z_Oqhy?rSaa?E4CfDISc9G`XqcDMn0sAqk5 zxbjCij4^8d$L*q=oZw(3ANJP64`O2KMZzj7Jbm=j9gbYAF{yML@5<9Zdi1Dqse`!P z$FxsRmGy-gSXioDh8rEf6!6hg`1x(M#2hYj9;}&iT&pR#SZiit(wCvOKT2hxMPhSq zw5`fx`rFs9)fG{v{#n&sk>+*ek?HDshki2K2Pj*S5bP`*-M)WUYj^Pvurj3UB=S8P zOF?_@{JTX52eah2{GC}H{z)&|-?gV-$SI17lII`<4Nb`v`pCR}XdOyvs8?6a^XN1= zr~F-;B831+r8?__r&a`~n$IekS#*`pZW$;xx%9of+?#nm^}xP;WCX#sZjbvq2M-sJK;ik&U%B}VJ4+)}HjS=i0TvA+%;qg%CfNF%B9h3$tYPo8`znq`r=vhcg-+GO{$RIA~C?&?mIcdhokVZ9{Pj=kFMDv0&fX89l{R);_s8z^<3_-rqK%JjL}t3&5fBwgOz zaHppBc422V4Zb&j>h&(gF!?%SxWhfEGjIVS)Kp6`+!VgNy+-3vcc=WzrNn@H8;|dt@Skh z!g!6(8U+P~S&g^n!Y{3`*_GMh>A~79(S2$E?kU-wVWM|Lciycxcv`CIoC%=Qr8<*x5mzk zjfwE{PhbT+di2Qnr4uKM+qG+yyLay0QvSz0C(CoW63zoy}5D6muO>DgNP4 zL^9T%_`FQDKo%uT$uO-x{>}bp3^w&RQ^I?wPf8o`LWF4dbuzyKEC(%=H_p>Lge9fq{ zGiW4f2s1Cdex=?w&|qKN`81?AF{JjfzvxiM4rZx<#|M8g2ZnS0O6ELz!mfjEz2;QC z9(g9)TaurFIVW0QMV#N9OXZu&i%(4S+}hk|Z&x3^?dj?1F+Je3zTz?(9!?iB@;Y#5 z#DSCi0s_PPAH>HG*7|J?yz=7Vk8B$)yV5UxbKw`e-@40GU&Wg@3c(gD-G%q=-BXS@ z&|m8(cldBtD?e6CAASaVpTH5*qVgT5{=jB_eqQ^+h3J2NVduntv1C*dWN3Q(R#8c5 zbMec?F9oIo0s`6tN2225;vyp>Cf8o^j1w)_Sv4E z9`WyL)%UZnIYb{0>+tIaxw%n|k4Wae&{naH z|72>?w4>bT%ez;h%2qVBA>%W>vNq1be++gR&r*#frZw*{QR1%r@QkRmlc?U`dpBSNegrHnfVWLljGxj@wHg0nHtIX-j9Y*Q&Usb)s&Q! z(YAO2`g>Rlw$|281J$byq5FBB2CEWRbv17(nM#@>gsCf{v(Itudp_mVtq_>ivuQ&V8Lbl4FIifOS-!oCCK9bAHMfNxh$YsQbw0JOt`6Pt@S#Jwd3njn z$;O6;J~IMJ#E;HBl-^jjv6jC6n{m&csDy-Hqoa%HgefVk)YQ~+a&n`VSFT*a`b3BI z-dwME_|Wu^2QzvM9o+>zy-Q9`PL7UV!;SZGkdLtBz7|;p2L}^0_4K;2;m~-9$X~v{;aZk@3cO$3cC4eMiU1^78W9&A*fQTwKf2($dR4#iN}$ z+9>P^si|_l>o+tsH0D|dsyv=wto6mU``ppt>gLwo+WHqaA%az^*nP5lx@PTMMecrK zldrXYwT~W|VgFK6QVtIf`~4lyYHx4HONi>;hU|@rjz0HDW_*0Sr{daAX%&`hBh67l z#xEnQcEsIh7N4jKAQ{F*J#pO$C{$5V5ub+ABW+&i|G~6q_az4hW>(hxg3WNxb3flD z+ge&)kSf#<5H%~ZY$&p7dL-j%>Fg|b`P(tNCiLuF(mwT*7M?fAJ8$>~8newzT|wbsjpO@K~=_<_l_U z1ewbBH~h#>$8-7|9r`K}z+^elezr-(|6{M8^Zw}O`&OqohzD!z7IvtF?lf$uS|X*m zeQQ`Uj^O|u-z;SuOH?y8`E7;AGLEe53H{Zc*IwTuGc+{BF5jBSd!8iW{9Z)e#bwsH zcH{cr1^ZwNg9lxmoeQ(GQ7zUvIXNS5k`|;6u~hqQ`TF|Sm+A3D@9V<`TPY;vEWv64^ueBS`}57cBVCn~lk?oUb7W*>EXSShhK6$Q zVdE2|3!znTt?(YK@!{j+o0ypR_U)UIkrDf*C6SEZCMy9eL+3$qQqtUq=q(L(b$*W( z9br8+G$5GBVX^X&iXpqeovDmtBcJa?z~(DktAw28QcPzYQfM%24h~N=mT7 zP>rP}B~xU5Zhp?xXf^i2DLOg*9`xJV7iMJnv9UV5BNTeM<~FXkbuzOf`S`6Va@4gr5Bt3+ zmbk`T=J>KeZ5zEAi5U=&LN!h)M_giZ^aUXI8Ef|AKBL4JO>0i9)*OjXq z${@rn>%D?j6fb0a$=W)IR{H8t9jchh%9kAN!;dxZiq^PKGSrIGdGy!#7@L~*x3=b@ zaT3fgU3zKv=?Q9Saza9OULLNp?`UgWp58MGYUOxLz#AJ}dV-w+8yyI=k{YE8rMX2p~&GuWrv@NF#A67Z9!;Mmz z6?XqVQCfO>&{nN0Q=DaKiR)-9AWM#R){dP!=UIA69rqI=&#Pfup}{f*hupn;)y0L= zMn*=4=70=20f)tZI%ty>;uBUcSL= z30kt9J3k~#oPVr|j%8R;n3FROn6(;dH8b=Y+iG-ll)Yy%E975KkILQE(J{BMz+-y}^X3pFOq!;}*O3!*&5>`;tvFu;nn6<=VkdOp&o(`tp zzkh4t^Lb!8RE7Dmciau#`G)l#wa3K8`+@LMQ&UxV#6(3CC0)2erV30dp7iy{dq*BI zDW_#)(~y19-QCUO)ZE_wd$6_^mGSoNhUG!OizX)OdMzz2Oy~v`6%{o#zQDc*WW5bm zN7_c`bo2Bos;V6A?ZvP3-T1pO?(M`E-{!YI+oW`#SxQO@OKWQKdF@yg{Qy26w`#|Z z9k`|VOI+J%T$u@5_%OJWsOOxuZwvaEQK>^lR+jChOUJJc$!}`hro9&)J{DRENb>#7 z?U7Q53a?+k_W8RIFfque>r0y3`&8kvh?tmB^$pRBV`$vuoO9=Dfc{X478e#++1S23 zzxW{@7nNHn3}nRaTgmv7Goi85KI)HTq-11L{Di>BYUp|1t>;u7ncB}=?kZ|I>Tf8v z##%7J?z!|wQ#wp^r@!c(J(ePk{)|H+<_sb`FObFR`mgX@&7cX z27Dl7d^Y0(UN5{!OhQ6NeCqo3y`g2u+i;^L(ds?@h=x&AbF zWjx^2xQ6a89viTna_G@pA^!QHp08iiv$8H$y6qxF$HV}lZ)|L!%f+>oprD@#WuOaD zv~h}S9TM)CFsPqA$FEs2x!>M4G$cgJjFFaBo;c*2bM4aEvuC9}rc5qen8R(J>aW^w z)dy%_Jo@qD$NMZ2?-NA1#l$)W27IxV(^IdaV843x%0N(W*I34dad1O{h-*ONRc_g-Zt%*6{W%DJf|rH?zbQ*2Lc7neX+0 zjGLRAt&h2#vDMJM^$SdTfPZn|=sQG&cwy6t{;FlX@I`SkL7taBoLT&A#!(T%$mnQA zc{zH2+{KP^m*Lj$9_s4t;3qk*9M#zOCMdrEd}{H+_wU{_1hB| z7zk8}6)l=wQBfv9~)IvV>7+gYGUb|$6@mtiH>)(hVA!C&m|}gYGJe`8m{x(@F*lD$pXe@GK0$D& z;2jDka>`RlR0MyPZdOxVo<~P*&&kWnL0E(Oaa`*F2_ z;L_~u_r11#H9pByniMoN=gyurLY2KmMkza~bu3-foghvXl=tj>RAE&z<^0|)s|D}^zhYQ;4M*gFC4!pZpkdOukEh!lpk^fvFmzS3p3rjL^)y~}qR#sPCSP#>ZukglFMR8dX z%Z*WS1PKZXiXA-){&CsdJZ+ucUdp~Rdk?|D$cUIQIC8!1+cs)j(& zkxD|9#F=u6i|a~hG^|}GK?mMLh$KB6hgQlbx1R6QJ~ub_?d4@~0V6S&ilU-zSa!|T zm08@u4Vc2;pU}bB1;{1t*iFB0pP`WvDhCBAv5${WoJK}^`c>AZ)cT35to!%udHnS0 zXP^um`})T6AYo>@b_=(93GMq`k~oP!zV)rQcN+T{eMmsRU}1iq`%m`Cn+qx^8{R83 z+wxI2-QK-x3*+}PEUJgHK1u01C$A0>LewF@_x1I47nmAL zZSJAC4FRL;)2G?x<%s+D56(t}hli{1T+r7)ytAO7;GDE>?$ztp^&XiI12S!Ela+2- zQY65$Nk?sI#-&U~`J`P(?q+1zU^mp)*N5J__wq`At4LU2V0{3|&I5A3zb7a2iqE1B zsU{l(&V(O0nUs)VP_M>8PtkID`&ziWFX93Lils`r+;VGq`SK+VIeEFy+RNw9oz>OV z?d*6f*32y|_AoISu5v%u18BU=MJ?YWb@iT`^{1=G zG&ifv`8;|0^eEW$LA3{+bI&&MvYR(;Hs!s zeQ3x1mF|-u`6!2kq3LO7E32m_=bfFW6A~CZQdG3)7te6S$4mXVm-0reozDRJY`9fR z^$zQ1)~vam$_K78l8k$W&|1=(hnX@b`wQ+kwy&v>lzfL86bu zZ0O^ubxSlZA)yYf8x^S<$ASHV(mm&Be0r6iP4cRFXSNpl{c4{o1u*w= z)SmvsU$?yU{f|AJea6(9Y{}Tz)g{i&J-j&i<;`CH^>Kc?%29>dO?|i=RGy z;tYJNGP4>5MGV3|B?U#&G5aUgDTo;wt?i}V&V|HaeXj1^CG zRasVQeg>p`{a$}3rsgTXPH>%Xbd1SEoGz|D9ijNei&Pm;1!;-s`ol5m?I}{%nj<-d zj7q*1TAY5D%=K_;V0CPpd=AESe*75O7lTgga`kFqS=nJ9vaYT!G)~nYpK+N`%i=C> zpuu?Sze3%>eml!U7k8u_%`PT_KqztQ>1b#eegE*pg&cY-Rz^mZ2S=$sqbwe^ecx$v zT3NXX8?n<{u?roPlkb9#k`fqLXu}WHm_xc{Pj25P^Q{ zmuJQjo6h*zW#-q6d-s0EKOfk%v@9gwxn_ap2OV1FD!lZZO1U~eguIn%1vycmmY z6dkA@yLRmYYj$^YBlZ9K)gXxWV$t!--)1C;fBg9IcX|3ltuC+(nCSy_q_#9+0f9EK zZ9m9_wZ0pFiv*J&JXrem>FM&j+vt}469bH0K0tp~_&c!w)2B~s*QToS^q&JAP(iyH zE=Tv8{#q0`y|Wjna-V=+dS)g7Xj5Zju%a8m{K}Pr=p(K8fD$Tg`ZGR!D#S4+s!WlD z-6U6|nJo2UJ0N&JbTDt^*hHg^uQUSbT|6DUe9UV53jw72SR+6bAjfU*HiK5-#2 zEin^8`HG)JntB583eIzzk*_W>2%9j{(M3b>ii=zP-SYxa#SZ$YwDh6FhhtI`CRzou ze&p?^7R=6b_Qpjuz1^;m>20Wf8osw}M`4ywYn3&Jl2+vKc zx^r~}Mn}^lxGLnKl9s%D`M$Na68Ej`fzYZL6el2ipdno)WnC_z1H0K}+nbv;EAFC+ zN2~Bye`MA<${Q{?vh$k{qpj`FWC`cspdh!YzE!K2^QzE{ET9kGzI~g9hNiV_pI(7U zPDVy4%&x*h5i6@Pz`)^Ahh z;K4HobzAf=`rH=vo&9lpZeO+QKo%rAhzn@aUeIoNo(Wi3SUj#~6A;MD&leO^)Tj{p z*3nVi>Jlq1J?Akwnet^8^ zXO_`WoSjWh2xphW$FHMQo(D(#^vMM}h&Q|b0MMsW!~vht)_qo1R>sM!nP6fyh{rK_Z*|QBZ zZYOltTp*|AZR7+&8$D76K*(dzXaBx^T{t<2k-vXC<>*K|pyEAM;+rnZ&gMOGYjF`f8;u#W;Pm98%G&qtGZufGY5+k)eq8Y zdAVemi>2igMXo&|iq_dJC%C2KX6VNMmjxJ=dJBOo7>mkvq|s8&i`<82SM#P0j}xZ!Es!0@Q70fB1hbVP)>)mG31@a{|RQ*sIlrY0tG zGcw$<>PAMaN5y9U`6Vqa&AuANy^q6QKlabpqIyWRt8=65FJ_nNn3$w4e=9*rV%WFu zr1y%Z=ND#X=C=4FAJA;io=uW;`SC$+D?j@6ncEK@z~0^Kc_#KC4^L2V@Z8C^Jnsxt z!S#)eS-&&N%G^Qf8X9rNc2FU(z%@1jA$xGBe-|gQ#~AkPc^$7cUYnGh>e3w+?^q`L04D;1YORr zrnTkp-UCL)#;^`+Aq5^flxfrU;HlF6T$xb$R9PQuF~hTGTX7O#@AA?giTx834Y4&` zlR3AfqYs3wyXNTdN2%m`3*{kp8Skl<}J&CFo`E<$f0CbYP@PxVQ~9R&*N&DP?M8&}f3nXsRMLGK);I>7qO=-oD2 zZVt91gU8+!2@;eDD!d(CwAWpWRtrMcN}kJPEngAcn0#<_?dH4nM%#0KwLKfg?}t78)9wl=KadYmWjN9amIyZ*NuWae-0$T`MPv3HB_f$la$0PI7Rh_4+NMv!Jc; z>*A7A*17+bR8i#_C8Vb$*{XSUYva+(enftV$-xq`8pkJh1e4@$AAY^21XJ=sZ;iMUAJc#2erAdfl z$IHvcbPG4VykcmcyoD69!Usv}88ifW`8trelTZ%kTuA&;;pg75El&^nt;{@*kJpE4 z+3X!=E!8N=&kyJ}+5hvG&sEmtcM^kczrW5x<3o5OjuL(DFb9Wx#h=-E&WqwGF9&xY zF{?JPDS~40Na|Yf7H0*}@tr%$XjnIIR>Cs$_O6DY3mURC)gMaBRj=W>jpByvzm@Po z8;O7LpwSujeDuIBf~A$!s!DrXn*cxmy{$tb_wFf>cgcnnfcwtT9|9+y#vdU0mRbJV zJ50gfcgsPSYpob|yzx)XwTOV+c**cMxRii^<4|1yA?@+w@2Dav^4L^DXpui9-#0b= z#7%*($kBF?E~M2lHY!T1;=Jq`EuH#cB6%Kjk*4T$UY;Wqghu~UyiOKYC62Cj(VMrN zUq%0w8vH%&D)4didRp5bqAMQ6rp~+FDtbPQJFwKtDL#Kdbd#{JL!yZFQh+u=kD%wT zKt!Q>x-Ht$)^>S)_1dPTpp=xMaC~o1Pt+*S=M#oz*F5JUr(NG{<#s1-RrMdyf*tE)0m-M+KfWJtJD`s(c?@?7V|((equ zIXv!~<2r=O47Qd4kd9T_tTO~Ghd1XwTDa%<@#8ybSQ({Ut)a)OsO?WY?Ti&X-Fszh zXsFQsb0%6Y74xy?zP@Sz00^qW!ot_DUpK3|F2r{fY;YSMmS24hU9qJlWVpPuQzvW| zoqzH3dAV29qR}mk&=QA-hG;=CA3rWatxZcy!%+ZiyzcKe6=aa}`Ag4Pa_#52xY*>< zz8luoBY=N^kT_#w8_|x)C)IC1e}?JlzVUYv>tcR+x%T=WX8WAbh={V166|Cc>7n2S z=y$?q)r|Z1D@R9>-+ohFU5!cr;WCG`EN}<4^o<#goYT-a-dfJWBY_`Hf8fBm##?%a zMMSQ`%mS%2eCd=}S@{wjDXiMynu|7 zVPtF!wrkwhOf<`RNF%5tlOSQB)Ii`{;O{@j#tICIZmBDzY4V;`Q-f|)Yu!Q@Y(c{+ zDSy*PU|Qk)`46qF6J>+GF8~}Qry%F}&W)hFh8PH5cX45oxiJGH7CVTSmVCSZn48Pj z#6yrD9v_#2XQGC}Z$=_p{{G)Ik(z=n_`e)^In@j$X-NZE9$2obwdp zgD6Zsa;Nda#hADjgC&h_wJ=d)d18yb+7Ao&$i9glBr2BwbD0nzVosW{v9%>nF?xab zyMGMHC8F;VE^Jp3uM;%akWUpIt~-GY2j3lI;()p@U%ud$!kL7}l>^jCP5q}MBQzr; z1IW9yR6J341(p{gR=W)|D(6_60Lft*Ew4@2et=Opr58-w+S0-&Ah6cF>xpCcjM0Rw zyW<7-+^}n4sH$F$mX=(*?x+;<#GSRWw4|pX8&_K${A>BZ2A~SsWsm}A;^&lOll|o` z+(JV4hb5y=gp(RsyI;I`5qXay18;0Lz-dTR9m}dV!&LQ=D#fsVoQt{DOiWBrG?6jD z(UB1cpr!U6E556KoP#c8!11O3?&0s>U9gTInVjW`#-{G+>3Mdsb`wiPyJrz*BMiW4 z=(2J*7kH(lDxj7fKL3cPf_LBx{3++Ya?u+z=CE_1f` zdQ#!f4*L0RVbw)CNkC`JFZha0MzRS>#~A9E+~&$KY5ShF@LN&xl3}LUE5nB2l4Cb_CnbKJ zp%xDxW4n^wXO+63q45Wwl1^!YkApXW`u$ORM8u0 zcI}e#UMYO`><ssB^u zhzH=fzdl(Z{svR_9n=r_QL38-IA}0_AAlyx{le?m)6kGrzYzBCk4LGl@m>|fxg0y@ zw!SjUQ)Mp%|LALPZ*r=*u%KXjQ`2Ed$pp8+u!snWW5;-M1OY{yot#+d={p~v-ibvm zWMMSq|#z8AU~t>KjG4Ksa9m?EL71YR%fV z?^DK6_7Dm=jD%vvHQSEKxN`5+V%S=v>RE7%shNmL2@E@u@=cTu{o+zFYTI?vh(c|X zc!QNU`jiEk3coxTnG4xnwX{Ogw+A_dZ@;;~8%LZ+5cE3{er_rAes~N?7J-P8i&B2t zM|5e|!Gi~>^~CV`K_4Oifo`eZn#5mXp5F|%IDVy1*#?>K7*?xFrs-we0 zPXT2~#-^2iGjkCOrtESr-ookfWeuf2^F%y3by!~SQ>3_Q$>}N1AS}`t&X(7quAEzP zWA+CiEi)sdphF9|n+CFe+GaM*#veG*o!`0`&o(2YQ6%o||5p zmK}`DvC8Sw+(}ejg9LtN26xwe{V>NYoLXE&iy;%R*X!5~j zqA1o8L30{fLK9Kejb5cDB0a*`=4XfHR0bIu5r5&GPdLqwGl(qjvK2>&V}5CAQ^$Dt z74)Z$3^kv%rGuWLfT>6)SHVp3C@|2{di_&#oxtubNu9qoI?Q6;NzN=4l{h{)yx7Pn zvNMceK=$@bpz+R+ImffTsv46O4~=lz?*u0KgCOCJ8#f?QLStuTWu;X>wMSH>JwX&@ z$7kbjX{)A6$HNW)(t%fANLe~`=O+dPobvVE1c5{8P}ds!{c!EbIL0oT$9JlQ>!%TQU_7Dax0sT5m>1P3?bxUn`+$2L`u%fgR? zw+bej*G{B)FhG8$;fxz1FF*y;DOV$&@a8_|jK9sW!|1+-o0wVL3)VJQR+5B0Iflu< z@+i*|;w|_%f(X~@qE<9!mv+mR+fbUeLZcYH1JA+2W#*4&9!>?a)6K?MP7NuX~N;-E){f)n<2zvI7hk@Ms3fk=H zRx9tl2o$%8t@349j)Y92m}~3lK@T_>)6)FQ8*Cvmk`{tD%*0>|l?yw?3|R@0_MfX? zy@LH%iyIE5LDqMEZtm|=uQPNWT`pca)yj$rCKi_3n~SYr^^J|0uCZq(KBN32n1hn1 z!V`^n&ojT)I%BluUqv1TU zl2)fJ7cKcF8Yf`1`$VUznpz(2GBm3#9iNKZ3g2O~LKihlPMcDrM^?Vqxpwx|>ut=! z!5FAgw=m;WJ?|<_vX5Ai!Ig>-xY9CiK%kM7;#fZvc^d1|pUA~u%71H<+lC#D#mB;1fEYOk-i8mRW<-#9?TAtAAh46#^Z zKBOOTS>&M@hXKl5ShJo#e-67wujT#wZQst`e!Mw~8->bPODhU!HaR)3px}zLGp;$c zZfQu=K}f7%qq*7Hh@vZDsSA8L1tH|Dl2WiHZ^?@nFN%u3t3_+5W^`b!>_1}GJnD+O zg^mG#JFX}J_1MP7M&WVf2hWf4ky@dpe5;622-s?;Z#JR&a0JPk_i_I-SCUHFSZ>nz zzRz#b;!1^yQDe&}m;3!gjncLE%l5}P)l)FcR-A8^3nt<8{k#!vwe^`LMrMSbz?#h& zNtvd_Xy@rO=Q$y(z1;S9PVUo2v<1P+P9nkUQFFS<$doCeYHaAo2g$zyHM{J75iB?7 z4@K(B8}6|_IJ|R9!H6}w=7;=!rkBK|Z?@#h3+SRJpXBG~7Z%c5nT!e&Gg={CINB1g z@;0MykBa9bLUeM_up~A0eb{BQpHH63aC+cPMyr4b#vS!TFJK-Fk2}(osBks4wzc@G zQrSB@U#w?Ph35_9x z${fK=A`z6S_167?S+g_84m^>#7R=E6m3fkpQ(PmAF;eYuHTm0$$gPy`Bjf~9^3W2^ zj~5EvZhZcsPfd3Mw`m_eeW9SS4b9Dt>8OZfjKQ3aeCK7&aK795J>5rdt6Oyvty_@4 z-m`yylSq3c=V?slynE-Cb}W@h&a1q251)Q{IoPD2_jmN*`HKr2hWyNg+MjP_t5ONuP~JQ)8yA! zyFZ@c?jFic{j@P}@Ve;k9{RDgA2M&#Bv$oA35{uWf=U!&w-tg))j!p(4*jAm&%8@R zfLt<-WEy}xiGM|fl>K17>qJHC>xenJxS{)7RbyYnc)rTO3PrF3>OEK;e4at3PrCZ4 z%lw36DNp|$85kh5YPX;jY1UL8)>xKcBIs3Xc;pbm$?7yS2`c_`JL~uc!ZWP`NN8gN zuW%&zWRsfL6tZwC@2USWDLwisif!RNV)t_gdefHjilu{Dy9m%O7^kU z8>4gQAIbEAoe^yB1mdNBe?5NxrC{35j-b%22HiX z4JHDI^KKM$rrnK!MCH;6ulz67@-XYEnJOp{E2!X|>F-r}?CtC8hkGcRCr^w~h=7{J ziM3l+L?2JE{JMceDm?R<ti47C^Y|&T6O;XSf~&R2+bYTxgBSAK+a(8L$xkBk!9?r zkNIKKU<=vHYhTwI@2r#V`ga}DcMWmYF$UeQ^ClzzOOik!mZ$ir=}$vhNk}5F?oit4 zR&{IixX9GjC81mz@%#tU#wU8GPFDV`ZU55I?RxcU_VmPmGiaS#p5#R4v=ad?B8+4K zGW*k+ID9DPSmhnk?8(Q3)4mfElMvF5hJ6VQj^jmS`rrAMU~((@6;rm22`;I7#U&-j ziVRHRz1H)osHgzF{t^jvr2TiRpJaclcix(P=juircvC4*`hNp|*+&@Pef@VX42@}# z?CG`<^xqEKAGX8&zmtSHB*NVJ-!}l=UfXFZ|LmyY{yUC|`ndS`-rn9X9zp{jz>_AcC_1wgt1bfj|8u^&Uy#Pp8q{4G zJoW#(i2v^!V6%st2pUzTpZA`p?nPcPUaw0VyB_BBQm9RA9jv2o|iJ>xDP~G_v;n z4twnWOSZNjpy-1MgIckRJ9HxF;qBuyS#HT5R(WM)Z z33VPhsfk~|I;*=8?0}Na$iT3J0QnWNZES6=-^0#jF5X= z6b`-ceG(V(N_Yb^d#p|G9-TDy+@2soAY>V|JXGWW?;fb5>!(r1AlZR)ep$DQfA~;{ zmc0GlJ0f#rP66!(eU|%epHF&4w~b;hqTFqqPD(!5BIV&jFovl=e>S3wH@O0(2}o`- zAH5XTst^9Xgg>-G~Lwt3x=Y?&?g`CPt z=~@veMHUHg7Kr7cliCw!@OOPt+d%+DG=6dEJ|az!FksvvI)yK-0mwp_?WW~9CVwHv z4Ph4uZwRtyJ*LDJSj=FA$==9FxH4GtFglu!l~w(LU%PV;k`=)JNRces=YI9~ym13V zjC%=^;cd;$lI{~a=gz$cG2Elj^=#YMWMg~Ss{bw31lfB?Ww7`#K!qG9Bx&(u#~fb# zfac-5IvNiSMdXjF3knAk8}rXERhE_}lgBRLc#-HH@t&V6SdKiYphKoC3#Y~BDz@+ z-J029slqq|iOKLc5#B1k@}m17vf-OaJ)b{ogAyWOw=K|OmG@N@tlpXdz=w=Bg^$qG z{D_^2oD(M9E+h+BVCWXq0wZ?D#=S#B=9sG>pgTb*mUR22sjeQT!h`5p=T7}KHR_4n zYY2Pd+C0awnvJKct1ITAf$p&{U>SHVPUs%#$~y&`0Hq8rD#8r#W?R;F5g@-KQojTl zTuE6O1}QTEbpVUIqw@DBe4y=>E51M9v&m-1sPKSpLjWB44hnHdJwd+mqMm1Nyr5wR zjLy06T1W{nJA$d0=DPGR64hZ~VtWR*?MQ8B&-ZLeuxC&t#J`~0WNPvPncoTwG&M0H zq+!A$LT>X095W1&K>tT}CoXPZZ8x+^@J754D*H5oFa-F$2tOd?-CN~xq{#&>e{nIo zp{WUSCXy05Qka@9cY*0(R_m*ApmlT(q`#@D32vD5i4(ioq)X8r;Wzd8Ns$=S1yWMB$D> zl}G)+6~@rS_DG|MNTkvWR0V1Z1T>)9frLVGMVcNvx1jH&n2^vPh`RU=segKUIsjMA z&Be3l&p*e19n{P^W@eBXR#Z+F{VxmPmzAClN`@d2)$*4WtiP})6raCk$f`Haq~Q>P%0z$-yW=&c9u!{e8Yl8;};{ch%=#+U=d;lrL2 zP$99Md=@9-;Mx08*A+YAMBoy*xD?&L{|<}*p)F!@Gg|&C*U@{Sp_Cyth$unIhN6oi zy8K=*0J{kZDqM*6RJqjMV@N@u+CUNqarA~k1l?C!O3LRe-(R^|uPrFnh!6lY!9yv< z_zyybm>gI%<my!h{>1o;mb+Q##PGp^o2;Rn^cS^M^Vfc?Y1{9==3B>lt_0L(tRYknLsnT?rU2 zN1i@6sfW_DfB$~$e3--EVLolo7#R-CM3|lts(lNsABCJ8%{_Fin&X0zG6cZq!w!Z#(6%~#))gJ^D zO~?_j;s36!&Ho{VVh<0H9j+#HhSZZ@2yL8tFx~UwCz8}i|6<8RzkIm_TWxjLtIL#@ ze|~1>SAUhn7AD0(fIPZWhs&|-iR;i&1SCEng1s<3$WB0)n~}90m8B)7xEvt| zm76Y@Y+z?kg&+<`JvrG#7^bBg!f}(E2dWHLZNHj1w$ozQ^cIKv%?sm`7eYwI#Kh1nR*t7)O#hid5#~L)j~v;}iD5I2C>1O!QzN6lw;SD&Q^R;C zfe=0nXs|s|3wg-a7NKjIlP95vs&J!%t9)l~5-H6O6mf~#(VRBtzkCuCaZ2*x7{!b$m0VCMg_GmwP+Ce(i z?F5fn#wYm2C?Pl-pBbjL3xSobOvi=G08 zHXNPRB|UT>Sp_?ed|dzhd?X`)Gw{>_d<+U^=7r2)?LQ+UJai%3A?DH1xTq*mq@N&R z!o1(dz)&o>R-vs~6O)w0>^G;n`$m|kLN@9Ej1ddQMp!=x=`HxaZ*Lboa%6dCW@z{4 zckkd-s;zeqczhlCi6Z-s3~%fN-^jt?Y`6js9o7W~-8)tP3_MFuA3;$Du#$sVi{*`h z3RT~dfmIjm^NP9a(7IQO+Q2H(E6ObnRG~=Y{>Q~5(-?E&x;SfTs}fN0>fpY)`N+7q zu8%e9+oZ$8>=tV7afeR3`e170OwcZ;rl#bBe{luZVx8|UGtZvc~hrlWSmht4iTNoNVdl8Ye}J{(NS)y+~xqyT^B7-{6@U5Ad@+J?@gV_;yQznHrZ^$6Gqqf-!;F{<6_ zbHq`y3oz@`C((lk8|vy{er`W-27^)%(dk0oe*0EA@cDPHx7M65vW?J0u*@)-yPd26 z1VD2+!plqWXF7U`H(p0udw8q+%a^VE(r46&NW#+5F?$pni`kN=L?k<^Z!Yo!@c~@4 zeu$0=peCvpk=s~^dHQr1KxjL$ikzHvGi`Q5li}!fqqyfz^)lB{Y~qgWl)aSqJzMt* zHGl1L^9K^SpzUYpI7r4GVTqkxw#G0mp2(4a=R-tCyMh30_X8dt#R{U+*u4mIk2;VN zu-D@Jn1kLNS-h}4$ka7jC{q+J+kLDju5O)`=8msA-? zw9Q~}(NLSW?JUGh@JNQa$J-A#IlcL0J3WF*-SPRe?v+pIMCZ7os8}U6!G~H}?BVb) z>eeUOJ37WJ9Ym@Vt4!(4nbp64(fJl0vfIYR7q{xa3}E-N5oZ-=qJNJ-(~m8LV>X*p zccRYaUV#i9DYw$=xjiY~(*xVZ5y4k*7+7-G5z`q>!`?*fH?+ZW?qq6wJf?Dr_HZlV zISB)`eyJbwRD+*vM1VNsF&$N2%LSWTR?{{0NtFXT{c9z5sbxAva#C;!Ke%hG!>XIV zS5aVt|BB(%r|wtJSYm%1Rn-P^Y#V9o7Mb3m~XLc%jWYF&y2x&dvzf?C0`pSdBElk)!Eud2P# zev^pJ9Ni1l+~f3eDEl!vXHZfS!Z6KkZOTeYzMJcy&#Um?4t>tc%|*Xu*uQ@n%p4sX z#zfS|+fTAl#f0PYFb0X)5mwd|m>}3!W9>|c#^EIwLQ%sXAeV*Vb7%q1u8h`rMAXX4 z3eXfq&1E$@^l17s4Gj%Za`s8Sc#_F8vzV*%V;W*3yYMs*GQvflptD-^vbM2$}#O1`OG*njc~BE+HS9nUOI*Ir&A$<%l*oE`aedjV}vg@}gDWaK{Y|S8-Rc z|H=G8vM}}gqQ~h|k!3QukkB(=6Ewv8hyy*rWGrt-Mqse*x{Cuzzlsk;D4`Q4T#SL408=b%s z%49Ns(eoR`_?g)&X&ISqSh*?&p+8em zS*fI`=+yi2e*Je~)4K~DWrc-|NIZdNVR)!H*-52rKj`hY4wRm5jrT>DLV5#3aNp-w zLE=X?3)0d=Fw6q@ctG;1ytei=yaM!GsWAP9#_($T|Fx-q--3IpOvJA$Kf(Dq^P0~kl{ z_qj{=B9HGLPUapawCX=&a-El-A9Gffdj>xW9Y}t+G(Ud=&Vso)2LS~PAX?Mdn1uj7 zmY$pY7kN~l&GjBs=`UZ(GL_Qruf%|VT>%QfvI1G<=jHwQ=@S!(7LdHE7YRe?cAySV z%z=|D9FGKc4gPtEg@7e6c8A>;s{zA7&$q_=s%uVgelG)v@dnPuP~ELtZ-7G*zvy!W z(K;w%JM(Z~A|Zq}7}ff|8tOw5Ceq@*eG(o3Gz}txwhA6cqOJWI!a(RQ z;xpJ15e=3IGW`V1%FWHiAlY`PCS4i|6PB|l_8E}Cb}z^J*42H7hdJcullr5v;5k@Y zb53`kFz4;F#AHIk9&^k_cHN7QkH>>nFgU|}f}5OkpX8JfGwE{s}+F=Xeeu^nTy(`+mKy*L9ued7anP^H}lS zZez$OkZ9ub#>JJEm2sgVb&~r~A%fCwrUyQQI>dh9524qb;C%V@JQ5J=6DJ0+0vg-( z3(qNX5kQO6I6eeGBk^i|maEG6Q~bTjf((EYq-ZET<>!jFj}(7&6W{N31!NDEKj#zo z!GU<2(jfe)UIVlR=yL&uj;lA^nn1!^9q+Jz%|*OSc5dg3e-sP{hf;U^eOdf&C5U(p z;)m#s9DD`k4M0&zmZQwX8+0A;Uwo;2Il=n}*SBG< z=+zU(rhW$wtWR{V?mFds>Xd~eJwMc;vrDUxtO1n?^lsk2bqXO6gs37HU86TXYc!MT z&J!L_&N_r%(>wxqJ)O>X?^f5>CZ(;>QR8yzl%)e06RX~iMb^|o?EZKo(BqBQ{i!s!W*hZ*zOF^`P__E z)*NJma_`NXH;wHSnz=E}4UPGWD?6-SY$lbZSbl7ZVfJ4+`g5wm{iE3{smJpRUuOmr zH&)#5Z)MGWrAZp1V>kQ$<&cZ}&N{z)?pp_9>0?>w!tNCnNsEg9=@sUb012$5XJyULPnKg4W2sF^qNbK{pjP##{?b^Rx~HR5)a~Oxe$_osZqaM%ay-uY z`+n)p9F>)PGW3TJzZ~RU|D&EE#-8InO67A|OK(eS@+LB}-3D9n*riUIgb~Y8OO94Q z@l}EpKGRcotrr()W1P(xY1n}OJ+|NC))#(`I9eF~U0<%(-aQJFI==4!<=FwZ4DiO1 zQp1h39i2gKsgT@lKFEYc@m|i-(AvV{BaQ-`miCDu*k{GX#j&wwS|a^o5djO;VO7g} z{SF-7krhVHLqnUFbI8m@YhB>gl9Ct7TMezNm!~cBukF9e&AeeRD86O+Pgw9jiwnj{ zyYHWj-A~0Yv>%Tihu=G-IbB_`){Ib_<7-+w-R6zlPpcrpJw2XsCGewg)Pw#Z-qiWE@q2 z8`VK^XXCYNdz>Pd9ZhsK35?mzRm< zRi$r7sgtRjo7M?|b#>0YmxCYe{NA@EAb_6ZlP&u`D*nTT*j;#7y)3D1V;+i=2pNGt#NoZzPzp=WM#QuT}sH#xa(h zB_>iN7<9nb_c@?^l(0ba3jmOQb5jK>hK!N~w;3-q>$==tay2tC;R757O>*~PDxODP<&ci6>hM6uF`_ zxUZ?s%?DF%L#QFnC|7Mmxm>i!$04Z$e4(3cuBAoLfTW=>U%IhhvGRal2Lie-iTXyp zb!&4Nk#R**f28onGbV_iTem2$0OG@DR#H@K5|bif{h)ox(Aby|BO9x)3B3rR0(fX4 z${&Fe3|wW-g9j*)?>&2#6!NWP79DK6&kleYwT~yin&ZNA>*7d#1n~lZ?4jQU?pEI4 z1RMssBGQ$&<1lbRh6~C2AN2C0!#P?b;q>7%ZGPp|mZ_pAz}-NK3#W^H>)B$FIg>#3 zAtvst^zCkJ6oFeveSLPj2Df21!rW$3?X#kX58Ys~2&A(9*^d`UfP|NV+J-$oxIKAB zLA`ajo!hzdpT-+`PDRcsEL`VAfdYb!1u%%*JO9^MV^k=+EZ=U%`Grh}mSuYy#FjMq z&I}_eSqakfVO&3~lf1xz0VKfT;okF5?=?tG&_@;Md;q5h0KE;j15HA3D`B+~L70(~ zi_gJPTLDzMvfX9QrxI-j9?$y#zeTb{VX4N#tM14VoprbFoHi`LhD6yZ_~cDs1>kPQ zHZ+E!8bRhS2q&Ud_zY8#c1M0odLJS;x}u4#eBnnQI|s+q^z>d-XqJVikx!viUCl!m z47e;T<%{3m8OHYds-{2OQGB1EsfDr-3X-rB^YX;mr)2o}Ahz4Hd$*ZH9+#S5QkK}x zAdcH`p1kp`dIvyC05ox{>>*iMb|xk!*oLPq_en`m=jYpY@a5XLk8Z5Z1Ok+S#4gq- zl|o0yuZdKoy;FbDMe+1N{3R68kV=5QR4ZVI^D&KMCOJ+qe`-g!?dF z3b!{9oyVYIOG_6}X{H2tfj|s{cZiuzO8v))pxT_E)t0|MbQ#+R< zb;LK+w8Dj_)&Y!T>GJHh5ATQ}AUb63K9??`j5z$t@XTN}AhGB>EAk&<5=t_-t5(&{ zzFs6GBm@L0TFJlK6-}+6`sug#h^t@TOd1*$y4k09J8pYOn3k8^W+Z<-<|$rVZuq9U zBEloulVwy^`T^0iN1yRDc;fr%6DTCw)A#C6nUfzl3&c#>B~M`IPW8ZbzsYwyVOwMM z;sQztcicOairpO@i>phr?1rO&xDhOPczA#}5Jz8UR_NWkHvV0x+;D~wFyqpp$ZhVeH^?gqdVSa-<2a}G^aJpi@zX;X|;3RIEhF1d9 zmkLl!^$iWamoFbvVQWM^$Fuj692Utn_Af;D9aIyXGPYO1p@G3ab4;|qwLV^w9h;L4 zgbiMnP{pdlco@!Er&9eMq~N2HT>;i&Qb3rLQt$Wb%1>KIc|*2OtOy;$aduXeB-kiI znr5HUHMg{yl*Hz8hO3vIU4*c-Mc*)P8KA`h;Q4r!w$@e%u5s)DS?5Nh2A(ngNBA}4 z%kNjU+45NsYQ=gZBp!UVX08`$&Icxod5 z<^)wtZ_!5^M)o0a?+fTt+y>v)l9%Odc2z)4L(AU-7zxI0SA+@FtQDpt%i1 z>BbEj($K;ZFW4ubtipG}akuz6g)?w>Aj!ZGEprk(!lCh6 zrr1=0ds;#tT4=@PmJ7VTC_^y>_fU|MW3)XuvvOQ4XU)V>b=0vAHX8vxfSw$o7}$U- zxCEFLAl-qV4Pee;5B%!iJ|>}$@vmOP;=+4+S{#YYb(+K@gBelwlhemt?5Ppit>q>& z=txt$8IP^$&WD^}@oz=fIbhKZ$m+k2jZIBXLiy1G>=Eg0Qxh7Jl!S!pTUzw@|5Al; z!RX+@EHMdX0dn*5&ucK{Wf$+TO-M8>5sDZs56q!ifKzh4e@&`-?zyWL@b_)Vf z+DBVtEXW_kBP}bu#8cbU(02?N`K=nHqu~&#&uzf%+pD+6IfX~rYXWJRpn$*ttQJwFzh&5{55I9^70rgaafBDA z_&d|^lV{KJ(9`3v+_zqdtAsUC_Il~=FYREN31r?tCmFp%5WJw1QdiGtet0tf>&VFb zSR1F=K4YALdV2ibdTSC(eI*Y1=tkQd;8cbN3Idj7X^~fg>&5oB_D)Vn;rwtkLM^p8 z^&5mrc67@%q?+*W&~g>wY=wm(Ix!^7<38f(0|KpkdIgmZh!!wy8wTrsAb4P*lbrDb zz%m^zZJMl8R78XxOy-fxfF#uGh=r|+g~fBb2I{c~5A*Xs6`wi)N*H~q@FTg16^cl3 z`O+m0c6I=P;%IFnTBJR|EF>i*A^XYC%S%)8bVR5^76$JSqIoN^<%o$1+!O4I&5Q34Q^jY zQ!{}zDZvo>Sc5paDMaHZID-=ew1n`O;WyETJ;4$IXMGt%E}WGueV@i#lLfmO24HsZ z7-a;6!E|9LiXaqX;Nc;R_+ce2h2H_dMR@}Bc>?ZnJ#heD(f@O1agX5EC^DRvo}Cj_ z>yRB0N|dV07dwY}b*-LatE1vTvED12Nnm+${s79PnvqEdiNhZ=r$^ z25bWq5n=>b4B-$25y`jnvx%u`va+{CY8$vm$fqogjH1deX!<{U@nXTJaxieCAM&OC z{wlh?rVS?W0WU5_iRH7gUIhv|u?1>Q(SB;=#6bOUMmAKc9jKJh(YfFv9-p9j=yJN| ztLK}2nzVugyU4TK!OZ25mDR(%JUvsULwOD33pkiK)fN+rkVqWR z)+QKM8JP|oDUW~1AwfktL&%owx>XoEw;07V%)K1;fPprGq_d+V42=X;R8(qD>Vn0G zdJX#lt#Y89KnW&UAa@SdhXO3E!46=%$8#&hY~ZH=?sGO|z|WMGkzaw*1Se=u0saB9 zHn_=pVM*Ws#&HTCv|}bF9_P;sI1y>grQhG1vzzL>%Kb<9RPfROtU(dsznUxA>u14d zLfk~8{%B5SNc)C|F*-_AMOUs|d2}}a;E~(bmedp!hSIr$XQbF7YR2s1pg?bCPttj)U#>SBppSXJT) zbt9PLYwOnQC|+?%P_@B_D^>ENoXw@o+*~|dM=T6S#~kyTV=uRW)rGGB-VHe{=?Y=P zRc4Pn26^yvtD;4gPkl!VM@vijmDpVeO+xF2L6Jy^i<4c!)5aJ076*s>_y(Gtp}q<} z!|R`)6n>ckYC2UYK47zD*(Qsh(xvwu>6bp9D>6e=s9jav*Sk79#gX>=FU$EZEn@Xc znO_B|G6g+Idisx<8Ji60hvXug6R24V-?2B@P!*=TbN$)Cm(2DE?=hrlJMwXq8h zkAX{HS@W{Hq8YeJA3l6oTv>q&7w!)=#Q+3!AijmVK!h?r%-h}tQ!#IEZ+Hwqu1d?J z3M;+eAZSnx@wG85`30cXKur&3PFGfc_a)K9766UHTC^6&kB5bXuVy&Vhv`|J=FR@9Z>OV!jGZXS_Nx zpd6z8hXl|~QFmvzT-ptFgR{@v6kc55zS6w)XN6Z@g9L7Sc7DWW??TIR0r%$=S5oYf zu)7W_?zc%RKij{I`?1?FI7!)i3PmaM?l!w(B(Lx-(*C9e6Y-|gFH*NcyfdHR)CfTe z#UM@REMb6;E<{mLC;qBkwK`JfkN1Hl#cvVl61}xw+Z6+NLr$&++a%?lAVnpmi0bc% z_c6XLgzp5FF&P9&5K38lWV}MDNmV4AlUxuY^iViM?4X85rrMTsigy59dBcAO&kmYB ze)eE^{JKHB2MJT|cq7@BHplDxyf8DV*()XQ}c) zH2)CHB;Kzz2gwtlRpcM5o(OYb~wIt9ygnbnpXLuSQ z?np1;0C(hwyoiV+%&thtM!n}obn;YBJ4aQZ8y~hj;%6Ucdpaq4Qws=cjIgF`+J|;o zd=)Yx;fgCQ^^`iwm-g17<0~h)gOC(wlkt&}t``^G=i2@ekBs%Gf1G1%Xeb45pUTQR zb;ELg3ql~6QyLdAuOh~d`F=tI?%LCQ?(b2rzijE{ee(*GWy_m4hC6DwiX3t49b;Cs^bL-I|UA>cU z(_cE){rYBdf-crNd9SJ}dbE#!4sqS{_>8Yo-!n2d1sZP4&jiDzr@x(cij?vE)@u{W z-Dify8*#~}Fa0XS_@zl5L1w}$`N@-_C%;(3>KOcZE56I^TkVeK_$q$oyWHw24zphI z{xe5ct~X6MaVWU4$zC{(LxT>Pfv!8`ZMpX?dhQr+l4Le ztEQNn zX~3ZJtJCyVCQ#o2rCa0}Y_0^3A*=xzFaBilBZLRrJx*P+S$JxaD zX>`iB5-+O0h{2Kf~ z^ak&?rKR0^6AMlro|}w~ht8jDVfj$SDM%pcX8$pH|Cc%ZVwp}#i^sz}dc7&Lpw*L}(fej5qUp9qp+eBjHc-8k@##1CWuh;)-Xed1sxynV& z{hLWIN1L%=Y&canw=^Wk zMZXc3oX5q`DIlP^OPWq7@QS|a*9#Z!tFM~O*KJevD<+oq_8w?QI90Y`ZPLdwA3ax$ zZj)DWqUXiM@07`1*uS};EM7vKjAUt%JeZ|(JCpxyeRhcF zauhD`VDaNMuvrxRtPH9{Rl{JzKS-wj9nzcx078ts37p6N*QKaQT> zwNWOE;EMn5rH8}IJ)N;NOJGI+zI?T;H(i@zS6f)_qsH@W-~abhUU>?41N+0DCyb8z zPvy6nl+STuT+iQsLBHI7gp);&ZAWu&yhM+ znt!nTiB}xlJZG9bVZ}(ht2=c4VS*gxL53TGI^7AXhwC;c|953GH=VhJ6m<;Zh00Tu zYm*8;Eh~3s(48Lr_dPWSMH#ar8nxx+YnZy34k^of%L|Jn>0}z5(k2LFyvg#}Esch; z?>mohRyRL31_?5<@$k3oe|ONoAphnY-7BJpX+`w7x%opgiax#BQ%i3&Q92Q8_vF9N zrP+44{7Xjo{?kl4F&TPdW!tSwK9L?(YoslP8%=DuYQ3nS%?bYRaty7?cS_*=GO-?1 zV*IqLMW5%r-o{?|EJ`C z57K?xA&p~aavR4NEF=?z$~`qZ)8cVkAI5KwoCp!Iv-;m%2?)P6(Zp2y%*dnAHPK18 z#+Z9`kFFp?8jvVS#{ce4ru+W=`!{cYze{82*n7KPrM;Pv^0g9*wHp*+9vc5X9{$7I zGc|-MHdt@gB)#?CHl_VkSWzdJ+i~xq|NbD;J>4WsK38do@$ew-E_q1)q|9H10&ZC5 zBfAsqd}>A81-@n&$!W#fV_W^bk0ypUr5fUlOhn`JZT%vMxb*iIdbv5a2MyeW*TxdF zc1pZ_Mkok~UsWG0E59c+7cX~JIa9IZ)7$?pEhPWQ)98Hu<^L&I=wnT;{?0297p}cH zdh&laLX*+%AF*BTt1KxeUS}8?ekU^qRW`(lY42ef@j2P(S3jQopb_ zI~2By=fBtNjSC54{y&@aM)wDHdM8`mypH&f^T#-ZMgRLqLR+}GxsgM{oM5wlu^y#& z7C^`MA1rVd6SpFh1GN4+a0+0drynyFFA4wm`g(@&bwK6P6D@A{H}DdF-o{3-AAfIQ z&yD{ZE&hM{ajxFt;@n^V2m+D!fW_es9eBnbrM-I}0xH;TQBqRk&_?=+b{7SH{-ET` z0Er^AU|>PAMGFovQ-+#y)QKR5te+?)5G^9&kq3>8Snl{BEk*+hST1557TJ6UpaodE z$?emGk(J;t8&pbYU#S4_wki9>ap3VxI`EmX@vuYh94yq}&z!F2|B2*_mj1G*PZM1j zk-M|RX=8v3U|pzO4?|goRw$!8i+CE2KcES-kbH-X#6Cr91WLU7V);OiHM=P;#w-dd zL8uN?2v`K9RA9%D{J#iTtDy0e+!Sl|v0lYn{r**(4=?TrxH#xZ418tsp503P?8y!~ zo4{Xxww5by*V5D!i+t18R(M8}^cYzmJ{j1b_b2=JpPTA|l}Xd2#fIR48^~@TPJ}FQ z+6n_spqFbAo2-C15`$}Eyq6dx*&NeR2Y%r;klnHFd_$H zR%65W%a*>FiiE0^==V5%8rQmvjt7kCt3jQQS}JQRW-UartaF&^>W0H;2jltBhKYKe z+zEzoKtGdHQ)Lf8haFs-T?{pQN1_V}5 zIN(#?ehe@GTVdNOp$-7rif%1%T5kSl*`RF!kEf&KE&c#qi(pzRKUshrCGoM%0!zxr zHTbEiD=R}&QgQZq;ce7S7L?w2xbnh;e<9iwLFIHqbq;;ZNKCXw_VmyDr& zEplNb^irT5&ZRA4VywC^f`1G|Z6+WffI8s?ilxj?_0c0+n_>efs@{2DCK5FU%K|BJIMe z{#16rC;&saZ~?pW^KORXGdU?qch_V}d}1Oc1qGq!A?wPQZT@q769fnY2m%FZ_a8kX z)CiBHujMzme5c_Pa>Hi?tLIqyvD|u>kPu-A1)3H%s1J=dJEx?}pH3(#nMX$s`sxft zU|g65vYr62q+C#*=c_gAK`dnflb>6!ao_1L>2h&$f=)+Q{E{pm-^+hFQ1CvEB5#NlE)0Cy1}@?TKk}fT4n0K!bh5Ef65v#mDqsX4a^(llbms${g#ch+{?ev>*sv%?9TN98g1Hor zod5lM01*3y3r}Dvlq~&>c*JJ{VB07I<42H*U^;Y9oJcnSTzCKa^%jHfS|_BU{+nqr9QZgF&_qnP<3y!T zCnlQZh~6{MTWf*K z=XkdNkiaG$Ewp53Uki)?_BIM6I{R!;&Lkw`ZD}g+2L}VOA<82sbnkUnMBKcI=m4uv zc_^yo8HqhlSU&V>K|-@TC8xPU)BC{< zz$VZsB==ncs677tJ4R}v?LOR6$IC#|dls7>fKhh}#-t6PB07>(Ryi-wBj8>H`CBMj^6e|Un#iN1fWqtbe57;5d znkcrK0g$WNSXa6AIxbuI6-2=pJcD+%Z?jcw5QKp;%A54Ln(~$#Dz6j%=Y>x&*R;QW zq%GmI`bMIfW;^}9?w^qCOdWj;t3=OegX0g*8%k52k7VeHZ=wDMEscpX%MWgxLGxLjpZnTU*1E+XIzGtv5($v{4-ZlMXfp z6EA~Zp6mh=fo%!lLMB!xER=srD>CJI6M5Ts(?Ti{xQz+(YI*f16GxkoQ&CCnA>5GwvI_TLtY4?1_n+XIns+3 z6VMQ73cv$AS!|9*@5mcB>`Oo59*w5{p6otw{P^)BN6PUr4Z^C>KZSpSeq=&z1V9wu zWs6Cus9c6j06Ylm8|GWDFE9TTZzkrh0QDp~m*GYLG{G@Br9>j0UdI%ao(0ZCq!Ng( zMfv#%*qmrO>79uz*CyTcC$3T^Sz#AaPFjE*4h35`b5aq?i z5QyL|@NHQQM=uC;=Xe5Q;^JU?LB~al#{*9shB^-|8*$(Rfq?4Nh?7=fw}wRl5gVL9 zAO~Yf3VxxR+m(vBm(+A&r8w|9Eg%NDFNR%rj5G_{H8Kkyr@7U-%$dQ36|J3npe}(J z4s<7wS_Dr|#XG>EAx!uP!C!s7J<$GEB|Ce2p%2*$085y3zGAKnMfcJ z_Ml4vHvqXCSo_!u*}ds1G_^a9Bc&iFW55gos5@X~^tHQw&bq$q&s)kvfBH_YOBN0x z_qiUvLLwDto${yaQ|CzT^PW>mrEqF=f5ARCy2;}Ec>mJ}~EQapYU#VPgjT@-B z<}`LJ(CMU~^q92zoKI8A4u!4lG?xAjP8;rT^BLF5EBS=qYuA z;z0t30S$=K=rx-}D}0Llbt}1W*SFeTPZgx4&*HcOohz!3RzxD^1B62-7P=V(J-^Ub zNM8N05Zu(>o+Bn9BGUN&{V`)>xP{P?K&7j=_J{%HMx?@=N+b`vTae6q0SE7UnN|^(Qi^nnf&E*zs*LBB^f^}(>B!-OR5nBc4c}tIz=H?CJ`s zDx6i57NM+H3nRyu6Fc?^Nfw{#$#8Q^&K3(x; zJ3x_(9v;||#I#8)xdK|1(-NlPKb|5Y;r<}sKtnuaJ%Vy_WuLgsK3^4*k$H@*1$bBn zw*D#?i-ux6ra#HgZwIpupO!03t%#1>>FMctd3Soz3Po`#Aqq!GiwtjDwqCIkMJ9r; z=NmBpCBE9*MGVu?cZe@_zs2a3lx6H{+~F?o-9tVR9W@naDCPYpCpWxqQqj-|=?ow@ z@02r?)*~L{b)w(OT#|(Tr`AKWNF=)Cs?asmrC$HxgAdY3E9pPTKLGZVGP1HX^Hn~$ zAr?Jo&Vg12Cl*>Xq*Ib{h!BO1-?iqdLP$e%CV?Cy|4q$tQM-%W%^hJCISU0jIjmgK zFmvhN2*Iy#eVr=y>>=!f|^YP(0+yqxZ_!J-^v1cJo zOiit0jv*n3z%&BP?;=`XT}v?nQn%X+2{V#9*>gyMNW`FPB;;f-0osSWvwUnD1ey#! zG01t~5p}?{2Pa?iLqZsUTHw>COIXqXL18>dK|-ny(jBBNluo3V#0*D7#A0D236Rhx z`%!}v_r?DHGyVCP+5E3V)es;^_X#!6y@QTQoG#8KFXg&#{86?4gO{(VR>0!YjrM zUpQ&mUg`K}q*NlwL{D!ENcpZVjJ)fEwi%KIbdfa8i3h0IQ$@R8oJXr8+-cx7;fbrk zA7GgwJBw9NBIM9 z(_Zw#)vtD-a1pMP>5yCd{!tc{61-?Ij|#bM_nS8dt*zl(a+7NtY6UbH(NIw-Vh|T9 zWfF`bI^?X)&716sK`uc~7LHafm`!yawzIPni$o_Zt6_I`RFqXXRjsvllznC|^wQ7) zW8PM|4Ls9BvU2|wUch%@_rzA&r#3YK$nhSyC<_aRGVx-dXWiHHRT!fb^R!irsr$Qs<{!HCLR$HB=}_wr)RWpZsZahy-FBY{Juu^vLbtSHc%bHSW!CwU zcWd>MI|e&KdMa<-)o0<3;CUh~ApGKSd`rQ@kz1d<3J2$}9}uv)8@M>&`8LhSv+jqe zW2f9&-s9Fcd%Q{lM_~snFrpTJWY(-?YoVW(@jyY2{j2eAK?SSnh0}}a;jFAYRW_U~ z?`wDde4DCg|KqVYgMZ*w-m>NSVwL9Fi?M;`tep-6lgB2PdISET!;aK}0W03#@GGAE zS4Ol4a|?Gr&5lf@8fEfEL>`|2I28?R&-+x|_3uh0khSb&7QMW-x*msW|s&0WjkFYe6kw)}U zV<-)1YZ&@h2drH}dl)urTqsIN!ynp&fa0%uf*8(=Ktc=?<>Ja;yI(-AVXj&YeDDMxLbJK_<-=gixGCWzL;;P-0;n zVr^3r9y6WXW(8MAVFyDRnw%t(a>TVmOy$m<0%Lqm#pVD85LPXqjzR7Rcwo^lz}0mM zqW>Pu9h%QI67!yab`wmfl*rPyPuUo6(tD->XG%3pYUJgAL0%!0{|2cNP6ix5yI8je z$AsU|c4F(v6bafgMR1fzEC0EI*M^+N*5nWPCOk|Mo4o5`^l75wVDda4V#mP`*{Psz zWj~I=n1^JGYyh9!O8O$k-jXuVMFR3(O%~}V;YhIPkWV|MdusrFz$FH#$BFSH6j4^e zc!2Q8g_85=8SMkzKY@Ut;?mQ*o->JR4Gt)1F6`Xc>`h<>Ea@^QgV7|u=tn(2JBu?9 znkG0$;J;|iKt0&nheEgb<9ATrvWK7))S?czl0J6g1P+OmvLE+cE+IOR5Tm!uJ0ZTq z5eD(N!9DEo*U*rV@V8EY%^=f7e=houpWD_3S6J^u`l5aJBo68Rs>_Lz(P?Q=^;Ckc zb-ax{ebY-MpEy@g7^2xV86blKw#)oY%$}ekwQd_79_D`*iar8(zQnu*9(w4=5kqL} z#-z<3zkKy7>r)?(st-L0{mUqr4%}d?bu1mMy8OD8IqpbjNYkXO=a_-xFvetIc!IyT z3O*#x9{RT95QE{C!NK6I!8tE4E|m+ChTSMg(Pe;RqcuoFA?a_cLN9;?x}{oX z^bB^cu5a! zpZ`o#JogG}tJVO%kjOEhWTO@|hTTXING8)->>9C@-Uz%5=XjeW)$XQ$=T)-(dS60q zBpy4w-ras&=vc_>qT>tO_VV2gE4=>L!b7BB=q~A5M#yLXN#B{ZKly7v z>~Cz(u{Z86gOVNF8(lz)vYms2HUMF`LXborJA4?`oI>d+Mo<9DnV6Dpv1{MDS^Uv9 z_T3@LyHCC%NBR`YW!Meb1vC|-PrR`niD&da0e5%om2SmJ^?=o+RtYI7;i~uo+9_A% z^Oul`0n`%_#bYP&arAU<3>g}~jqa>z%)C!}(TF|?;3>^# ztXH{fkoMvYS`|=blMNvsEUOo$u3fu;sc9rd&oLTV!k_}7yw&(^Zm-73)Re26TdU-A zs08QlRF#yV+s7WTQ0qg;T{gYM`?{oHn~H=Li;3iJw=|gNEz@%u%Alh>s9|V?W8uI} z4q}GYd{QKAe*v3Ol5iN~29X{k)dfHTLvL)q_oy#DhVf%!n5HjaInut!i|Besu{siV zTt-0wFk+EbI~;(IZc68HEUFb-?ky;=+=vlrXy8^>P z7lkD(U2Hwnsd(L33$(%DRnS{MikT{JP>OZEGi|+~oB6xoly&q-J0!oCfZGu8rnx!h zQnY^le3`&zf3Fb&_SRMyISB{tB>_*hK~ES$A(3(ko<7rGaT^8&SVhgEdE`Zipzyg| zSb~v02m;8PAPeK)CP;>UcS=Avt=qg{=0w8ZK#BbYv5>G`0%T6e{?Wb&EaVJAKwJTU zfN+IMAzdCQe;JvXp~;6UP9>@bvr%t#0_C zO-*%<5SljvpyfmgBKrWLMxy(Hkq2w9mr&f;K7#@!{%phdI5)}ol$9-KZFLO{Flj`f zYLVx!h`kxX4C6Y~Av*+N5F>0Mc@+IpdJ7m-L(G4xRVctC1kP{&2&7EBi&YvnNlXlp zf@6Fha4Yy5{IpxHK<2Rq2C$Lpn!6A(@Q+N6_wV-K$R~|;s%q-s|us%#@dhlSJ$cnJ#1_M@ZN)ljF7f<9z zQRJd1@4*v+=UQ<+tJ6D0xpo#5WJ(q>a07yQh)&CLg>)yOw+ou=egpxio7*aSCOczZ z{}p*kldP)!l2v&b?+B}TBe0uAGS6d0qT;~RjK?`Ab5J6MHgeMY>Al2a1{?`YOawHN>c;t74rNCj=sc!h(sIaCGX#>?he zB_71KFJpzGsLqx-2n2&FN@597ALj==KjuC&_-6*QanXfYm16)HGaK8)*jUb^M|Y#6 zq5qrh_0}_vMKIqwpbB4k2V?|4rLTvFFV276D*i(j=1(YoK)%2QsVR3^&M@x1;%IvzN<`=n@q zeLx0nQG5aGL*<%6n1bHBCn(#ei(CyxCOC>3+(j{0JIB-vHhN4a#fI!dyV=suYi&*9IH+LF$%S~Q>0(!OfDaG#hEVb5$ zyJHNx3P0~Ja;M>FmK>f|6k&zKJ zE`iJudzJ??XtVt5QBfk7Q*{juk=SgHpaBEe`eiQZ6fw5UPi+Hx4f_uxcJXuKqe7ye z-_I-`Gdi;Hr=|5($wAhYz{dCo8eJ!iy?Sqx-~GT==0-*m%3Y~#_c>#36|m)xhMAEC zg?osW{Fiq*ykNm(41eDaugH}O0_17QQ5?`n77(nxJ3^(s8djTnmTokGCz@mU1qfs*a8g@wzRGgY>=|8%Yi zii9CW!Xh!K*8wgcqvjZlH0kx1Ir8f}v;zXvmiWpT;K7NFlakZWH>bBCT+6I?fKkL7 z6;&`c7LFZ?ZMtSw#E=bz+_+gjVxC)MAw40OG`?r%Rj12;>4=85Nfl1iXX3 z%gKG_5Lql$8 zgAuB#YjA&%Q!HGMTaK^s^N8pbj72+^+xHM~AOL*(pIfC9VbAmI;>?LmzxgG<^QVzu z+>D68sjUR?*202)@_YUG0>ty$)Nil?5Vf~$+^SlkyUyXViKNydeP*&M{0gnceJTL~ ziQI5I_bdH}RO_QCetl0DyYxHiP2R;UQQO)9qkRly;*r$BRJt)Ivfuan1-2&dOq7z= zA)i^>tEQG}A8R0yY(JKkn~PlJ*pVaPi34!v;>NCu35kg8m--05Ss3Ib%p_JIeL6fh_g!ND#USbViM_41PY)q(t)NX`H_EuMGgmN0SR1xbZ&Vi+s_%LRY}6$3D& zz6RJHA3b_-gen;0j3}fXg@lDWi{9Xnfr@=$zYkg|5~HJab#*gt0^;s}>V6m07EBCq z-y9r-)iMb4qYD?t3Lj!_=pts~8NtSql3qeml4jes8BCgk3vd8qLxc6e+N~wX-y0hC zB44Oz#R&jr<;zZ<9&4cqgT}Y}N5@(70db(^ZszmTZK4?o&*(M5DuTCsbzgGo`X;~O zAt!%=wM1%$0uDV!q&B6uHxt_&-D2Q=aUgW)Lb`#{y)&(6kLLu0S@cQ;YY+(ZA*ZwU<8loJ%(6h>}e zY!3FI#}6&IHF$Pl)zBOGz3;hf5$RrjzA^^5A03&Ea=*`&HTj9$AWQnm6-0p zW(5Je#fI*O$@PnWkTIM%cyJ2!*Wz4RP7c^A0vc8np03wLkFS(e1BEDDj}a-OH8_y4 zK?Lv#WX8a@LM#zN+XN&``lT(17&KjyYj7!gkeqOXhhnp#Px9UY#sF^V9!GkiOfdl3 zhkA3-(+YmWSln~|8L)o9mhI^?1JdpWWDi$s-=y{sq!1S!WevxJ9|9UqB?u_cOC%<; z{_;)a3kL>I)GcWCT-@4Ngm(^R)7eDf@-0>@aR8w zo^VM3jgeYitrj4U$*CtD971_pU@qM8$VNSw4=EsyA~#}xiI zJM`ka*}0sgCybo@^-QO-Sr2>EF!gJ$Tc0k@FPiK87C7*Zft=beJx5|^ub#zb{%j#Z zmtL*CL%yzyFDkdJ{aPw|f7n=f-e1FXN&V!XXXM4Jf5y`T_s?CMM8h?qNJz-%bJQ8!PsFXqKWfY?~a(3uOo3} zgXU^}i&x@?GsFS!EPf-Op4xuinhIhZq^!C}ZCc;HMX=m?_6xiR9Z#Ozg(-1<@yC93;!9fUWwl5y>qxXdf z0?N~K0eo|`sW2E{WN|(l0^k8|CnSt4p8NnR2}R0j*L7bCX!!l--!3EJ-Sp2t9-*%M zfpbvM0k+EPS0P52l50e}=V<$8WnhO&@7Qtg_m@Afc>_@og_~X6-=t@U1ggLn3Kyb9 z5c})dCiI?ZVUcl3Q*EWAg94B8m)f9^lzPSng~=LY*&(FHz?zW{3cnT;6XOC-jw%Vs zDHsZoxaRs8Pqd@nxT|>XEA8q!)+A%iSPckFfFkf)z&rvrnnuLm+3g;%X9p{WV^zO^ z>lVG;?qu*)9D6T$fg8h&V0^l`*jTV=D65%pEl>4%g;G0IkSg-^$mT^ZTsEH09oL}7ZVrv4d9Em zb_hT@_4-O&STt(S5kXDY8G$+DE6^y!UsqTM<{Y893g%_KF`pN zckmz(j4ji?Ti=2bb&b4U-P1Q>eIV+j44Zy}qy@wYK(cU`q{Irs$7|_cOJy)m z0kfHvO^*Hw)!h7Aqz=6{C_HHH@MA_sMwkb!p|2Kxv8b9b<`o@{LoXkURy4i6M!@&f z)EE|%f@=o!3OE-qVpMA4>?e$`L{f~IoCxF?O31>=c^M-P*x8dIMH!~nM0Nn`-ls+i zS}jyih&bGP&RRj-2x}u8cV~Sy>9ui6zyT4Z9D4q6>LBMLzA)NwMI?9%kQV6dp7Cuo ztjWlg&lSx|GoBa0Ek_G>APhvnWu&1I5EwI{?MK!xuALrp3PQre z!QnB0)dX=2lyG3-PGOQKGjpim$+MrzBI<@w8!P$m<8S|;7C=pH9h8eC%hsKv7$bYC z*c@REpIcg7oWI}|(XS4LsH$pTW8;2HOJxOxcWrHF>cbhJo>BDa2cfz2Ba(%~J@2BD zs&~W3@$Svu`mGXG+^I^ZJTB(2ZQ}1KDD6VfNtJH6F;%dj_Tc;IRqb7G#u>!VjWu5H zbn#{W^yd5Jou%ob%WZQ~flmIQMny(8M^10J;>ESU;b6BYqn}LQ@-ML^hdu%UQk{C81Fadf#+o< zKm3|j-h8@N7Cc|J8d=0~o{FC?Ol8HRsq*Ar22IxV%17>_(mXmdqnr6L-LD`k>pawX zXgGsJ7LOI&I;e2o#ar>SJ1n7huen@OUf$c)m4XEdS)a$@R_HnqBO;Kw4kUzHvA%-U z3LI`a1_lBUE`a`n6hV-AmR}kg+JK`Ud?@G|JQYvKhG5MEWgDvc5kqvgnZy4Q$Yu4r z>I&%;KrwUxNx_3)r=n_Yq!nIN*j9J(G5W{>Pu{q&!33-7O8`9qt^zoe5*H^%QsZjK z`-8~|vI}-uXu<{5f~*?viu%ZdeLu2s0|T1C-55d80j5jDIQ=_(k3p88vlsYH(ebBI zewKh!U=h>=j-Or=tXok*L7m}R=xPI2E3`k;m^lEk2jfWCl%rDDBPvRagm-ZGMTp#-+F?UFa-ak!_am$4 zB^ennVX^_gUVx+6NsIfDCM3ti%t56TxAWUlmdNSbo0h*^ok+~so3Xz}=m3$ys0Z06 zZYI|NjYdrW1+ar+_PmD`J`&I4$2kv75U3=eWWb})wcy%`w#VJ)%*+@y zo8XFIl7Qj@a;|eTI}lRqZoX*B^SfumNDeVM&?w+E@Gf(Qj*EvU>UA7h_^ws6?KM94 z0I;I>lrRuQ1q*@YxuH`d`VXaC?H~V(ky7_R+JKvs{pr*CoDnMlwG1E5(RhJEt30v$ z6Hn2=6&1&qfbp6ZJIOVeSXcxO8c1oh)P5sG6URzZqN8P8dvc-6JapAFlJ1z-@bqJ{ zV@9dFPHpCS2<0xOz#;OZ*kAeejTl?~rD>g91LQ0OcfoCr0BGshcaMU?_whp$CI;c9 zwgsT)F0lppSssoQw6Y?OA&g^S4z383T#nk%HNm)(Il6)YX1_ri}>9A^l%U@>tZI$$&l zdYy5U!b4?nkT=jg<6RL9q9clncdfU5JrBMj6VMZSr_nGB{9GZm?fBOl@V!+9D^qzL zkE-Hi-R7w%4mhLw4p9X^1I~@tgDeD@81yYoQDmsbP>CRH!L;qm*jPexGRB|VLiC7h zMuL6B(D3F=KEf!9wWZ&5!Go@MmAxY?u2)|Yd0k&`kRq3d!x0=ajsl2t1s>Tw1OpDF z6bS%)&9F%8ZM+fEgC4Xe?Yi3L=>D;U;&lf0!6%T%aFRiZgeF#<&A&&_KZm?oK}H7M zx177|wDIJzJp)&!_ox!1xe<~T%Wzf6xVP|%iH|>w#5Vzkbf5D!+mx|g13@Y*I&2&c zD)?*Kj?pviJyo|_-&=7PueApqe{}lWMnuc8!KS$i#y!qoXzzU`Yvc(^Ob>XxE*yR% zK*{B8-GhkdMk_oPJ||L3{&c2&2&5RKwCpyDFyZymn~KX{2Op`&$c{`TR&IE_rx?E)H~qA~*xd8Xa_wv5 z?VehLV;|g}6_5NGC)c>~yW-;SbMr+7I}fa1y7+RL?`fRzp@oS=Sq10{35lwj-v@{( z@epcjgI164qpDg`#5f%kOa@CRI4)nljHn2R6$S(`2k!oPVn9_boja?iv-U!dUfav- zC*(dr-#q@<*qd~NM}Wl|=n6Vd8`?d!1rsUOUThmD2HtB_iV z7P?D6o%`4~?HpFZnD#ulxuD=mPai-W&8u}h_Ru_N#+_WFr?;1IN&oAH`Dd#48#e#w zE)*PnsH@=12XQiN^uA9_==vwn|77JwtG3X`Km9-yn@CRt)J#qOaB9#)yn`+gOyz#* zAcGKDt&*)Ls^{%p4m<*929_Z9H>;$1G(2hAzmAVPxwweqegQNe9uBGtVkDo#@&on; zLx67A%IzCb0ebo(BV=ceeNtQ&zL%|S`#xEdo?K)77X7QioO5c>JSjD;1zU<+dDZRg zRN3OMdbR(?R}#LsTL6)X8YJP3y0b})(>6MWn-1NmO;`=o$($xDp5xUoD&*#OPIy+9 zWf7*;E>&JWl{p~dOMZr*j}OY?SSNwz(u@ap^${c!DGk%Q10mD4;jsF$P-o`=1}a5Oxb)lY(`nU<>w5Y za0Tx{n8^V?N9PI(U`ThLLja@~C!!HScg69lf;V1g-wUm8#Ti-?tEV!1Al*uu5;52-^HsXkuH z`68Mr?!o_3Y-sV;sMZNz9Yd`_X}@La9R)>!F=ozLj~-_7$!Ic9bReaI3hL*tUph2r zY4%ZeHTLct*5}4cbo>}n%h}jkahm#uQ|`+Iwv)?WSWj=XKtirVQ*>tju0#+n3VIg;Wo7U7VXW zLq7N^HM$HA7QSe`e9j_kESb;b90$YYh|4RiMKzpzJa+Bz*?Dl=fpWheRirC3|4@5p zoGK_R+>2cZaPRoDJMcF_`L@Tq2(>YMzgM9-MCIppaT0u(Z4c zA{cpi*3Rcoo?Jj;3pxs_I4vp=^S!OD4?ixE8}_25YQt#UZ8~mN#r3nzcc}w=^FsQR zD{@=do#ls51U2L^@AbLepS9{uMb66nfPq&t!uXMizkICOK1R30v9*R<_)BcErn+X* z)JY`GjwQv332@lR?O`B+2YkHb4#X2sRwA8ytmlc-jz0uV5WTO;FFLn<_Ldr+nIR0| zk-bqH=M)rhvar0%79JlT-y+~%?-tX6VJ|F8@a5nGkjNO5?lU8O{7VXEx zQ|#=Ew(wNs7DhMSCT}xl>zF@QCLjIrNcdtdPr}~!yzVE(vqz#zH=lW-v6-S)yn^Os zL|(>_<)>>y0wN;)c0ZkjRetNk^^{+9mnRg*CDV?;FnS5vuul)qSJw{z{`~0^t?Uk< zYCU4&L8YT*U-$Ji2kdC@s{4Elj1*7k_xBhQW~rcbobQ?JiPb0)14m{}P|`j15&rZE z3l%MH$Kzs`3wB$(u=U+MIMQk8!(}+nB>6dDCV4XjmTylu?tXoJeenA@({UPufOL;e zOzc6g+WZp93;VnY!M84RM#&qO(Vor8z2B|4*&~Mn+;Zi_}AO- zf5O5-#G*9x^$`9wPE}ZHLSLRjq8O7n(yrDaJt7Aq{uBj(dJ61l7?f9n3|jYh=v>fw zV`DI>ps9q41xagWAao#)^7C6We-?Y53SS>_!6K;W&Ys-?RO2rCQ*I1KL>-5M0bpNR z8aG!&k~nHzS3&~7%#4KyN6;E5qg2Q#Fyv=+VggbS;eV%Fs}@|c zQ2O6TuC=i-3Ye2fuEV_oxEntwV8oQTxDaD035m&`vX=V#;P-a0n6^T?o*Ybi`|e$` z*ty%NW@>BKYkU^8v^Q)t^1<~%J%bV$iAWfcx7mmJLz47%=|JXyLf&9}4`O~8`dkQ+ zCMPj&W8WC*lggX z4jXh@l`u}=E`den7^;XL!B@h0f>AUa#55jsei);J^%FC56N~Hn*qApgqY$c~xCQyM zfj~?@bKO)Ezw?~g46IqrQXa!zO0zz*ZXX|v)#eu^7FJCZZ_Ks)I@2%ED#zje&*agL zoYv=NlcZM=7P_?OElK_|_3l%Pr!CLV(B3X8WIdi) zyYu(yx91~P3!Wk zW^N|)cF(6kRmSeiEdjkLoaWyq*l+xO&Nne3TX};)G-gkh4JmCwW>+qmRnqp;g%;;1 znLUndUqNJ1L|Bb=ASb+5n}0uu`0m^ z4h~~zopWPQ9}`MIVTYBK6{s7+;U1`{`11AZVL7=g0O%m~z+*>`2csj?#Ru8I?I7uV z)!&co{w&ny=zDRQXwaOqvm;3F(NTnVuH#w+lYoddfbe8{EBIDXy8+)togIL_3aDgE z43gbefp`$kJ3yH@1|Y`q?xKBLvbTB~e~#h{U037QQ$v*Jt`b8J(E*@s)X~xcu=s`# z#!}z{Q$K$as2U(>AkaZlT>!{{7=Nt`+(03TM&d6jLa3249_&{1lL_dQ_x*i9u{h`e z{s;>auy!3564+);!7TRpX;%BsjkHghE=35OMv043Wfti73ZSy@NOmHvE_T^KZ~^3s z7#oLNA{=QS62vu*mFIyfwK;y=8;=$4kN!L&{rBl{h|dv3CKtF~gANG<7v^u5*fvMw zQ%6;QfLsKfFiK#&8R&utjT173?g*j61UN`=VW@N=ghJJTA6HQ6H2i<4zL)S}Vf8@J zBvZjx73Rx@7(4^ZE8)r{_}{yn@QDDZPfz#aKEB5)T6o|*j$SZKuyWQngAzi`TgWqP`~rAnJLf z0Re$P4^cxNp?(fmuc&Adp7QF-3Yc{w$_xyj%3a0*%MhLbedGQg_oq_CPfe+Q#D;3f zNt3)VHn1fy>8#{um1kj9UsJ-f-u*J2ZMva&y02`+OJ_#9^LX#fcu3tV#lg9ylXk*~ zcReh4*Z=*@(BUVRg9{Q~Y4(0~x?Hsjo?V~JKPyN|=}zVE6DphVt6$8GD3uVJ7IIsR zknA-4D)s(V;j`a))VF?$s?rYd1{-+zXSJ+1w#Llc_x?UUcIuZ@*5%ek?`wIqK}nXK zq`8N`*Gq1WT+S>^wLEFe9cQ()x0o!*^huJJD&~|H&rLR7nXx4Q16wI1lr>9sBB2q# z{0)%}h3pkbr=aG;G{lsDf8p-@mL!mm-=HIVT?cqGf&{hmcio#w!^rDpqI@~(4!jEa_^PCOfEb{? znodAahYKIoLn!qPmw!G%hy&gjz*h4iNQ`kUP%7i5J%zFn9J)8$bP6%e;P3e>iwKOR z5|Lhoh!J#)gK!!mX|BT_6FUOl{&j-*(iDaB?b_aW~@|Kmh05$+! z3=a&~%vf$Po1FO5I>59sYXWSjT(iF{|q|zVc^}mCr_uJ>M8gCznGH&!$I{wF= zUl}Ft^pk>T($=>Hg%mWK4q0%`@{>c#U#zu$65L;u{P2M7+kOqeDNK}4$BV}De7Tt@ z4J}S|c`ce2?Z0TtUMKXAddn8(5^dU`ie-rh5gI%m3C2r`J>R*Hl7eRjH1Qw%?d}iG zNiK%Je%3E#!DVpLy0zLdxpkMMv`R~P@_}Bz(&@Ji>KnVl?faa)&KDkk_ROhq{7u|U zg<3+gy^xHub47a~bD5tfBL&aFgR?(xEn{9S<_EO?>kN4H;1TLN02nBut-kYyovKj1 zsck>|fU%Sgq%U@MnuH^>?Dw3s01RyciR(O(iJL^b{1(7#pcFurPvoC7u_eYH-MiP& z8Z|UDgar5H@IPQBAa`$vRy-N^-P?wSt#zEN)~c$isEKfkG#^6cx_qk z#PUICcHs;|8)0#XnfhlVcMAKFs?9AXGFlwl+LA3MXrtbALF&k%i!VNQh?F ziyl1^lag9@GI?|Uisl*NuY^+;O~dk~Gaw7_mF9#_u)|`9Qv;xrq?8mI0%#5)pIU*m zk0t|f7?g5U*E1eI#LxEq+czUa!#SjXU_vFXqzV7@?k;wqu-p(M`u=vA|5F1(3CQhC zz#k_qd?qn>+UqYbT)OlhJ@Oj#1yCG8&E=EffeKhfCFpP){Ocps(lD@rUN>{$b?7rb zsy?4q$EP6w4{xvxM^sN&_`xaFb+dah^<})`bo(!9=L=WopJOro7FhMD_?yn5ev6bM zMHcA`6~ljiMx9-^vingoYh!vEN48b(i-b;_G!F4*EpGD8jVOIi!$)!BYHzCEK9}D= zqzpR8TU$Z-R^^I7c!E#p%gC`V4Zk+hp_G0rAJ3=Tq8{_^ZoU_)^;XB1)6g{jhOKFo zmO1}RmRcu@BYUm_(6@%Q74mF=79-#vIfD)Gvt?wQj$u(#Og@c@uBPB-&(+e>Ul5oS zf+>Ja4^AUk3SrRU2`(-vX{IuaM{qWz`2POSF+e1g4m*W}v@gT?A#P}|YxRVt25kjX ztLIST4VHcCZH55=G%%9`$RfO!FtVT$ZqmdKEM>dC%m(=(AJbjdH(nc{SVw;RAO>pU z3hkUj-VAeX>Ay?w-|`C7C$ghgaWSEatKs_ zdJ94DTtY=Bc6aw>9w?0-OmFU~aJ7=0EzwHZdyHdwmIyd@hvGkCvz$bbtqnM2il`qi; z%17!xQIB-%F__-a#e2|m zWh-nuRioc((&@0xJ90*)wkrAlB~MR0V^b9s6^Q=258j-)4`gTFe|<;nQMBzMaoP;p zvp9HAO$H-IvI|x;7^uMzU{*6sn~vKPJAVkVE5{_p+OyBU-fC0IurpeEs8)^-L+UEs z&%r(hG8xxA2I6vZaKK}o_*4X?5!#jhx6F|0mB!jAiJgeKbFgC8t*>gEnR()n z8Bxh?DZ)H1QBl@87uEnPRYOzLb7#+fgi;)W7~*nORW+zvhp?udy%OdpERDedUN@Nm zTS$9wLIGiTIJJQlUL-QxYaN}rqjq4nu^Ad-=vEl_NYU4A9h3xb1ScoXW(X><@Z{yI z!S3Pop}IaaICzz`4TkoWM$s9o) z<@5tD2*+h%;aw8K_;WZ3fz7kGf;5ds-aluZ+mpvDbc<9~`+*F>+Twtn&?O5kNuPg$ zjy$-xAv)>5FYmV2Kj{zK4I_HNg;eb%`&^^jL{UoK+j-6N zWlv8PfOc5dN8*vobW~040ai2(SulpkztMX&b%~Pb15UIM<6wX?RorHbX@-yog0`12 za1$b)yMJPj%IMe_qG4uWbi-7q+VeQm1CZOg5{f~%kZ(oQvGGJbE5**&5nRB`z>#>g zCXX%3wvoZw{Z|Boe2`!RaEL8i9@Dj!jPQ@BeuUgb5d+@fMRauUaPr(S%gOR)dv*%N@EFCKXr%@YVy@=BN|Cqf6tbdG+?~ zwzB5JyJ2DZC5z@UDZ~e@i-eG_z~~2J4`|b;efj0Z#oORZ|Jj4)4!-Io z^rSW^bN^j60L?-e_FzCFy`jC^|7ZcmabnCC_}+p~f1ilRH11nOaVyg>aLSd!W{SQW zXEYWKO$HVheq>0waef9Hz=sZ#m1X>;egBu7r;ip2(l_<>v;8c!iNz>YP{%JzE0fR@ z0Nm;5>S{DRn&(vsw;OaWs%riUyvKY$svXr+kM@ukyw0&>rS??kUEjIg0;REUuDm@% z|0dR%D!Z|1U3@IoqSWW6Er(S_N#`^@%SB5$mw){SfPl6i{BjV$9phhb^ z`P>N_ZAV8R02i=`x~t=5qRIz+Hagz_%M=o+aRx*a1;f-U= z*I6g09sm_~kNwekA>cS3#zV}~#YF^kPA~OvrCXnz)#ps+05j=Z5Y#{w06ewnMJmo? zAa;GQ#1d1hGmb*miEdvWtQy+HjL*HFRM38aL3Tmy2tG;1{T)g}G@Iaq2uc_?IYbLR z-QB_`iur|vp#Ng0VdU;8F~R51Q~2EZ;3Td14x@tw*hj2 z{}O*6mKMNUL%VwNyiCN!#d}O98sOt~qo$@t9f*yLX~SSTp#e0^8jlXnLFx?Z2}GEs z0A#AG+kogD3~`IC7%(&>Ce&i4E4)I~)FF^@;EdhF#&+DoLh4-L#|8ax#SSVBH%T)l*E3`(bcQ(~D(o-5Kdi;lX?*}qW#w|-UKZG&r~SvIXzQO{Fi=BtJ|Ejc*p zoom%jg_d#+UH{?sYT9?A&(5(XxdrGOS?M4C>l2)D`*CMk?ytlv*R0tOO2#%wn95Y% z@VGdm*p>C@jNVe!m6qF?C(j8KM<2MAn%MWN%(?%2;NHjU1}1&_8iprt-ym5O+iKd` zeaFH{ER)|vA-vgU;)w4>NHU@l3>*2*Y&&-bXRVz^ zr;1vu8m4!g{d4hH8;DFfhs}rkkqd5Ie33cvQka+VZwYCKl~us%xDakaxOO93pcqrL zu;_t$>0;TNx{dW}Q;To9P?3{W{fCj*!_LZz*1HPr0XzXHv1+~NUSTG6zNHo++5vWi z9=oct5(v^Xnj#s!Hz0N)`@8`88kP;F@hSp{fQ|$LL5whQFL|cootjszx9lFwy}A88 zY>Jp21k5qBv*IMwf-&DDQa)_Q4d007!;`>OmLX^W^lF-E_5Oo@FH9CD{D9CsKi?;cUURhxQyH zEBOCGBpkpIw{9UK$OZ@pWWgBPMcD8xr(LgK*)4>mm;~66ZOCe{YA^Xim zFCO;s$Hq}5HjV?d9rRRZVLs|C_mseM2C1h?!@w>2OP{N&D|;z%;9Gn{eG-O zpkb9=jA8N9J=@DWvl|lb-0rzKaWimR2vYc8DCcaVaE(y*eE*fdg6i;z?OaOlhE}Ix z&fpjcXh)!o-Q#o10;}?Ujp977Is!3*k~oplMfCuuqY;EF`xW0Ghzt-o74T90cQ$T{ z>7KIv7_mgZWiRiLWlwwAD0%siiMhEm?k#U`mx0>nn&Ru!Uc~eBx#xB+=SrF0x09@b||_-c8A5ozicaPg>?osYYnncgiV z?%1*F-aC2av0F-m$vvmauUE}0qjo<~Fga&uF_zmR6{tqBmWysqC2Y4~CN%6%MK5sf zotKskHU6f&wh^(?L=#|AG44mFsih^AwQgzr31I>|cXB8Nd~I&d%ZrG}y;ok^=ah7G z_Uxd0<$rsb$B%b*G-*X{^b`WaUciS$FuS)<*@kv8PFzUQTUzpg`&T@3qe#>Z5)gdp zWT+fQ7H;k|6SDncWqPkmv_xw{M!};~SI)!8Jodc9VD?V42XQ9iB%^y#kx}SWDf|LK z#}6hQN3^3ecfsA$3M!9eN1mC6t$jL6VJC^nX8wR@whPRkKnVl zk3ZP01w5s9mG7kMnWvcjtGuW^JDgiow4IiA96W{Z^1DTBUo-@Q9?uyliqPfaSVGYe zRZw$ep%gzjE+`3!PWW;R>UJun;zIx;8vcDPz3&^ds?){Aff>4CrF&gc?gA&yaELs+ zdvt8?F@~QRP8nIb?^rhaeJIlvoQ5aj{zrqbnTh5Bi>Z?C{<|$A`*U++PKjN~ZSL_j zKlJgwdF)MQPKq_|M}}} z*OW6D|NAfH4(|PbN=fm*`r-xOaW$iSajjH0vn+f(iWNNnyyU+hj$$)#SRlw{qwKvN z8Pjw0Qmqwv17z<1{^z!nczyr<{r^V#@xS`O|KEMo|BFvv>I|CdtU*&t3)G+G=cw?a za(Blo_5Bnd05AuAoxzQ{nnV~N$|?(s*u~88XY}fu&~`?tJMh#3ii)S zkXLVp3u|Tm+(bN-f2hAv46pc}%PS-F*BFDnG2LDA15T3K^8q~>+-F0?1cmOGj z#ZRBE)UGr`2H)DcosJG@;AHGS#>gte(8<16=TOF?Zr-zTKQi(Qbi`pLv^_iHTK+;b z3yB4Bjp9Cr02f1i)r^eheSaVa|HzIb8H}YiM6n6hKm6Cy(#EE39)y{}eRSKp0stmP zi!&<^1MXN~Mq!KTb!(QuCaYN|a?#l&!Y9MR^5)b*i{fMiQh_mDvqU1;HV;kW$dk=FC*k(Rpp!>NTWJ=+10u>4HxWleg&UYp?f3_()x z9IWe-RDpGXt&voQ-oL1527T_Df;)BX9(0yqh1Zf^4*?aqRB7cDgn|na%q&Qb2l%{> z@Z+%(falCi(r$_)z7(z#N8~@XwP7M)lYH%eMzy%6akmv>*yl*VQ!xHBDJh;#$haLj zi9P~G5FeOg(bwfVqUc(CUQAuf3QTdn9%c`i#R7hfMP+1=6Vfr;3kBA|p^(9R$OMma z)kCr5H#12=QL98C5ED3jp@mj3Eo0 zm$uf*qiu$%9=%VLO&t>goizszX-~C1tI!$j*g{dtOMK1S7EA8v<59y3QG-FTF*AGP zB?6g@7|$07xJZQ20M%mqr%y1+z_-$fHxFd5f1hj(ocoeNR_HzPVldn7fUq#AJz#-U zj2vS_L&{Qlui!V?O;6QrK~E-zKi34q)=|IXJdv$OlYz)6s3I_(`TW<4sQck)MKB5u zH8W^u+`-sMc-#o0IE-Pp-s=!afxXYl%e%Go4%s|#B;3dbqsVs&6N)ePbRw10paesX zURDQx{rVLZ1qwjuVvWHnG&K=*BOZR1n#A7;5tU4}x*D&URLEk%E&vmSw70Nu8gB-y z7$&YXCsp`p7)N4wTn&fQDi$^bL12fT9n2R-cPa^b*Zm3PCs_PgF{o`vz9Us5+KT{) z#jeH2ABSiePYt3VKsU`2cpNe>F}6=+FF^8t@Gh~1!~P>CM*E9kg<#u?Jo}2A9H9h4 zP!()|2pQ0C_+)qP=)kW6W&==47bYux@>h#VOREDOfHYI2ls@Tp0S$nw9-Ra+?y+CMkvE=ZB-cw_Y}9Qrv?a6&r6qHFHHcO<2dXc z`FaR^=2a^X_~e`Fb>EuCFmgy5sACYHHqgV(qM+7TrX(w z((po_uBmzJem^)o%spVgbfw$Rer z=8@3p4BqdW*;aPU|3Szz!}jgwhK55aa?tXD3Z4*iG-r{mz45FVw?}^+Bl3)}NkLWf zG}5f@pr>%yh5Hs|DOe8BAr&U~AU+_7%Cv|UD_bV|xTXu^^tGMWevg5gz!N9V(?khJ zBlNm}*P191J7i;h3^xQOIUYT_WhNV;p*_ZWEx^676>fSFE(c^9t{l?BAz*AA!!82j z6=4U#@0WHnl4E0Hh9@U=!p>g<;f<8wNcr1Pr?mkl!cuh{#%ZPFZ^DbHvw*C-%ZwO? zhQ3{_LP;0Y$KoYjV=pFM3yVa+30SUM1(4r!+0zr-v^4TYsJFyc*wAEN(`%j5M8N=- z7Lf1f_fF!r=gt{Y1LwO&LfQ+JEFqD@UC}8)scg`QEt@WC8C+*~YUJjE{~h?YU!y@- z4hVqw#{R;EgRZtEtrv`t?gEbv#E0HO#AI8LMnQJjmGI2tba8faq7=YT$G`X+lmZS; zQl5xw!e8pI@&Ph{>w#(R(nWCteR}lHwT`t7oa1QOu&W>mESLn4Oq~JAU@AsECNCyF2nNIl8B>!g7D- zw}`r`Zq+@QnC~DeaH;4h6zmv-d;TDpz3R;{Vsjb(JlXSaJSksra!N{b6l$w)f8ru0 z(476phKr*NDzDO|MqHJypAKZf3LPAYp<$350e90%iM@Yc-OP-ZfdT1J4?f@Dedr9O z+oYcVCVj-PfB!@BgV+#IjbS6OGT6c|V5nE8C>c-Vl9yMGU^PxeXrP*vt)yLm&n*9w zJafsO4uH7vQaK=PJa~9tq};yB9C{k)rVQq9WH*!;!qJKelAR7SVZzx3`_)r3^^=Js01`|IUQhW`SPU#CwV7}6{RwL69T{t^>X|6nFUu7Fc;8@w%QW@0-tYim=hwqaKRT-m-o0=$$_?<$g`h|C+| zR&@Z*z)Q4rA*~>0DzABfPF=M$d8Gcm(Dr73K<&rH#B@yv?*f!>yIJ49PojBKWCXb)@u*o7Aazzi3kGpN+*=(= zvickSnc)q*Dj-Fg@=)YIEObNlUEnTpgJh&z+I?b|iuYm*Y8pgiLN@EfJ`tTKa0rjj zY1cU?fl_QWLsnnJr@BYuG7{GA91bN-bQN)*2mz$^3H?DZVy% z^x15XKE*eMq>w3JoH-8?BQFLpWz)His%{*IFuc^q1iyJ=l8)Tds#8KtPG^Yp8* zZ-s=~Qx6<+$|wZXL7vp1!&$e7MQe!vWlL0NfMA!MFL$53tO z(z%=ST%QTwo=k?)R4)aZSHlVR=8z`4v>{4;#|c<#I(K8(VeUQeJpF|rVtjEAE0~yU zy>IT7oo1%s6NKNXpFGL6c;&?NE0V(ErEF5~_jmde>`fDk)xfX2CYF}ku1+vqYNh03 zqWS9L@nacx&le_o4LbbUCnFOtm-YO)f~+RS&aD2(!%1KQ8H8>SHMuxA;iG!YPYr}t3+}AuC8K;0F||-Q4oqcM7tqb@_4I(3Y&c@e77v$E z*#VKoUW*(h67E6N%3Y!K@mG6|c`w~mRZY!s_>UpozAxD0Yqja4Ie9W1jEjA1NNa8` ze5To{;mR1YgNy5fm*K#eLqOQ$@_JP8xn+}J0dgN)DL&eSbddQxjOsM9L93UU8?aun@4-=+GqCx2anQyS!`vmucE&;;xml| z;@>=gSgLxZ3w$I~L>w z^ZOZ-v#X@%Ft!7b!3T>}WH7rUBdUziMQiwiI`<`z>(M?*jmky0hs{Zb;}O=bL`@@; z8ECODvnS{6Y&N4iyEJ* zy!f3ST%o%Da?i^B^CRj^mu{4eym)&2fbt<x1LPX_P zeHM&9_v0d=P3a6^Lle>|88BXYijaxZve8o|yg-)cF@r_!EdzCPX-a{DfyR~shK6-p zB2SOzwx2^ttIrP@=ABO}Zq2X*-fh;j!{}!r9qkwKis1ud->J{PcwXKvb>dt}cd5`Z zmM~&a-l-Q9kgV6Xe*qsoK0JIq5DX$hsL?4H@%@|)aQ@4Z=zlYi58Dud3Rz|hrw+1# z(b*$&VGvO;mQVjw6rIcf2?Y$IbRj2i=dN9Ort2gX)SQUTkO^WEzDuY3vp?#B70JE1 z=7t~B%(0-iDz2V`0Ti8Q$zw%Z1{jZRqL`xGIdSBVN*EXUFxy$ous}an;J*=wO!dtx zmIkU~6W;U9sjH!Buzbj~4jda5R(54ZQY{2RLSw@^Erwon%unnQ$ClUnKGP&_m8C~8 zA1aW3WU0j10V4oFAaFKr02L+1M2y%opunhA;3%)c>m9%+MIw-~wKXSKvV>!FQqrF3 z#NA|xqTqOVp?D}XRh3Q9nwTBfL7;EMs6hO03N{Jp_x|Y;&Hfb;RZk}Y= za(Mz^8{x7)MKg=)>3w!EykT}%3J21sJaIIyTnRw=SNd&Cx?@s&&GJi${dS*MfCJ_s zJ^--B>&#Z}_QSe!zXW(j9H#eq6cntFX_ZIKpW3%_%|DBe|AMR{)f4Xd>27MiABd2M<^jmvJ45p@{b+~cd2v0v zT4JrzbxL!h4t}5qAc5sUj(odekwFlTLpT>AmTCukZrO2*T4Zxry!eYu*q(Sn$CfQi zThh70pjPPdszj?iTiXHv>OVhcnL*>8hE&xlvHfy#iJt?lLq6VFnnp91zP{=A{Cp-$ z2oxMBb_rAIP5NBj{cP`&q(ub-F+oQ&Raj+id-6;5sYboCD&hOvAetwRWBjYf?eC{qnOTf4BXGJhJ%rS*R*t;IMr5 zrZ_L}pGC_tU}Cv5%0N;(qE$XT`#3oaWx47U{~!=FkhS%72+D%?#=>Z{NO=3#?N{0F zKZg4dFxQCiPxM?cO8rrU0xguj2c0Ufm{_z$^>qv8^xbe=;6FDjgQT>9Zm`#`u*~r6 zaVWVEDTP^UuF!M)Wxbh$JiPbmS?b-znGm5vP!BN;(gn~FqkQfJ{f@bFEG)}})qI=-Ey=IXyyEim}y)dsLDBfec9SdidAqoNxvKX?gYMK2D|=D46Cf`_axp zeL!eLMEoym_{b!{X}>sZy5QLkw+^1T_Gh~qh!g+@PfbqZ;=G$ovG0&wUn_~mt77Rko&JsPfL%}lO;&@>`k82PI}%lePy zWIF%ey)F#oAkvf?!IOX+5d}j`1!HDb$$!-~zZr32lbLE^O>U3(tT*=ihp1g#W1vW@ z-ZDM*+>3H6r5b5x-l5}d+-Ee-ye`~(iuSfOAC=^9)N?$ZS-e48m5)${^X`4FIWsj; z8>sWuWiPd7^Pj(m6&0VkdV1O|xz$a+I}j(26bj%2klvxsSby@`#81!vFN|AY$(O~G z-h^@9g}AF1k||=V0ekk!8PiU+~KXeepdt_R5_w?M1WxB1BPz< z>luV74%2UZgQnC2Qt@I5#2e)bFM-o+Vu!2y&srZS<|$x^4<6h|jzObz@7@h1@dOryX8GP1SUNCkEo- zEE#aFI7M;9{K$g#>``PPclMsZf|{V%<7$|6?YZ|HuY$tg{dUv)<7F?|X>1Af>cg=C zo?m1{U=u!XIG(R$aYL?Q7BG-LHwQ>^b}}<}7&;+C1k{P6*Q6HAX*lP|C#>rN{L$WO z6p3Su2Z3%{Hc+Hlp_o^PU##HdZk-2@AN#>_AiAEo=vjcArB4n4CQ2q|c`z0c4vG{$ z1QHq!e*viA|J`y}e%{f3nH71fs;=xufUBaIlqfrn{0w)z`qr;0@W8@*1}|~12s0BC zz`podb4EXnG%Fh@wb;b%bb1%D)1N?DsQ;l=y0Bt{0$EtlH5sKnKZ^$gxEZ*7msj&H ziu5p^+$~B(l)3RAJsL;fTV)fyAAhgB*{+9Zdqu?noKbk~2UxT%EqmMBpCW4D6+##e zz$d5Z5oTxPp`$-w2}6Rd93T*g z`@k=NK@dHz3P=&27h=2zV84F@JS1Y8p>T#?84-^*Kq&ylR!4vMfpm1AwdJS(rep|) zHDK*wMBMVBlAgE3l1jPk<#m41PW%xn9k(w}ULtxNy*H4uH*apraeVu+94x-({H4^< zSV!mG#`=;H1k5a9gi{>ovYoB1PKsQa=`U?5!*vcA)Bv+!%){Dr3^TTQljyD;Iij~s@!-LO%kDl@6cn>W zGBg;esOy$rVWVxS?WVt4NW;j8AU(zPuP`t;SzeqC2Qd@VmxJ7=&*K9pH&LYF?Ix{n z;!;Uo)6x4TImf@wy-E<{4!Vfkx-*90fe`xL^ZpB4)?-kb;eOK41JU)hwKXEyWl_+` z-N0;Cl0()#n4={2*hvU%1Vc^;(LQMXu|&{qvFN=j zDkv}i+}QX75)#hm6|L><7vX{eB){@z_m;tpJv2m21EzW@ryvv-LY!RD8`Oi&5TRhh zRsp2{7bp{nYx#tx3E78)|N6I^e3Rnx6*MVekKz!9laz#?I2&gmrs?CpQd zq{vgO+gweD)ZvBw#S0hku%Z2WF%wCsO@N3leb|ZbJi9@2CxDiMVzA^eEU3Qr|Hevi zGn`cXAB9988zqsCLHue9SN=cqCI0_E{(s|{-13{DC%(^Mwd1LU{+c5BJ!!^feFhfB z1DzcQ1}`Q^D4%DjwBgWsx7az7E#M&0v-Cj_fBp2JCg(CaH$VTQ+~D``H%VgB(l!uD zR`-Igkdu+Q>~90)UV?oJl588npP0=2#YHMy9hkgTjx(mO;Z*odH(h0nbBvDV!18_H z$tt}C7D%?T+EHmpw=gRzU}^tWGeUSkdb&R&T^?P*GZ? z6blI|X8p=yzU61=Fu{6q;N9OZ&jSyL*t9i$FIIJsTAeTcM$Z)|G`NuTCHYR~?a83% zi5@u^DoFKRA8?XgZQZ0l@nZXF_#=VVFE1br2i-Y_S_BJ0p>^YYFHm$?n~Rd_MB$Ud z4Bj94)4WPb8@PUe$bpExj4~21QA9+mdEao(xK=+lGdS1kXe8>#V+#QLt~HoewD$D; z4LN|_yLd8+U<}VYe_j;Pgj+vGgoh&vZ8!3}S#=ibhQi{0|KN8xn>}gwAdJLI+Lz~8 zwKGlpPg~Z4cEEIgK96nw2&NTQ-9OD@krqE3q0Cc1O6TpD8TCH@{P$QB=~9W0-d2Ts zQwHa~IT&;9YR8(Jc1cUj?mUN+tWNnp-kYBo4SwDsOqe7qQBzS(jE{qo$`9+s-87Ux zGX8VfDB@~x1#++uhK5m{;UwHuOf&?)x6&j3m(sd93AYbOEpww@3}VEv*a%W5nv!=w zIY5#l5EW(|G)wEn$mg8xpPpskYn$KO zN|$PH?w6Ea`0&=B+VaY(TgD%oL~>mmPM3tS%kqYb3g4cPRSmlu!B%?zK8v0NmN8QwCSm~kP*g(b@cB2dnH-PYe!z#m7qbZv!dGg z-Rv{Gup+@8t~X=!b8dC@3i9kCTKl|S56`Kp)_#)~PixKjIL_%2b*7~IvfSSV?iN22 zITxv~`WW>pS(F<;_9rM}IL+$Q^pNYHH{apyn^0 z#5Z`cVK=2Y52=k-B>c!`u5cb2)!`kKDdxw{i)6Gvpk*>m4ZWNDC2nIM-pA}px<2Qa z9x8iKzj%;Pz2ZI(Leku!>yyTDDJcpVEeWd^J}Tl$6})HDv$8S%4ag%~;K?MsE*WFNCI}9VF@L=xTxr1SC2y?8y#f>Wz_ou9T_7pGvYO%ic z<;$g6SqKw)j8r)I)qo%M>V~(BLPS4<*B*sitgS6x>>L(?X0F5H_HQ z8yT@xP8^a zF~$!S6aEpJwa1Shz1k1^m=11el*k)_y8-GTU3nd`1~eJCKEK&J zpk#s~=6y>`mgifrioa%O+mPL7Xo&QLWfXc%u{caXWWs-eyqgbao}wuxv9flR-$u;} zt#BHA>w^3Fb@y2b0#1Of;mvr=jjPp6f&v07tE;$VzyJ8bmtn5#=2nQ11EkY|$p!p^ z+VJ9@9?y@Jek;XrjH2@gQ-cf!KsS2X#l_L;S=N}k+}Ro1a0W9b&qA?+G8)y*XLza1 z%uZr}?Gc{kT*|Dc7l~_ zqnuu}!0D8zBGdW0)vDpdJ#`w{vy>aZTknqYpBN`h%W3OfTVSo$-`Y{4_Hm>{(1B^KiAn|h&Q-i zP3^eOp^LxF9*E=8nJDQ%{iIPwfywo%B?3!pTBuL3Y6x@`}2r~G9!G3mg!k{8Z;7?!(8q8*J%}8Rce9Z$;^rpLeH<`!~gboCE z7*azbnFt{r%#&l7sW#R|#=8NRSsv93wH{K?o?w}xJ>4dYVFkaSx4~9HUAT@I81x|t z82MK13~jhKKqi#+L}(4ZDxjqw?-D*vC!!{oVTSXZFqI;94~q@B5Izwy0T7C)j5siS z%7+gHgAI&~IzVaC(a~Yd>@$FA*u*IOyFPsyg4+h}PR(P-D5IF~_RM$Z%x!KxEz_fx94QrudN=kv~(qR)I_RbCSOMOdYY0pAZE zoC9c$AsvW?z*E6|tP6WXfgH?$Ge(36#!U2TYiX&6vp0;EbMX^#qWJbwQc`;Q`uL(# z61tC2se{)*(mtez$I(f?emx9~a-;)cUjV0o?7pUfRc zkU#|HlUrN+6J7=LB1}*s$fw10bPeJ_pkYM#SNtCcAqRRfEDX3~5QBtwJ-6dCf0=Lj zpc*(dz#sEVOOV@*B0sF$rMu&z>BjYzQOrI)jd`!oIeJ{Y*o~tB>$9r(w4>|7nj`Iv6mBdY6dZe}>}%`L0iwdd-_iSe=gV*?fnUsd#(L{q$$pJr6O z6Kq{qN^nm0kM$Y4D3}^JtJkyIW_wn+k|E}Cvad7cfG(X(%#rCikATIY>ho)Tf83Jf zju_r7`~9t?G-$%Ca$laCi_>Yk<->|;Ek71012o zjQQ+O@qPPmSG(6>^VKRi1khH6~(Z58F=c>&+E)yTJR9RN`N9oSe00TO)aKr8QX z%CnFq5ERE1@eG(Agq~vExJKRq1jO4}-V{Ybcoc*^xC;QS(2(K3hGgSn<=1lI&j{(m zM6%{TdOATmfcI`LwzH;STPzpW;bV11dp1q5578)(VsuNp&$t2Z}(`f&yU`_`)Ay zZQvfpl>zz=+1wD@;5Vj@BaZh} zgEvi#xLq!Z%s+)!PqOH8*j^=7 zrN-tI@wygfT_r_FkqZ8^J_(nv4nc-zyE*g@5g0Qh8oBs^L<*U>;DG_q&cN_ROWq z%_4EFqs)G(ds7TzneUxB7cVz8W;!O4Ps{yWtb#~_t{UXcfmX`g%^W^0%5${082vnpI>tgc6b?TuD zmx>a^?XURy0;s;P!%Dqa+lqA$4DyJ*Pt}P^pH}e$v4nV-kfP?QUOSY*%F*(fsp>a zWa3mgb_^XrL%TyMk=`1HXp!D~>C<2>P`%pPfvAHKt?A=KYv_<+AAV_iopk2uaV{<{ zq+$q|U$}UY+&YVcI8(}gStwf*$$dvt&q}bn5;vQm?%USZMoz)zO^(4fTQN-wX<4uF z4IRMh;i%|*Za`g5#J!`fq};5GvXv;M5A&8WXmv*aUX5xzKaeP0pPhC2=C4ouO8Yg%FYalPR+ALAT4&p%;NBj;=?a$j$q}<} z>3W)pH~8d&BU5je#?z#Tl1jA_1{Y{HMUZCecOO#=I{k<@Q>gWHM}X;a7hxIzKig>&ZGzmkP`j%j>jgL?PAMl$aKf_%wG4B^YeOx| z5&we^v$AlY%wzr&W`R3Jp_iNHgb67(BSV?#-6MI7&z~ zEzP5&$SS$8VPOg-T7U2UU;|)&wVQ8UaZuS;SW$5t1~!Xq8O0gE)LeSO*(jL&CjY=i zT%Kcw?AwsnFJr8gstR*7KRqJN=1Vq}#2q*^WR69kxTux=alGK$1*c}r`9z+xCgJU@ zuZLp6&y_as(*?eQZ?E+&V-Q?fE=NgBpShBgN2^~Ve0Wu0UHYv>+?}z5=Gs&B@8&1e zJ%8CWyyr+wDsv0*jkPOFIbrcJm}C_7$Nn8N?ZeQW+Q0l42MXdw`Q=5q4OOmE`pGfx zz5Ve(XG8LpS?VN-H5a4NGgjYSj>{a`Z)quKo!sZatjcn%$kV(-MzxajSHG(i~0Sbyy!YOo6>`fxbM=QU+^{*M+QHT5Lk zGuTK}7fs7J9Wf+2d+IH&1PvMm|5X#{@Zh{?#c)ILi?3bL2>5AcwdXSQUPwBUTpU?L z+kWp7Mb=%(gCcqJ>tRQJcEP5b%6zHcFe^V%F zXpDAGkVq<=i#1bM61nM=0;@NQm*14sTvOlYWM%99P6|oX&Hvh$nTw(0ze7t(h0|NzZoARcsDw zKDv#*Jz~!l;Ic2IwoB#0Whmci`G-TU&&6R(qPGRFPOQ|AAHqj5RI} z9Bwe$qOge!0|AB->*i~IkT)Q;C&Io1zQ`prFrGrv2r)xuc=#wMski{SsvyOKc)-D+ z24Lir3nn=w^%9A?l9I3w#k3o0yxzkb3?&k7h9e9Uxu-i&^Um&Nr6J=0c=s+)ZjInA z&}TV+ehkra#ZgYicWoiQmnYs5%?DmRJjXz&bab+7IRl_70?OrW39f=fDJwxB_ZVG) z4?nFhz<=H*Ir28*ZTRpSdUR1*tt7lWHS2x2*a+Vp+U}zd6Cj7%q@*P5&K(d0FT$X6>~8lF zA<>|P1B1~8e#wu8R@S3V0Hjh!hmre*c6eSz*F|3HHEWAev*iUhoKx^I{-)=s)~)u& z!d=JkOZPGvWVv?!oQxHTHT}!3vI`Mu7^e zphgWp#cy;po5J}b-7=iTPxOsX@eq*vwqJQ$HaJ1B7v<%l-^_&f6LTkRZAb8g<=DHX zp=QQzK(IWaNrl@QuT368$>28p9)ShW70Cho!S=zihTit@L^ECkEENxR5{hxWqx{0S zAq5GEUr;3L2*&g@#Q;>2yNp;QH*9m2lXu*loiTIdGkgrludRLbXaaQ~77p6o0yA;A z4Ps-h%AAN)Avj%7QNYZ7+}s>xy{)t$ z$3>mJyt)dQ87m7&mMydi-t}=Xjv_b_cR|fPKqCldLm7(^i%<`vvqrujVut39aQZ1K zNlJPE??t-|cJDiImG+RU#E>Y|#x}UCu^+|6#R>b5k&zex0J#)ky8Qg-)8I2u1V4-W z+xGnaXe_7V;>3feU!n1|fHWV*Jam?5<0H?j;J>KQ09S4Kmrc0{A(pK)5xfk=nJ;FM zVBy+{C4f=|b?j|oRI`FAozQ0byC7rtHY#p8Sy>`6_5jOIl&M4}9r^(w4#$7Ntyt_+ zjR*gb$%nvmLIeW*0jDw;;Z(fkzJRMLg+cHf5$5uyNHQj)@57`5poW-CQ+=y#{4z>s zT#kc6nZ&`D(+EtuO6Fj*xfn8d(gN3g5GRH!8_d<0GUqsy@w4lZHrJh%rpj2;G(&Gy z7+iUO>sDJ>KKrn^mL-Ez$L$*aOP^bxGxlo^&r(XM@7vuVDKu?gVd0cCPSgA7UX*{C z@4nmj%uN1ynt0;Or%>aFWtaM_IUje*m+52?mBYam5^`zUJBo%J&1M;nM{lFafG-%I z{#}YZ>-H*yh!1K|0GhS#P8pyrr^`M2(icAH^iXmM1Y&frod}~cyj}M zpr82?m~#Bk6m<=5c+miXbaiz2ZJy(ePXUxk#iB<;g6oXXu^f^*hWs3$Wc|hGv^833 znN-l9;~LUMEGmFX^lrc$fRB23Sb7ye833WWJCqu*KY-f6fW9RlAHY!w@W&BGj4vG> z?r2l+3;q#5#kAGQ{TN4vb_AEjzmN=!I1oc$LiLDSXKgq_X|{Tiq=MUukNe|ki2DdC z03Zf>M?@#%Op|y1e4xewcP%(t@WdaX55f|E=GqIe@J3n#fWBeas|a|j@h+qX-$0un zHYET?2;_{`__HH^1%pSC81<}oO5<1qfF?IwI_T#tDlVKx#p~j7;6xt)Pe`;-BctlY zDuWBkR_F-T^*KCEAfxyCa=qLqZ^Dstw5gIbd*GNDM^ z++anBlngM~P;cXCy1Q>0tR}h{*x*x?0s|m8K;ub3NEm2=@_VeBPy7>KA2Rx$5`r zZ=BktkVKc1)r;G!!XU!Kfu{e4Dv5+WVc*XnBt{`t+dgTI|qUe_e=@;|&Sx79% zZVB7^f#J+&ofBKs)XoJP;O0Sb*u7^5?$wB^>b1(tS%Y_X+b+@jv8utKM%sMsYVuCM zwns-9DF*A1U`9;U!&4r4!HhMC+7==~S5^()m5e9dlc=2a*_uWgpPqUdgopSrJKN7r z4*JOM?rtoD*DO5N}8Bk#jpX|1dmoY^ML&xHSAD%v_H8``2qe}X^rz+mV1&%eJHzl-lk z)VUwbW^ANl6ulOMwEwF`{Qpw-(=ECIfJ=WjSr9X$TR1tlO@MSzqNK!pQPapH*1yR~#`Cb7Kcq8H~?#kt;bF+nBonhKf+}w8kdl%)g zeJCmhBbuRWVAr0q=cqU#fB@U$`m6)2saxOi)uW3RvP_f+--DoID)3KPP%oR}aPgw} z!~NSOo`c0Kq*6?S=f0_R-FktJ8#Aw#oW!IP1J5KU8QcmFQvvc+r1l+b`;Xp~bLBon zy8hpn8)@9p4oOp7%D=CPFDUi@`=f^i`M(+j;(z`B=&vRABkYAPXt~tZlUT`OQjz)c zkwb?h6I&tIEN_}EiU5K^7xWEo+V{uzb94eMp$l>VVt&1$Q7!W=G2DIZGh`cJ!t1&k zzJz3#mAOG|10_3vG0G3OF%CB9GIZj-!VTRKES#KjiAeKvw6en3`?XZ*vWlUWNWq%k z-nC!J+XD#IY6}niiwX{-B=8OZ-E|U?btB(*jjE*_l~qJ*KAj6bbWgKaf!};#wd{%dC z^@oE)io476lNxPIRztVBU&y%Rh&}o4a<5^mBc#_rqqd#H`EHq#o0~ezfG7mFiOI<@ zt{UKDf$2Ao@bmSBmIx}~iRhod$R@r;ZW~~$!U+Z0>~>iXSTE2_5#iyRWj!mWE3(>y>lj2Gh?!bAsfZ}ght7jG;hrsa zh~-8fff$kB(>=;el$d(Kb^5f+oPvF4SMi=*U(~M%$WTi0+;LMfTAlQtvuXHsu*H_L ztaB!8=ehF%ZflQqgal7;WSP)y*IeXyF>!L8fo}ivx%6mWiKfWhk*zuz#TR?zVlB#6 zl^#FyYka;P$?DU6XRGtGm@}2iwcT~uaSP24F8t>23`=Q_Z!h208PbebEhu}Oo8k0# zi+Ke)Bu~bh*z0g|!E(_%8#E1P09v4;`*%7r1-I*vmIm2C`TES8;NWI_@^VWC@fY)^ zL$XhwKNkRr0av?g^o_LFum3^Ef%`LXkzMCH(KUg&O^V2}MhM>=W;UP{kjh_^%o3Q7 zgq@tn7xM^A*qE$j^Hvs&&69KnnK_!4mOXq-8gE45uCKmW*5e>? z<%V!-L~qMD-GC@f>}%D(EM5HrqM`#k&X$u5Zi_O6{N1gLCadMAdj(0kC!gj{^MzFx z&wu7s92c6bJ~DQVPMSgpy4kNW!Rf^$9>33;3(E*(0r;{_7LBQdZ0k0+8L}CPOVZNa zjNiBA2iW5p`=t*shiR&A^ElR1QC6xsY2HeFd;02SJ5pxrGhduEcTr@g|2b8;GF31} z|H`9b+p{@NQT|WgAiAQ3OQWN<|J>N?CIV1cO-V_L6jLgDD~5D}xK>=mgDxXvw-ep& z{j+ow3~0;{>yEP-2ku$x;m^>Y+HB$oc!D`?C|M8$P+2()bIEAJ|AiRtI04MDv91oj zrY~ThAq^bez&Q?YBOUrkd3oU6O3wE%Z4`e%QmiA48SZ{f0@9edC5lK zJA>ZM!O98=y3qHYz_d~b!ljHsudcK{jfwdVJB2SzEqsr#CH0sIy+WV}Rs$z4BpSJY zQt;Fvy$~=UAoFskg`F*q&_JLafSbCR);=`#@QwdsTd_a7AMM#Ag)30n{OoIqQ zF8Vlmh_Z7$bsQhJ1HXrH_6C`2K9bIt1)h`lTxjt^Z%(Uz{KX-WOOOj zI^rmN(82#YJ$lqe8ir>?!KMQQMPr6ff%_xMi5!=wC|{xFKnISDsC4J+vOh1O_=b<| z$noRyiLE%UQh&7hs0BS8z})y^#2o--A*v{fi!p)0E=&izu9aJe@{)4WZ2zN9|;Rle|>?M1FCxAi3@7m9Ng$tlC`w;kIpI{%#aXGm7Q zP#M%~^LPG-uxT^{Eu%-s-I6WZ3xJczCmEW3VRJ)KYy_hauJGk* z?2WXhLmbQL+2z?5Qlw*QXSpvdf7WZV?+fpG5%Z)a+j!#Ps`9COYECnt4hGX2zG!l_ zoKh9u+C1RCW&LLLxqQbtcC(OP+85u$l}e*tin=+6{aNIKHsfRoS0Y>6?3u;g(&iGX)AwZlD*w3(z?1kAKmFhTLWz&B z5cmzK8oUsk@n332eLV|dq7ee@ul^EdV18JAhzb{^6r1Eh1pp5)fMSgDr6PF-+5KE9 zVuc<+#DEgSAG!@5pHfXY$a#uc+Q;*XCmc|05`0IR1*L`s10BOMSpP;z89uPXcbXA zL$iuE!>D!i%bm#LojH?VzX9}xQVPout|lVT0?EK3DI(6p#fPEo)ps#agos_CuSsk* z1~_6G{pP7Y!Y+{gO{`SV;G$naiWZcy$mo9J*rdx34Iug%M;tC3%5Rv6R)v7}JFv@o zSIqzYD}h)QNRcR|U~i??-iZ^28rpUmnmCNg#SA&)BZm*8N^qeI5`s3Y0?^#FnYE7%ess<*p91VlzT;Bd)!^Ty%71fPTOb6rjZ!jJDOCWf1h&B($6 zqLT^;u%OxEIDHx-vRt|HnLJ&HQxX#ebw7E4>isVj7YhZA$538-Jwz~vq<|HR0i%L} z_5RtY@^T4DO60Bb3k$QWY37-Pmz21G^x4w%fa(m@9t5HJm`TAFdLB+)FD9H+SPkAp zY!`A68f_+=N;sggwK0>jfCi}0#c`J3q%9-ki@$~e02@-s2~i_+^CklUp!osHF9q*` zmsUh|0n?y;y6X9g2j5y-BLxiWvD08RaP#ttLyoD~5~qrGvr3mA;DzJb@B1!SQD6